Skip to content

Commit 7062f94

Browse files
author
Lachlan Grose
committed
Merge branch 'intrusions' of github.com:Loop3D/LoopStructural into intrusions
2 parents a78760f + 0b60347 commit 7062f94

20 files changed

+677
-741
lines changed

LoopStructural/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
ch = logging.StreamHandler()
1616
formatter = logging.Formatter(
17-
"%(asctime)s ~ %(name)-12s ~ %(levelname)-10s ~ %(message)s"
17+
"%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d -- %(message)s"
1818
)
1919
ch.setFormatter(formatter)
2020
ch.setLevel(logging.WARNING)

LoopStructural/datasets/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
from ._base import value_headers
1717
from ._base import load_unconformity
1818
from ._base import load_duplex
19+
from ._base import load_tabular_intrusion

LoopStructural/datasets/_base.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,17 @@ def strike_dip_headers():
142142

143143
def normal_vector_headers():
144144
return ["X", "Y", "Z", "nx", "ny", "nz"]
145+
146+
def load_tabular_intrusion():
147+
"""Model dataset sampled for a model of a tabular intrusion
148+
149+
150+
Returns
151+
-------
152+
tuple
153+
pandas data frame with loopstructural dataset and numpy array for bounding box
154+
"""
155+
module_path = dirname(__file__)
156+
data = pd.read_csv(join(module_path, Path("data/tabular_intrusion.csv")))
157+
bb = np.array([[0, 0, 0], [5,5,5]])
158+
return data, bb
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
feature_name,X,Y,Z,coord,val,gx,gy,gz,intrusion_contact_type,intrusion_anisotropy,intrusion_side
2+
stratigraphy,0.00,0.00,2.00,,0,,,,,,
3+
stratigraphy,0.00,5.00,2.00,,0,,,,,,
4+
stratigraphy,5.00,0.00,2.00,,0,,,,,,
5+
stratigraphy,5.00,5.00,2.00,,0,,,,,,
6+
stratigraphy,2.50,2.50,2.00,,,0,0,1,,,
7+
tabular_intrusion,3.04,2.12,2.18,,,,,,roof,stratigraphy,
8+
tabular_intrusion,4.02,3.85,2.14,,,,,,roof,stratigraphy,TRUE
9+
tabular_intrusion,2.02,3.16,2.02,,,,,,roof,stratigraphy,
10+
tabular_intrusion,2.12,2.16,2.02,,,,,,roof,stratigraphy,
11+
tabular_intrusion,1.14,3.50,2.04,,,,,,roof,,TRUE
12+
tabular_intrusion,4.08,3.02,1.18,,,,,,floor,,TRUE
13+
tabular_intrusion,1.14,1.06,1.16,,,,,,floor,,TRUE
14+
tabular_intrusion,4.20,2.18,1.12,,,,,,floor,,TRUE
15+
tabular_intrusion,1.02,3.02,1.12,,,,,,floor,,TRUE
16+
tabular_intrusion_frame,2.00,2.00,2.00,0,,0,0,-1,,,
17+
tabular_intrusion_frame,3.00,1.00,2.00,0,,0,0,-1,,,
18+
tabular_intrusion_frame,1.00,3.00,2.00,0,,0,0,-1,,,
19+
tabular_intrusion_frame,3.00,2.00,1.00,1,0,,,,,,
20+
tabular_intrusion_frame,3.00,2.00,1.00,1,,0,1,0,,,
21+
tabular_intrusion_frame,2.50,1.00,1.00,2,0,,,,,,
22+
tabular_intrusion_frame,2.50,2.00,1.00,2,0,,,,,,
23+
tabular_intrusion_frame,2.50,2.00,1.00,2,,1,0,0,,,

