Skip to content

Commit 6aaa302

Browse files
committed
move to rely on boundingbox class for boundingbox
1 parent 8dbc401 commit 6aaa302

File tree

1 file changed

+47
-153
lines changed

1 file changed

+47
-153
lines changed

LoopStructural/modelling/core/geological_model.py

Lines changed: 47 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Main entry point for creating a geological model
33
"""
44

5-
from ...utils import getLogger, log_to_file
5+
from ...utils import getLogger
66

77
import numpy as np
88
import pandas as pd
@@ -61,33 +61,24 @@ class GeologicalModel:
6161
the origin of the model box
6262
parameters : dict
6363
a dictionary tracking the parameters used to build the model
64-
scale_factor : double
65-
the scale factor used to rescale the model
66-
64+
6765
6866
"""
6967

7068
def __init__(
7169
self,
72-
origin: np.ndarray,
73-
maximum: np.ndarray,
74-
data=None,
75-
nsteps=(50, 50, 25),
76-
reuse_supports=False,
77-
logfile=None,
78-
loglevel="info",
70+
*args
7971
):
8072
"""
8173
Parameters
8274
----------
83-
origin : numpy array
84-
specifying the origin of the model
85-
maximum : numpy array
86-
specifying the maximum extent of the model
87-
rescale : bool
88-
whether to rescale the model to between 0/1
89-
epsion : float
90-
a fudge factor for isosurfacing, used to make sure surfaces appear
75+
bounding_box : BoundingBox
76+
the bounding box of the model
77+
origin : np.array(3,dtype=doubles)
78+
the origin of the model
79+
maximum : np.array(3,dtype=doubles)
80+
the maximum of the model
81+
9182
Examples
9283
--------
9384
Demo data
@@ -111,38 +102,32 @@ def __init__(
111102
112103
113104
"""
114-
if logfile:
115-
self.logfile = logfile
116-
log_to_file(logfile, level=loglevel)
117-
105+
args = list(args)
106+
if len(args) == 0:
107+
raise ValueError("Must provide either bounding_box or origin and maximum")
108+
if len(args) == 1:
109+
bounding_box = args[0]
110+
if not isinstance(bounding_box, BoundingBox):
111+
raise ValueError("Must provide a bounding box")
112+
self.bounding_box = bounding_box
113+
if len(args) == 2:
114+
origin = args[0]
115+
maximum = args[1]
116+
if not isinstance(origin, np.ndarray) or not isinstance(maximum, np.ndarray):
117+
raise ValueError("Must provide origin and maximum as numpy arrays")
118+
self.bounding_box = BoundingBox(
119+
dimensions=3,
120+
origin=np.zeros(3),
121+
maximum=maximum - origin,
122+
global_origin=origin,
123+
)
118124
logger.info("Initialising geological model")
119125
self.features = []
120126
self.feature_name_index = {}
121127
self._data = pd.DataFrame() # None
122-
if data is not None:
123-
self.data = data
124-
self.nsteps = nsteps
125-
126-
# we want to rescale the model area so that the maximum length is
127-
# 1
128-
self.origin = np.array(origin).astype(float)
129-
originstr = f"Model origin: {self.origin[0]} {self.origin[1]} {self.origin[2]}"
130-
logger.info(originstr)
131-
self.maximum = np.array(maximum).astype(float)
132-
maximumstr = "Model maximum: {} {} {}".format(
133-
self.maximum[0], self.maximum[1], self.maximum[2]
134-
)
135-
logger.info(maximumstr)
136-
137-
self.scale_factor = 1.0
138-
139-
self.bounding_box = BoundingBox(
140-
dimensions=3,
141-
origin=np.zeros(3),
142-
maximum=self.maximum - self.origin,
143-
global_origin=self.origin,
144-
)
128+
145129

130+
146131
self.stratigraphic_column = None
147132

