LoopStructural 1.0.9__cp36-cp36m-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. LoopStructural/__init__.py +36 -0
  2. LoopStructural/analysis/__init__.py +2 -0
  3. LoopStructural/analysis/_fault_displacement.py +67 -0
  4. LoopStructural/analysis/_fault_intersection.py +36 -0
  5. LoopStructural/analysis/_plane_fit.py +43 -0
  6. LoopStructural/datasets/__init__.py +12 -0
  7. LoopStructural/datasets/_base.py +65 -0
  8. LoopStructural/datasets/data/claudius.csv +21049 -0
  9. LoopStructural/datasets/data/claudiusbb.txt +2 -0
  10. LoopStructural/datasets/data/duplex.csv +126 -0
  11. LoopStructural/datasets/data/duplexbb.txt +2 -0
  12. LoopStructural/datasets/data/intrusion.csv +1017 -0
  13. LoopStructural/datasets/data/intrusionbb.txt +2 -0
  14. LoopStructural/datasets/data/onefoldbb.txt +2 -0
  15. LoopStructural/datasets/data/onefolddata.csv +2226 -0
  16. LoopStructural/datasets/data/refolded_bb.txt +2 -0
  17. LoopStructural/datasets/data/refolded_fold.csv +2126 -0
  18. LoopStructural/interpolators/__init__.py +34 -0
  19. LoopStructural/interpolators/base_structured_3d_support.py +101 -0
  20. LoopStructural/interpolators/cython/__init__.py +0 -0
  21. LoopStructural/interpolators/cython/dsi_helper.c +31046 -0
  22. LoopStructural/interpolators/cython/dsi_helper.cp36-win32.pyd +0 -0
  23. LoopStructural/interpolators/discrete_fold_interpolator.py +202 -0
  24. LoopStructural/interpolators/discrete_interpolator.py +584 -0
  25. LoopStructural/interpolators/finite_difference_interpolator.py +399 -0
  26. LoopStructural/interpolators/geological_interpolator.py +197 -0
  27. LoopStructural/interpolators/operator.py +61 -0
  28. LoopStructural/interpolators/piecewiselinear_interpolator.py +437 -0
  29. LoopStructural/interpolators/structured_grid.py +421 -0
  30. LoopStructural/interpolators/structured_tetra.py +681 -0
  31. LoopStructural/interpolators/surfe_wrapper.py +118 -0
  32. LoopStructural/modelling/__init__.py +46 -0
  33. LoopStructural/modelling/core/__init__.py +0 -0
  34. LoopStructural/modelling/core/geological_model.py +1513 -0
  35. LoopStructural/modelling/core/geological_model_graph.py +880 -0
  36. LoopStructural/modelling/core/stratigraphic_column.py +5 -0
  37. LoopStructural/modelling/fault/__init__.py +4 -0
  38. LoopStructural/modelling/fault/fault_builder.py +143 -0
  39. LoopStructural/modelling/fault/fault_function.py +188 -0
  40. LoopStructural/modelling/fault/fault_function_feature.py +76 -0
  41. LoopStructural/modelling/fault/fault_segment.py +298 -0
  42. LoopStructural/modelling/features/__init__.py +8 -0
  43. LoopStructural/modelling/features/cross_product_geological_feature.py +90 -0
  44. LoopStructural/modelling/features/geological_feature.py +252 -0
  45. LoopStructural/modelling/features/geological_feature_builder.py +414 -0
  46. LoopStructural/modelling/features/lambda_geological_feature.py +36 -0
  47. LoopStructural/modelling/features/region_feature.py +34 -0
  48. LoopStructural/modelling/features/structural_frame.py +133 -0
  49. LoopStructural/modelling/features/structural_frame_builder.py +206 -0
  50. LoopStructural/modelling/features/unconformity_feature.py +74 -0
  51. LoopStructural/modelling/fold/__init__.py +8 -0
  52. LoopStructural/modelling/fold/fold.py +143 -0
  53. LoopStructural/modelling/fold/fold_rotation_angle.py +133 -0
  54. LoopStructural/modelling/fold/fold_rotation_angle_feature.py +58 -0
  55. LoopStructural/modelling/fold/foldframe.py +193 -0
  56. LoopStructural/modelling/fold/svariogram.py +180 -0
  57. LoopStructural/utils/__init__.py +18 -0
  58. LoopStructural/utils/bounding_box.py +21 -0
  59. LoopStructural/utils/exceptions.py +10 -0
  60. LoopStructural/utils/helper.py +382 -0
  61. LoopStructural/utils/logging.py +60 -0
  62. LoopStructural/utils/map2loop.py +353 -0
  63. LoopStructural/utils/regions.py +11 -0
  64. LoopStructural/utils/utils.py +69 -0
  65. LoopStructural/visualisation/__init__.py +31 -0
  66. LoopStructural/visualisation/map_viewer.py +322 -0
  67. LoopStructural/visualisation/model_plotter.py +17 -0
  68. LoopStructural/visualisation/model_visualisation.py +1113 -0
  69. LoopStructural/visualisation/rotation_angle_plotter.py +83 -0
  70. LoopStructural/visualisation/sphinx_scraper.py +34 -0
  71. LoopStructural/visualisation/stratigraphic_column.py +60 -0
  72. LoopStructural-1.0.9.dist-info/LICENSE +21 -0
  73. LoopStructural-1.0.9.dist-info/METADATA +19 -0
  74. LoopStructural-1.0.9.dist-info/RECORD +77 -0
  75. LoopStructural-1.0.9.dist-info/WHEEL +5 -0
  76. LoopStructural-1.0.9.dist-info/top_level.txt +2 -0
  77. tests/__init__.py +0 -0
