11from typing import Union
22from ._structural_frame_builder import StructuralFrameBuilder
3+ from .. import AnalyticalGeologicalFeature
34from LoopStructural .utils import get_vectors
45import numpy as np
56import pandas as pd
@@ -90,6 +91,9 @@ def create_data_from_geometry(
9091 points = False ,
9192 force_mesh_geometry = False ,
9293 fault_buffer = 0.2 ,
94+ fault_trace_anisotropy = 1.0 ,
95+ fault_dip = 90 ,
96+ fault_dip_anisotropy = 1.0 ,
9397 ):
9498 """Generate the required data for building a fault frame for a fault with the
9599 specified parameters
@@ -140,12 +144,34 @@ def create_data_from_geometry(
140144 fault_frame_data .loc [normal_mask , ["nx" , "ny" , "nz" ]].to_numpy (),
141145 ]
142146 )
147+
143148 if len (vector_data ) == 0 :
144149 logger .error (
145150 "You cannot model a fault without defining the orientation of the fault\n \
146- Either add orientation data or define the fault normal vector argument"
151+ Defaulting to a vertical fault"
152+ )
153+ coefficients = np .polyfit (
154+ fault_frame_data .loc [trace_mask , "X" ],
155+ fault_frame_data .loc [trace_mask , "Y" ],
156+ 1 ,
147157 )
148- raise ValueError (f"There are no points on the fault trace" )
158+ slope , intercept = coefficients
159+
160+ # Create a direction vector using the slope
161+ direction_vector = np .array ([1 , slope ])
162+ direction_vector /= np .linalg .norm (direction_vector )
163+ print (f"Fault dip: { fault_dip } " )
164+ vector_data = np .array (
165+ [
166+ [
167+ direction_vector [1 ],
168+ - direction_vector [0 ],
169+ np .sin (np .deg2rad (fault_dip )),
170+ ]
171+ ]
172+ )
173+ vector_data /= np .linalg .norm (vector_data , axis = 1 )[:, None ]
174+
149175 fault_normal_vector = np .mean (vector_data , axis = 0 )
150176
151177 logger .info (f"Fault normal vector: { fault_normal_vector } " )
@@ -283,9 +309,9 @@ def create_data_from_geometry(
283309 fault_frame_data ["coord" ] == 0 ,
284310 ~ np .isnan (fault_frame_data ["nx" ]),
285311 )
286- fault_frame_data .loc [
287- mask , ["gx " , "gy " , "gz" ]
288- ] = fault_frame_data . loc [ mask , [ "nx" , "ny" , "nz" ]]
312+ fault_frame_data .loc [mask , [ "gx" , "gy" , "gz" ]] = (
313+ fault_frame_data . loc [ mask , ["nx " , "ny " , "nz" ] ]
314+ )
289315
290316 fault_frame_data .loc [mask , ["nx" , "ny" , "nz" ]] = np .nan
291317 mask = np .logical_and (
@@ -411,7 +437,37 @@ def create_data_from_geometry(
411437 1 ,
412438 w ,
413439 ]
440+ fault_frame_data .loc [
441+ len (fault_frame_data ),
442+ [
443+ "X" ,
444+ "Y" ,
445+ "Z" ,
446+ "feature_name" ,
447+ "nx" ,
448+ "ny" ,
449+ "nz" ,
450+ "val" ,
451+ "coord" ,
452+ "w" ,
453+ ],
454+ ] = [
455+ fault_center [0 ],
456+ fault_center [1 ],
457+ fault_center [2 ],
458+ self .name ,
459+ fault_normal_vector [0 ],
460+ fault_normal_vector [1 ],
461+ fault_normal_vector [2 ],
462+ np .nan ,
463+ 0 ,
464+ w ,
465+ ]
414466 self .add_data_from_data_frame (fault_frame_data )
467+ if fault_trace_anisotropy > 0 :
468+ self .add_fault_trace_anisotropy (fault_trace_anisotropy )
469+ if fault_dip_anisotropy > 0 :
470+ self .add_fault_dip_anisotropy (fault_dip_anisotropy )
415471 if force_mesh_geometry :
416472 self .set_mesh_geometry (fault_buffer , None )
417473 self .update_geometry (fault_frame_data [["X" , "Y" , "Z" ]].to_numpy ())
@@ -425,7 +481,6 @@ def set_mesh_geometry(self, buffer, rotation):
425481 percentage of length to add to edges
426482 """
427483 length = np .max (self .maximum - self .origin )
428- print (self .origin , length * buffer , self .maximum )
429484 # for builder in self.builders:
430485 # all three coordinates share the same support
431486 self .builders [0 ].set_interpolation_geometry (
@@ -464,6 +519,50 @@ def splayregion(xyz):
464519 self .builders [0 ].add_equality_constraints (splay , splayregion , scalefactor )
465520 return splayregion
466521
522+ def add_fault_trace_anisotropy (self , w = 1.0 ):
523+ trace_data = self .builders [0 ].data .loc [self .builders [0 ].data ["val" ] == 0 , :]
524+ coefficients = np .polyfit (
525+ trace_data ["X" ],
526+ trace_data ["Y" ],
527+ 1 ,
528+ )
529+ slope , intercept = coefficients
530+
531+ # Create a direction vector using the slope
532+ direction_vector = np .array ([1 , slope ])
533+ direction_vector /= np .linalg .norm (direction_vector )
534+ vector_data = np .array ([[direction_vector [1 ], - direction_vector [0 ], 0 ]])
535+ anisotropy_feature = AnalyticalGeologicalFeature (
536+ vector = vector_data , origin = [0 , 0 , 0 ], name = "fault_trace_anisotropy"
537+ )
538+ self .builders [0 ].add_orthogonal_feature (
539+ anisotropy_feature , w = w , region = None , step = 1 , B = 0
540+ )
541+
542+ def add_fault_dip_anisotropy (self , dip , w = 1.0 ):
543+ trace_data = self .builders [0 ].data .loc [self .builders [0 ].data ["val" ] == 0 , :]
544+ coefficients = np .polyfit (
545+ trace_data ["X" ],
546+ trace_data ["Y" ],
547+ 1 ,
548+ )
549+ slope , intercept = coefficients
550+
551+ # Create a direction vector using the slope
552+ direction_vector = np .array ([1 , slope ])
553+ direction_vector /= np .linalg .norm (direction_vector )
554+ vector_data = np .array ([[direction_vector [0 ], direction_vector [1 ], 0 ]])
555+
556+ vector_data = np .array (
557+ [[direction_vector [1 ], - direction_vector [0 ], np .sin (np .deg2rad (dip ))]]
558+ )
559+ anisotropy_feature = AnalyticalGeologicalFeature (
560+ vector = vector_data , origin = [0 , 0 , 0 ], name = "fault_dip_anisotropy"
561+ )
562+ self .builders [0 ].add_orthogonal_feature (
563+ anisotropy_feature , w = w , region = None , step = 1 , B = 0
564+ )
565+
467566 def update (self ):
468567 for i in range (3 ):
469568 self .builders [i ].update ()
0 commit comments