11""" 
22Geological features 
33""" 
4- 
4+ from   LoopStructural . utils . maths   import   regular_tetraherdron_for_points ,  gradient_from_tetrahedron 
55from  ...modelling .features  import  BaseFeature 
66from  ...utils  import  getLogger 
77from  ...modelling .features  import  FeatureType 
88import  numpy  as  np 
99from  typing  import  Callable , Optional 
10+ from  ...utils  import  LoopValueError 
1011
1112logger  =  getLogger (__name__ )
1213
@@ -68,11 +69,11 @@ def evaluate_value(self, pos: np.ndarray, ignore_regions=False) -> np.ndarray:
6869        mask  =  self ._calculate_mask (pos , ignore_regions = ignore_regions )
6970        pos  =  self ._apply_faults (pos )
7071        if  self .function  is  not   None :
71-              
72+ 
7273            v [mask ] =  self .function (pos [mask ,:])
7374        return  v 
7475
75-     def  evaluate_gradient (self , pos : np .ndarray , ignore_regions = False ) ->  np .ndarray :
76+     def  evaluate_gradient (self , pos : np .ndarray , ignore_regions = False , element_scale_parameter = None ) ->  np .ndarray :
7677        """_summary_ 
7778
7879        Parameters 
@@ -85,7 +86,60 @@ def evaluate_gradient(self, pos: np.ndarray, ignore_regions=False) -> np.ndarray
8586        np.ndarray 
8687            _description_ 
8788        """ 
89+         if  pos .shape [1 ] !=  3 :
90+             raise  LoopValueError ("Need Nx3 array of xyz points to evaluate gradient" )
91+         logger .info (f'Calculating gradient for { self .name }  ' )
92+         if  element_scale_parameter  is  None :
93+             if  self .model  is  not   None :
94+                 element_scale_parameter  =  np .min (self .model .bounding_box .step_vector ) /  10 
95+             else :
96+                 element_scale_parameter  =  1 
97+         else :
98+             try :
99+                 element_scale_parameter  =  float (element_scale_parameter )
100+             except  ValueError :
101+                 logger .error ("element_scale_parameter must be a float" )
102+                 element_scale_parameter  =  1 
88103        v  =  np .zeros ((pos .shape [0 ], 3 ))
104+         v  =  np .zeros (pos .shape )
105+         v [:] =  np .nan 
106+         mask  =  self ._calculate_mask (pos , ignore_regions = ignore_regions )
107+         # evaluate the faults on the nodes of the faulted feature support 
108+         # then evaluate the gradient at these points 
109+         if  len (self .faults ) >  0 :
110+             # generate a regular tetrahedron for each point 
111+             # we will then move these points by the fault and then recalculate the gradient. 
112+             # this should work... 
113+             resolved  =  False 
114+             tetrahedron  =  regular_tetraherdron_for_points (pos , element_scale_parameter )
115+ 
116+             while  resolved :
117+                 for  f  in  self .faults :
118+                     v  =  (
119+                         f [0 ]
120+                         .evaluate_value (tetrahedron .reshape (- 1 , 3 ), fillnan = 'nearest' )
121+                         .reshape (tetrahedron .shape [0 ], 4 )
122+                     )
123+                     flag  =  np .logical_or (np .all (v  >  0 , axis = 1 ), np .all (v  <  0 , axis = 1 ))
124+                     if  np .any (~ flag ):
125+                         logger .warning (
126+                             f"Points are too close to fault { f [0 ].name }  . Refining the tetrahedron" 
127+                         )
128+                         element_scale_parameter  *=  0.5 
129+                         tetrahedron  =  regular_tetraherdron_for_points (pos , element_scale_parameter )
130+ 
131+                 resolved  =  True 
132+ 
133+             tetrahedron_faulted  =  self ._apply_faults (np .array (tetrahedron .reshape (- 1 , 3 ))).reshape (
134+                 tetrahedron .shape 
135+             )
136+ 
137+             values  =  self .function (tetrahedron_faulted .reshape (- 1 , 3 )).reshape (
138+                 (- 1 , 4 )
139+             )
140+             v [mask , :] =  gradient_from_tetrahedron (tetrahedron [mask , :, :], values [mask ])
141+ 
142+             return  v 
89143        if  self .gradient_function  is  None :
90144            v [:, :] =  np .nan 
91145        else :
0 commit comments