@@ -0,0 +1,414 @@
1
+ """
2
+ Feature builder
3
+ """
4
+ import copy
5
+ import logging
6
+
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+ from LoopStructural.utils import getLogger
11
+ logger = getLogger(__name__)
12
+
13
+ from LoopStructural.utils.helper import xyz_names, val_name, normal_vec_names, \
14
+ weight_name, gradient_vec_names, tangent_vec_names, interface_name
15
+ from LoopStructural.modelling.features import GeologicalFeature
16
+ from LoopStructural.utils.helper import get_data_bounding_box_map as get_data_bounding_box
17
+ from LoopStructural.utils import get_data_axis_aligned_bounding_box
18
+ from LoopStructural.utils import RegionEverywhere
19
+
20
+ class GeologicalFeatureInterpolator:
21
+ """[summary]
22
+
23
+ [extended_summary]
24
+ """
25
+ def __init__(self, interpolator, name='Feature', region=None, **kwargs):
26
+ """
27
+ Constructor for a GeologicalFeatureInterpolator
28
+
29
+ Parameters
30
+ ----------
31
+ interpolator : GeologicalInterpolator
32
+ An empty GeologicalInterpolator
33
+ region : lambda function
34
+ defining whether the location (xyz) should be included in the
35
+ kwargs - name of the feature, region to interpolate the feature
36
+ """
37
+ self._interpolator = interpolator
38
+ self._name = name
39
+ self._interpolator.set_property_name(self._name)
40
+ # everywhere region is just a lambda that returns true for all locations
41
+ if region is None:
42
+ self.region = RegionEverywhere()
43
+ else:
44
+ self.region = region
45
+ header = xyz_names()+val_name()+gradient_vec_names()+\
46
+ normal_vec_names()+tangent_vec_names()+weight_name()
47
+ self.data = pd.DataFrame(columns=header)
48
+ self.faults = []
49
+ self.data_added = False
50
+ self._interpolator.set_region(region=self.region)
51
+ self._feature = None
52
+ self._up_to_date = False
53
+ self._build_arguments = {}
54
+ self.fold = None
55
+ self._feature = GeologicalFeature(self._name,
56
+ self._interpolator,
57
+ builder=self,
58
+ region=self.region,
59
+ faults=self.faults,
60
+ fold = self.fold
61
+ )
62
+ self._orthogonal_features = {}
63
+ @property
64
+ def feature(self):
65
+ return self._feature
66
+
67
+ @property
68
+ def build_arguments(self):
69
+ return self._build_arguments
70
+
71
+ @build_arguments.setter
72
+ def build_arguments(self, build_arguments):
73
+ # self._build_arguments = {}
74
+ for k, i in build_arguments.items():
75
+ self._build_arguments[k] = i
76
+
77
+ def update(self):
78
+ self.build(**self.build_arguments)
79
+ @property
80
+ def name(self):
81
+ return self._name
82
+ @property
83
+ def interpolator(self):
84
+ return self._interpolator
85
+
86
+ def up_to_date(self):
87
+ #has anything changed in the builder since we built the feature? if so update
88
+ if self._up_to_date == False:
89
+ self.update()
90
+ #check if the interpolator is up to date, if not solve
91
+ if self._interpolator.up_to_date == False:
92
+ self.update()
93
+
94
+
95
+ def add_fault(self, fault):
96
+ """
97
+ Add a fault to the geological feature builder
98
+
99
+ Parameters
100
+ ----------
101
+ fault : FaultSegment
102
+ A faultsegment to add to the geological feature
103
+
104
+ Returns
105
+ -------
106
+
107
+ """
108
+ self._up_to_date = False
109
+ self.faults.append(fault)
110
+
111
+ def add_data_from_data_frame(self, data_frame, overwrite = False):
112
+ """
113
+ Extract data from a pandas dataframe with columns for
114
+
115
+ Parameters
116
+ ----------
117
+ data_frame : pd.DataFrame
118
+ a dataframe containing the data to be added
119
+
120
+ Returns
121
+ -------
122
+
123
+ """
124
+ self.data = data_frame.copy()
125
+
126
+ def add_orthogonal_feature(self, feature, w=1., region=None,step=1,B=0):
127
+ """
128
+ Add a constraint to the interpolator so that the gradient of an exisitng feature is orthogonal
129
+ to the feature being built. E.g. dot product between gradients should be = 0
130
+
131
+ Parameters
132
+ ----------
133
+ feature : GeologicalFeature
134
+ feature which we want to be orthogonal to
135
+ w : double
136
+ how much to weight in least squares sense
137
+ region : unused
138
+ step : int
139
+ numpy slicing step size to see how many tetras to add
140
+
141
+ Returns
142
+ -------
143
+
144
+ Notes
145
+ -----
146
+ The constraint can be applied to a random subset of the tetrahedral elements in the mesh
147
+ in theory this shu
148
+ """
149
+ self._orthogonal_features[feature.name] = [feature,w,region,step,B]
150
+ self._up_to_date = False
151
+
152
+
153
+ def add_data_to_interpolator(self, constrained=False, force_constrained=False, **kwargs):
154
+ """
155
+ Iterates through the list of data and applies any faults active on the
156
+ data in the order they are added
157
+
158
+ Parameters
159
+ -----------
160
+ constrained : boolean
161
+ force_constrained : boolean
162
+
163
+ Returns
164
+ -------
165
+
166
+ """
167
+ # first move the data for the fault
168
+ logger.info("Adding %i faults to %s" % (len(self.faults), self.name))
169
+ data = self.data.copy()
170
+ # convert data locations to numpy array and then update
171
+ for f in self.faults:
172
+ data.loc[:,xyz_names()] = f.apply_to_points(
173
+ self.get_data_locations())
174
+ # Now check whether there are enough constraints for the
175
+ # interpolator to be able to solve
176
+ # we need at least 2 different value points or a single norm
177
+ # constraint. If there are not enough
178
+ # try converting grad to norms, if still not enough send user an error
179
+ if constrained:
180
+ # Change normals to gradients
181
+ mask = np.all(~np.isnan(data.loc[:, normal_vec_names()]),axis=1)
182
+ if mask.shape[0] > 0:
183
+ data.loc[mask, gradient_vec_names()] = data.loc[mask,
184
+ normal_vec_names()].to_numpy()
185
+ data.loc[mask, normal_vec_names()] = np.nan
186
+ if self.get_norm_constraints().shape[0] > 0:
187
+ constrained = True
188
+
189
+ if np.unique(self.get_value_constraints()[:,3]).shape[0]>1:
190
+ constrained = True
191
+
192
+ if not constrained or force_constrained:
193
+ # change gradient constraints to normal vector constraints
194
+ mask = np.all(~np.isnan(data.loc[:, gradient_vec_names()]), axis=1)
195
+ if mask.shape[0] > 0:
196
+
197
+ data.loc[mask, normal_vec_names()] = data.loc[mask,
198
+ gradient_vec_names()].to_numpy()
199
+ data.loc[mask, gradient_vec_names()] = np.nan
200
+ logger.info(
201
+ "Setting gradient points to norm constraints")
202
+ constrained = True
203
+ mask = np.all(
204
+ ~np.isnan(data.loc[:, normal_vec_names()].to_numpy()),
205
+ axis=1)
206
+
207
+ if not constrained:
208
+ logger.error("Not enough constraints for scalar field add more")
209
+ # self.interpolator.reset()
210
+ mask = ~np.isnan(data.loc[:,val_name()].to_numpy())
211
+ # add value constraints
212
+ if mask.shape[0]>0:
213
+ value_data = data.loc[mask[:,0],xyz_names()+val_name()+weight_name()].to_numpy()
214
+ self.interpolator.set_value_constraints(value_data)
215
+
216
+ # add gradient constraints
217
+ mask = np.all(~np.isnan(data.loc[:, gradient_vec_names()].to_numpy()), axis=1)
218
+ if mask.shape[0]>0:
219
+ gradient_data = data.loc[
220
+ mask, xyz_names() + gradient_vec_names() + weight_name()].to_numpy()
221
+ self.interpolator.set_gradient_constraints(gradient_data)
222
+
223
+ # add normal vector data
224
+ mask = np.all(~np.isnan(data.loc[:, normal_vec_names()].to_numpy()), axis=1)
225
+ if mask.shape[0]>0:
226
+ normal_data = data.loc[
227
+ mask, xyz_names() + normal_vec_names() + weight_name()].to_numpy()
228
+ self.interpolator.set_normal_constraints(normal_data)
229
+
230
+ # add tangent data
231
+ mask = np.all(~np.isnan(data.loc[:, tangent_vec_names()].to_numpy()), axis=1)
232
+ if mask.shape[0]>0:
233
+ tangent_data = data.loc[
234
+ mask, xyz_names() + tangent_vec_names() + weight_name()].to_numpy()
235
+ self.interpolator.set_tangent_constraints(tangent_data)
236
+
237
+ # add interface constraints
238
+ mask = np.all(~np.isnan(data.loc[:, interface_name()].to_numpy()), axis=1)
239
+ if mask.shape[0] > 0:
240
+ interface_data = data.loc[
241
+ mask, xyz_names() + interface_name() + weight_name()].to_numpy()
242
+ self.interpolator.set_interface_constraints(interface_data)
243
+
244
+ self.data_added = True
245
+ self._up_to_date = False
246
+
247
+ def install_gradient_constraint(self):
248
+ for g in self._orthogonal_features.values():
249
+ feature,w,region,step,B = g
250
+ vector = feature.evaluate_gradient(self.interpolator.support.barycentre())
251
+ vector /= np.linalg.norm(vector,axis=1)[:,None]
252
+ element_idx = np.arange(self.interpolator.support.n_elements)
253
+ np.random.shuffle(element_idx)
254
+ self.interpolator.add_gradient_orthogonal_constraint(
255
+ self.interpolator.support.barycentre()[element_idx[::step],:],
256
+ vector[element_idx[::step],:],
257
+ w=w,
258
+ B=B
259
+ )
260
+ def get_value_constraints(self):
261
+ """
262
+ Get the value constraints for this geological feature
263
+
264
+ Returns
265
+ -------
266
+ np.array((N,4),dtype=double)
267
+ """
268
+ header = xyz_names()+val_name()+weight_name()
269
+ mask = ~np.isnan(self.data.loc[:,val_name()].to_numpy())
270
+ return self.data.loc[mask[:,0],header].to_numpy()
271
+
272
+ def get_gradient_constraints(self):
273
+ """
274
+ Get the gradient direction constraints
275
+
276
+ Returns
277
+ -------
278
+ numpy array
279
+ """
280
+ mask = np.all(
281
+ ~np.isnan(self.data.loc[:, gradient_vec_names()].to_numpy()),
282
+ axis=1)
283
+ if mask.shape[0] > 0:
284
+ return self.data.loc[
285
+ mask, xyz_names() + gradient_vec_names() + weight_name(
286
+ )].to_numpy()
287
+ else:
288
+ return np.zeros((0, 7))
289
+
290
+ def get_tangent_constraints(self):
291
+ """
292
+
293
+ Returns
294
+ -------
295
+ numpy array
296
+ """
297
+ mask = np.all(
298
+ ~np.isnan(self.data.loc[:, tangent_vec_names()].to_numpy()),
299
+ axis=1)
300
+ if mask.shape[0] > 0:
301
+ return self.data.loc[
302
+ mask, xyz_names() + tangent_vec_names() + weight_name(
303
+ )].to_numpy()
304
+ else:
305
+ return np.zeros((0, 7))
306
+
307
+ def get_norm_constraints(self):
308
+ """
309
+ Get the gradient norm constraints
310
+
311
+ Returns
312
+ -------
313
+ numpy array
314
+ """
315
+ mask = np.all(~np.isnan(self.data.loc[:, normal_vec_names()].to_numpy()),
316
+ axis=1)
317
+ if mask.shape[0] > 0:
318
+ return self.data.loc[
319
+ mask, xyz_names() + normal_vec_names() + weight_name(
320
+
321
+ )].to_numpy()
322
+ else:
323
+ return np.zeros((0,7))
324
+
325
+ def get_interface_constraints(self):
326
+ mask = np.all(~np.isnan(self.data.loc[:, interface_name()].to_numpy()), axis=1)
327
+ if mask.shape[0] > 0:
328
+ return self.data.loc[
329
+ mask, xyz_names() + interface_name() + weight_name()].to_numpy()
330
+ else:
331
+ return np.zeros((0,5))
332
+
333
+ def get_data_locations(self):
334
+ """
335
+ Get only the location for all data points
336
+
337
+ Returns
338
+ -------
339
+
340
+ """
341
+ return self.data.loc[:, xyz_names()].to_numpy()
342
+ def set_interpolation_geometry(self,origin,maximum):
343
+ """Update the interpolation support geometry to new bounding box
344
+
345
+ Parameters
346
+ ----------
347
+ origin : np.array(3)
348
+ origin vector
349
+ maximum : np.array(3)
350
+ maximum vector
351
+ """
352
+ logger.info("Setting mesh origin: {} {} {} ".format(origin[0],origin[1],origin[2]))
353
+ logger.info("Setting mesh maximum: {} {} {}".format(maximum[0],maximum[1],maximum[2]))
354
+ self.interpolator.support.origin = origin
355
+ self.interpolator.support.maximum = maximum
356
+
357
+
358
+ self._up_to_date = False
359
+
360
+ def build(self, fold=None, fold_weights={}, data_region=None, **kwargs):
361
+ """
362
+ Runs the interpolation and builds the geological feature
363
+
364
+ Parameters
365
+ ----------
366
+ fold : FoldEvent
367
+ fold_weights : dict
368
+ data_region : double <1
369
+ If not none adds a region around the data points to the interpolation
370
+ with data_region as a buffer
371
+ kwargs
372
+
373
+ Returns
374
+ -------
375
+
376
+ """
377
+
378
+
379
+ self.add_data_to_interpolator(**kwargs)
380
+ if data_region is not None:
381
+ xyz = self.interpolator.get_data_locations()
382
+ bb, region = get_data_bounding_box(xyz, data_region)
383
+ # if self.model.reuse_supports == False:
384
+ if np.any(np.min(bb[0,:])< self.interpolator.support.origin):
385
+ neworigin = np.min([self.interpolator.support.origin,bb[0,:]],axis=0)
386
+ logger.info("Changing origin of support for {} from {} {} {} to {} {} {}"\
387
+ .format(self.name,self.interpolator.support.origin[0],\
388
+ self.interpolator.support.origin[1],self.interpolator.support.origin[2],\
389
+ neworigin[0],neworigin[1],neworigin[2]))
390
+ self.interpolator.support.origin = neworigin
391
+ if np.any(np.max(bb[0,:])< self.interpolator.support.maximum):
392
+ newmax = np.max([self.interpolator.support.maximum,bb[0,:]],axis=0)
393
+ logger.info("Changing origin of support for {} from {} {} {} to {} {} {}"\
394
+ .format(self.name,self.interpolator.support.maximum[0],
395
+ self.interpolator.support.maximum[1],self.interpolator.support.maximum[2],\
396
+ newmax[0],newmax[1],newmax[2]))
397
+
398
+ self.interpolator.support.maximum = newmax
399
+ self.interpolator.set_region(region=region)
400
+ # moving this to init because it needs to be done before constraints
401
+ # are added?
402
+ if fold is not None:
403
+ logger.info("Adding fold to %s" % self.name)
404
+ self.interpolator.fold = fold
405
+ # if we have fold weights use those, otherwise just use default
406
+ self.interpolator.add_fold_constraints(**fold_weights)
407
+ if 'cgw' not in kwargs:
408
+ # try adding very small cg
409
+ kwargs['cgw'] = 0.0
410
+ self.install_gradient_constraint()
411
+ self.interpolator.setup_interpolator(**kwargs)
412
+ self.interpolator.solve_system(**kwargs)
413
+ self._up_to_date = True
414
+ return self._feature
@@ -0,0 +1,36 @@
1
+ """
2
+ Geological features
3
+ """
4
+ import logging
5
+
6
+ import numpy as np
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class LambdaGeologicalFeature:
12
+ def __init__(self,function = None,name = 'unnamed_lambda', gradient_function = None, model = None):
13
+ self.function = function
14
+ self.name = name
15
+ self.gradient_function = gradient_function
16
+ self.model = model
17
+ def evaluate_value(self, xyz):
18
+ v = np.zeros((xyz.shape[0]))
19
+ if self.function is None:
20
+ v[:] = np.nan
21
+ else:
22
+ v[:] = self.function(xyz)
23
+ return v
24
+ def evaluate_gradient(self,xyz):
25
+ v = np.zeros((xyz.shape[0],3))
26
+ if self.gradient_function is None:
27
+ v[:,:] = np.nan
28
+ else:
29
+ v[:,:] = self.gradient_function(xyz)
30
+ return v
31
+ def min(self):
32
+ return 0
33
+ def max(self):
34
+ return 0
35
+
36
+
@@ -0,0 +1,34 @@
1
+ class RegionFeature:
2
+ """
3
+ """
4
+ def __init__(self, function):
5
+ """
6
+ Create a GeologicalFeature to represent a region in a model
7
+ The region is defined by a boolean function on position.
8
+
9
+ Parameters
10
+ ----------
11
+ function lambda function
12
+ lambda function true inside region, false outside region
13
+
14
+ """
15
+ self.function = function
16
+ self.name = 'region'
17
+
18
+ def evaluate_value(self, pos):
19
+ return self.function(pos).astype(float)
20
+
21
+ def mean(self):
22
+ return 0
23
+
24
+ def max(self):
25
+ return 1
26
+
27
+ def min(self):
28
+ return -1
29
+
30
+ def name(self):
31
+ return self.name
32
+
33
+ # class VectorFeature:
34
+ # def __init__(self, function)
@@ -0,0 +1,133 @@
1
+ """
2
+ Structural frames
3
+ """
4
+ import logging
5
+ import numpy as np
6
+ from LoopStructural.utils import getLogger
7
+ logger = getLogger(__name__)
8
+
9
+
10
+ class StructuralFrame:
11
+ """[summary]
12
+
13
+ [extended_summary]
14
+ """
15
+ def __init__(self, name, features, fold=None):
16
+ """
17
+ Structural frame is a curvilinear coordinate system defined by
18
+ structural observations associated with a fault or fold.
19
+
20
+ Parameters
21
+ ----------
22
+ name - name of the structural frame
23
+ features - list of features to build the frame with
24
+ """
25
+ self.name = name
26
+ self.features = features
27
+ self.data = None
28
+ self.fold = fold
29
+
30
+ def __getitem__(self, item):
31
+ """
32
+
33
+ Parameters
34
+ ----------
35
+ item index of feature to access
36
+
37
+ Returns
38
+ -------
39
+ the structural frame geological feature
40
+ """
41
+ return self.features[item]
42
+
43
+ def set_model(self, model):
44
+ """Link the model that created the frame to the frame
45
+ and the features that make up the frame
46
+
47
+ Parameters
48
+ ----------
49
+ model : GeologicalModel
50
+ the geological model that created the fold frame
51
+ """
52
+ self.model = model
53
+ for f in self.features:
54
+ if f is None:
55
+ continue
56
+ f.set_model(model)
57
+
58
+ def add_region(self, region):
59
+ for i in range(3):
60
+ self.features[i].add_region(region)
61
+
62
+ def get_feature(self, i):
63
+ """
64
+ Return the ith feature
65
+
66
+ Parameters
67
+ ----------
68
+ i
69
+
70
+ Returns
71
+ -------
72
+
73
+ """
74
+ return self.features[i]
75
+
76
+ def set_data(self, data):
77
+ """
78
+ Associate data with structural frame
79
+
80
+ Parameters
81
+ ----------
82
+ data
83
+
84
+ Returns
85
+ -------
86
+
87
+ """
88
+ self.data = data
89
+
90
+ def evaluate_value(self, evaluation_points):
91
+ """
92
+ Evaluate the value of the structural frame for the points.
93
+ Can optionally only evaluate one coordinate
94
+
95
+ Parameters
96
+ ----------
97
+ evaluation_points
98
+ i
99
+
100
+ Returns
101
+ -------
102
+
103
+ """
104
+ v = np.zeros(evaluation_points.shape) #create new 3d array of correct length
105
+ v[:] = np.nan
106
+ v[:,0] = self.features[0].evaluate_value(evaluation_points)
107
+ v[:,1] = self.features[1].evaluate_value(evaluation_points)
108
+ v[:,2] = self.features[2].evaluate_value(evaluation_points)
109
+ return v
110
+
111
+ def evaluate_gradient(self, evaluation_points, i=None):
112
+ """
113
+ Evaluate the gradient of the structural frame.
114
+ Can optionally only evaluate the ith coordinate
115
+
116
+ Parameters
117
+ ----------
118
+ evaluation_points
119
+ i
120
+
121
+ Returns
122
+ -------
123
+
124
+ """
125
+ if i is not None:
126
+ return self.features[i].support.evaluate_gradient(
127
+ evaluation_points)
128
+ return (self.features[0].support.evaluate_gradient(evaluation_points),
129
+ self.features[1].support.evaluate_gradient(evaluation_points),
130
+ self.features[2].support.evaluate_gradient(evaluation_points))
131
+
132
+
133
+