148133
self.tol = 1e-10 * np.max(self.bounding_box.maximum - self.bounding_box.origin)
@@ -160,10 +145,7 @@ def to_dict(self):
160145
json = {}
161146
json["model"] = {}
162147
json["model"]["features"] = [f.name for f in self.features]
163-
# json["model"]["data"] = self.data.to_json()
164-
# json["model"]["origin"] = self.origin.tolist()
165-
# json["model"]["maximum"] = self.maximum.tolist()
166-
# json["model"]["nsteps"] = self.nsteps
148+
json['model']['bounding_box'] = self.bounding_box.to_dict()
167149
json["model"]["stratigraphic_column"] = self.stratigraphic_column
168150
# json["features"] = [f.to_json() for f in self.features]
169151
return json
@@ -192,86 +174,12 @@ def to_dict(self):
192174
# model.features.append(GeologicalFeature.from_json(feature,model))
193175
# return model
194176
def __str__(self):
195-
lengths = self.maximum - self.origin
196-
_str = "GeologicalModel - {} x {} x {}\n".format(*lengths)
197-
_str += "------------------------------------------ \n"
198-
_str += "The model contains {} GeologicalFeatures \n".format(len(self.features))
199-
_str += ""
200-
_str += "------------------------------------------ \n"
201-
_str += ""
202-
_str += "Model origin: {} {} {}\n".format(self.origin[0], self.origin[1], self.origin[2])
203-
_str += "Model maximum: {} {} {}\n".format(
204-
self.maximum[0], self.maximum[1], self.maximum[2]
205-
)
206-
_str += "Model rescale factor: {} \n".format(self.scale_factor)
207-
_str += "------------------------------------------ \n"
208-
_str += "Feature list: \n"
209-
for feature in self.features:
210-
_str += " {} \n".format(feature.name)
211-
return _str
177+
return f"GeologicalModel with {len(self.features)} features"
212178

213179
def _ipython_key_completions_(self):
214180
return self.feature_name_index.keys()
215181

216-
@classmethod
217-
def from_map2loop_directory(
218-
cls,
219-
m2l_directory,
220-
foliation_params={},
221-
fault_params={},
222-
use_thickness=True,
223-
vector_scale=1,
224-
gradient=False,
225-
**kwargs,
226-
):
227-
"""Alternate constructor for a geological model using m2l output
228-
229-
Uses the information saved in the map2loop files to build a geological model.
230-
You can specify kwargs for building foliation using foliation_params and for
231-
faults using fault_params. faults is a flag that allows for the faults to be
232-
skipped.
233-
234-
Parameters
235-
----------
236-
m2l_directory : string
237-
path to map2loop directory
238-
239-
Returns
240-
-------
241-
(GeologicalModel, dict)
242-
the created geological model and a dictionary of the map2loop data
243-
244-
Notes
245-
------
246-
For additional information see :class:`LoopStructural.modelling.input.Map2LoopProcessor`
247-
and :meth:`LoopStructural.GeologicalModel.from_processor`
248-
"""
249-
from LoopStructural.modelling.input.map2loop_processor import Map2LoopProcessor
250-
251-
log_to_file(f"{m2l_directory}/loopstructural_log.txt")
252-
logger.info("Creating model from m2l directory")
253-
processor = Map2LoopProcessor(m2l_directory, use_thickness)
254-
processor._gradient = gradient
255-
processor.vector_scale = vector_scale
256-
for foliation_name in processor.stratigraphic_column.keys():
257-
if foliation_name != "faults":
258-
if foliation_name in foliation_params.keys():
259-
processor.foliation_properties[foliation_name] = foliation_params[
260-
foliation_name
261-
]
262-
else:
263-
processor.foliation_properties[foliation_name] = foliation_params
264-
265-
for fault_name in processor.fault_names:
266-
if fault_name in fault_params.keys():
267-
for param_name, value in fault_params[fault_name].items():
268-
processor.fault_properties.loc[fault_name, param_name] = value
269-
else:
270-
for param_name, value in fault_params.items():
271-
processor.fault_properties.loc[fault_name, param_name] = value
272-
273-
model = GeologicalModel.from_processor(processor)
274-
return model, processor
182+
275183

