'''Noddy history file wrapper
Created on 24/03/2014
@author: Florian Wellmann
'''
import time # for header in model generation
import numpy as np
# import numpy as np
# import matplotlib.pyplot as plt
import events
[docs]class NoddyHistory(object):
"""Class container for Noddy history files"""
def __init__(self, history=None, **kwds):
"""Methods to analyse and change Noddy history files
**Arguments**:
- *history* = string : Name of Noddy history file
**Optional Keywords**:
- *url* = url : link to history file on web (e.g. to download
and open directly from Atlas of Structural Geophysics,
http://virtualexplorer.com.au/special/noddyatlas/index.html
- *verbose* = True if this function should print output to the printstream. Default is False.
Note: if both a (local) history is given and a URL, the local
file is opened!
"""
vb = kwds.get('verbose',False)
if history is None:
if kwds.has_key("url"):
self.load_history_from_url(kwds['url'])
self.determine_events(verbose = vb)
else:
# generate a new history
self.create_new_history()
else:
# load existing history
self.load_history(history)
self.determine_events(verbose = vb)
def __repr__(self):
"""Print out model information"""
return self.get_info_string()
[docs] def info(self, **kwds):
"""Print out model information
**Optional keywords**:
- *events_only* = bool : only information on events
"""
print self.get_info_string(**kwds)
[docs] def get_info_string(self, **kwds):
"""Get model information as string
**Optional keywords**:
- *events_only* = bool : only information on events
"""
events_only = kwds.get("events_only", False)
local_os = ""
if not events_only:
# First: check if all information available
if not hasattr(self, 'extent_x'): self.get_extent()
if not hasattr(self, 'origin_x'): self.get_origin()
if not hasattr(self, 'cube_size'): self.get_cube_size()
if not hasattr(self, 'filename'): self.get_filename()
if not hasattr(self, 'date_saved'): self.get_date_saved()
local_os +=(60 * "*" + "\n\t\t\tModel Information\n" + 60 * "*")
local_os +=("\n\n")
if self.n_events == 0:
local_os +=("The model does not yet contain any events\n")
else:
local_os +=("This model consists of %d events:\n" % self.n_events)
for k,ev in self.events.items():
local_os +=("\t(%d) - %s\n" % (k,ev.event_type))
if not events_only:
local_os +=("The model extent is:\n")
local_os +=("\tx - %.1f m\n" % self.extent_x)
local_os +=("\ty - %.1f m\n" % self.extent_y)
local_os +=("\tz - %.1f m\n" % self.extent_z)
local_os +=("Number of cells in each direction:\n")
local_os +=("\tnx = %d\n" % (self.extent_x / self.cube_size))
local_os +=("\tny = %d\n" % (self.extent_y / self.cube_size))
local_os +=("\tnz = %d\n" % (self.extent_z / self.cube_size))
local_os +=("The model origin is located at: \n\t(%.1f, %.1f, %.1f)\n" % (self.origin_x,
self.origin_y,
self.origin_z))
local_os +=("The cubesize for model export is: \n\t%d m\n" % self.cube_size)
# and now some metadata
local_os +=("\n\n")
local_os +=(60 * "*" + "\n\t\t\tMeta Data\n" + 60 * "*")
local_os +=("\n\n")
local_os +=("The filename of the model is:\n\t%s\n" % self.filename)
local_os +=("It was last saved (if origin was a history file!) at:\n\t%s\n" % self.date_saved)
return local_os
[docs] def get_origin(self):
"""Get coordinates of model origin and return and store in local variables
**Returns**: (origin_x, origin_y, origin_z)
"""
# check if footer_lines exist (e.g. read in from file)
# if not: create from template
if not hasattr(self, "footer_lines"):
self.create_footer_from_template()
for i,line in enumerate(self.footer_lines):
if "Origin X" in line:
self.origin_x = float(self.footer_lines[i].split("=")[1])
self.origin_y = float(self.footer_lines[i+1].split("=")[1])
self.origin_z = float(self.footer_lines[i+2].split("=")[1])
break
return(self.origin_x, self.origin_y, self.origin_z)
[docs] def set_origin(self, origin_x, origin_y, origin_z):
"""Set coordinates of model origin and update local variables
**Arguments**:
- *origin_x* = float : x-location of model origin
- *origin_y* = float : y-location of model origin
- *origin_z* = float : z-location of model origin
"""
# check if footer_lines exist (e.g. read in from file)
# if not: create from template
if not hasattr(self, "footer_lines"):
self.create_footer_from_template()
self.origin_x = origin_x
self.origin_y = origin_y
self.origin_z = origin_z
origin_x_line = " Origin X = %.2f\n" % origin_x
origin_y_line = " Origin Y = %.2f\n" % origin_y
origin_z_line = " Origin Z = %.2f\n" % origin_z
for i,line in enumerate(self.footer_lines):
if "Origin X" in line:
self.footer_lines[i] = origin_x_line
self.footer_lines[i+1] = origin_y_line
self.footer_lines[i+2] = origin_z_line
break
[docs] def get_extent(self):
"""Get model extent and return and store in local variables
**Returns**: (extent_x, extent_y, extent_z)
"""
# check if footer_lines exist (e.g. read in from file)
# if not: create from template
if not hasattr(self, "footer_lines"):
self.create_footer_from_template()
for i,line in enumerate(self.footer_lines):
if "Length X" in line:
self.extent_x = float(self.footer_lines[i].split("=")[1])
self.extent_y = float(self.footer_lines[i+1].split("=")[1])
self.extent_z = float(self.footer_lines[i+2].split("=")[1])
break
return(self.extent_x, self.extent_y, self.extent_z)
[docs] def set_extent(self, extent_x, extent_y, extent_z):
"""Set model extent and update local variables
**Arguments**:
- *extent_x* = float : extent in x-direction
- *extent_y* = float : extent in y-direction
- *extent_z* = float : extent in z-direction
"""
# check if footer_lines exist (e.g. read in from file)
# if not: create from template
if not hasattr(self, "footer_lines"):
self.create_footer_from_template()
self.extent_x = extent_x
self.extent_y = extent_y
self.extent_z = extent_z
extent_x_line = " Length X = %.2f\n" % extent_x
extent_y_line = " Length Y = %.2f\n" % extent_y
extent_z_line = " Length Z = %.2f\n" % extent_z
for i,line in enumerate(self.footer_lines):
if "Length X" in line:
self.footer_lines[i] = extent_x_line
self.footer_lines[i+1] = extent_y_line
self.footer_lines[i+2] = extent_z_line
break
[docs] def get_drillhole_data(self, x, y, **kwds):
"""Get geology values along 1-D profile at position x,y with a 1 m resolution
The following steps are performed:
1. creates a copy of the entire object,
2. sets values of origin, extent and geology cube size,
3. saves model to a temporary file,
4. runs Noddy on that file
5. opens and analyses output
6. deletes temporary files
Note: this method only works if write access to current directory
is enabled and noddy can be executed!
**Arguments**:
- *x* = float: x-position of drillhole
- *y* = float: y-position of drillhole
**Optional Arguments**:
- *z_min* = float : minimum depth of drillhole (default: model range)
- *z_max* = float : maximum depth of drillhole (default: model range)
- *resolution* = float : resolution along profile (default: 1 m)
"""
# resolve keywords
resolution = kwds.get("resolution", 1)
self.get_extent()
self.get_origin()
z_min = kwds.get("z_min", self.origin_z)
z_max = kwds.get("z_max", self.extent_z)
# 1. create copy
import copy
tmp_his = copy.deepcopy(self)
tmp_his.write_history("test.his")
# 2. set values
tmp_his.set_origin(x, y, z_min)
tmp_his.set_extent(resolution, resolution, z_max)
tmp_his.change_cube_size(resolution)
# 3. save temporary file
tmp_his_file = "tmp_1D_drillhole.his"
tmp_his.write_history(tmp_his_file)
tmp_out_file = "tmp_1d_out"
# 4. run noddy
import pynoddy
import pynoddy.output
pynoddy.compute_model(tmp_his_file, tmp_out_file)
# 5. open output
tmp_out = pynoddy.output.NoddyOutput(tmp_out_file)
# 6.
return tmp_out.block[0,0,:]
[docs] def load_history(self, history):
"""Load Noddy history
**Arguments**:
- *history* = string : Name of Noddy history file
"""
self.history_lines = open(history, 'r').readlines()
# set flag for model loaded from file
self._from_file = True
# get footer lines
self.get_footer_lines()
[docs] def load_history_from_url(self, url):
"""Directly load a Noddy history from a URL
This method is useful to load a model from the Structural Geophysics
Atlas on the pages of the Virtual Explorer.
See: http://tectonique.net/asg
**Arguments**:
- *url* : url of history file
"""
import urllib2
response = urllib2.urlopen(url)
tmp_lines = response.read().split("\n")
self.history_lines = []
for line in tmp_lines:
# append EOL again for consistency
self.history_lines.append(line + "\n")
# set flag for model loaded from URL
self._from_url = True
# get footer lines
self.get_footer_lines()
[docs] def determine_model_stratigraphy(self):
"""Determine stratigraphy of entire model from all events"""
self.model_stratigraphy = []
for e in np.sort(self.events.keys()):
if self.events[e].event_type == 'STRATIGRAPHY':
self.model_stratigraphy += self.events[e].layer_names
if self.events[e].event_type == 'UNCONFORMITY':
self.model_stratigraphy += self.events[e].layer_names
if self.events[e].event_type == 'DYKE':
self.model_stratigraphy += self.events[e].name
if self.events[e].event_type == 'PLUG':
self.model_stratigraphy += self.events[e].name
[docs] def determine_events(self, **kwds):
"""Determine events and save line numbers
.. note:: Parsing of the history file is based on a fixed Noddy output order.
If this is, for some reason (e.g. in a changed version of Noddy) not the case, then
this parsing might fail!
**Optional Keywords**:
- verbose = True if this function is should write to the print bufffer, otherwise False. Default is False.
"""
vb = kwds.get('verbose',False)
self._raw_events = []
for i,line in enumerate(self.history_lines):
if "No of Events" in line:
self.n_events = int(line.split("=")[1])
elif "Event #" in line:
event = {'type': line.split('=')[1].rstrip(), 'num': int(line[7:9]), 'line_start': i}
self._raw_events.append(event)
# finally: if the definition for BlockOptions starts, the event definition is over
elif "BlockOptions" in line:
last_event_stop = i-2
# now: find the line ends for the single event blocks
for i,event in enumerate(self._raw_events[1:]):
self._raw_events[i]['line_end'] = event['line_start']-1
# now adjust for last event
self._raw_events[-1]['line_end'] = last_event_stop
self.events = {} # idea: create events as dictionary so that it is easier
# to swap order later!
# now create proper event objects for these events
if vb:
print "Loaded model with the following events:"
for e in self._raw_events:
event_lines = self.history_lines[e['line_start']:e['line_end']+1]
if vb:
print e['type']
if 'FAULT' in e['type']:
ev = events.Fault(lines = event_lines)
elif 'SHEAR_ZONE' in e['type']:
ev = events.Shear(lines = event_lines)
elif 'FOLD' in e['type']:
ev = events.Fold(lines = event_lines)
elif 'UNCONFORMITY' in e['type']:
ev = events.Unconformity(lines = event_lines)
elif 'STRATIGRAPHY' in e['type']:
ev = events.Stratigraphy(lines = event_lines)
elif 'TILT' in e['type']: # AK
ev = events.Tilt(lines = event_lines)
elif 'DYKE' in e['type']:
ev = events.Dyke(lines = event_lines)
elif 'PLUG' in e['type']:
ev = events.Plug(lines = event_lines)
elif 'STRAIN' in e['type']:
ev = events.Strain(lines = event_lines)
else:
print "Warning: event of type %s has not been implemented in PyNoddy yet" % e['type']
continue
# now set shared attributes (those defined in superclass Event)
order = e['num'] #retrieve event number
self.events[order] = ev #store events sequentially
# determine overall begin and end of the history events
self.all_events_begin = self._raw_events[0]['line_start']
self.all_events_end = self._raw_events[-1]['line_end']
[docs] def copy_events(self):
"""Create a copy of the current event state"""
import copy
return copy.deepcopy(self.events)
[docs] def get_cube_size(self, **kwds):
"""Determine cube size for model export
**Optional Args**
-type: choose geology or geophysics cube size to return. Should be either 'Geology' (default) or 'Geophysics'
"""
#get args
sim_type = kwds.get("type", 'Geophysics') #everything seems to use this
cube_string = 'Geophysics Cube Size' #get geology cube size by default
if ('Geology' in sim_type):
cube_string = 'Geology Cube Size' #instead get geology cube size
print "Warning: pynoddy uses the geophysics cube size for all calculations... changing the geology cube size will have no effect internally."
# check if footer exists, if not: create from template
if not hasattr(self, "footer_lines"):
self.create_footer_from_template()
for line in self.footer_lines:
if cube_string in line:
self.cube_size = float(line.split('=')[1].rstrip())
return self.cube_size
[docs] def get_filename(self):
"""Determine model filename from history file/ header"""
self.filename = self.history_lines[0].split('=')[1].rstrip()
[docs] def get_date_saved(self):
"""Determine the last savepoint of the file"""
self.date_saved = self.history_lines[1].split('=')[1].rstrip()
[docs] def change_cube_size(self, cube_size, **kwds):
"""Change the model cube size (isotropic)
**Arguments**:
- *cube_size* = float : new model cube size
"""
# check if footer_lines exist (e.g. read in from file)
# if not: create from template
if not hasattr(self, "footer_lines"):
self.create_footer_from_template()
# lines_new = self.history_lines[:]
for i,line in enumerate(self.footer_lines):
if "Geophysics Cube Size" in line: #correct line, make change
l = line.split('=')
l_new = '%7.2f\r\n' % cube_size
line_new = l[0] + "=" + l_new
self.footer_lines[i] = line_new
if "Geology Cube Size" in line: #change geology cube size also
l = line.split('=')
l_new = '%7.2f\r\n' % cube_size
line_new = l[0] + "=" + l_new
self.footer_lines[i] = line_new
# assign changed lines back to object
# self.history_lines = lines_new[:]
[docs] def swap_events(self, event_num_1, event_num_2):
"""Swap two geological events in the timeline
**Arguments**:
- *event_num_1/2* = int : number of events to be swapped ("order")
"""
# events have to be copied, otherwise only a reference is passed!
event_tmp = self.events[event_num_1]
self.events[event_num_1] = self.events[event_num_2]
self.events[event_num_2] = event_tmp
self.update_event_numbers()
[docs] def reorder_events(self, reorder_dict):
"""Reorder events accoring to assignment in reorder_dict
**Arguments**:
- *reorder_dict* = dict : for example {1 : 2, 2 : 3, 3 : 1}
"""
tmp_events = self.events.copy()
for key, value in reorder_dict.items():
try:
tmp_events[value] = self.events[key]
except KeyError:
print("Event with id %d is not defined, please check!" % value)
self.events = tmp_events.copy()
self.update_event_numbers()
[docs] def update_event_numbers(self):
"""Update event numbers in 'Event #' line in noddy history file"""
for key, event in self.events.items():
event.set_event_number(key)
[docs] def update_all_event_properties(self):
"""Update properties of all events - in case changes were made"""
for event in self.events.values():
event.update_properties()
#
#class NewHistory():
# """Methods to create a Noddy model"""
#
[docs] def create_new_history(self):
"""Methods to create a Noddy model
"""
# set event counter
self.event_counter = 0
self.all_events_begin = 7 # default after header
self.all_events_end = 7
# initialise history lines
self.history_lines = []
self.events = {}
[docs] def get_ev_counter(self):
"""Event counter for implicit and continuous definition of events"""
self.event_counter += 1
return self.event_counter
[docs] def add_event(self, event_type, event_options, **kwds):
"""Add an event type to history
**Arguments**:
- *event_type* = string : type of event, legal options to date are:
'stratigraphy', 'fault', 'fold', 'unconformity'
- *event_options* = list : required options to create event (event dependent)
**Optional keywords**:
- *event_num* = int : event number (default: implicitly defined with increasing counter)
"""
event_num = kwds.get("event_num", self.get_ev_counter())
if event_type == 'stratigraphy':
ev = self._create_stratigraphy(event_options)
ev.event_type = 'STRATIGRAPHY'
elif event_type == 'fault':
ev = self._create_fault(event_options)
ev.event_type = 'FAULT'
elif event_type == 'tilt': # AK
ev = self._create_tilt(event_options)
ev.event_type = 'TILT'
elif event_type == 'unconformity': # AK
ev = self._create_unconformity(event_options)
ev.event_type = 'UNCONFORMITY'
else:
raise NameError('Event type %s not (yet) implemented' % event_type)
ev.set_event_number(event_num)
self.events[event_num] = ev
# update beginning and ending of events in history
self.all_events_end = self.all_events_end + len(ev.event_lines)
# add event to history lines, as well (for consistency with other methods)
self.history_lines[:self.all_events_begin] + \
ev.event_lines + \
self.history_lines[self.all_events_end:]
def _create_header(self):
"""Create model header, include actual date"""
t = time.localtime() # get current time
time_string = "%d/%d/%d %d:%d:%d" % (t.tm_mday,
t.tm_mon,
t.tm_year,
t.tm_hour,
t.tm_min,
t.tm_sec)
self.header_lines = """#Filename = """ + self.filename + """
#Date Saved = """ + time_string + """
FileType = 111
Version = 7.11
"""
@staticmethod
def _create_stratigraphy(event_options):
"""Create a stratigraphy event
**Arguments**:
- *event_options* = list : list of required and optional settings for event
Options are:
'num_layers' = int : number of layers (required)
'layer_names' = list of strings : names for layers (default names otherwise)
'layer_thickness' = list of floats : thicknesses for all layers
"""
ev = events.Stratigraphy()
tmp_lines = [""]
tmp_lines.append("\tNum Layers\t= %d" % event_options['num_layers'])
for i in range(event_options['num_layers']):
"""Add stratigraphy layers"""
layer_name = event_options['layer_names'][i]
cum_thickness = np.cumsum(event_options['layer_thickness'])
layer_lines = _Templates().strati_layer
# now replace required variables
layer_lines = layer_lines.replace("$NAME$", layer_name)
layer_lines = layer_lines.replace("$HEIGHT$", "%.1f" % cum_thickness[i])
layer_lines = layer_lines.replace(" ", "\t")
# split lines and add to event lines list:
for layer_line in layer_lines.split("\n"):
tmp_lines.append(layer_line)
# append event name
tmp_lines.append("""\tName\t= Strat
""")
# event lines are defined in list:
tmp_lines_list = []
for line in tmp_lines:
tmp_lines_list.append(line + "\n")
ev.set_event_lines(tmp_lines_list)
ev.num_layers = event_options['num_layers']
return ev
def _create_fault(self, event_options):
"""Create a fault event
**Arguments**:
- *event_options* = list : list of required and optional settings for event;
Options are:
'name' = string : name of fault event
'pos' = (x,y,z) : position of reference point (floats)
.. note:: for convenience, it is possible to assign 'top' to z
for position at "surface"
'dip_dir' = [0,360] : dip direction of fault
'dip' = [0,90] : dip angle of fault
'slip' = float : slip along fault
"""
ev = events.Fault()
tmp_lines = [""]
fault_lines = _Templates.fault
# substitute text with according values
fault_lines = fault_lines.replace("$NAME$", event_options['name'])
fault_lines = fault_lines.replace("$POS_X$", "%.1f" % event_options['pos'][0])
fault_lines = fault_lines.replace("$POS_Y$", "%.1f" % event_options['pos'][1])
if event_options['pos'] == 'top':
# recalculate z-value to be at top of model
z = self.zmax
fault_lines = fault_lines.replace("$POS_Z$", "%.1f" % z)
else:
fault_lines = fault_lines.replace("$POS_Z$", "%.1f" % event_options['pos'][2])
fault_lines = fault_lines.replace("$DIP_DIR$", "%.1f" % event_options['dip_dir'])
fault_lines = fault_lines.replace("$DIP$", "%.1f" % event_options['dip'])
fault_lines = fault_lines.replace("$SLIP$", "%.1f" % event_options['slip'])
# now split lines and add as list entries to event lines
# event lines are defined in list:
# split lines and add to event lines list:
for layer_line in fault_lines.split("\n"):
tmp_lines.append(layer_line)
tmp_lines_list = []
for line in tmp_lines:
tmp_lines_list.append(line + "\n")
ev.set_event_lines(tmp_lines_list)
return ev
# AK 2014-10
def _create_tilt(self, event_options):
"""Create a tilt event
**Arguments**:
- *event_options* = list : list of required and optional settings for event;
Options are:
'name' = string : name of fault event
'pos' = (x,y,z) : position of reference point (floats)
.. note:: for convenience, it is possible to assign 'top' to z
for position at "surface"
'rotation' = [0,360] : dip?
'plunge_direction' = [0,360] : strike of plunge, measured from x axis
'plunge' = float : ?
"""
ev = events.Tilt()
tmp_lines = [""]
tilt_lines = _Templates.tilt
# substitute text with according values
tilt_lines = tilt_lines.replace("$NAME$", event_options['name'])
tilt_lines = tilt_lines.replace("$POS_X$", "%.1f" % event_options['pos'][0])
tilt_lines = tilt_lines.replace("$POS_Y$", "%.1f" % event_options['pos'][1])
if event_options['pos'] == 'top':
# recalculate z-value to be at top of model
z = self.zmax
tilt_lines = tilt_lines.replace("$POS_Z$", "%.1f" % z)
else:
tilt_lines = tilt_lines.replace("$POS_Z$", "%.1f" % event_options['pos'][2])
tilt_lines = tilt_lines.replace("$ROTATION$", "%.1f" % event_options['rotation'])
tilt_lines = tilt_lines.replace("$PLUNGE_DIRECTION$", "%.1f" % event_options['plunge_direction'])
tilt_lines = tilt_lines.replace("$PLUNGE$", "%.1f" % event_options['plunge'])
# now split lines and add as list entries to event lines
# event lines are defined in list:
# split lines and add to event lines list:
for tilt_line in tilt_lines.split("\n"):
tmp_lines.append(tilt_line)
tmp_lines_list = []
for line in tmp_lines:
tmp_lines_list.append(line + "\n")
ev.set_event_lines(tmp_lines_list)
return ev
# AK 2014-10
def _create_unconformity(self, event_options):
"""Create a unconformity event
**Arguments**:
- *event_options* = list : list of required and optional settings for event;
Options are:
'name' = string : name of unconformity event
'pos' = (x,y,z) : position of reference point (floats)
.. note:: for convenience, it is possible to assign 'top' to z
for position at "surface"
'rotation' = [0,360] : dip?
'plunge_direction' = [0,360] : strike of plunge, measured from x axis
'plunge' = float : ?
"""
ev = events.Unconformity()
tmp_lines = [""]
unconformity_lines = _Templates.unconformity
# substitute text with according values
unconformity_lines = unconformity_lines.replace("$NAME$", event_options['name'])
unconformity_lines = unconformity_lines.replace("$POS_X$", "%.1f" % event_options['pos'][0])
unconformity_lines = unconformity_lines.replace("$POS_Y$", "%.1f" % event_options['pos'][1])
if event_options['pos'] == 'top':
# recalculate z-value to be at top of model
z = self.zmax
unconformity_lines = unconformity_lines.replace("$POS_Z$", "%.1f" % z)
else:
unconformity_lines = unconformity_lines.replace("$POS_Z$", "%.1f" % event_options['pos'][2])
unconformity_lines = unconformity_lines.replace("$DIP_DIRECTION$", "%.1f" % event_options['dip_direction'])
unconformity_lines = unconformity_lines.replace("$DIP$", "%.1f" % event_options['dip'])
# split lines and add to event lines list:
for unconformity_line in unconformity_lines.split("\n"):
tmp_lines.append(unconformity_line)
# unconformity has a stratigraphy block
tmp_lines.append("\tNum Layers\t= %d" % event_options['num_layers'])
for i in range(event_options['num_layers']):
"""Add stratigraphy layers"""
layer_name = event_options['layer_names'][i]
cum_thickness = np.cumsum(event_options['layer_thickness'])
layer_lines = _Templates().strati_layer
# now replace required variables
layer_lines = layer_lines.replace("$NAME$", layer_name)
layer_lines = layer_lines.replace("$HEIGHT$", "%.1f" % cum_thickness[i])
layer_lines = layer_lines.replace(" ", "\t")
# split lines and add to event lines list:
for layer_line in layer_lines.split("\n"):
tmp_lines.append(layer_line)
# append event name
tmp_lines.append("""\tName\t= %s""" % event_options.get('name','Unconf'))
tmp_lines_list = []
for line in tmp_lines:
tmp_lines_list.append(line + "\n")
ev.set_event_lines(tmp_lines_list)
return ev
[docs] def set_event_params(self, params_dict):
"""set multiple event parameters according to settings in params_dict
**Arguments**:
- *params_dict* = dictionary : entries to set (multiple) parameters
"""
for key,sub_dict in params_dict.items():
for sub_key, val in sub_dict.items():
self.events[key].properties[sub_key] = val
[docs] def change_event_params(self, changes_dict):
"""Change multiple event parameters according to settings in changes_dict
**Arguments**:
- *changes_dict* = dictionary : entries define relative changes for (multiple) parameters
Per default, the values in the dictionary are added to the event parameters.
"""
#print changes_dict
for key,sub_dict in changes_dict.items(): #loop through events (key)
for sub_key, val in sub_dict.items(): #loop through parameters being changed (sub_key)
self.events[key].properties[sub_key] += val
[docs] def get_event_params( self, event_number ):
'''
Returns the parameter dictionary for a given event.
**Arguments**:
- *event_number* = the event to get a parameter for (integer)
**Returns**
- Returns the parameter dictionary for the requested event
'''
return self.events[event_number].properties
[docs] def get_event_param( self, event_number, name ):
'''
Returns the value of a given parameter for a given event.
**Arguments**:
- *event_number* = the event to get a parameter for (integer)
- *name* = the name of the parameter to retreive (string)
**Returns**
- Returns the value of the request parameter, or None if it does not
exists.
'''
try:
ev = self.events[event_number].properties
return ev[name]
except KeyError:
return None #property does not exist
[docs] def write_history(self, filename):
"""Write history to new file
**Arguments**:
- *filename* = string : filename of new history file
.. hint:: Just love it how easy it is to 'write history' with Noddy ;-)
"""
# before saving: update all event properties (in case changes were made)
self.update_all_event_properties()
# first: create header
if not hasattr(self, "filename"):
self.filename = filename
self._create_header()
# initialise history lines
history_lines = []
# add header
for line in self.header_lines.split("\n"):
history_lines.append(line + "\n")
# add number of events
history_lines.append("No of Events\t= %d\n" % len(self.events))
# add events
for event_id in sorted(self.events.keys()):
for line in self.events[event_id].event_lines:
history_lines.append(line)
# add footer: from original footer or from template (if new file):
if not hasattr(self,"footer_lines"):
self.create_footer_from_template()
# add footer
for line in self.footer_lines:
history_lines.append(line)
f = open(filename, 'w')
for i,line in enumerate(history_lines):
# add empty line before "BlockOptions", if not there:
if ('BlockOptions' in line) and (history_lines[i-1] != "\n"):
f.write("\n")
#write line
f.write(line)
f.close()
#===============================================================================
# End of NoddyHistory
#===============================================================================
#===============================================================================
# Templates for Noddy history file
#===============================================================================
class _Templates():
header = """#Filename = simple_two_faults.his
#Date Saved = 24/3/2014 14:21:0
FileType = 111
Version = 7.11"""
strati_layer = """ Unit Name = $NAME$
Height = $HEIGHT$
Apply Alterations = ON
Density = 4.00e+000
Anisotropic Field = 0
MagSusX = 1.60e-003
MagSusY = 1.60e-003
MagSusZ = 1.60e-003
MagSus Dip = 9.00e+001
MagSus DipDir = 9.00e+001
MagSus Pitch = 0.00e+000
Remanent Magnetization = 0
Inclination = 30.00
Angle with the Magn. North = 30.00
Strength = 1.60e-003
Color Name = Color 92
Red = 0
Green = 153
Blue = 48 """
fault = """ Geometry = Translation
Movement = Hanging Wall
X = $POS_X$
Y = $POS_Y$
Z = $POS_Z$
Dip Direction = $DIP_DIR$
Dip = $DIP$
Pitch = 90.00
Slip = $SLIP$
Rotation = 30.00
Amplitude = 2000.00
Radius = 1000.00
XAxis = 2000.00
YAxis = 2000.00
ZAxis = 2000.00
Cyl Index = 0.00
Profile Pitch = 90.00
Color Name = Custom Colour 8
Red = 0
Green = 0
Blue = 254
Fourier Series
Term A 0 = 0.00
Term B 0 = 0.00
Term A 1 = 0.00
Term B 1 = 1.00
Term A 2 = 0.00
Term B 2 = 0.00
Term A 3 = 0.00
Term B 3 = 0.00
Term A 4 = 0.00
Term B 4 = 0.00
Term A 5 = 0.00
Term B 5 = 0.00
Term A 6 = 0.00
Term B 6 = 0.00
Term A 7 = 0.00
Term B 7 = 0.00
Term A 8 = 0.00
Term B 8 = 0.00
Term A 9 = 0.00
Term B 9 = 0.00
Term A 10 = 0.00
Term B 10 = 0.00
Name = Fault Plane
Type = 1
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 6.280000
Min Y Scale = -1.000000
Max Y Scale = 1.000000
Scale Origin = 0.000000
Min Y Replace = -1.000000
Max Y Replace = 1.000000
Num Points = 21
Point X = 0
Point Y = 0
Point X = 31
Point Y = 30
Point X = 62
Point Y = 58
Point X = 94
Point Y = 80
Point X = 125
Point Y = 94
Point X = 157
Point Y = 99
Point X = 188
Point Y = 95
Point X = 219
Point Y = 81
Point X = 251
Point Y = 58
Point X = 282
Point Y = 31
Point X = 314
Point Y = 0
Point X = 345
Point Y = -31
Point X = 376
Point Y = -59
Point X = 408
Point Y = -81
Point X = 439
Point Y = -95
Point X = 471
Point Y = -100
Point X = 502
Point Y = -96
Point X = 533
Point Y = -82
Point X = 565
Point Y = -59
Point X = 596
Point Y = -32
Point X = 628
Point Y = -1
Alteration Type = NONE
Num Profiles = 12
Name = Density
Type = 2
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = 0.000000
Max Y Scale = 4.000000
Scale Origin = 1.000000
Min Y Replace = 0.000000
Max Y Replace = 10.000000
Num Points = 2
Point X = 0
Point Y = -50
Point X = 628
Point Y = -50
Name = Anisotropy
Type = 3
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -10.000000
Max Y Scale = 10.000000
Scale Origin = 0.000000
Min Y Replace = -10.000000
Max Y Replace = 10.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - X Axis (Sus)
Type = 4
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -5.000000
Max Y Scale = 5.000000
Scale Origin = 0.000000
Min Y Replace = 2.000000
Max Y Replace = 8.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Y Axis (Sus)
Type = 5
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -5.000000
Max Y Scale = 5.000000
Scale Origin = 0.000000
Min Y Replace = 2.000000
Max Y Replace = 8.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Z Axis (Sus)
Type = 6
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -5.000000
Max Y Scale = 5.000000
Scale Origin = 0.000000
Min Y Replace = 2.000000
Max Y Replace = 8.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Dip (Sus)
Type = 7
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -180.000000
Max Y Scale = 180.000000
Scale Origin = 1.000000
Min Y Replace = -180.000000
Max Y Replace = 180.000000
Num Points = 2
Point X = 0
Point Y = 1
Point X = 628
Point Y = 1
Name = - Dip Dir (Sus)
Type = 8
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -360.000000
Max Y Scale = 360.000000
Scale Origin = 1.000000
Min Y Replace = -360.000000
Max Y Replace = 360.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Pitch (Sus)
Type = 9
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -360.000000
Max Y Scale = 360.000000
Scale Origin = 1.000000
Min Y Replace = -360.000000
Max Y Replace = 360.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = Remanence
Type = 10
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -10.000000
Max Y Scale = 10.000000
Scale Origin = 0.000000
Min Y Replace = -10.000000
Max Y Replace = 10.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Declination (Rem)
Type = 11
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -360.000000
Max Y Scale = 360.000000
Scale Origin = 1.000000
Min Y Replace = -360.000000
Max Y Replace = 360.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Inclination (Rem)
Type = 12
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -360.000000
Max Y Scale = 360.000000
Scale Origin = 1.000000
Min Y Replace = -360.000000
Max Y Replace = 360.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Name = - Intensity (Rem)
Type = 13
Join Type = LINES
Graph Length = 200.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = -5.000000
Max Y Scale = 5.000000
Scale Origin = 0.000000
Min Y Replace = -5.000000
Max Y Replace = 5.000000
Num Points = 2
Point X = 0
Point Y = 0
Point X = 628
Point Y = 0
Surface Type = FLAT_SURFACE
Surface Filename =
Surface Directory = \\psf\Home
Surface XDim = 0.000000
Surface YDim = 0.000000
Surface ZDim = 0.000000
Name = $NAME$"""
# AK 2014-10
tilt = """X = $POS_X$
Y = $POS_Y$
Z = $POS_Z$
Rotation = $ROTATION$
Plunge Direction = $PLUNGE_DIRECTION$
Plunge = $PLUNGE$
Name = $NAME$"""
unconformity = """X = $POS_X$
Y = $POS_Y$
Z = $POS_Z$
Dip Direction = $DIP_DIRECTION$
Dip = $DIP$
Alteration Type = NONE
Num Profiles = 1
Name =
Type = 0
Join Type = LINES
Graph Length = 0.000000
Min X = 0.000000
Max X = 0.000000
Min Y Scale = 0.000000
Max Y Scale = 0.000000
Scale Origin = 0.000000
Min Y Replace = 0.000000
Max Y Replace = 0.000000
Num Points = 0
Surface Type = FLAT_SURFACE
Surface Filename =
Surface Directory = /tmp_mnt/sci6/users/mark/Atlas/case
Surface XDim = 0.000000
Surface YDim = 0.000000
Surface ZDim = 0.000000"""
temp = """
Num Layers = 5
Unit Name = UC Base
Height = -32000
Apply Alterations = ON
Density = 3.50e+00
Anisotropic Field = 0
MagSusX = 1.50e-06
MagSusY = 1.60e-03
MagSusZ = 1.60e-03
MagSus Dip = 9.00e+01
MagSus DipDir = 9.00e+01
MagSus Pitch = 0.00e+00
Remanent Magnetization = 0
Inclination = 30.00
Angle with the Magn. North = 30.00
Strength = 1.60e-03
Color Name = Color 98
Red = 84
Green = 153
Blue = 0
Unit Name = UC Layer 1
Height = 5650
Apply Alterations = ON
Density = 3.50e+00
Anisotropic Field = 0
MagSusX = 1.50e-06
MagSusY = 1.60e-03
MagSusZ = 1.60e-03
MagSus Dip = 9.00e+01
MagSus DipDir = 9.00e+01
MagSus Pitch = 0.00e+00
Remanent Magnetization = 0
Inclination = 30.00
Angle with the Magn. North = 30.00
Strength = 1.60e-03
Color Name = Color 68
Red = 204
Green = 117
Blue = 0
Name = $NAME$"""
# everything below events
footer = """
#BlockOptions
Number of Views = 1
Current View = 0
NAME = Default
Origin X = 0.00
Origin Y = 0.00
Origin Z = 5000.00
Length X = 10000.00
Length Y = 7000.00
Length Z = 5000.00
Geology Cube Size = 50.00
Geophysics Cube Size = 50.00
#GeologyOptions
Scale = 10.00
SectionDec = 90.00
WellDepth = 5000.00
WellAngleZ = 0.00
BoreholeX = 0.00
BoreholeX = 0.00
BoreholeX = 5000.00
BoreholeDecl = 90.00
BoreholeDip = 0.00
BoreholeLength = 5000.00
SectionX = 0.00
SectionY = 0.00
SectionZ = 5000.00
SectionDecl = 90.00
SectionLength = 10000.00
SectionHeight = 5000.00
topofile = FALSE
Topo Filename =
Topo Directory = .
Topo Scale = 1.00
Topo Offset = 0.00
Topo First Contour = 100.00
Topo Contour Interval = 100.00
Chair Diagram = FALSE
Chair_X = 5000.00
Chair_Y = 3500.00
Chair_Z = 2500.00
#GeophysicsOptions
GPSRange = 0
Declination = 0.00
Inclination = -67.00
Intensity = 63000.00
Field Type = FIXED
Field xPos = 0.00
Field yPos = 0.00
Field zPos = 5000.00
Inclination Ori = 0.00
Inclination Change = 0.00
Intensity Ori = 90.00
Intensity Change = 0.00
Declination Ori = 0.00
Declination Change = 0.00
Altitude = 80.00
Airborne= FALSE
Calculation Method = SPATIAL
Spectral Padding Type = RECLECTION_PADDING
Spectral Fence = 100
Spectral Percent = 100
Constant Boxing Depth = 0.00
Clever Boxing Ratio = 1.00
Deformable Remanence= FALSE
Deformable Anisotropy= TRUE
Vector Components= FALSE
Project Vectors= TRUE
Pad With Real Geology= FALSE
Draped Survey= FALSE
#3DOptions
Declination = 150.000000
Elevation = 30.000000
Scale = 1.000000
Offset X = 1.000000
Offset Y = 1.000000
Offset Z = 1.000000
Fill Type = 2
#ProjectOptions
Susceptibility Units = CGS
Geophysical Calculation = 2
Calculation Type = LOCAL_JOB
Length Scale = 0
Printing Scale = 1.000000
Image Scale = 10.000000
New Windows = FALSE
Background Red Component = 254
Background Green Component = 254
Background Blue Component = 254
Internet Address = 255.255.255.255
Account Name =
Noddy Path = ./noddy
Help Path = iexplore %h
Movie Frames Per Event = 3
Movie Play Speed = 10.00
Movie Type = 0
Gravity Clipping Type = RELATIVE_CLIPPING
Gravity Image Display Clip Min = 0.000000
Gravity Image Display Clip Max = 100.000000
Gravity Image Display Type = GREY
Gravity Image Display Num Contour = 25
Magnetics Clipping Type = RELATIVE_CLIPPING
Magnetics Image Display Clip Min = 0.000000
Magnetics Image Display Clip Max = 100.000000
Magnetics Image Display Type = GREY
Magnetics Image Display Num Contour = 25
False Easting = 0.000000
False Northing = 0.000000
#Window Positions
Num Windows = 16
Name = Block Diagram
X = 60
Y = 60
Width = 500
Height = 300
Name = Movie
X = 60
Y = 60
Width = -1
Height = -1
Name = Well Log
X = 60
Y = 60
Width = 400
Height = 430
Name = Section
X = 14
Y = 16
Width = 490
Height = -1
Name = Topography Map
X = 60
Y = 60
Width = 490
Height = 375
Name = 3D Topography Map
X = 60
Y = 60
Width = 490
Height = 375
Name = 3D Stratigraphy
X = 60
Y = 60
Width = 490
Height = 375
Name = Line Map
X = 60
Y = 60
Width = 490
Height = -1
Name = Profile - From Image
X = 60
Y = 60
Width = 490
Height = 600
Name = Sterographic Projections
X = 60
Y = 60
Width = 430
Height = 430
Name = Stratigraphic Column
X = 60
Y = 60
Width = 230
Height = 400
Name = Image
X = 30
Y = 30
Width = -1
Height = -1
Name = Contour
X = 30
Y = 30
Width = -1
Height = -1
Name = Toolbar
X = 10
Y = 0
Width = -1
Height = -1
Name = History
X = 229
Y = 160
Width = 762
Height = 898
Name = History
X = 229
Y = 160
Width = 762
Height = 898
#Icon Positions
Num Icons = 3
Row = 1
Column = 1
X Position = 1
Y Position = 1
Row = 1
Column = 2
X Position = 4
Y Position = 1
Row = 1
Column = 3
X Position = 7
Y Position = 1
Floating Menu Rows = 1
Floating Menu Cols = 24
End of Status Report"""
if __name__ == '__main__':
# some testing and debugging:
import os
os.chdir(r'C:\Users\Sam\OneDrive\Documents\Masters\Models\Mt Painter')
H1 = NoddyHistory("mt_pa_simplified.his")
H1.swap_events(2, 3)
H1.write_history("test")
H2 = NoddyHistory("test")
H2.events[10].properties['Radius'] = 2000
H2.write_history("test2")