Skip to content

Commit f8140f5

Browse files
authored
Merge pull request #110 from Loop3D/2d_interpolation
2d interpolation
2 parents ef109d1 + 3f8e490 commit f8140f5

33 files changed

+2036
-664
lines changed

LoopStructural/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
"""
2-
3-
42
LoopStructural API
53
=======================
64
@@ -12,6 +10,7 @@
1210
from pathlib import Path
1311
from .version import __version__
1412

13+
experimental = False
1514
ch = logging.StreamHandler()
1615
formatter = logging.Formatter(
1716
"%(levelname)s: %(asctime)s: %(filename)s:%(lineno)d -- %(message)s"

LoopStructural/analysis/__init__.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44
55
Various tools for analysing loopstructural models, including calculating fault intersections and fault toplogies
66
"""
7-
from ._fault_displacement import displacement_missfit
8-
from ._fault_intersection import calculate_fault_intersections
9-
from ._topology import calculate_fault_topology_matrix
7+
from LoopStructural.utils import getLogger
8+
import LoopStructural
9+
10+
logger = getLogger(__name__)
11+
if LoopStructural.experimental:
12+
logger.warning(
13+
"LoopStructural.analysis is experimental and may not perform as expected"
14+
)
15+
from ._fault_displacement import displacement_missfit
16+
from ._fault_intersection import calculate_fault_intersections
17+
from ._topology import calculate_fault_topology_matrix

LoopStructural/analysis/_topology.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
logger = getLogger(__name__)
55

66

7-
def calculate_fault_topology_matrix(model, xyz=None, threshold=0.001):
7+
def calculate_fault_topology_matrix(model, xyz=None, threshold=0.001, scale=True):
88
"""Calculate fault ellipsoid and hw/fw
99
1010
Parameters
@@ -13,12 +13,18 @@ def calculate_fault_topology_matrix(model, xyz=None, threshold=0.001):
1313
the model containing the faults
1414
xyz : np.array
1515
xyz locations in model coordinates
16-
16+
threshold : float
17+
threshold for determining if point is inside fault volume
18+
scale : bool
19+
flag whether to rescale xyz to model coordinates
1720
Returns
1821
-------
1922
topology_matrix : np.array
2023
matrix containing nan (outside), 0 (footwall), 1 (hangingwall)
2124
"""
25+
if xyz is not None and scale == True:
26+
logger.warning("Scaling XYZ to model coordinate system")
27+
xyz = model.scale(xyz, inplace=False)
2228
if xyz is None:
2329
xyz = model.regular_grid(rescale=False, shuffle=False)
2430
topology_matrix = np.zeros((xyz.shape[0], len(model.faults)))

LoopStructural/interpolators/__init__.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
"""
55
from enum import IntEnum
66

7+
from LoopStructural.utils import getLogger
8+
import LoopStructural
9+
10+
logger = getLogger(__name__)
11+
712

813
class InterpolatorType(IntEnum):
914
"""
@@ -18,21 +23,37 @@ class InterpolatorType(IntEnum):
1823
FINITE_DIFFERENCE = 2
1924
DISCRETE_FOLD = 3
2025
PIECEWISE_LINEAR = 4
26+
PIECEWISE_QUADRATIC = 5
2127
BASE_DATA_SUPPORTED = 10
2228
SURFE = 11
2329

2430

