Skip to content

Commit 71c6f17

Browse files
committed
fix: calculate fault normal from plane of points as well as trace
1 parent f409ad8 commit 71c6f17

1 file changed

Lines changed: 55 additions & 15 deletions

File tree

LoopStructural/modelling/features/builders/_fault_builder.py

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -295,29 +295,69 @@ def create_data_from_geometry(
295295
self.fault_dip = fault_dip
296296
if fault_normal_vector is None:
297297
if fault_frame_data.loc[
298+
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["gx"].notna())].shape[0]>0:
299+
fault_normal_vector = fault_frame_data.loc[
300+
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["gx"].notna()),
301+
["gx", "gy", "gz"],
302+
].to_numpy().mean(axis=0)
303+
elif fault_frame_data.loc[
298304
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["nx"].notna())].shape[0]>0:
299305
fault_normal_vector = fault_frame_data.loc[
300306
np.logical_and(fault_frame_data["coord"] == 0, fault_frame_data["nx"].notna()),
301307
["nx", "ny", "nz"],
302308
].to_numpy().mean(axis=0)
303309

304310
else:
311+
fault_surface_pts = fault_frame_data.loc[
312+
np.logical_and(
313+
fault_frame_data["coord"] == 0,
314+
fault_frame_data["val"] == 0,
315+
),
316+
["X", "Y", "Z"],
317+
].to_numpy()
318+
fault_surface_pts = fault_surface_pts[
319+
~np.isnan(fault_surface_pts).any(axis=1)
320+
]
305321

306-
# Calculate fault strike using eigenvectors
307-
pts = fault_trace - fault_trace.mean(axis=0)
308-
# Calculate covariance matrix
309-
cov_matrix = pts.T @ pts
310-
# Get eigenvectors and eigenvalues
311-
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
312-
# Use eigenvector with largest eigenvalue as strike direction
313-
strike_vector = eigenvectors[:, np.argmax(eigenvalues)]
314-
strike_vector = np.append(strike_vector, 0) # Add z component
315-
strike_vector /= np.linalg.norm(strike_vector)
316-
317-
fault_normal_vector = np.cross(strike_vector, [0, 0, 1])
318-
# Rotate the fault normal vector according to the fault dip
319-
rotation_matrix = rotation(strike_vector[None, :], np.array([90 - fault_dip]))
320-
fault_normal_vector = np.einsum("ijk,ik->ij", rotation_matrix, fault_normal_vector[None, :])[0]
322+
if fault_surface_pts.shape[0] >= 3:
323+
pts_3d = fault_surface_pts - fault_surface_pts.mean(axis=0)
324+
cov_matrix = pts_3d.T @ pts_3d
325+
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
326+
# If points span a surface, use the smallest eigenvector as plane normal
327+
if eigenvalues[-1] > 0 and (eigenvalues[1] / eigenvalues[-1]) > 0.05:
328+
fault_normal_vector = eigenvectors[:, 0]
329+
else:
330+
# Fall back to line-on-map strike logic
331+
pts = fault_trace - fault_trace.mean(axis=0)
332+
cov_matrix = pts.T @ pts
333+
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
334+
strike_vector = eigenvectors[:, np.argmax(eigenvalues)]
335+
strike_vector = np.append(strike_vector, 0) # Add z component
336+
strike_vector /= np.linalg.norm(strike_vector)
337+
338+
fault_normal_vector = np.cross(strike_vector, [0, 0, 1])
339+
rotation_matrix = rotation(
340+
strike_vector[None, :], np.array([90 - fault_dip])
341+
)
342+
fault_normal_vector = np.einsum(
343+
"ijk,ik->ij", rotation_matrix, fault_normal_vector[None, :]
344+
)[0]
345+
else:
346+
# Fall back to line-on-map strike logic
347+
pts = fault_trace - fault_trace.mean(axis=0)
348+
cov_matrix = pts.T @ pts
349+
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)
350+
strike_vector = eigenvectors[:, np.argmax(eigenvalues)]
351+
strike_vector = np.append(strike_vector, 0) # Add z component
352+
strike_vector /= np.linalg.norm(strike_vector)
353+
354+
fault_normal_vector = np.cross(strike_vector, [0, 0, 1])
355+
rotation_matrix = rotation(
356+
strike_vector[None, :], np.array([90 - fault_dip])
357+
)
358+
fault_normal_vector = np.einsum(
359+
"ijk,ik->ij", rotation_matrix, fault_normal_vector[None, :]
360+
)[0]
321361

322362
if not isinstance(fault_normal_vector, np.ndarray):
323363
fault_normal_vector = np.array(fault_normal_vector)

0 commit comments

Comments
 (0)