LoopStructural/interpolators/discrete_interpolator.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,13 +361,17 @@ def build_matrix(self, square=True, damp=0.0):
361361
# which is not ideal.
362362
max_weight = 0
363363
for c in self.constraints.values():
364+
if len(c["w"]) == 0:
365+
continue
364366
if c["w"].max() > max_weight:
365367
max_weight = c["w"].max()
366368
a = []
367369
b = []
368370
rows = []
369371
cols = []
370372
for c in self.constraints.values():
373+
if len(c["w"]) == 0:
374+
continue
371375
aa = (c["A"] * c["w"][:, None] / max_weight).flatten()
372376
b.extend((c["B"] * c["w"] / max_weight).tolist())
373377
mask = aa == 0

LoopStructural/interpolators/finite_difference_interpolator.py

Lines changed: 12 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ def add_vaue_constraints(self, w=1.0):
177177
w=w * points[inside, 4],
178178
name="value",
179179
)
180-
180+
if np.sum(inside)<=0:
181+
logger.warning(f"{self.propertyname}: {np.sum(~inside)} value constraints not added: outside of model bounding box")
181182
def add_interface_constraints(
182183
self, w=1.0
183184
): # for now weight all value points the same
@@ -242,40 +243,7 @@ def add_interface_constraints(
242243
name="interface_{}".format(unique_id),
243244
)
244245

245-
# if points.shape[0] > 1:
246-
# node_idx, inside = self.support.position_to_cell_corners(
247-
# points[:, :3])
248-
# # print(points[inside,:].shape)
249-
250-
# gi = np.zeros(self.support.n_nodes)
251-
# gi[:] = -1
252-
# gi[self.region] = np.arange(0, self.nx)
253-
# idc = np.zeros(node_idx.shape)
254-
# idc[:] = -1
255-
256-
# idc[inside, :] = gi[node_idx[inside, :]]
257-
# inside = np.logical_and(~np.any(idc == -1, axis=1), inside)
258-
# a = self.support.position_to_dof_coefs(points[inside, :3]).T
259-
# # create oversided array for storing constraints
260-
# A = np.zeros((a.shape[0]*a.shape[0],a.shape[1]*2))
261-
# interface_idc = np.zeros((a.shape[0]*a.shape[0],a.shape[1]*2),dtype=int)
262-
# interface_idc[:] = -1
263-
# c_i = 0
264-
265-
# for i in np.unique(points[np.logical_and(~np.isnan(points[:,3]),inside),3]):
266-
# mask = points[inside,3] == i
267-
# for p1 in range(points[inside][mask].shape[0]):
268-
# for p2 in range(p1+1,points[inside][mask].shape[0]):
269-
# A[c_i,:8] = a[mask][p1,:]
270-
# A[c_i,8:] -= a[mask][p2,:]
271-
# interface_idc[c_i,:8] = idc[inside,:][mask,:][p1,:]
272-
# interface_idc[c_i,8:] = idc[inside,:][mask,:][p2,:]
273-
# c_i+=1
274-
# outside = ~np.any(interface_idc == -1, axis=1)
275-
276-
# self.add_constraints_to_least_squares(A[outside,:] * w,
277-
# np.zeros(A[outside,:].shape[0]),
278-
# interface_idc[outside, :], name='interface')
246+
279247

280248
def add_gradient_constraints(self, w=1.0):
281249
"""
@@ -310,7 +278,7 @@ def add_gradient_constraints(self, w=1.0):
310278
vertices,
311279
T,
312280
elements,
313-
inside,
281+
inside_,
314282
) = self.support.get_element_gradient_for_location(points[inside, :3])
315283
# normalise constraint vector and scale element matrix by this
316284
norm = np.linalg.norm(points[:, 3:6], axis=1)
@@ -327,7 +295,8 @@ def add_gradient_constraints(self, w=1.0):
327295
self.add_constraints_to_least_squares(
328296
A, B, idc[inside, :], w=w * self.vol, name="gradient"
329297
)
330-
298+
if np.sum(inside)<=0:
299+
logger.warning(f"{self.propertyname}: {np.sum(~inside)} norm constraints not added: outside of model bounding box")
331300
def add_norm_constraints(self, w=1.0):
332301
"""
333302
Add constraints to control the norm of the gradient of the scalar field
@@ -361,7 +330,7 @@ def add_norm_constraints(self, w=1.0):
361330
vertices,
362331
T,
363332
elements,
364-
inside,
333+
inside_,
365334
) = self.support.get_element_gradient_for_location(points[inside, :3])
366335
# T*=np.product(self.support.step_vector)
367336
# T/=self.support.step_vector[0]
@@ -387,6 +356,8 @@ def add_norm_constraints(self, w=1.0):
387356
w=w * self.vol,
388357
name="norm",
389358
)
359+
if np.sum(inside)<=0:
360+
logger.warning(f"{self.propertyname}: {np.sum(~inside)} norm constraints not added: outside of model bounding box")
390361