25-
from LoopStructural.interpolators.geological_interpolator import GeologicalInterpolator
26-
from LoopStructural.interpolators.discrete_interpolator import DiscreteInterpolator
27-
from LoopStructural.interpolators.structured_tetra import TetMesh
28-
from LoopStructural.interpolators.unstructured_tetra import UnStructuredTetMesh
29-
from LoopStructural.interpolators.structured_grid import StructuredGrid
30-
from LoopStructural.interpolators.finite_difference_interpolator import (
31+
from LoopStructural.interpolators._geological_interpolator import GeologicalInterpolator
32+
from LoopStructural.interpolators._discrete_interpolator import DiscreteInterpolator
33+
from LoopStructural.interpolators.supports import (
34+
TetMesh,
35+
StructuredGrid,
36+
UnStructuredTetMesh,
37+
P1Unstructured2d,
38+
P2Unstructured2d,
39+
StructuredGrid2D,
40+
P2UnstructuredTetMesh,
41+
)
42+
43+
44+
from LoopStructural.interpolators._finite_difference_interpolator import (
3145
FiniteDifferenceInterpolator,
3246
)
3347
from LoopStructural.interpolators.piecewiselinear_interpolator import (
3448
PiecewiseLinearInterpolator,
3549
)
36-
from LoopStructural.interpolators.discrete_fold_interpolator import (
50+
from LoopStructural.interpolators._discrete_fold_interpolator import (
3751
DiscreteFoldInterpolator,
3852
)
53+
54+
if LoopStructural.experimental:
55+
logger.warning(
56+
"Using experimental interpolators: P1Interpolator and P2Interpolator"
57+
)
58+
from ._p1interpolator import P1Interpolator
59+
from ._p2interpolator import P2Interpolator

LoopStructural/interpolators/discrete_fold_interpolator.py renamed to LoopStructural/interpolators/_discrete_fold_interpolator.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
import logging
55

66
import numpy as np
7-
from LoopStructural.interpolators.cython.dsi_helper import fold_cg
87

98
from LoopStructural.interpolators import PiecewiseLinearInterpolator, InterpolatorType
109

1110
from LoopStructural.utils import getLogger
1211

1312
logger = getLogger(__name__)
1413

14+
from .cython.dsi_helper import fold_cg
15+
1516

1617
class DiscreteFoldInterpolator(PiecewiseLinearInterpolator):
1718
""" """
@@ -29,7 +30,7 @@ def __init__(self, support, fold):
2930
"""
3031

3132
PiecewiseLinearInterpolator.__init__(self, support)
32-
self.type = InterpolatorType.FINITE_DIFFERENCE
33+
self.type = InterpolatorType.DISCRETE_FOLD
3334
self.fold = fold
3435

3536
@classmethod
@@ -126,7 +127,7 @@ def add_fold_constraints(
126127
]
127128
# calculate the fold geometry for the elements barycentre
128129
deformed_orientation, fold_axis, dgz = self.fold.get_deformed_orientation(
129-
self.support.barycentre()
130+
self.support.barycentre
130131
)
131132
element_idx = np.arange(self.support.n_elements)
132133
np.random.shuffle(element_idx)
@@ -249,5 +250,5 @@ def add_fold_constraints(
249250
B = np.zeros(A.shape[0])
250251
idc = np.array(idc[:ncons, :])
251252
self.add_constraints_to_least_squares(
252-
A, B, fold_regularisation[1], idc, name="fold regularisation 3"
253+
A, B, fold_regularisation[2], idc, name="fold regularisation 3"
253254
)

LoopStructural/interpolators/discrete_interpolator.py renamed to LoopStructural/interpolators/_discrete_interpolator.py

Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
logger = getLogger(__name__)
1818

19+
from ._geological_interpolator import GeologicalInterpolator
20+
1921

2022
class DiscreteInterpolator(GeologicalInterpolator):
2123
""" """
@@ -70,7 +72,7 @@ def nx(self):
7072

7173
@property
7274
def region(self):
73-
return self.region_function(self.support.nodes)
75+
return self.region_function(self.support.nodes).astype(bool)
7476

7577
@property
7678
def region_map(self):
@@ -166,11 +168,13 @@ def add_constraints_to_least_squares(self, A, B, idc, w=1.0, name="undefined"):
166168
# logger.debug('Adding constraints to interpolator: {} {} {}'.format(A.shape[0]))
167169
# print(A.shape,B.shape,idc.shape)
168170
if A.shape != idc.shape:
171+
logger.error("Cannot add constraints: A and indexes have different shape")
169172
return
170173

171174
if len(A.shape) > 2:
172175
nr = A.shape[0] * A.shape[1]
173-
w = np.tile(w, (A.shape[1]))
176+
if isinstance(w, np.ndarray):
177+
w = np.tile(w, (A.shape[1]))
174178
A = A.reshape((A.shape[0] * A.shape[1], A.shape[2]))
175179
idc = idc.reshape((idc.shape[0] * idc.shape[1], idc.shape[2]))
176180
B = B.reshape((A.shape[0]))
@@ -184,7 +188,6 @@ def add_constraints_to_least_squares(self, A, B, idc, w=1.0, name="undefined"):
184188
A[length > 0, :] /= length[length > 0, None]
185189
if isinstance(w, (float, int)):
186190
w = np.ones(A.shape[0]) * w
187-
188191
if isinstance(w, np.ndarray) == False:
189192
raise BaseException("w must be a numpy array")
190193

@@ -277,7 +280,7 @@ def add_equality_constraints(self, node_idx, values, name="undefined"):
277280
gi[self.region] = np.arange(0, self.nx)
278281
idc = gi[node_idx]
279282
outside = ~(idc == -1)
280-
283+
281284
self.equal_constraints[name] = {
282285
"A": np.ones(idc[outside].shape[0]),
283286
"B": values[outside],
@@ -423,7 +426,7 @@ def build_matrix(self, square=True, damp=0.0, ie=False):
423426
# can help speed up solving, but might also introduce some errors
424427

425428
if len(self.equal_constraints) > 0:
426-
logger.info("Equality block is %i x %i" % (self.eq_const_c, self.nx))
429+
logger.info(f"Equality block is {self.eq_const_c} x {self.nx}")
427430
# solving constrained least squares using
428431
# | ATA CT | |c| = b
429432
# | C 0 | |y| d
@@ -448,7 +451,6 @@ def build_matrix(self, square=True, damp=0.0, ie=False):
448451
cols.extend(c["col"].flatten()[~mask].tolist())
449452

450453
C = coo_matrix(
451-
452454
(np.array(a), (np.array(rows), cols)),
453455
shape=(self.eq_const_c, self.nx),
454456
dtype=float,
@@ -500,30 +502,6 @@ def _solve_osqp(self, P, A, q, l, u, mkl=False):
500502
import osqp
501503
except ImportError:
502504
raise LoopImportError("Missing osqp pip install osqp")
503-
# m = A.shape[0]
504-
# n = A.shape[1]
505-
# Ad = sparse.random(m, n, density=0.7, format='csc')
506-
# b = np.random.randn(m)
507-
508-
# # OSQP data
509-
# P = sparse.block_diag([sparse.csc_matrix((n, n)), sparse.eye(m)], format='csc')
510-
# q = np.zeros(n+m)
511-
# A = sparse.vstack([
512-
# sparse.hstack([Ad, -sparse.eye(m)]),
513-
# sparse.hstack([sparse.eye(n), sparse.csc_matrix((n, m))])], format='csc')
514-
# l = np.hstack([b, np.zeros(n)])
515-
# u = np.hstack([b, np.ones(n)])
516-
517-
# # Create an OSQP object
518-
# prob = osqp.OSQP()
519-
520-
# # Setup workspace
521-
# prob.setup(P, q, A, l, u)
522-
523-
# # Solve problem
524-
# res = prob.solve()
525-
526-
# Create an OSQP object
527505
prob = osqp.OSQP()
528506

529507
# Setup workspace
@@ -727,6 +705,7 @@ def _solve(self, solver="cg", **kwargs):
727705
self.c[self.region] = self._solve_chol(A, B)
728706
if solver == "lu":
729707
logger.info("Solving using scipy LU")
708+
print(self.region)
730709
self.c[self.region] = self._solve_lu(A, B)
731710
if solver == "pyamg":
732711
try:

LoopStructural/interpolators/finite_difference_interpolator.py renamed to LoopStructural/interpolators/_finite_difference_interpolator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import numpy as np
77

88
from LoopStructural.utils.helper import get_vectors
9-
from .discrete_interpolator import DiscreteInterpolator
9+
from ._discrete_interpolator import DiscreteInterpolator
1010
from LoopStructural.interpolators import InterpolatorType
1111

12-
from .operator import Operator
12+
from ._operator import Operator
1313

1414
from LoopStructural.utils import getLogger
1515

LoopStructural/interpolators/geological_interpolator.py renamed to LoopStructural/interpolators/_geological_interpolator.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ def __init__(self):
4747
self.valid = False
4848

4949
def __str__(self):
50-
51-
return self.__str
50+
name = f"{self.type} \n"
51+
name += f"{self.n_g} gradient points\n"
52+
name += f"{self.n_i} interface points\n"
53+
name += f"{self.n_n} normal points\n"
54+
name += f"{self.n_t} tangent points\n"
55+
name += f"{self.n_g + self.n_i + self.n_n + self.n_t} total points\n"
56+
return name
5257

5358
def set_region(self, **kwargs):
5459
pass

0 commit comments

Comments
 (0)