Reproducible Experiments with pynoddy ===================================== All ``pynoddy`` experiments can be defined in a Python script, and if all settings are appropriate, then this script can be re-run to obtain a reproduction of the results. However, it is often more convenient to encapsulate all elements of an experiment within one class. We show here how this is done in the ``pynoddy.experiment.Experiment`` class and how this class can be used to define simple reproducible experiments with kinematic models. .. code:: python from IPython.core.display import HTML css_file = 'pynoddy.css' HTML(open(css_file, "r").read()) .. raw:: html .. code:: python %matplotlib inline .. code:: python # here the usual imports. If any of the imports fails, # make sure that pynoddy is installed # properly, ideally with 'python setup.py develop' # or 'python setup.py install' import sys, os import matplotlib.pyplot as plt import numpy as np # adjust some settings for matplotlib from matplotlib import rcParams # print rcParams rcParams['font.size'] = 15 # determine path of repository to set paths corretly below repo_path = os.path.realpath('../..') import pynoddy.history import pynoddy.experiment reload(pynoddy.experiment) rcParams.update({'font.size': 15}) Defining an experiment ---------------------- We are considering the following scenario: we defined a kinematic model of a prospective geological unit at depth. As we know that the estimates of the (kinematic) model parameters contain a high degree of uncertainty, we would like to represent this uncertainty with the model. Our approach is here to perform a randomised uncertainty propagation analysis with a Monte Carlo sampling method. Results should be presented in several figures (2-D slice plots and a VTK representation in 3-D). To perform this analysis, we need to perform the following steps (see main paper for more details): 1. Define kinematic model parameters and construct the initial (base) model; 2. Assign probability distributions (and possible parameter correlations) to relevant uncertain input parameters; 3. Generate a set of n random realisations, repeating the following steps: 1. Draw a randomised input parameter set from the parameter distribu- tion; 2. Generate a model with this parameter set; 3. Analyse the generated model and store results; 4. Finally: perform postprocessing, generate figures of results It would be possible to write a Python script to perform all of these steps in one go. However, we will here take another path and use the implementation in a Pynoddy Experiment class. Initially, this requires more work and a careful definition of the experiment - but, finally, it will enable a higher level of flexibility, extensibility, and reproducibility. Loading an example model from the Atlas of Structural Geophysics ---------------------------------------------------------------- As in the example for geophysical potential-field simulation, we will use a model from the Atlas of Structural Geophysics as an examlpe model for this simulation. We use a model for a fold interference structure. A discretised 3-D version of this model is presented in the figure below. The model represents a fold interference pattern of "Type 1" according to the definition of Ramsey (1967). .. figure:: 6-Reproducible-Experiments_files/typeb.jpg :alt: Fold interference pattern Fold interference pattern Instead of loading the model into a history object, we are now directly creating an experiment object: .. code:: python reload(pynoddy.history) reload(pynoddy.experiment) from pynoddy.experiment import monte_carlo model_url = 'http://tectonique.net/asg/ch3/ch3_7/his/typeb.his' ue = pynoddy.experiment.Experiment(url = model_url) For simpler visualisation in this notebook, we will analyse the following steps in a section view of the model. We consider a section in y-direction through the model: .. code:: python ue.write_history("typeb_tmp3.his") .. code:: python ue.write_history("typeb_tmp2.his") .. code:: python ue.change_cube_size(100) ue.plot_section('y') .. figure:: 6-Reproducible-Experiments_files/6-Reproducible-Experiments_10_0.png :alt: png png Before we start to draw random realisations of the model, we should first store the base state of the model for later reference. This is simply possibel with the freeze() method which stores the current state of the model as the "base-state": .. code:: python ue.freeze() We now intialise the random generator. We can directly assign a random seed to simplify reproducibility (note that this is not *essential*, as it would be for the definition in a script function: the random state is preserved within the model and could be retrieved at a later stage, as well!): .. code:: python ue.set_random_seed(12345) The next step is to define probability distributions to the relevant event parameters. Let's first look at the different events: .. code:: python ue.info(events_only = True) :: This model consists of 3 events: (1) - STRATIGRAPHY (2) - FOLD (3) - FOLD .. code:: python ev2 = ue.events[2] .. code:: python ev2.properties :: {'Amplitude': 1250.0, 'Cylindricity': 0.0, 'Dip': 90.0, 'Dip Direction': 90.0, 'Pitch': 0.0, 'Single Fold': 'FALSE', 'Type': 'Sine', 'Wavelength': 5000.0, 'X': 1000.0, 'Y': 0.0, 'Z': 0.0} Next, we define the probability distributions for the uncertain input parameters: .. code:: python param_stats = [{'event' : 2, 'parameter': 'Amplitude', 'stdev': 100.0, 'type': 'normal'}, {'event' : 2, 'parameter': 'Wavelength', 'stdev': 500.0, 'type': 'normal'}, {'event' : 2, 'parameter': 'X', 'stdev': 500.0, 'type': 'normal'}] ue.set_parameter_statistics(param_stats) .. code:: python resolution = 100 ue.change_cube_size(resolution) tmp = ue.get_section('y') prob_4 = np.zeros_like(tmp.block[:,:,:]) n_draws = 100 for i in range(n_draws): ue.random_draw() tmp = ue.get_section('y', resolution = resolution) prob_4 += (tmp.block[:,:,:] == 4) # Normalise prob_4 = prob_4 / float(n_draws) .. code:: python fig = plt.figure(figsize = (12,8)) ax = fig.add_subplot(111) ax.imshow(prob_4.transpose()[:,0,:], origin = 'lower left', interpolation = 'none') plt.title("Estimated probability of unit 4") plt.xlabel("x (E-W)") plt.ylabel("z") :: .. figure:: 6-Reproducible-Experiments_files/6-Reproducible-Experiments_22_1.png :alt: png png This example shows how the base module for reproducible experiments with kinematics can be used. For further specification, child classes of ``Experiment`` can be defined, and we show examples of this type of extension in the next sections. .. code:: python