391362
def add_gradient_orthogonal_constraints(self, points, vector, w=1.0, B=0):
392363
"""
@@ -427,7 +398,7 @@ def add_gradient_orthogonal_constraints(self, points, vector, w=1.0, B=0):
427398
vertices,
428399
T,
429400
elements,
430-
inside,
401+
inside_,
431402
) = self.support.get_element_gradient_for_location(points[inside, :3])
432403
T /= norm[:, None, None]
433404

@@ -437,6 +408,8 @@ def add_gradient_orthogonal_constraints(self, points, vector, w=1.0, B=0):
437408
self.add_constraints_to_least_squares(
438409
A, B, idc[inside, :], w=w * self.vol, name="gradient orthogonal"
439410
)
411+
if np.sum(inside)<=0:
412+
logger.warning(f"{self.propertyname}: {np.sum(~inside)} gradient constraints not added: outside of model bounding box")
440413

441414
def add_regularisation(self, operator, w=0.1):
442415
"""

LoopStructural/interpolators/structured_tetra.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,11 +451,15 @@ def get_element_gradient_for_location(self, pos):
451451
],
452452
]
453453
)
454+
# m[~inside,:,:] = np.nan
454455
I = np.array(
455456
[[-1.0, 1.0, 0.0, 0.0], [-1.0, 0.0, 1.0, 0.0], [-1.0, 0.0, 0.0, 1.0]]
456457
)
457458
m = np.swapaxes(m, 0, 2)
458-
element_gradients = np.linalg.inv(m)
459+
element_gradients = np.zeros_like(m)
460+
element_gradients[:] = np.nan
461+
element_gradients[inside,:,:] = np.linalg.inv(m[inside,:,:])
462+
# element_gradients = np.linalg.inv(m)
459463

460464
element_gradients = element_gradients.swapaxes(1, 2)
461465
element_gradients = element_gradients @ I

LoopStructural/modelling/core/geological_model.py

