From fb7e47b1afc4e849685ddf969d953b000ebcd35f Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Sat, 27 Jun 2020 19:55:31 +1000 Subject: [PATCH 1/8] (BUGFIX) changing default number of elements --- LoopStructural/modelling/core/geological_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopStructural/modelling/core/geological_model.py b/LoopStructural/modelling/core/geological_model.py index 2212601b6..4565de605 100644 --- a/LoopStructural/modelling/core/geological_model.py +++ b/LoopStructural/modelling/core/geological_model.py @@ -262,7 +262,7 @@ def create_from_feature_list(self, features): if featuretype == 'folded_strati': self.create_and_add_folded_foliation(f) - def get_interpolator(self, interpolatortype='PLI', nelements=5e5, + def get_interpolator(self, interpolatortype='PLI', nelements=1e5, buffer=0.2, **kwargs): """ Returns an interpolator given the arguments, also constructs a From 9a98f1309251d0274549d6084d475561dd35e014 Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Mon, 29 Jun 2020 14:26:11 +1000 Subject: [PATCH 2/8] (DOC) updating examples, splitting visualisation from model building --- examples/1_basic/plot_1_data_prepration.py | 20 ++- examples/1_basic/plot_2_surface_modelling.py | 130 +-------------- .../1_basic/plot_3_model_visualisation.py | 157 ++++++++++++++++++ 3 files changed, 178 insertions(+), 129 deletions(-) create mode 100644 examples/1_basic/plot_3_model_visualisation.py diff --git a/examples/1_basic/plot_1_data_prepration.py b/examples/1_basic/plot_1_data_prepration.py index 11f3e870e..7ae51ada6 100644 --- a/examples/1_basic/plot_1_data_prepration.py +++ b/examples/1_basic/plot_1_data_prepration.py @@ -123,6 +123,14 @@ # Geological feature can be evaluated: # * for the scalar field value at a location # * for the gradient of the scalar field at a location +# To evaluate a model feature (scalar value or gradient) use the: +# :code:`model.evaluate_feature_value(feature_name, locations)` or +# :code:`model.evaluate_feature_gradient(feature_name, locations)` +# Where the feature_name is the string naming the feature and locations is a numpy array of +# xyz coordinates. +# +# In the following example we will use matplotlib to visualise these results however, the +# next tutorial will show how to use the lavavu visualisation model. import matplotlib.pyplot as plt @@ -135,10 +143,10 @@ xx = np.zeros_like(yy) xx[:] = 5 -vals = conformable_feature.evaluate_value(model.scale(np.array([xx.flatten(),yy.flatten(),zz.flatten()]).T)) +vals = model.evaluate_feature_value('conformable',np.array([xx.flatten(),yy.flatten(),zz.flatten()]).T) fig, ax = plt.subplots(1,2,figsize=(20,10)) -ax[0].contourf(vals.reshape((100,100))) -ax[0].contour(vals.reshape((100,100)),[0,1]) +ax[0].contourf(vals.reshape((100,100)),extent=(0,10,0,10)) +ax[0].contour(vals.reshape((100,100)),[0,1],extent=(0,10,0,10)) # Y section x = np.linspace(0,10,100) @@ -148,8 +156,8 @@ yy = np.zeros_like(xx) yy[:] = 5 -vals = conformable_feature.evaluate_value(model.scale(np.array([xx.flatten(),yy.flatten(),zz.flatten()]).T)) -ax[1].contourf(vals.reshape((100,100))) -ax[1].contour(vals.reshape((100,100)),[0,1]) +vals = model.evaluate_feature_value('conformable',np.array([xx.flatten(),yy.flatten(),zz.flatten()]).T) +ax[1].contourf(vals.reshape((100,100)),extent=(0,10,0,10)) +ax[1].contour(vals.reshape((100,100)),[0,1],extent=(0,10,0,10)) plt.show() diff --git a/examples/1_basic/plot_2_surface_modelling.py b/examples/1_basic/plot_2_surface_modelling.py index 89ffcefcb..0d697c08e 100644 --- a/examples/1_basic/plot_2_surface_modelling.py +++ b/examples/1_basic/plot_2_surface_modelling.py @@ -148,130 +148,14 @@ damp=True ) - -###################################################################### -# Visualising results -# ~~~~~~~~~~~~~~~~~~~ -# -# The LavaVuModelViewer is an LoopStructural class that provides easy 3D -# plotting options for plotting data points and resulting implicit -# functions. -# -# The implicit function can be visualised by looking at isosurfaces of the -# scalar field. -# -# .. code:: python -# -# viewer = LavaVuModelViewer() -# viewer.add_isosurface(feature,**kwargs) -# -# Where optional kwargs can be: -# -# - ``nslices`` specifying the number of regularly spaced isosurfaces -# - ``slices`` a numpy array or list of isovalues to slice -# - ``isovalue`` an isovalue to slice -# - ``paint_with`` the geological feature to colour the surface with -# - ``cmap`` colour map for the colouring -# - ``normals`` to plot the normal vectors to the surface -# - ``name`` to give the surface -# - ``colour`` the colour of the surface -# - ``voxet`` dict with ``bounding_box=boundary_points`` and -# ``nsteps = (nx,ny,nz)`` -# - other kwargs for passing directly to lavavu -# -# Alternatively the scalarfields can be displayed on a rectangular cuboid. -# -# .. code:: python -# -# viewer.add_scalar_field(boundary_points,dimensions,**kwargs) -# -# Where ``boundary_points`` is a numpy array -# ``[[minx,miny,minz],[maxx,maxy,maxz]]`` and ``dimensions`` corresponds -# to the number of samples along each axis. -# -# Other possible kwargs are: -# -# - ``paint_with`` the geological feature to colour the box with -# - ``colour`` a single colour to colour the surfaces with -# - ``cmap`` colour map for the property -# -# The input data for the model can be visualised by calling either: -# -# .. code:: python -# -# viewer.add_data(feature,addgrad=True,addvalue=True,**kwargs) -# -# Where both the point and vector data linked to the feature are added to -# the plot or by calling. -# -# .. code:: python -# -# viewer.add_vector_data(position,vector,name,**kwargs) -# -# Where ``position`` is an array or x,y,z coordinates and vector is a -# similarly sized array of ``vectors``. These can be extracted from a -# geological feature by calling. -# ``feature.support.interpolator.get_gradient_constraint()`` which returns -# a Nx6 matrix of position and vectors. -# -# The value data can be plotted by calling. -# -# .. code:: python -# -# viewer.add_value_data(position,value,name,**kwargs) -# -# Where ``position`` is an array or x,y,z coordinates and value is a -# similarly sized vector of values. These can be extracted from a -# geological feature by calling. -# ``feature.support.interpolator.get_value_constraint()`` which returns a -# Nx4 matrix of position and values. -# -# Other possible options for plotting are to \* plot point locations. -# -# .. code:: python -# -# viewer.add_points(position, name, **kwargs) -# -# - plot a vector field using the gradient of a geological feature -# -# .. code:: python -# -# viewer.add_vector_field(feature, locations, **kwargs) -# -# Where ``locations`` are an array of points to evaluate the gradient at, -# for example the barycentric coordinates. It is recommended to visualise -# the vectorfield at a lower resolution than the mesh otherwise it can be -# difficult to see the vectors. You can use numpy stepping along the -# array: ``locations = mesh.barycentre[::20,:]`` which will sample every -# 20th sample in the numpy array. -# - -viewer = LavaVuModelViewer(model,background="white") - -# determine the number of unique surfaces in the model from -# the input data and then calculate isosurfaces for this -unique = np.unique(strati.interpolator.get_value_constraints()[:,3]) -viewer.add_isosurface(strati, - slices=unique, - cmap='prism', - paint_with=strati, - voxet=model.voxet()) - -viewer.add_section(strati, - axis='x', - value=0., - boundary_points=model.bounding_box, - nsteps=np.array([30,30,30]), - cmap='prism') -viewer.add_scalar_field(strati, - cmap='prism') +viewer = LavaVuModelViewer(model) +viewer.add_model_surfaces() +viewer.rotate([-85.18760681152344, 42.93233871459961, 0.8641873002052307]) +viewer.display() +viewer = LavaVuModelViewer(model) viewer.add_model() - -# Add the data addgrad/addvalue arguments are optional -viewer.add_data(strati,addgrad=True,addvalue=True, cmap='prism') -viewer.lv.rotate([-85.18760681152344, 42.93233871459961, 0.8641873002052307]) -viewer.display()# to add an interactive display - +viewer.rotate([-85.18760681152344, 42.93233871459961, 0.8641873002052307]) +viewer.display() diff --git a/examples/1_basic/plot_3_model_visualisation.py b/examples/1_basic/plot_3_model_visualisation.py new file mode 100644 index 000000000..9956ef5b7 --- /dev/null +++ b/examples/1_basic/plot_3_model_visualisation.py @@ -0,0 +1,157 @@ +""" + +1c. Visualising models +=============================== + + +""" + +######################################################################### +# Imports +# ~~~~~~~ +# Import the required objects from LoopStructural for visualisation and +# model building + +from LoopStructural import GeologicalModel +from LoopStructural.visualisation import LavaVuModelViewer + +from LoopStructural.datasets import load_claudius #demo data + +import pandas as pd +import numpy as np + +##################### +# Build the model +# ~~~~~~~~~~~~~~~~~ +data, bb = load_claudius() +model = GeologicalModel(bb[0,:],bb[1,:]) +model.set_model_data(data) +strati = model.create_and_add_foliation("strati") +strat_column = {'strati':{}} +vals = [0,60,250,330,600] +for i in range(len(vals)-1): + strat_column['strati']['unit_{}'.format(i)] = {'min':vals[i],'max':vals[i+1],'id':i} +model.set_stratigraphic_column(strat_column) + +###################################################################### +# Visualising results +# ~~~~~~~~~~~~~~~~~~~ +# +# The LavaVuModelViewer is an LoopStructural class that provides easy 3D +# plotting options for plotting data points and resulting implicit +# functions. +# +# The implicit function can be visualised by looking at isosurfaces of the +# scalar field. +# +# .. code:: python +# +# viewer = LavaVuModelViewer() +# viewer.add_isosurface(feature,**kwargs) +# +# Where optional kwargs can be: +# +# - ``nslices`` specifying the number of regularly spaced isosurfaces +# - ``slices`` a numpy array or list of isovalues to slice +# - ``isovalue`` an isovalue to slice +# - ``paint_with`` the geological feature to colour the surface with +# - ``cmap`` colour map for the colouring +# - ``normals`` to plot the normal vectors to the surface +# - ``name`` to give the surface +# - ``colour`` the colour of the surface +# - ``voxet`` dict with ``bounding_box=boundary_points`` and +# ``nsteps = (nx,ny,nz)`` +# - other kwargs for passing directly to lavavu +# +# Alternatively the scalarfields can be displayed on a rectangular cuboid. +# +# .. code:: python +# +# viewer.add_scalar_field(boundary_points,dimensions,**kwargs) +# +# Where ``boundary_points`` is a numpy array +# ``[[minx,miny,minz],[maxx,maxy,maxz]]`` and ``dimensions`` corresponds +# to the number of samples along each axis. +# +# Other possible kwargs are: +# +# - ``paint_with`` the geological feature to colour the box with +# - ``colour`` a single colour to colour the surfaces with +# - ``cmap`` colour map for the property +# +# The input data for the model can be visualised by calling either: +# +# .. code:: python +# +# viewer.add_data(feature,addgrad=True,addvalue=True,**kwargs) +# +# Where both the point and vector data linked to the feature are added to +# the plot or by calling. +# +# .. code:: python +# +# viewer.add_vector_data(position,vector,name,**kwargs) +# +# Where ``position`` is an array or x,y,z coordinates and vector is a +# similarly sized array of ``vectors``. These can be extracted from a +# geological feature by calling. +# ``feature.support.interpolator.get_gradient_constraint()`` which returns +# a Nx6 matrix of position and vectors. +# +# The value data can be plotted by calling. +# +# .. code:: python +# +# viewer.add_value_data(position,value,name,**kwargs) +# +# Where ``position`` is an array or x,y,z coordinates and value is a +# similarly sized vector of values. These can be extracted from a +# geological feature by calling. +# ``feature.support.interpolator.get_value_constraint()`` which returns a +# Nx4 matrix of position and values. +# +# Other possible options for plotting are to \* plot point locations. +# +# .. code:: python +# +# viewer.add_points(position, name, **kwargs) +# +# - plot a vector field using the gradient of a geological feature +# +# .. code:: python +# +# viewer.add_vector_field(feature, locations, **kwargs) +# +# Where ``locations`` are an array of points to evaluate the gradient at, +# for example the barycentric coordinates. It is recommended to visualise +# the vectorfield at a lower resolution than the mesh otherwise it can be +# difficult to see the vectors. You can use numpy stepping along the +# array: ``locations = mesh.barycentre[::20,:]`` which will sample every +# 20th sample in the numpy array. +# + +viewer = LavaVuModelViewer(model,background="white") + +# determine the number of unique surfaces in the model from +# the input data and then calculate isosurfaces for this +unique = np.unique(strati.interpolator.get_value_constraints()[:,3]) +viewer.add_isosurface(strati, + slices=unique, + cmap='prism', + paint_with=strati, + voxet=model.voxet()) + +viewer.add_section(strati, + axis='x', + value=0., + boundary_points=model.bounding_box, + nsteps=np.array([30,30,30]), + cmap='prism') +viewer.add_scalar_field(strati, + cmap='prism') +viewer.add_model() + +# Add the data addgrad/addvalue arguments are optional +viewer.add_data(strati,addgrad=True,addvalue=True, cmap='prism') +viewer.lv.rotate([-85.18760681152344, 42.93233871459961, 0.8641873002052307]) +viewer.display()# to add an interactive display \ No newline at end of file From 9c5161bc986cb9131893550e2b4cc1720ae90522 Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Mon, 29 Jun 2020 20:25:28 +1000 Subject: [PATCH 3/8] (FEATURE) updating map2loop for supergoups with only one unit. --- LoopStructural/utils/map2loop.py | 27 ++++++++++++++++--- .../visualisation/model_visualisation.py | 5 ++-- .../1_basic/plot_3_model_visualisation.py | 5 ++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/LoopStructural/utils/map2loop.py b/LoopStructural/utils/map2loop.py index ad1c12944..9e3f51036 100644 --- a/LoopStructural/utils/map2loop.py +++ b/LoopStructural/utils/map2loop.py @@ -43,6 +43,8 @@ def process_map2loop(m2l_directory, flags={}): except: for g in groups['group'].unique(): supergroups[g] = g + supergroups.pop('\n') + bb = pd.read_csv(m2l_directory+'/tmp/bbox.csv') @@ -85,6 +87,23 @@ def process_map2loop(m2l_directory, flags={}): unit_id += 1 strat_val[c] = val[g] val[g] += thickness[c] + group_name = None + for g, i in stratigraphic_column.items(): + if len(i) ==0: + for gr, sg in supergroups.items(): + if sg == g: + group_name = gr + break + try: + if group_name is None: + continue + c=groups.loc[groups['group']==group_name,'code'].to_numpy()[0] + strat_val[c] = 0 + stratigraphic_column[g] = {c:{'min':0,'max':9999,'id':unit_id}} + unit_id+=1 + group_name = None + except: + print('Couldnt process {}'.format(g)) contacts['val'] = np.nan for o in strat_val: contacts.loc[contacts['formation'] == o, 'val'] = strat_val[o] @@ -140,7 +159,7 @@ def process_map2loop(m2l_directory, flags={}): 'bounding_box':bb, 'strat_va':strat_val} -def build_model(m2l_data, skip_faults = False, fault_params = None, foliation_params=None): +def build_model(m2l_data, skip_faults = False, unconformities=True, fault_params = None, foliation_params=None): """[summary] [extended_summary] @@ -199,12 +218,12 @@ def build_model(m2l_data, skip_faults = False, fault_params = None, foliation_pa ## loop through all of the groups and add them to the model in youngest to oldest. group_features = [] - for i in m2l_data['groups']['group number'].unique(): + for i in np.sort(m2l_data['groups']['group number'].unique()): g = m2l_data['groups'].loc[m2l_data['groups']['group number'] == i, 'group'].unique()[0] group_features.append(model.create_and_add_foliation(g, **foliation_params)) # if the group was successfully added (not null) then lets add the base (0 to be unconformity) - # if group_features[-1]: - # model.add_unconformity(group_features[-1], 0) + if group_features[-1] and unconformities: + model.add_unconformity(group_features[-1], 0) model.set_stratigraphic_column(m2l_data['stratigraphic_column']) return model \ No newline at end of file diff --git a/LoopStructural/visualisation/model_visualisation.py b/LoopStructural/visualisation/model_visualisation.py index 2772cbd94..666abfabd 100644 --- a/LoopStructural/visualisation/model_visualisation.py +++ b/LoopStructural/visualisation/model_visualisation.py @@ -149,8 +149,9 @@ def add_section(self, geological_feature=None, axis='x', value=None, **kwargs): geological_feature.name, geological_feature.min(), geological_feature.max())) surf.colourmap(cmap, range=[geological_feature.min(), geological_feature.max()]) - def add_isosurface(self, geological_feature, value = None, isovalue=None, paint_with=None, - slices=None, colour='red', nslices=None, cmap=None, **kwargs): + def add_isosurface(self, geological_feature, value = None, isovalue=None, + paint_with=None, slices=None, colour='red', nslices=None, + cmap=None, **kwargs): """ Plot the surface of a geological feature [extended_summary] diff --git a/examples/1_basic/plot_3_model_visualisation.py b/examples/1_basic/plot_3_model_visualisation.py index 9956ef5b7..c835f2bbd 100644 --- a/examples/1_basic/plot_3_model_visualisation.py +++ b/examples/1_basic/plot_3_model_visualisation.py @@ -2,7 +2,12 @@ 1c. Visualising models =============================== +The following tutorial will demonstrate how to use the Loop structural visualisation module. +This module provides a wrapper for the lavavu model that is written by +Owen Kaluza. +Lavavu allows for interactive visualisation of 3D models within a jupyter +notebook environment. """ From 992f3ebece79b70435d8d7e493825d02a92d17b8 Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Wed, 1 Jul 2020 09:53:03 +1000 Subject: [PATCH 4/8] (FEATURE) export surface files using meshio --- .../visualisation/model_visualisation.py | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/LoopStructural/visualisation/model_visualisation.py b/LoopStructural/visualisation/model_visualisation.py index 666abfabd..17bcec0f9 100644 --- a/LoopStructural/visualisation/model_visualisation.py +++ b/LoopStructural/visualisation/model_visualisation.py @@ -151,7 +151,7 @@ def add_section(self, geological_feature=None, axis='x', value=None, **kwargs): def add_isosurface(self, geological_feature, value = None, isovalue=None, paint_with=None, slices=None, colour='red', nslices=None, - cmap=None, **kwargs): + cmap=None, filename=None, **kwargs): """ Plot the surface of a geological feature [extended_summary] @@ -174,6 +174,8 @@ def add_isosurface(self, geological_feature, value = None, isovalue=None, [description], by default None cmap : [type], optional [description], by default None + filename: string, optional + filename for exporting Returns ------- @@ -189,7 +191,7 @@ def add_isosurface(self, geological_feature, value = None, isovalue=None, # do isosurfacing of support using marching tetras/cubes x = np.linspace(self.bounding_box[0, 0], self.bounding_box[1, 0], self.nsteps[0]) y = np.linspace(self.bounding_box[0, 1], self.bounding_box[1, 1], self.nsteps[1]) - z = np.linspace(self.bounding_box[1, 2], self.bounding_box[0, 2], self.nsteps[2]) + z = np.linspace(self.bounding_box[0, 2], self.bounding_box[1, 2], self.nsteps[2]) xx, yy, zz = np.meshgrid(x, y, z, indexing='ij') points = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T val = geological_feature.evaluate_value(points) @@ -241,9 +243,20 @@ def add_isosurface(self, geological_feature, value = None, isovalue=None, except ValueError: logger.warning("no surface to mesh, skipping") continue + + name = geological_feature.name name = kwargs.get('name', name) name += '_iso_%f' % isovalue + if filename is not None: + try: + import meshio + except ImportError: + logger.error("Could not save surfaces, meshio is not installed") + meshio.write_points_cells(filename.format(name), + self.model.rescale(verts), + [("triangle", faces)] + ) surf = self.lv.triangles(name) surf.vertices(verts) surf.indices(faces) @@ -354,12 +367,12 @@ def add_model_surfaces(self, faults = True, cmap='tab20', **kwargs): if g in self.model.feature_name_index: feature = self.model.features[self.model.feature_name_index[g]] for u, vals in self.model.stratigraphic_column[g].items(): - self.add_isosurface(feature, isovalue=vals['max'],name=u,colour=tab.colors[ci,:]) + self.add_isosurface(feature, isovalue=vals['max'],name=u,colour=tab.colors[ci,:],**kwargs) ci+=1 if faults: for f in self.model.features: if f.type == 'fault': - self.add_isosurface(f,isovalue=0) + self.add_isosurface(f,isovalue=0,**kwargs) def add_vector_field(self, geological_feature, **kwargs): From 5260f0f5d882c61dd365ba4994bd39f66ca60b95 Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Wed, 1 Jul 2020 09:53:52 +1000 Subject: [PATCH 5/8] (FEATURE) adding flag to skip unconformities for debugging --- LoopStructural/utils/map2loop.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopStructural/utils/map2loop.py b/LoopStructural/utils/map2loop.py index 9e3f51036..c12ba0a26 100644 --- a/LoopStructural/utils/map2loop.py +++ b/LoopStructural/utils/map2loop.py @@ -159,7 +159,7 @@ def process_map2loop(m2l_directory, flags={}): 'bounding_box':bb, 'strat_va':strat_val} -def build_model(m2l_data, skip_faults = False, unconformities=True, fault_params = None, foliation_params=None): +def build_model(m2l_data, skip_faults = False, unconformities=False, fault_params = None, foliation_params=None): """[summary] [extended_summary] From 80bc6dfc362e81e338597c59065b55ab9da500dd Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Wed, 1 Jul 2020 09:54:15 +1000 Subject: [PATCH 6/8] (BUG) trying to fix domain faults --- LoopStructural/modelling/core/geological_model.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/LoopStructural/modelling/core/geological_model.py b/LoopStructural/modelling/core/geological_model.py index 4565de605..315a7b834 100644 --- a/LoopStructural/modelling/core/geological_model.py +++ b/LoopStructural/modelling/core/geological_model.py @@ -622,6 +622,8 @@ def _add_domain_fault_above(self, feature): """ for f in reversed(self.features): + if f.name == feature.name: + continue if f.type == 'domain_fault': feature.add_region(lambda pos: f.evaluate_value(pos) < 0) break @@ -642,6 +644,8 @@ def _add_domain_fault_below(self, domain_fault): """ for f in reversed(self.features): + if f.name == domain_fault.name: + continue f.add_region(lambda pos: domain_fault.evaluate_value(pos) > 0) if f.type == 'unconformity': break @@ -818,11 +822,14 @@ def create_and_add_domain_fault(self, fault_surface_data, **kwargs): # build feature domain_fault = domain_fault_feature_builder.build(**kwargs) domain_fault.type = 'domain_fault' + self._add_feature(domain_fault) + self._add_domain_fault_below(domain_fault) + # uc_feature = UnconformityFeature(uc_feature_base,0) # iterate over existing features and add the unconformity as a # region so the feature is only # evaluated where the unconformity is positive - return self.add_unconformity(domain_fault, 0) + return domain_fault def create_and_add_fault(self, fault_surface_data, displacement, **kwargs): """ @@ -988,7 +995,7 @@ def voxet(self, nsteps=(50, 50, 25)): """ return {'bounding_box': self.bounding_box, 'nsteps': nsteps} - def regular_grid(self, nsteps=(50, 50, 25), shuffle = True): + def regular_grid(self, nsteps=(50, 50, 25), shuffle = True, rescale=True): """ Return a regular grid within the model bounding box @@ -1012,6 +1019,8 @@ def regular_grid(self, nsteps=(50, 50, 25), shuffle = True): locs = np.array([xx.flatten(), yy.flatten(), zz.flatten()]).T if shuffle: np.random.shuffle(locs) + if rescale: + locs = self.rescale(locs) return locs def evaluate_model(self, xyz, rescale=True): From 8f1f96b60d15d5ffe758445de9d22b16386d4f12 Mon Sep 17 00:00:00 2001 From: Lachlan Grose Date: Wed, 1 Jul 2020 12:39:05 +1000 Subject: [PATCH 7/8] (FEATURE) export model as pickle object requires using dill because of lambda functions --- .../modelling/core/geological_model.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/LoopStructural/modelling/core/geological_model.py b/LoopStructural/modelling/core/geological_model.py index 315a7b834..1d230d051 100644 --- a/LoopStructural/modelling/core/geological_model.py +++ b/LoopStructural/modelling/core/geological_model.py @@ -128,6 +128,31 @@ def from_map2loop_directory(cls, m2l_directory,**kwargs): m2l_data = process_map2loop(m2l_directory) return build_model(m2l_data,**kwargs), m2l_data + @classmethod + def from_file(cls, file): + try: + import dill as pickle + except ImportError: + logger.error("Cannot import from file, dill not installed") + return None + model = pickle.load(open(file,'rb')) + if type(model) == GeologicalModel: + return model + else: + logger.error('{} does not contain a geological model'.format(file)) + return None + + def to_file(self, file): + try: + import dill as pickle + except ImportError: + logger.error("Cannot write to file, dill not installed") + return + try: + pickle.dump(self,open(file,'wb')) + except pickle.PicklingError: + logger.error('Error saving file') + def _add_feature(self, feature): """ Add a feature to the model stack From 73f5ce7f89e8eccb8082001ff9e2e98d8c4c9792 Mon Sep 17 00:00:00 2001 From: lachlangrose Date: Wed, 1 Jul 2020 13:02:57 +1000 Subject: [PATCH 8/8] (BUGFIX) adding python wheels and source to pypi --- .github/workflows/python-publish.yml | 51 ++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 403cb9949..0117968bd 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -5,7 +5,7 @@ on: types: [created] jobs: - deploy: + flake8: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -16,12 +16,25 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install twine flake8 + pip install flake8 - name: Lint with flake8 for syntax errors run: | pip install flake8 flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + manylinux: + runs-on: ubuntu-latest + needs: flake8 + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install twine - name: Build manylinux Python wheels uses: RalfG/python-wheels-manylinux-build@v0.2.2-manylinux2010_x86_64 with: @@ -36,3 +49,37 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | twine upload wheelhouse/*-manylinux*.whl + + build-windows: + runs-on: windows-latest + needs: flake8 + strategy: + matrix: + python: ['3.6','3.7','3.8'] + steps: + - uses: actions/checkout@v2 + - uses: goanpeca/action-setup-conda@v1 + with: + python-version: ${{ matrix.python }} + activate-environment: loop + - name: Installing dependencies + shell: bash -l {0} + run: | + python --version + pip install -r requirements.txt + conda info + conda list + - name: Building Loop wheel and installing + shell: bash -l {0} + run: | + python setup.py bdist_wheel + python setup.py bdist + - name: Publish wheels to PyPI + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + shell: bash -l {0} + run : | + pip install twine + twine upload dist/* +