276184
@classmethod
277185
def from_processor(cls, processor):
@@ -565,13 +473,9 @@ def data(self, data: pd.DataFrame):
565473
raise BaseException("Cannot load data")
566474
logger.info(f"Adding data to GeologicalModel with {len(data)} data points")
567475
self._data = data.copy()
568-
569-
self._data["X"] -= self.origin[0]
570-
self._data["Y"] -= self.origin[1]
571-
self._data["Z"] -= self.origin[2]
572-
self._data["X"] /= self.scale_factor
573-
self._data["Y"] /= self.scale_factor
574-
self._data["Z"] /= self.scale_factor
476+
self._data[['X','Y','Z']] = self.bounding_box.project(self._data[['X','Y','Z']].to_numpy())
477+
478+
575479
if "type" in self._data:
576480
logger.warning("'type' is deprecated replace with 'feature_name' \n")
577481
self._data.rename(columns={"type": "feature_name"}, inplace=True)
@@ -1413,7 +1317,7 @@ def create_and_add_fault(
14131317
if "data_region" in kwargs:
14141318
kwargs.pop("data_region")
14151319
logger.error("kwarg data_region currently not supported, disabling")
1416-
displacement_scaled = displacement / self.scale_factor
1320+
displacement_scaled = displacement
14171321
fault_frame_builder = FaultBuilder(
14181322
interpolatortype,
14191323
bounding_box=self.bounding_box,
@@ -1434,11 +1338,11 @@ def create_and_add_fault(
14341338
if fault_center is not None and ~np.isnan(fault_center).any():
14351339
fault_center = self.scale(fault_center, inplace=False)
14361340
if minor_axis:
1437-
minor_axis = minor_axis / self.scale_factor
1341+
minor_axis = minor_axis
14381342
if major_axis:
1439-
major_axis = major_axis / self.scale_factor
1343+
major_axis = major_axis
14401344
if intermediate_axis:
1441-
intermediate_axis = intermediate_axis / self.scale_factor
1345+
intermediate_axis = intermediate_axis
14421346
fault_frame_builder.create_data_from_geometry(
14431347
fault_frame_data=fault_data,
14441348
fault_center=fault_center,
@@ -1493,11 +1397,9 @@ def rescale(self, points: np.ndarray, *, inplace: bool = False) -> np.ndarray:
14931397
points : np.array((N,3),dtype=double)
14941398
14951399
"""
1496-
if not inplace:
1497-
points = points.copy()
1498-
points *= self.scale_factor
1499-
points += self.origin
1500-
return points
1400+
1401+
return self.bounding_box.reproject(points,inplace=inplace)
1402+
15011403

15021404
# TODO move scale to bounding box/transformer
15031405
def scale(self, points: np.ndarray, *, inplace: bool = False) -> np.ndarray:
@@ -1515,16 +1417,8 @@ def scale(self, points: np.ndarray, *, inplace: bool = False) -> np.ndarray:
15151417
points : np.a::rray((N,3),dtype=double)
15161418
15171419
"""
1518-
points = np.array(points).astype(float)
1519-
if not inplace:
1520-
points = points.copy()
1521-
# if len(points.shape) == 1:
1522-
# points = points[None,:]
1523-
# if len(points.shape) != 2:
1524-
# logger.error("cannot scale array of dimensions".format(len(points.shape)))
1525-
points -= self.origin
1526-
points /= self.scale_factor
1527-
return points
1420+
return self.bounding_box.project(np.array(points).astype(float),inplace=inplace)
1421+
15281422

15291423
def regular_grid(self, *, nsteps=None, shuffle=True, rescale=False, order="C"):
15301424
"""
@@ -1673,7 +1567,7 @@ def evaluate_fault_displacements(self, points, scale=True):
16731567
if f.type == FeatureType.FAULT:
16741568
disp = f.displacementfeature.evaluate_value(points)
16751569
vals[~np.isnan(disp)] += disp[~np.isnan(disp)]
1676-
return vals * -self.scale_factor # convert from restoration magnutude to displacement
1570+
return vals # convert from restoration magnutude to displacement
16771571

16781572
def get_feature_by_name(self, feature_name) -> GeologicalFeature:
16791573
"""Returns a feature from the mode given a name

0 commit comments

Comments
 (0)