Lines changed: 71 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,8 @@ def from_map2loop_directory(
231231
the created geological model and a dictionary of the map2loop data
232232
"""
233233
from LoopStructural.modelling.input.map2loop_processor import Map2LoopProcessor
234-
234+
log_to_file(f"{m2l_directory}/loopstructural_log.txt")
235+
logger.info('Creating model from m2l directory')
235236
processor = Map2LoopProcessor(m2l_directory, use_thickness)
236237
processor._gradient = gradient
237238
processor.vector_scale = vector_scale
@@ -263,9 +264,11 @@ def from_map2loop_directory(
263264

264265
@classmethod
265266
def from_processor(cls, processor):
267+
logger.info('Creating model from processor')
266268
model = GeologicalModel(processor.origin, processor.maximum)
267269
model.data = processor.data
268270
for i in processor.fault_network.faults:
271+
logger.info(f"Adding fault {i}")
269272
model.create_and_add_fault(
270273
i,
271274
**processor.fault_properties.to_dict("index")[i],
@@ -311,6 +314,7 @@ def from_processor(cls, processor):
311314
faults = None
312315
if processor.fault_stratigraphy is not None:
313316
faults = processor.fault_stratigraphy[s]
317+
logger.info(f'Adding foliation {s}')
314318
f = model.create_and_add_foliation(
315319
s, **processor.foliation_properties[s], faults=faults
316320
)
@@ -1073,6 +1077,11 @@ def create_and_add_intrusion(
10731077
**kwargs,
10741078
):
10751079
"""
1080+
An intrusion in built in two main steps:
1081+
(1) Intrusion network and intrusion frame: the algorithm first identify the intrusion network, which is a set of points
1082+
representing the roof or floor contact of the intrusion. Then this set of points is used to contraint the main structural
1083+
direction of the structural frame.
1084+
(2) Intrusion body: simulation of lateral and vertical extent of intrusion, using parameterization provided by the structural frame
10761085
10771086
Parameters
10781087
----------
@@ -1130,28 +1139,50 @@ def create_and_add_intrusion(
11301139
INet.build(**kwargs)
11311140

11321141
# Create intrusion frame, using intrusion network points, propagation and inflation direction
1142+
if "gxxgz" in kwargs: # weight for orthogonality constraint between coord 0 and coord 2
1143+
gxxgz = kwargs["gxxgz"]
1144+
else:
1145+
gxxgz = 0
11331146

1134-
gxxgz = 0 # weight for orthogonality constraint between coord 0 and coord 2
1135-
gxxgy = 0 # weight for orthogonality constraint between coord 0 and coord 1
1136-
gyxgz = 0 # weight for orthogonality constraint between coord 1 and coord 2
1147+
if "gxxgy" in kwargs: # weight for orthogonality constraint between coord 0 and coord 1
1148+
gxxgy = kwargs["gxxgy"]
1149+
else:
1150+
gxxgy = 0
1151+
1152+
if "gyxgz" in kwargs: # weight for orthogonality constraint between coord 1 and coord 2
1153+
gyxgz = kwargs["gyxgz"]
1154+
else:
1155+
gyxgz = 0
1156+
1157+
if "interpolatortype" in kwargs: # number of elements for interpolation
1158+
interpolatortype = kwargs["interpolatortype"]
1159+
else:
1160+
interpolatortype = 'FDI'
1161+
1162+
if "nelements" in kwargs: # number of elements for interpolation
1163+
nelements = kwargs["nelements"]
1164+
else:
1165+
nelements = 1e2
11371166

11381167
weights = [gxxgz, gxxgy, gyxgz]
1139-
# reg=np.array([1,0.5,1])
11401168
logger.info("building intrusion frame")
1141-
interpolator = self.get_interpolator(interpolatortype="FDI")
1169+
interpolator = self.get_interpolator(interpolatortype=interpolatortype)
11421170
frame_data = self.data[self.data["feature_name"] == intrusion_frame_name].copy()
11431171
IFrame_builder = IntrusionBuilder(
1144-
interpolator, model=self, feature_name=intrusion_frame_name
1172+
interpolator,
1173+
model=self,
1174+
name=intrusion_frame_name
11451175
)
11461176
IFrame_builder.set_data(frame_data, INet.intrusion_network_outcome)
1147-
IFrame = IFrame_builder.setup(
1148-
nelements=1e2,
1149-
solver="lu",
1150-
gxxgz=weights[0],
1151-
gxxgy=weights[1],
1177+
IFrame_builder.setup(
1178+
nelements = nelements,
1179+
# solver = solver,
1180+
w2=weights[0],
1181+
w1=weights[1],
11521182
gxygz=weights[2],
11531183
)
11541184

1185+
IFrame = IFrame_builder.frame
11551186
# Create intrusion feature
11561187
intrusion_feature = IntrusionFeature(
11571188
intrusion_name, structural_frame=IFrame, model=self
@@ -1171,14 +1202,17 @@ def create_and_add_intrusion(
11711202
intrusion_feature.set_intrusion_frame(IFrame)
11721203
intrusion_feature.set_intrusion_body(IBody)
11731204

1205+
#set data for simulations
1206+
logger.info("setting data for lateral thresholds simulation")
1207+
IBody.set_data_for_s_simulation()
1208+
logger.info("setting data for vertical thresholds simulation")
1209+
IBody.set_data_for_g_simulation()
1210+
11741211
if intrusion_lateral_extent_model == None:
11751212
logger.error(
11761213
"Specify conceptual model function for intrusion lateral extent"
11771214
)
1178-
1179-
else:
1180-
logger.info("setting data for lateral thresholds simulation")
1181-
IBody.set_data_for_s_simulation()
1215+
else:
11821216
IBody.set_lateral_extent_conceptual_model(intrusion_lateral_extent_model)
11831217
IBody.set_s_simulation_GSLIBparameters(lateral_extent_sgs_parameters)
11841218
IBody.make_s_simulation_variogram(lateral_extent_sgs_parameters)
@@ -1192,10 +1226,7 @@ def create_and_add_intrusion(
11921226
logger.error(
11931227
"Specify conceptual model function for intrusion vertical extent"
11941228
)
1195-
1196-
else:
1197-
logger.info("setting data for vertical thresholds simulation")
1198-
IBody.set_data_for_g_simulation()
1229+
else:
11991230
IBody.set_vertical_extent_conceptual_model(intrusion_vertical_extent_model)
12001231
IBody.set_g_simulation_GSLIBparameters(vertical_extent_sgs_parameters)
12011232
IBody.make_g_simulation_variogram(vertical_extent_sgs_parameters)
@@ -1493,6 +1524,18 @@ def create_and_add_fault(
14931524
fault : FaultSegment
14941525
created fault
14951526
"""
1527+
logger.info(f'Creating fault "{fault_surface_data}"')
1528+
logger.info(f'Displacement: {displacement}')
1529+
logger.info(f'Tolerance: {tol}')
1530+
logger.info(f'Fault function: {faultfunction}')
1531+
logger.info(f'Fault slip vector: {fault_slip_vector}')
1532+
logger.info(f'Fault center: {fault_center}')
1533+
logger.info(f'Major axis: {major_axis}')
1534+
logger.info(f'Minor axis: {minor_axis}')
1535+
logger.info(f'Intermediate axis: {intermediate_axis}')
1536+
for k, v in kwargs.items():
1537+
logger.info(f'{k}: {v}')
1538+
14961539
if tol is None:
14971540
tol = self.tol
14981541
self.parameters["features"].append(
@@ -1523,6 +1566,12 @@ def create_and_add_fault(
15231566
fault_frame_data = self.data[
15241567
self.data["feature_name"] == fault_surface_data
15251568
].copy()
1569+
trace_mask = np.logical_and(fault_frame_data['coord'] ==0,fault_frame_data['val'] == 0)
1570+
logger.info(f'There are {np.sum(trace_mask)} points on the fault trace')
1571+
if np.sum(trace_mask) == 0:
1572+
logger.error('You cannot model a fault without defining the location of the fault')
1573+
raise ValueError(f'There are no points on the fault trace')
1574+
15261575
mask = np.logical_and(
15271576
fault_frame_data["coord"] == 0, ~np.isnan(fault_frame_data["gz"])
15281577
)
@@ -1562,9 +1611,7 @@ def create_and_add_fault(
15621611
# if we haven't defined a fault centre take the center of mass for lines assocaited with
15631612
# the fault trace
15641613
if (
1565-
"centreEasting" in kwargs
1566-
and "centreNorthing" in kwargs
1567-
and "centreAltitude" in kwargs
1614+
~np.isnan(kwargs.get("centreEasting",np.nan)) and ~np.isnan(kwargs.get('centreNorthing',np.nan)) and ~np.isnan(kwargs.get('centreAltitude',np.nan))
15681615
):
15691616
fault_center = self.scale(
15701617
np.array(
@@ -1598,7 +1645,7 @@ def create_and_add_fault(
15981645
minor_axis=minor_axis,
15991646
major_axis=major_axis,
16001647
intermediate_axis=intermediate_axis,
1601-
points=kwargs.get("points", None),
1648+
points=kwargs.get("points", True),
16021649
)
16031650

16041651
# if minor_axis == None or major_axis == None or intermediate_axis == None:

0 commit comments

Comments
 (0)