import os
import shutil
import sys
from abc import ABC
import numpy as np
import pandas as pn
from typing import Union, Iterable
import warnings
from gempy.core.data_modules.geometric_data import Orientations, SurfacePoints, \
ScalingSystem, Surfaces, Grid
from gempy.core.data_modules.stack import Stack, Faults, Series
from gempy.core.data import AdditionalData, MetaData, Options, Structure, \
KrigingParameters
from gempy.core.solution import Solution
from gempy.core.interpolator import InterpolatorModel, InterpolatorGravity
from gempy.utils.meta import _setdoc, _setdoc_pro
import gempy.utils.docstring as ds
from gempy.plot.decorators import *
pn.options.mode.chained_assignment = None
class RestrictingWrapper(object):
def __init__(self, w, accepted_members=['__repr__', '_repr_html_', '__str__']):
self._w = w
self._accepted_members = accepted_members
def __repr__(self):
return self._w.__repr__()
def __getattr__(self, item):
if item in self._accepted_members:
return getattr(self._w, item)
else:
raise AttributeError(item)
[docs]@_setdoc_pro([Grid.__doc__, Faults.__doc__, Series.__doc__, Surfaces.__doc__,
SurfacePoints.__doc__,
Orientations.__doc__, ScalingSystem.__doc__, AdditionalData.__doc__,
InterpolatorModel.__doc__,
Solution.__doc__])
class ImplicitCoKriging(object):
"""This class handles all the mutation of the data objects of the model involved on the
implicit cokriging ensuring the synchronization of all the members.
Attributes:
_grid (:class:`gempy.core.data.Grid`): [s0]
_faults (:class:`gempy.core.data.Grid`): [s1]
_stack (:class:`gempy.core.data_modules.stack.Stack`): [s2]
_surfaces (:class:`gempy.core.data.Surfaces`): [s3]
_surface_points (:class:`gempy.core.data_modules.geometric_data.SurfacePoints`): [s4]
_orientations (:class:`gempy.core.data_modules.geometric_data.Orientations`): [s5]
_rescaling (:class:`gempy.core.data_modules.geometric_data.Rescaling`): [s6]
_additional_data (:class:`gempy.core.data.AdditionalData`): [s7]
_interpolator (:class:`gempy.core.interpolator.InterpolatorModel`): [s8]
solutions (:class:`gempy.core.solutions.Solutions`): [s9]
"""
[docs] def __init__(self):
self._grid = Grid()
# Old way
self._faults = Faults()
self._stack = Series(self._faults)
# New way
self._stack = Stack()
self._faults = self._stack.faults
self._series = self._stack
self._surfaces = Surfaces(self._stack)
self._surface_points = SurfacePoints(self._surfaces)
self._orientations = Orientations(self._surfaces)
self._rescaling = ScalingSystem(self._surface_points, self._orientations,
self._grid)
self._additional_data = AdditionalData(self._surface_points,
self._orientations, self._grid,
self._faults,
self._surfaces, self._rescaling)
self._interpolator = InterpolatorModel(self._surface_points,
self._orientations, self._grid,
self._surfaces,
self._stack, self._faults,
self._additional_data)
self.solutions = Solution(self._grid, self._surfaces, self._stack)
# Previous values of sfai.
self._sfai_order_0 = None
@_setdoc_pro(Grid.__doc__)
@property
def grid(self):
""" :class:`gempy.core.data.Grid` [s0]
"""
return RestrictingWrapper(
self._grid,
accepted_members=['__repr__', '__str__', 'values', 'regular_grid',
'sections', 'centered_grid'])
@_setdoc_pro(Faults.__doc__)
@property
def faults(self):
""":class:`gempy.core.data_modules.stack.Faults` [s0]"""
return RestrictingWrapper(self._faults,
accepted_members=['__repr__', '_repr_html_',
'faults_relations_df'])
@_setdoc_pro(Stack.__doc__)
@property
def stack(self):
""":class:`gempy.core.data_modules.stack.Stack` [s0]"""
return RestrictingWrapper(self._stack,
accepted_members=['__repr__', '_repr_html_', 'df'])
@_setdoc_pro(Series.__doc__)
@property
def series(self):
""""""
# warnings.warn(DeprecationWarning, 'series will be deprecated in the future.'
# 'Use stack instead.')
return RestrictingWrapper(self._stack,
accepted_members=['__repr__', '_repr_html_', 'df'])
@_setdoc_pro(Surfaces.__doc__)
@property
def surfaces(self):
""":class:`gempy.core.data.Surfaces` [s0]"""
return RestrictingWrapper(self._surfaces,
accepted_members=['__repr__', '_repr_html_',
'colors', 'df'])
@_setdoc_pro(SurfacePoints.__doc__)
@property
def surface_points(self):
""":class:`gempy.core.data_modules.geometric_data.SurfacePoints` [s0]"""
return RestrictingWrapper(self._surface_points,
accepted_members=['__repr__', '_repr_html_', 'df'])
@_setdoc_pro(Orientations.__doc__)
@property
def orientations(self):
""":class:`gempy.core.data_modules.geometric_data.Orientations` [s0]"""
return RestrictingWrapper(self._orientations,
accepted_members=['__repr__', '_repr_html_', 'df'])
@_setdoc_pro(ScalingSystem.__doc__)
@property
def rescaling(self):
""":class:`gempy.core.data_modules.geometric_data.Rescaling` [s0]"""
return RestrictingWrapper(self._rescaling)
@_setdoc_pro(AdditionalData.__doc__)
@property
def additional_data(self):
""":class:`gempy.core.data.AdditionalData` [s0]"""
return RestrictingWrapper(self._additional_data,
accepted_members=['__repr__', '_repr_html_',
'structure_data', 'options',
'kriging_parameters',
'kriging_data',
'rescaling_data'])
@_setdoc_pro(InterpolatorModel.__doc__)
@property
def interpolator(self):
""":class:`gempy.core.interpolator.InterpolatorModel` [s0]"""
return RestrictingWrapper(self._interpolator,
accepted_members=['__repr__', '_repr_html_',
'theano_graph'])
def _add_valid_idx_s(self, idx):
if idx is None:
idx = self._surface_points.df.index.max()
if idx is np.nan:
idx = 0
else:
idx += 1
else:
assert isinstance(idx, (
int, list, np.ndarray)), 'idx must be an int or a list of ints'
return idx
def _add_valid_idx_o(self, idx):
if idx is None:
idx = self._orientations.df.index.max()
if idx is np.nan:
idx = 0
else:
idx += 1
else:
assert isinstance(idx, (
int, list, np.ndarray)), 'idx must be an int or a list of ints'
return idx
[docs] @_setdoc_pro([AdditionalData.update_structure.__doc__,
InterpolatorModel.set_theano_shared_structure.__doc__,
InterpolatorModel.modify_results_matrices_pro.__doc__,
InterpolatorModel.modify_results_weights.__doc__])
def update_structure(self, update_theano=None, update_series_is_active=True,
update_surface_is_active=True):
"""Update python and theano structure parameters.
[s0]
[s1]
Args:
update_theano (str{'matrices', 'weights'}):
* matrices [s2]
* weights [s3]
Returns:
:class:`gempy.core.data.Structure`
"""
self._additional_data.update_structure()
if update_series_is_active is True:
len_series_i = self._additional_data.structure_data.df.loc[
'values', 'len series surface_points'] - \
self._additional_data.structure_data.df.loc[
'values', 'number surfaces per series']
len_series_o = self._additional_data.structure_data.df.loc[
'values', 'len series orientations'].astype(
'int32')
# Remove series without data
non_zero_i = len_series_i.nonzero()[0]
non_zero_o = len_series_o.nonzero()[0]
non_zero = np.intersect1d(non_zero_i, non_zero_o)
bool_vec = np.zeros_like(self._stack.df['isActive'], dtype=bool)
bool_vec[non_zero] = True
self._stack.df['isActive'] = bool_vec
if update_surface_is_active is True:
act_series = self._surfaces.df['series'].map(
self._stack.df['isActive']).astype(bool)
unique_surf_points = np.unique(self._surface_points.df['id'])
if len(unique_surf_points) != 0:
bool_surf_points = np.zeros_like(act_series, dtype=bool)
bool_surf_points[unique_surf_points.astype('int') - 1] = True
# This is necessary to find the intersection between orientations
# (series) and surface points
self._surfaces.df['isActive'] = (
act_series & bool_surf_points) | self._surfaces.df['isBasement']
self._surfaces.df['hasData'] = (
act_series | bool_surf_points)
if update_theano == 'matrices':
self._interpolator.modify_results_matrices_pro()
elif update_theano == 'weights':
self._interpolator.modify_results_weights()
self._interpolator.set_theano_shared_structure()
return self._additional_data.structure_data
# region Grid
[docs] def update_from_grid(self):
"""Update objects dependent from the grid.
"""
self._rescaling.rescale_data()
self._interpolator.set_initial_results_matrices()
if 'gravity' in self._interpolator.theano_graph.output or 'magnetics' in self._interpolator.theano_graph.output:
self._interpolator.set_theano_shared_l0_l1()
# Check if grid is shared
if hasattr(self._interpolator.theano_graph.grid_val_T, 'get_value'):
self._interpolator.theano_graph.grid_val_T.set_value(
self._grid.values_r.astype(self._interpolator.dtype))
[docs] def set_active_grid(self, grid_name: Union[str, Iterable[str]], reset=False):
"""Set active a given or several grids.
Args:
grid_name (str, list[str]): Name of the grid you want to activate. Options are
{regular, custom, topography, centered}
reset (bool): If True set inactive all grids not in grid_name
Returns:
:class:`gempy.core.data.Grid`
"""
if reset is True:
self._grid.deactivate_all_grids()
self._grid.set_active(grid_name)
self.update_from_grid()
print(f'Active grids: {self._grid.grid_types[self._grid.active_grids]}')
return self._grid
def get_active_grids(self):
return self._grid.grid_types[self._grid.active_grids]
[docs] def set_grid_object(self, grid: Grid, update_model=True):
"""Not implemented
# TODO this should go to the api and let call all different grid types
Args:
grid:
update_model:
Returns:
"""
raise NotImplementedError
# @_setdoc(Grid.create_regular_grid.__doc__)
[docs] @_setdoc_pro()
def set_regular_grid(self, extent, resolution):
"""Set a regular grid, rescale data and initialize theano solutions.
Args:
extent: [s_extent]
resolution: [s_resolution]
Returns:
:class:`gempy.core.data.Grid`
See Also:
:class:`gempy.core.data.Grid.create_regular_grid`
:class:`gempy.core.data.grid_modules.grid_types.RegularGrid`
"""
if self._grid.regular_grid is None:
self._grid.create_regular_grid(extent=extent, resolution=resolution)
else:
self._grid.regular_grid.set_regular_grid(extent=extent,
resolution=resolution)
self._grid.set_active('regular')
if self._grid.topography is not None and self._grid.topography.values.shape[
0] != 0:
self._grid.regular_grid.set_topography_mask(self._grid.topography)
self.update_from_grid()
print(f'Active grids: {self._grid.grid_types[self._grid.active_grids]}')
return self._grid
[docs] @_setdoc_pro()
def set_custom_grid(self, custom_grid):
"""Set custom grid, rescale gird and initialize theano solutions. foo
Args:
custom_grid: [s_coord]
Returns:
:class:`gempy.core.data.Grid`
See Also:
:class:`gempy.core.data.Grid.create_custom_grid`
:class:`gempy.core.data.grid_modules.grid_types.CustomGrid`
"""
if self._grid.custom_grid is None:
self._grid.create_custom_grid(custom_grid)
else:
self._grid.custom_grid.set_custom_grid(custom_grid)
self._grid.update_grid_values()
self.update_from_grid()
print(f'Active grids: {self._grid.grid_types[self._grid.active_grids]}')
return self._grid
[docs] @plot_set_topography
def set_topography(self, source='random', set_mask=True, **kwargs):
"""Create a topography grid and activate it.
Args:
source:
* 'gdal': Load topography from a raster file.
* 'random': Generate random topography (based on a fractal grid).
* 'saved': Load topography that was saved with the topography.save() function.
this is useful after loading and saving a heavy raster file with gdal once
or after saving a random topography with the save() function. This .npy file can then be set as
topography.
Keyword Args:
source = 'gdal':
* filepath: path to raster file, e.g. '.tif', (for all file formats see
https://gdal.org/drivers/raster/index.html)
source = 'random':
* fd: fractal dimension, defaults to 2.0
* d_z: maximum height difference. If none, last 20% of the model in z direction
* extent: extent in xy direction. If none, geo_model.grid.extent
* resolution: desired resolution of the topography array. If none, geo_model.grid.resoution
source = 'saved':
* filepath: path to the .npy file that was created using the topography.save() function
source = 'numpy':
* array: numpy array containing the data
Returns:
:class:`gempy.core.data.Grid`
See Also:
:class:`gempy.core.grid_modules.grid_types.Topography`
"""
self._grid.create_topography(source, **kwargs)
if set_mask is True:
try:
self._grid.regular_grid.set_topography_mask(self._grid.topography)
except AttributeError:
pass
self.update_from_grid()
print(f'Active grids: {self._grid.grid_types[self._grid.active_grids]}')
return self._grid
[docs] @_setdoc_pro(Grid.create_centered_grid.__doc__)
def set_centered_grid(self, centers, radius, resolution=None):
"""[s0]
Args:
centers (numpy.ndarray[float, 3]): Location of the center of each kernel.
radius (float): Distance from each center to create each XYZ point
resolution (numpy.ndarray[3]): Number of voxels in each direction per kernel
Returns:
:class:`gempy.core.data.Grid`
See Also:
:class:`gempy.core.grid_modules.grid_types.CenteredGrid`
"""
if self._grid.centered_grid is None:
self._grid.create_centered_grid(centers, radius, resolution=resolution)
else:
self._grid.centered_grid.set_centered_grid(centers=centers,
radius=radius,
resolution=resolution)
self._grid.update_grid_values()
self.set_active_grid('centered')
self.update_from_grid()
# print(f'Active grids: {self._grid.grid_types[self._grid.active_grids]}')
return self._grid
[docs] @_setdoc_pro(Grid.create_section_grid.__doc__)
def set_section_grid(self, section_dict):
"""[s0]
Args:
section_dict: [s_section_dict]
Returns:
:class:`gempy.core.grid_modules.grid_types.Sections`
See Also:
:class:`gempy.core.grid_modules.grid_types.Sections`
"""
# TODO being able to change the regular grid associated to the section grid
if self._grid.sections is None:
self._grid.create_section_grid(section_dict=section_dict)
else:
self._grid.sections.set_sections(section_dict,
regular_grid=self._grid.regular_grid)
self.set_active_grid('sections')
self.update_from_grid()
return self._grid.sections
# endregion
# region Series
[docs] def set_series_object(self):
"""
Not implemented yet. Exchange the series object of the Model object.
"""
raise NotImplementedError
[docs] @_setdoc([Series.set_bottom_relation.__doc__], indent=False)
def set_bottom_relation(self, series: Union[str, list],
bottom_relation: Union[str, list]):
""""""
self._stack.set_bottom_relation(series, bottom_relation)
self._interpolator.set_theano_shared_relations()
return self._stack
[docs] @_setdoc_pro(Stack.reset_order_series.__doc__)
def add_features(self, features_list: Union[str, list], reset_order_series=True):
""" Add series, update the categories dependent on them and reset the flow control.
Args:
features_list: (str, list): name or list of names of the series to apply the functionality
reset_order_series: if true [s0]
Returns:
:class:`gempy.core.data_modules.stack.Stack`
"""
self._stack.add_series(features_list, reset_order_series)
self._surfaces.df['series'].cat.add_categories(features_list, inplace=True)
self._surface_points.df['series'].cat.add_categories(features_list,
inplace=True)
self._orientations.df['series'].cat.add_categories(features_list,
inplace=True)
self.update_structure()
self._interpolator.set_flow_control()
self._interpolator.set_theano_shared_kriging()
return self._stack
[docs] @_setdoc(Series.add_series.__doc__, indent=False)
def add_series(self, series_list: Union[str, list], reset_order_series=True):
warnings.warn('Series are getting renamed to Stack/features.'
'Please use add_features instead', DeprecationWarning, )
return self.add_features(series_list, reset_order_series)
[docs] @_setdoc_pro(Stack.reset_order_series.__doc__)
def delete_features(self, indices: Union[str, list], reset_order_features=True,
remove_surfaces=False, remove_data=False):
""" Delete series, update the categories dependent on them and reset the flow control.
Args:
indices (str, list): name or list of names of the series to apply the functionality
reset_order_features: (bool): if true [s0]
remove_surfaces (bool): if True remove the surfaces associated with the feature.
remove_data (bool): if True remove the geometric data associated with the feature
Returns:
:class:`gempy.core.data_modules.stack.Stack`
"""
indices = np.atleast_1d(indices)
self._stack.delete_series(indices, reset_order_features)
if remove_surfaces is True:
for s in indices:
self.delete_surfaces(
self._surfaces.df.groupby('series').get_group(s)['surface'],
remove_data=remove_data)
self._surfaces.df['series'].cat.remove_categories(indices, inplace=True)
self._surface_points.df['series'].cat.remove_categories(indices,
inplace=True)
self._orientations.df['series'].cat.remove_categories(indices, inplace=True)
self.map_geometric_data_df(self._surface_points.df)
self.map_geometric_data_df(self._orientations.df)
self.update_structure()
self._interpolator.set_theano_shared_relations()
self._interpolator.set_theano_shared_kriging()
self._interpolator.set_flow_control()
return self._stack
[docs] @_setdoc(delete_features.__doc__, indent=False)
def delete_series(self, indices: Union[str, list], refactor_order_series=True,
remove_surfaces=False, remove_data=False):
warnings.warn(DeprecationWarning,
'Series are getting renamed to Stack/features.'
'Please use delete_features instead')
return self.delete_features(indices, refactor_order_series,
remove_surfaces, remove_data)
[docs] @_setdoc(Series.rename_series.__doc__, indent=False)
def rename_features(self, new_categories: Union[dict, list]):
"""Rename features and update the category dependent on them.
Args:
new_categories (list, dict):
* list-like: all items must be unique and the number of items in the new
categories must match the existing number of categories.
* dict-like: specifies a mapping from old categories to new. Categories
not contained in the mapping are passed through and extra categories in the mapping are ignored.
Returns:
:class:`gempy.core.data_modules.stack.Stack`
"""
self._stack.rename_series(new_categories)
self._surfaces.df['series'].cat.rename_categories(new_categories,
inplace=True)
self._surface_points.df['series'].cat.rename_categories(new_categories,
inplace=True)
self._orientations.df['series'].cat.rename_categories(new_categories,
inplace=True)
return self._stack
[docs] @_setdoc(rename_features.__doc__, indent=False)
def rename_series(self, new_categories: Union[dict, list]):
warnings.warn('Series are getting renamed to Stack/features.'
'Please use rename_features instead', DeprecationWarning)
self.rename_features(new_categories)
[docs] def modify_order_features(self, new_value: int, idx: str):
"""Modify order of the feature. Reorder categories of the link Surfaces, sort surface (reset the basement layer)
remap the Stack and Surfaces to the corespondent dataframes, sort Geometric objects, update structure and
reset the flow control objects.
Args:
new_value (int): New location
idx (str): name of the feature to be moved
Returns:
:class:`gempy.core.data_modules.stack.Stack`
"""
self._stack.modify_order_series(new_value, idx)
self._surfaces.df['series'].cat.reorder_categories(
np.asarray(self._stack.df.index),
ordered=False, inplace=True)
self._surfaces.sort_surfaces()
self._surfaces.set_basement()
self.map_geometric_data_df(self._surface_points.df)
self._surface_points.sort_table()
self.map_geometric_data_df(self._orientations.df)
self._orientations.sort_table()
self._interpolator.set_flow_control()
self.update_structure()
return self._stack
[docs] @_setdoc(Series.modify_order_series.__doc__, indent=False)
def modify_order_series(self, new_value: int, idx: str):
warnings.warn('Series are getting renamed to Stack/features.'
'Please use modify_order_features instead',
DeprecationWarning, )
return self.modify_order_features(new_value, idx)
[docs] def reorder_features(self, new_categories: Iterable[str]):
"""Reorder series. Reorder categories of the link Surfaces, sort surface (reset the basement layer)
remap the Series and Surfaces to the corespondent dataframes, sort Geometric objects, update structure and
reset the flow control objects.
Args:
new_categories (list): list with all series names in the desired order.
Returns:
:class:`gempy.core.data_modules.stack.Stack`
"""
self._stack.reorder_series(new_categories)
self._surfaces.df['series'].cat.reorder_categories(
np.asarray(self._stack.df.index),
ordered=False, inplace=True)
self._surfaces.sort_surfaces()
self._surfaces.set_basement()
self.map_geometric_data_df(self._surface_points.df)
self._surface_points.sort_table()
self.map_geometric_data_df(self._orientations.df)
self._orientations.sort_table()
self._interpolator.set_flow_control()
self.update_structure(update_theano='weights')
return self._stack
[docs] @_setdoc(reorder_features.__doc__, indent=False)
def reorder_series(self, new_categories: Iterable[str]):
warnings.warn('Series are getting renamed to Stack/features.'
'Please use reorder_features instead', DeprecationWarning)
return self.reorder_features(new_categories)
# endregion
# region Faults
[docs] def set_fault_object(self):
"""Not implemented"""
raise NotImplementedError
[docs] @_setdoc([Faults.set_is_fault.__doc__], indent=False)
def set_is_fault(self, feature_fault: Union[str, list] = None,
toggle: bool = False,
change_color: bool = True, twofins=False):
"""
Set a feature to fault and update all dependent objects of the Model.
Args:
feature_fault(str, list[str]): Name of the series which are faults
toggle (bool): if True, passing a name which is already True will set it False.
twofins (bool): If True, it allows to set several surfaces of a given geological feature to fault.
This is behaviour is not tested and could have unexpected behaviour.
change_color (bool): If True faults surfaces get the default fault color (light gray)
Returns:
:class:`gempy.core.data_modules.stack.Faults`
See Also:
:class:`gempy.core.data_modules.stack.Faults.set_is_fault`
"""
feature_fault = np.atleast_1d(feature_fault)
if twofins is False:
for fault in feature_fault:
if self._surfaces.df.shape[0] == 0:
aux_assert = True
elif np.sum(self._surfaces.df.groupby('isBasement').get_group(False)[
'series'] == fault) < 2:
aux_assert = True
else:
aux_assert = False
assert aux_assert, \
'Having more than one fault in a series is generally rather bad. Better go' \
' back to the function map_series_to_surfaces and give each fault its own' \
' series. If you are really sure what you are doing, you can set twofins to' \
' True to suppress this error.'
self._faults.set_is_fault(feature_fault, toggle=toggle)
if toggle is True:
already_fault = self._stack.df.loc[
feature_fault, 'BottomRelation'] == 'Fault'
self._stack.df.loc[
feature_fault[already_fault], 'BottomRelation'] = 'Erosion'
self._stack.df.loc[
feature_fault[~already_fault], 'BottomRelation'] = 'Fault'
else:
self._stack.df.loc[feature_fault, 'BottomRelation'] = 'Fault'
self._additional_data.structure_data.set_number_of_faults()
self._interpolator.set_theano_shared_relations()
self._interpolator.set_theano_shared_loop()
if change_color:
print(
'Fault colors changed. If you do not like this behavior, set change_color to False.')
self._surfaces.colors.make_faults_black(feature_fault)
self.update_from_series(False, False, False)
self.update_structure(update_theano='matrices')
return self._faults
[docs] @_setdoc([Faults.set_is_finite_fault.__doc__], indent=False)
def set_is_finite_fault(self, series_fault=None, toggle: bool = True):
""""""
s = self._faults.set_is_finite_fault(series_fault,
toggle) # change df in Fault obj
# change shared theano variable for infinite factor
self._interpolator.set_theano_shared_is_finite()
return s
[docs] @_setdoc([Faults.set_fault_relation.__doc__], indent=False)
def set_fault_relation(self, rel_matrix):
""""""
self._faults.set_fault_relation(rel_matrix)
# Updating
self._interpolator.set_theano_shared_fault_relation()
self._interpolator.set_theano_shared_weights()
return self._faults.faults_relations_df
# endregion
# region Surfaces
[docs] def set_surfaces_object(self):
"""
Not implemented yet. Exchange the surface object of the Model object
Returns:
"""
raise NotImplementedError
[docs] @_setdoc(Surfaces.add_surface.__doc__, indent=False)
def add_surfaces(self, surface_list: Union[str, list], update_df=True):
self._surfaces.add_surface(surface_list, update_df)
self._surface_points.df['surface'].cat.add_categories(surface_list,
inplace=True)
self._orientations.df['surface'].cat.add_categories(surface_list,
inplace=True)
self.update_structure()
return self._surfaces
[docs] @_setdoc_pro([Surfaces.update_id.__doc__])
def delete_surfaces(self, indices: Union[str, Iterable[str]], update_id=True,
remove_data=True):
"""
@TODO When implemeted activate geometric data, change remove data to False by default
Delete a surface and update all related object.
Args:
indices (str, list): name or list of names of the series to apply the functionality
update_id (bool): if true [s0]
remove_data (bool): if true delete all GeometricData labeled with the given surface.
Returns:
:class:`gempy.core.data.Surfaces`
"""
indices = np.atleast_1d(indices)
self._surfaces.delete_surface(indices, update_id)
if indices.dtype == int:
surfaces_names = self._surfaces.df.loc[indices, 'surface']
else:
surfaces_names = indices
if remove_data:
self._surface_points.del_surface_points(
self._surface_points.df[
self._surface_points.df.surface.isin(surfaces_names)].index)
self._orientations.del_orientation(
self._orientations.df[
self._orientations.df.surface.isin(surfaces_names)].index)
self._surface_points.df['surface'].cat.remove_categories(surfaces_names,
inplace=True)
self._orientations.df['surface'].cat.remove_categories(surfaces_names,
inplace=True)
self.map_geometric_data_df(self._surface_points.df)
self.map_geometric_data_df(self._orientations.df)
self._surfaces.colors.delete_colors(surfaces_names)
if remove_data:
self.update_structure(update_theano='matrices')
self.update_structure(update_theano='weights')
return self._surfaces
[docs] @_setdoc(Surfaces.rename_surfaces.__doc__, indent=False)
def rename_surfaces(self, to_replace: Union[dict], **kwargs):
self._surfaces.rename_surfaces(to_replace, **kwargs)
self._surface_points.df['surface'].cat.rename_categories(to_replace,
inplace=True)
self._orientations.df['surface'].cat.rename_categories(to_replace,
inplace=True)
return self._surfaces
[docs] @_setdoc(Surfaces.modify_order_surfaces.__doc__, indent=False)
def modify_order_surfaces(self, new_value: int, idx: int,
series_name: str = None):
""""""
self._surfaces.modify_order_surfaces(new_value, idx, series_name)
self.map_geometric_data_df(self._surface_points.df)
self._surface_points.sort_table()
self.map_geometric_data_df(self._orientations.df)
self._orientations.sort_table()
self.update_structure()
return self._surfaces
[docs] @_setdoc(Surfaces.add_surfaces_values.__doc__, indent=False)
def add_surface_values(self, values_array: Iterable,
properties_names: Iterable[str] = np.empty(0)):
self._surfaces.add_surfaces_values(values_array, properties_names)
self.update_structure(update_theano='matrices')
return self._surfaces
[docs] @_setdoc(Surfaces.delete_surface_values.__doc__, indent=False)
def delete_surface_values(self, properties_names: list):
self.delete_surface_values(properties_names)
return self._surfaces
[docs] @_setdoc(Surfaces.modify_surface_values.__doc__, indent=False)
def modify_surface_values(self, idx, properties_names, values):
self._surfaces.modify_surface_values(idx, properties_names, values)
return self._surfaces
[docs] @_setdoc(Surfaces.set_surfaces_values.__doc__, indent=False)
def set_surface_values(self, values_array: Iterable,
properties_names: list = np.empty(0)):
self._surfaces.set_surfaces_values(values_array, properties_names)
return self._surfaces
[docs] def map_stack_to_surfaces(self,
mapping_object: Union[dict, pn.Categorical] = None,
set_series=True, sort_geometric_data: bool = True,
remove_unused_series=True,
twofins=False
):
"""Map series to surfaces and update all related objects accordingly to the following arguments:
Args:
mapping_object (dict, :class:`pandas.DataFrame`):
* dict: keys are the series and values the surfaces belonging to that series
* pandas.DataFrame: Dataframe with surfaces as index and a column series with the correspondent series
name of each surface
set_series (bool): if True, if mapping object has non existing series they will be created.
sort_geometric_data (bool): If true geometric data will be sorted accordingly to the new order of the
series
remove_unused_series (bool): if true, if an existing series is not assigned with a surface, it will get
removed from the Series object.
twofins (bool): If True, it allows to set several surfaces of a given geological feature to fault.
This is behaviour is not tested and could have unexpected behaviour.
Returns:
:class:`gempy.core.data.Surfaces`
"""
# Add New series to the series df
if set_series is True:
if type(mapping_object) is dict:
series_list = list(mapping_object.keys())
self._stack.add_series(series_list)
elif isinstance(mapping_object, pn.Categorical):
series_list = mapping_object['series'].values
self._stack.add_series(series_list)
else:
raise AttributeError(
str(type(mapping_object)) + ' is not the right attribute type.')
self._surfaces.map_series(mapping_object)
# Here we remove the series that were not assigned to a surface
if remove_unused_series is True:
self._surfaces.df['series'].cat.remove_unused_categories(inplace=True)
unused_cat = self._stack.df.index[~self._stack.df.index.isin(
self._surfaces.df['series'].cat.categories)]
self._stack.delete_series(unused_cat)
self._stack.reset_order_series()
self.update_from_surfaces()
self.update_from_series()
if sort_geometric_data is True:
self._surface_points.sort_table()
self._orientations.sort_table()
if set_series is True and self._stack.df.index.isin(['Basement']).any():
aux = self._stack.df.index.drop('Basement').array
self.reorder_features(np.append(aux, 'Basement'))
if twofins is False: # assert if every fault has its own series
for serie in list(
self._faults.df[self._faults.df['isFault'] == True].index):
assert np.sum(self._surfaces.df['series'] == serie) < 2, \
'Having more than one fault in a series is generally rather bad. Better give each ' \
'fault its own series. If you are really sure what you are doing, you can set ' \
'twofins to True to suppress this error.'
self.update_structure()
return self.surfaces
[docs] @_setdoc(map_stack_to_surfaces.__doc__, indent=False)
def map_series_to_surfaces(self, *args, **kwargs):
return self.map_stack_to_surfaces(*args, **kwargs)
# endregion
# region Surface_points
[docs] def set_surface_points_object(self, surface_points: SurfacePoints,
update_model=True):
"""Not Implemented"""
raise NotImplementedError
@staticmethod
def _check_possible_column_names(table, possible_candidates):
possible_candidates = np.array(possible_candidates)
return possible_candidates[np.isin(possible_candidates, table.columns)][0]
[docs] @_setdoc_pro(SurfacePoints.set_surface_points.__doc__)
def set_surface_points(self, table: pn.DataFrame, **kwargs):
"""Set coordinates and surface columns on the df.
Args:
table (pandas.Dataframe): table with surface points data.
Keyword Args:
bool add_basement: add a basement surface to the df.
See Also:
:class:`gempy.core.data_modules.geometric_data.SurfacePoints`
"""
coord_x_name = kwargs.get('coord_x_name') if 'coord_x_name' in kwargs \
else self._check_possible_column_names(table, ['X', 'x'])
coord_y_name = kwargs.get('coord_y_name') if 'coord_y_name' in kwargs \
else self._check_possible_column_names(table, ['Y', 'y'])
coord_z_name = kwargs.get('coord_z_name') if 'coord_z_name' in kwargs \
else self._check_possible_column_names(table, ['Z', 'z'])
surface_name = kwargs.get('surface_name') if 'surface_name' in kwargs \
else self._check_possible_column_names(table,
['surface', 'Surface', 'surfaces',
'surfaces', 'formations',
'formation'])
update_surfaces = kwargs.get('update_surfaces', True)
if update_surfaces is True:
self.add_surfaces(table[surface_name].unique())
c = np.array(self._surface_points._columns_i_1)
surface_points_table = table.assign(
**dict.fromkeys(c[~np.in1d(c, table.columns)], np.nan))
self._surface_points.set_surface_points(
surface_points_table[[coord_x_name, coord_y_name, coord_z_name]],
surface=surface_points_table[surface_name])
if 'add_basement' in kwargs:
if kwargs['add_basement'] is True:
self._surfaces.add_surface(['basement'])
self.map_stack_to_surfaces({'Basement': 'basement'}, set_series=True)
self.map_geometric_data_df(self._surface_points.df)
self._rescaling.rescale_data()
self.update_structure()
return self._surface_points
[docs] @_setdoc(Orientations.set_orientations.__doc__, indent=False, position='beg')
def set_orientations(self, table: pn.DataFrame, **kwargs):
""" Set coordinates, surface and orientation data.
If both are passed pole vector has priority over orientation
Args:
table (pn.Dataframe): table with surface points data.
Returns:
:class:`gempy.core.data_modules.geometric_data.Orientations`
See Also:
:class:`gempy.core.data_modules.geometric_data.Orientations`
"""
g_x_name = kwargs.get('G_x_name', 'G_x')
g_y_name = kwargs.get('G_y_name', 'G_y')
g_z_name = kwargs.get('G_z_name', 'G_z')
azimuth_name = kwargs.get('azimuth_name', 'azimuth')
dip_name = kwargs.get('dip_name', 'dip')
polarity_name = kwargs.get('polarity_name', 'polarity')
update_surfaces = kwargs.get('update_surfaces', False)
coord_x_name = kwargs.get('coord_x_name') if 'coord_x_name' in kwargs \
else self._check_possible_column_names(table, ['X', 'x'])
coord_y_name = kwargs.get('coord_y_name') if 'coord_y_name' in kwargs \
else self._check_possible_column_names(table, ['Y', 'y'])
coord_z_name = kwargs.get('coord_z_name') if 'coord_z_name' in kwargs \
else self._check_possible_column_names(table, ['Z', 'z'])
surface_name = kwargs.get('surface_name') if 'surface_name' in kwargs \
else self._check_possible_column_names(table,
['surface', 'Surface', 'surfaces',
'surfaces', 'formations',
'formation', 'Formation'])
if update_surfaces is True:
self.add_surfaces(table[surface_name].unique())
c = np.array(self._orientations._columns_o_1)
orientations_read = table.assign(
**dict.fromkeys(c[~np.in1d(c, table.columns)], np.nan))
self._orientations.set_orientations(
coord=orientations_read[[coord_x_name, coord_y_name, coord_z_name]],
pole_vector=orientations_read[[g_x_name, g_y_name, g_z_name]].values,
orientation=orientations_read[
[azimuth_name, dip_name, polarity_name]].values,
surface=orientations_read[surface_name])
self.map_geometric_data_df(self._orientations.df)
self._rescaling.rescale_data()
self.update_structure()
return self._orientations
[docs] @_setdoc_pro(ds.recompute_rf)
@_setdoc(SurfacePoints.add_surface_points.__doc__, indent=False, position='beg')
@plot_add_surface_points
def add_surface_points(self, X, Y, Z, surface,
idx: Union[int, Iterable[int]] = None,
recompute_rescale_factor=False):
"""
Args:
X:
Y:
Z:
surface (str):
idx: Index of the point. If None, next available index will be used
recompute_rescale_factor (bool): [s0].
"""
surface = np.atleast_1d(surface)
idx = self._add_valid_idx_s(idx)
self._surface_points.add_surface_points(X, Y, Z, surface, idx)
if recompute_rescale_factor is True or idx < 20:
# This will rescale all data again
self._rescaling.rescale_data()
self._interpolator.set_theano_shared_kriging()
else:
# This branch only recompute the added point
self._rescaling.set_rescaled_surface_points(idx)
self.update_structure(update_theano='matrices')
self._interpolator.set_theano_shared_nuggets()
return self._surface_points, idx
[docs] @_setdoc(SurfacePoints.del_surface_points.__doc__, indent=False, position='beg')
@plot_delete_surface_points
def delete_surface_points(self, idx: Union[int, Iterable[int]]):
self._surface_points.del_surface_points(idx)
self.update_structure(update_theano='matrices')
return self._surface_points
[docs] def delete_surface_points_basement(self):
"""Delete surface points belonging to the basement layer if any"""
basement_name = \
self._surfaces.df['surface'][self._surfaces.df['isBasement']].values[0]
select = (self._surface_points.df['surface'] == basement_name)
self.delete_surface_points(self._surface_points.df.index[select])
return True
[docs] @_setdoc_pro()
@plot_move_surface_points
def modify_surface_points(self, indices: Union[int, list],
recompute_rescale_factor=False, **kwargs):
"""Allows modification of the x,y and/or z-coordinates of an interface at specified dataframe index.
Args:
indices: [s_idx_sp]
recompute_rescale_factor: [s_recompute_rf]
Keyword Args:
* X: [s_x]
* Y: [s_y]
* Z: [s_z]
* surface: [s_surface_sp]
Returns:
:class:`gempy.core.data_modules.geometric_data.SurfacePoints`
"""
keys = list(kwargs.keys())
is_surface = np.isin('surface', keys).all()
if is_surface:
assert (
~self._surfaces.df[self._surfaces.df['isBasement']]['surface'].isin(
np.atleast_1d(kwargs['surface']))).any(), \
'Surface points cannot belong to Basement. Add a new surface.'
self._surface_points.modify_surface_points(indices, **kwargs)
if recompute_rescale_factor is True or np.atleast_1d(indices).shape[0] < 20:
# This will rescale all data again
self._rescaling.rescale_data()
self._interpolator.set_theano_shared_kriging()
else:
# This branch only recompute the added point
self._rescaling.set_rescaled_surface_points(indices)
keys = list(kwargs.keys())
is_surface = np.isin('surface', keys).all()
if is_surface == True:
self.update_structure(update_theano='matrices')
if 'smooth' in kwargs:
self._interpolator.set_theano_shared_nuggets()
return self._surface_points
# endregion
# region Orientation
def set_orientations_object(self, orientations: Orientations, update_model=True):
raise NotImplementedError
[docs] @_setdoc_pro()
@plot_add_orientation
def add_orientations(self, X, Y, Z, surface, pole_vector: Iterable = None,
orientation: Iterable = None, idx=None,
recompute_rescale_factor=False):
"""Add orientation.
Args:
X: [s_x]
Y: [s_y]
Z: [s_z]
surface: [s_surface_sp]
pole_vector: [s_pole_vector]
orientation: [s_orientations]
idx: [s_idx_sp]
recompute_rescale_factor: [s_recompute_rf]
Returns:
:class:`gempy.core.data_modules.geometric_data.Orientations`
"""
surface = np.atleast_1d(surface)
idx = self._add_valid_idx_o(idx)
self._orientations.add_orientation(X, Y, Z, surface, pole_vector=pole_vector,
orientation=orientation, idx=idx)
if recompute_rescale_factor is True or idx < 5:
# This will rescale all data again
self._rescaling.rescale_data()
else:
# This branch only recompute the added point
self._rescaling.set_rescaled_orientations(idx)
self.update_structure(update_theano='weights')
self._interpolator.set_theano_shared_nuggets()
return self._orientations, idx
[docs] @_setdoc(Orientations.del_orientation.__doc__, indent=False, position='beg')
@plot_delete_orientations
def delete_orientations(self, idx: Union[list, int]):
self._orientations.del_orientation(idx)
self.update_structure(update_theano='weights')
return self._orientations
[docs] @_setdoc(Orientations.modify_orientations.__doc__, indent=False, position='beg')
@plot_move_orientations
def modify_orientations(self, idx: list, **kwargs):
idx = np.array(idx, ndmin=1)
keys = list(kwargs.keys())
is_surface = np.isin('surface', keys).all()
self._orientations.modify_orientations(idx, **kwargs)
self._rescaling.set_rescaled_orientations(idx)
if is_surface:
self.update_structure(update_theano='weights')
if 'smooth' in kwargs:
self._interpolator.set_theano_shared_nuggets()
return self._orientations
# endregion
# region Options
[docs] @_setdoc(Options.modify_options.__doc__, indent=False, position='beg')
def modify_options(self, attribute, value):
self._additional_data.options.modify_options(attribute, value)
warnings.warn(
'You need to recompile the Theano code to make it the changes in options.')
return self._additional_data.options
# endregion
# region Kriging
[docs] @_setdoc(KrigingParameters.modify_kriging_parameters.__doc__, indent=False,
position='beg')
def modify_kriging_parameters(self, attribute, value, **kwargs):
self._additional_data.kriging_data.modify_kriging_parameters(attribute,
value, **kwargs)
self._interpolator.set_theano_shared_kriging()
if attribute == 'drift equations':
self._interpolator.set_initial_results()
self.update_structure()
return self._additional_data.kriging_data
# endregion
# region rescaling
[docs] @_setdoc(ScalingSystem.modify_rescaling_parameters.__doc__, indent=False,
position='beg')
def modify_rescaling_parameters(self, attribute, value):
self._additional_data.rescaling_data.modify_rescaling_parameters(attribute,
value)
self._additional_data.rescaling_data.rescale_data()
self._additional_data.update_default_kriging()
return self._additional_data.rescaling_data
# endregion
# ======================================
# --------------------------------------
# ======================================
[docs] def set_default_surface_point(self, **kwargs):
"""Set a default surface point if the df is empty. This is necessary for some
type of functionality such as qgrid.
Args:
**kwargs: Same as :func:`gempy.core.data_modules.geometric_data.SurfacePoints.add_surface_points`
Returns:
:class:`gempy.core.data_modules.geometric_data.SurfacePoints`
"""
if self._surface_points.df.shape[0] == 0:
self.add_surface_points(0.00001, 0.00001, 0.00001,
self._surfaces.df['surface'].iloc[0],
recompute_rescale_factor=True, **kwargs)
return self._surface_points
[docs] def set_default_orientation(self, **kwargs):
"""Set a default orientation if the df is empty. This is necessary for some type of functionality such as qgrid
Args:
**kwargs: Same as :func::class:`gempy.core.data_modules.geometric_data.Orientations.add_orientation`
Returns:
:class:`gempy.core.data_modules.geometric_data.Orientations`
"""
if self._orientations.df.shape[0] == 0:
# TODO DEBUG: I am not sure that surfaces always has at least one entry. Check it
self.add_orientations(.00001, .00001, .00001,
self._surfaces.df['surface'].iloc[0],
[0, 0, 1], recompute_rescale_factor=True, **kwargs)
[docs] def set_default_surfaces(self):
"""Set two default surfaces if the df is empty. This is necessary for some type of functionality such as qgrid
Returns:
:class:`gempy.core.data.Surfaces`
"""
if len(self._surfaces.df['surface']) != 0:
self.delete_surfaces(self._surfaces.df['surface'])
if self._surfaces.df.shape[0] == 0:
self.add_surfaces(['surface1', 'surface2'])
self.update_from_surfaces()
return self._surfaces
[docs] @_setdoc_pro()
def set_extent(self, extent: Iterable):
"""
Set project extent
Args:
extent: [s_extent]
Returns:
:class:`gempy.core.data.Grid`
"""
extent = np.atleast_1d(extent)
self._grid.extent = extent
self._rescaling.set_rescaled_grid()
return self._grid
[docs] def update_from_series(self, reorder_series=True, sort_geometric_data=True,
update_interpolator=True):
"""Update all objects dependent on series.
This method is a bit of a legacy and has been substituted by :meth:`rename_series` and :meth:`reorder_series`,
however is useful if you want to make sure all objects are up to date with the latest changes on series.
Args:
reorder_series (bool): if True reorder all pandas categories accordingly to the series.df
sort_geometric_data (bool): It True sort the geometric data after mapping the new order
update_interpolator (bool): If True update the theano shared variables dependent on the structure
Returns:
True
"""
if reorder_series is True:
self._surfaces.df['series'].cat.reorder_categories(
np.asarray(self._stack.df.index),
ordered=False, inplace=True)
self._stack.df.index = self._stack.df.index.reorder_categories(
self._stack.df.index.array,
ordered=False)
self._surfaces.sort_surfaces()
self.update_from_surfaces(set_categories_from_series=False,
set_categories_from_surfaces=True,
map_surface_points=False,
map_orientations=False,
update_structural_data=False)
# Update surface is active from series does not work because you can have only a subset of surfaces of a
# series active
self._surfaces.df['isFault'] = self._surfaces.df['series'].map(
self._faults.df['isFault'])
self._surfaces.set_basement()
# Add categories from series
self._surface_points.set_series_categories_from_series(self._stack)
self._orientations.set_series_categories_from_series(self._stack)
self._surface_points.map_data_from_series(self._stack, 'order_series')
self._orientations.map_data_from_series(self._stack, 'order_series')
if sort_geometric_data is True:
self._surface_points.sort_table()
self._orientations.sort_table()
self._additional_data.update_structure()
# For the drift equations.
self._additional_data.update_default_kriging()
if update_interpolator is True:
self._interpolator.set_theano_shared_structure(reset_ctrl=True)
return True
[docs] def update_from_surfaces(self, set_categories_from_series=True,
set_categories_from_surfaces=True,
map_surface_points=True, map_orientations=True,
update_structural_data=True):
"""
Update all objects dependt on surfaces.
Args:
set_categories_from_series (bool): If True update the pandas categories with the Series object
set_categories_from_surfaces (bool): If True update the pandas categories with the surfaces object
map_surface_points (bool): If True map the surface points fields with the Surfaces obejct
map_orientations (bool): If True map the orientations fields with the Surfaces object
update_structural_data (bool): If true update the Structure with the Surface object
Returns:
True
"""
# Add categories from series
if set_categories_from_series is True:
self._surface_points.set_series_categories_from_series(
self._surfaces.series)
self._orientations.set_series_categories_from_series(
self._surfaces.series)
# Add categories from surfaces
if set_categories_from_surfaces is True:
self._surface_points.set_surface_categories_from_surfaces(self._surfaces)
self._orientations.set_surface_categories_from_surfaces(self._surfaces)
if map_surface_points is True:
self._surface_points.map_data_from_surfaces(self._surfaces, 'series')
self._surface_points.map_data_from_surfaces(self._surfaces, 'id')
if map_orientations is True:
self._orientations.map_data_from_surfaces(self._surfaces, 'series')
self._orientations.map_data_from_surfaces(self._surfaces, 'id')
if update_structural_data is True:
self._additional_data.update_structure()
return True
# region Theano interface
[docs] @_setdoc(InterpolatorModel.__doc__)
def set_theano_graph(self, interpolator: InterpolatorModel,
update_structure=True, update_kriging=True):
""" Pass a theano graph of a Interpolator instance other than the Model compose
Use this method only if you know what are you doing!
Args:
interpolator (:class:`InterpolatorModel`): [s0]
Returns:
True """
warnings.warn(
'This function is going to be deprecated. Use Model.set_theano_function instead',
DeprecationWarning)
self._interpolator.theano_graph = interpolator.theano_graph
self._interpolator.theano_function = interpolator.theano_function
self.update_additional_data(update_structure=update_structure,
update_kriging=update_kriging)
self.update_to_interpolator()
return True
# @_setdoc(InterpolatorModel.__doc__)
[docs] def set_theano_function(self, interpolator: InterpolatorModel,
update_structure=True, update_kriging=True):
"""Pass a theano function and its correspondent graph from an Interpolator
instance other than the Model compose
Args:
interpolator (:class:`gempy.core.interpolator.InterpolatorModel`): interpolator object
with the compile graph.
update_kriging (bool): if True update kriging parameters
update_structure (bool): if Ture update structure
Returns:
bool: True
See Also:
:class:`gempy.core.interpolator.InterpolatorModel`
"""
self._interpolator.theano_graph = interpolator.theano_graph
self._interpolator.theano_function = interpolator.theano_function
self.update_additional_data(update_structure=update_structure,
update_kriging=update_kriging)
self.update_to_interpolator()
return True
def update_additional_data(self, update_structure=True, update_kriging=True):
if update_structure is True:
self.update_structure(update_theano='matrices')
if update_kriging is True:
print('Setting kriging parameters to their default values.')
self._additional_data.update_default_kriging()
return self._additional_data
[docs] def update_to_interpolator(self, reset=True):
"""Update all shared parameters from the data objects
Args:
reset (bool): if True reset the flow control and initialize results arrays
Returns:
True
"""
self._interpolator.set_all_shared_parameters()
if reset is True:
self._interpolator.reset_flow_control_initial_results()
return True
# endregion
[docs] def map_geometric_data_df(self, d: pn.DataFrame):
"""
Map a geometric data dataframe from the linked objects (at 07.2019 surfaces and series)
Args:
d (pn.DataFrame): Geometric data dataframe to be mapped
Returns:
DataFrame
"""
d['series'] = d['surface'].map(
self._surfaces.df.set_index('surface')['series'])
d['id'] = d['surface'].map(
self._surfaces.df.set_index('surface')['id']).astype(int)
d['order_series'] = d['series'].map(self._stack.df['order_series']).astype(
int)
return d
[docs] def set_surface_order_from_solution(self):
"""
Order the surfaces respect the last computation. Therefore if you call this method,
after sorting surface_points without recomputing you may get wrong results.
Returns:
Surfaces
"""
sfai_order = self.solutions.scalar_field_at_surface_points.sum(axis=0)
# Check if the order has changed
if not np.array_equal(sfai_order, self._sfai_order_0):
self._sfai_order_0 = sfai_order
sel = self._surfaces.df['isActive'] & ~self._surfaces.df['isBasement']
self._surfaces.df.loc[sel, 'sfai'] = sfai_order
self._surfaces.df.sort_values(by=['series', 'sfai'], inplace=True,
ascending=False)
self._surfaces.reset_order_surfaces()
self._surfaces.sort_surfaces()
self._surfaces.set_basement()
self._surface_points.df['id'] = self._surface_points.df['surface'].map(
self._surfaces.df.set_index('surface')['id']).astype(int)
self._orientations.df['id'] = self._orientations.df['surface'].map(
self._surfaces.df.set_index('surface')['id']).astype(int)
self._surface_points.sort_table()
self._orientations.sort_table()
self.update_structure()
return self._surfaces
def Model(project_name='default_project'):
""" Container class of all objects that constitute a GemPy model.
In addition the class provides the methods that act in more than one of this class. Model is a child class of
:class:`DataMutation` and :class:`MetaData`.
"""
warnings.warn('This C;ass is going to be deprecated in GemPy 2.3. '
'Use Project instead.',
DeprecationWarning)
return Project(project_name)
[docs]class Project(ImplicitCoKriging):
"""Container class of all objects that constitute a GemPy model.
In addition the class provides all the methods you need to construct a geological
model with :class:`ImplicitCoKriging`.
See Also:
:class:`MetaData`, :class:`ImplicitCoKriging`
"""
[docs] def __init__(self, project_name='default_project'):
self.meta = MetaData(project_name=project_name)
super().__init__()
def __repr__(self):
return self.meta.project_name + ' ' + self.meta.date
[docs] def new_model(self, name_project='default_project'):
"""Reset the model object."""
self.__init__(name_project)
[docs] def save_model_pickle(self, path=False):
"""
Short term model storage. Object to a python pickle (serialization of python). Be aware that if the dependencies
versions used to export and import the pickle differ it may give problems
Args:
path (str): path where save the pickle
Returns:
True
"""
# Deleting qi attribute otherwise doesnt allow to pickle
if hasattr(self, 'qi'):
self.__delattr__('qi')
sys.setrecursionlimit(10000)
if not path:
path = './' + self.meta.project_name
import pickle
with open(path + '.pickle', 'wb') as f:
# Pickle the 'data' dictionary using the highest protocol available.
pickle.dump(self, f, pickle.HIGHEST_PROTOCOL)
return True
[docs] @staticmethod
def load_model_pickle(path):
"""
Read InputData object from python pickle.
Args:
path (str): path where save the pickle
Returns:
:class:`gempy.core.model.Model`
"""
import pickle
with open(path, 'rb') as f:
# The protocol version used is detected automatically, so we do not
# have to specify it.
model = pickle.load(f)
return model
[docs] def save_model(self, name=None, path=None, compress=True):
"""
Save model in new folder. Input data is saved as csv files. Solutions, extent and resolutions are saved as npy.
Args:
name (str): name of the newly created folder and the part of the files name
path (str): path where save the model folder.
compress (bool): If true create a zip
Returns:
True
"""
if name is None or path is None:
from gempy.api_modules.io import default_path_and_name
name, path = default_path_and_name(self, name, path)
# save dataframes as csv
self._surface_points.df.to_csv(f'{path}/{name}_surface_points.csv')
self._surfaces.df.to_csv(f'{path}/{name}_surfaces.csv')
self._orientations.df.to_csv(f'{path}/{name}_orientations.csv')
self._stack.df.to_csv(f'{path}/{name}_series.csv')
self._faults.df.to_csv(f'{path}/{name}_faults.csv')
self._faults.faults_relations_df.to_csv(
f'{path}/{name}_faults_relations.csv')
self._additional_data.kriging_data.df.to_csv(
f'{path}/{name}_kriging_data.csv')
self._additional_data.rescaling_data.df.to_csv(
f'{path}/{name}_rescaling_data.csv')
self._additional_data.options.df.to_csv(f'{path}/{name}_options.csv')
# # save resolution and extent as npy
np.save(f'{path}/{name}_extent.npy', self._grid.regular_grid.extent)
np.save(f'{path}/{name}_resolution.npy', self._grid.regular_grid.resolution)
if self._grid.topography is not None:
self._grid.topography.save(f'{path}/{name}_topography.npy')
# if compress is True:
# shutil.make_archive(name, 'zip', path)
# shutil.rmtree(path)
return True
def save_solution(self):
pass
[docs] def read_data(self, source_i=None, source_o=None, add_basement=True, **kwargs):
"""
Read data from a csv, or directly supplied dataframes
Args:
source_i: Path to the data bases of surface_points. Default os.getcwd(), or direct pandas data frame
source_o: Path to the data bases of orientations. Default os.getcwd(), or direct pandas data frame
add_basement (bool): if True add a basement surface. This wont be interpolated it just gives the values
for the volume below the last surface.
Keyword Args:
update_surfaces (bool): True
Returns:
bool: True
See Also:
* :class:`gempy.core.data_modules.geometric_data.SurfacePoints.read_surface_points.`
* :class:`gempy.core.data_modules.geometric_data.Orientations.read_orientations`
"""
if 'update_surfaces' not in kwargs:
kwargs['update_surfaces'] = True
if 'path_i' in kwargs:
source_i = kwargs['path_i']
if 'path_o' in kwargs:
source_o = kwargs['path_o']
if isinstance(source_i, pn.DataFrame) or source_i:
self._surface_points.read_surface_points(source_i, inplace=True,
**kwargs)
if isinstance(source_o, pn.DataFrame) or source_o:
self._orientations.read_orientations(source_o, inplace=True, **kwargs)
if add_basement is True:
self._surfaces.add_surface(['basement'])
self.map_stack_to_surfaces({'Basement': 'basement'}, set_series=True)
self._rescaling.rescale_data()
self._additional_data.update_structure()
self._additional_data.update_default_kriging()
return True
[docs] @_setdoc_pro()
def get_data(self, itype='data', verbosity=0, numeric=False):
"""Method to return the data stored in :class:`panda.DataFrame` within a
:class:`gempy.core.model.Project` data object.
Args:
itype: [s_itype]
numeric(bool): Return only the numerical values of the dataframe. This is much lighter database for storing
traces
verbosity (int): Number of properties shown
Returns:
pandas.DataFrame: Data Object df.
"""
if verbosity == 0:
show_par_f = self._orientations._columns_rend
show_par_i = self._surface_points._columns_rend
elif verbosity == 1:
show_par_f = self._orientations._columns_o_1
show_par_i = self._surface_points._columns_i_1
if numeric:
show_par_f = self._orientations._columns_o_num
show_par_i = self._surface_points._columns_i_num
dtype = 'float'
if itype == 'orientations':
raw_data = self._orientations.df[show_par_f] # .astype(dtype)
# Be sure that the columns are in order when used for operations
if numeric:
raw_data = raw_data[
['X', 'Y', 'Z', 'G_x', 'G_y', 'G_z', 'dip', 'azimuth',
'polarity']]
elif itype == 'surface_points' or itype == 'surface points':
raw_data = self._surface_points.df[show_par_i] # .astype(dtype)
# Be sure that the columns are in order when used for operations
if numeric:
raw_data = raw_data[
['X', 'Y', 'Z', 'G_x', 'G_y', 'G_z', 'dip', 'azimuth',
'polarity']]
elif itype == 'data':
raw_data = pn.concat(
[self._surface_points.df[show_par_i], # .astype(dtype),
self._orientations.df[show_par_f]], # .astype(dtype)],
keys=['surface_points', 'orientations'],
sort=False)
# Be sure that the columns are in order when used for operations
if numeric:
raw_data = raw_data[
['X', 'Y', 'Z', 'G_x', 'G_y', 'G_z', 'dip', 'azimuth',
'polarity']]
elif itype == 'surfaces':
raw_data = self._surfaces
elif itype == 'series':
raw_data = self._stack
elif itype == 'faults':
raw_data = self._faults
elif itype == 'faults_relations_df' or itype == 'faults_relations':
raw_data = self._faults.faults_relations_df
elif itype == 'additional data' or itype == 'additional_data':
raw_data = self._additional_data
elif itype == 'kriging':
raw_data = self._additional_data.kriging_data
else:
raise AttributeError(
'itype has to be \'data\', \'additional data\', \'surface_points\', \'orientations\','
' \'surfaces\',\'series\', \'faults\' or \'faults_relations_df\'')
return raw_data
def get_additional_data(self):
return self._additional_data.get_additional_data()