Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: updating scaling for plotting #219

Merged
merged 38 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
600f67e
docs: add pyvista plot directives and update some documentations
lachlangrose Feb 5, 2025
b636876
fix: interpolatorbuilder syntax/method naming
lachlangrose Feb 5, 2025
08bff10
fix: bb global origin should be 0 if it is not being used.
lachlangrose Feb 5, 2025
2d4ff78
fix: allow fitting of rotation to be disabled for the euclidean trans…
lachlangrose Feb 5, 2025
b7aac57
update copy of points
lachlangrose Feb 5, 2025
b59271b
Add Optional back
lachlangrose Feb 5, 2025
55b384f
fix: geoh5 format for grids
lachlangrose Feb 7, 2025
10f1aab
fix: adding spatially varying regularisation for fdi
lachlangrose Feb 7, 2025
e18145d
fix: interpolator builder use bounding box geometry if no other mesh …
lachlangrose Feb 7, 2025
be8f6a8
Merge branch 'fix/docs_and_cleanup' of github.com:Loop3D/LoopStructur…
lachlangrose Feb 7, 2025
718317e
Merge branch 'master' into fix/docs_and_cleanup
lachlangrose Feb 12, 2025
9cfa132
style: style fixes by ruff and autoformatting by black
lachlangrose Feb 12, 2025
c77c12a
fix: change nelements for interpolator using builder kwargs
lachlangrose Feb 12, 2025
1eec6dc
style: black
lachlangrose Feb 12, 2025
9adac9e
style: remove unused imports
lachlangrose Feb 12, 2025
1c48d61
fix: allowing nelements to be updated for an interpolator
lachlangrose Feb 14, 2025
b644fb7
fix: allow small structured grid. needed for tetra
lachlangrose Feb 17, 2025
e895af5
fix: adding distance to bounding box
lachlangrose Feb 17, 2025
3b0c5ca
Merge branch 'fix/docs_and_cleanup' of github.com:Loop3D/LoopStructur…
lachlangrose Feb 17, 2025
bba3623
style: style fixes by ruff and autoformatting by black
lachlangrose Feb 17, 2025
f6ee608
Merge branch 'master' into fix/docs_and_cleanup
lachlangrose Feb 17, 2025
8b5a8e3
Merge branch 'fix/docs_and_cleanup' of github.com:Loop3D/LoopStructur…
lachlangrose Feb 17, 2025
c380d84
style: style fixes by ruff and autoformatting by black
lachlangrose Feb 17, 2025
34823e2
fix: don't scale fdi regularisation
lachlangrose Feb 18, 2025
050ba87
fix: add helper to get interpolator support as vtk with solution as n…
lachlangrose Feb 18, 2025
7c3af3d
fix: if interpolation geometry changed, make sure build args are updated
lachlangrose Feb 18, 2025
1e16aee
Merge branch 'fix/docs_and_cleanup' of github.com:Loop3D/LoopStructur…
lachlangrose Feb 18, 2025
5c11d74
style: style fixes by ruff and autoformatting by black
lachlangrose Feb 18, 2025
e0f4329
style: black autoformat
lachlangrose Feb 18, 2025
54a495a
Merge branch 'fix/docs_and_cleanup' of github.com:Loop3D/LoopStructur…
lachlangrose Feb 18, 2025
b013903
style: style fixes by ruff and autoformatting by black
lachlangrose Feb 18, 2025
acce4bc
tests: add fdi test for structural frame and make isclose less sensitive
lachlangrose Feb 18, 2025
6000f1d
Merge branch 'fix/docs_and_cleanup' of github.com:Loop3D/LoopStructur…
lachlangrose Feb 18, 2025
7067631
tests: reducing sensitivity further...
lachlangrose Feb 18, 2025
3a78c99
fix: scaling vectors for visualisation
lachlangrose Feb 20, 2025
f1dd6d4
fix: copy interpolated feature
lachlangrose Feb 20, 2025
6dd5611
fix: add build args accessor/settor for structural frame
lachlangrose Feb 20, 2025
e655f47
style: style fixes by ruff and autoformatting by black
lachlangrose Feb 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions LoopStructural/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
loggers = {}
from .modelling.core.geological_model import GeologicalModel
from .interpolators._api import LoopInterpolator
from .interpolators import InterpolatorBuilder
from .datatypes import BoundingBox
from .utils import log_to_console, log_to_file, getLogger, rng, get_levels

Expand Down
60 changes: 50 additions & 10 deletions LoopStructural/datatypes/_bounding_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(
if maximum is None and nsteps is not None and step_vector is not None:
maximum = origin + nsteps * step_vector
if origin is not None and global_origin is None:
global_origin = origin
global_origin = np.zeros(3)
self._origin = np.array(origin)
self._maximum = np.array(maximum)
self.dimensions = dimensions
Expand Down Expand Up @@ -90,7 +90,7 @@ def global_origin(self, global_origin):

@property
def global_maximum(self):
return self.maximum - self.origin + self._global_origin
return self.maximum + self.global_origin

@property
def valid(self):
Expand Down Expand Up @@ -242,6 +242,8 @@ def fit(self, locations: np.ndarray, local_coordinate: bool = False) -> Bounding
)
origin = locations.min(axis=0)
maximum = locations.max(axis=0)
origin = np.array(origin)
maximum = np.array(maximum)
if local_coordinate:
self.global_origin = origin
self.origin = np.zeros(3)
Expand Down Expand Up @@ -273,15 +275,50 @@ def with_buffer(self, buffer: float = 0.2) -> BoundingBox:
if self.origin is None or self.maximum is None:
raise LoopValueError("Cannot create bounding box with buffer, no origin or maximum")
# local coordinates, rescale into the original bounding boxes global coordinates
origin = self.origin - buffer * (self.maximum - self.origin)
maximum = self.maximum + buffer * (self.maximum - self.origin)
origin = self.origin - buffer * np.max(self.maximum - self.origin)
maximum = self.maximum + buffer * np.max(self.maximum - self.origin)
return BoundingBox(
origin=origin,
maximum=maximum,
global_origin=self.global_origin + origin,
global_origin=self.global_origin,
dimensions=self.dimensions,
)

# def __call__(self, xyz):
# xyz = np.array(xyz)
# if len(xyz.shape) == 1:
# xyz = xyz.reshape((1, -1))

# distances = np.maximum(0,
# np.maximum(self.global_origin+self.origin - xyz,
# xyz - self.global_maximum))
# distance = np.linalg.norm(distances, axis=1)
# distance[self.is_inside(xyz)] = -1
# return distance

def __call__(self, xyz):
# Calculate center and half-extents of the box
center = (self.maximum + self.global_origin + self.origin) / 2
half_extents = (self.maximum - self.global_origin + self.origin) / 2

# Calculate the distance from point to center
offset = np.abs(xyz - center) - half_extents

# Inside distance: negative value based on the smallest penetration
inside_distance = np.min(half_extents - np.abs(xyz - center), axis=1)

# Outside distance: length of the positive components of offset
outside_distance = np.linalg.norm(np.maximum(offset, 0))

# If any component of offset is positive, we're outside
# Otherwise, we're inside and return the negative penetration distance
distance = np.zeros(xyz.shape[0])
mask = np.any(offset > 0, axis=1)
distance[mask] = outside_distance
distance[~mask] = -inside_distance[~mask]
return distance
# return outside_distance if np.any(offset > 0) else -inside_distance

def get_value(self, name):
ix, iy = self.name_map.get(name, (-1, -1))
if ix == -1 and iy == -1:
Expand Down Expand Up @@ -319,7 +356,7 @@ def regular_grid(
self,
nsteps: Optional[Union[list, np.ndarray]] = None,
shuffle: bool = False,
order: str = "C",
order: str = "F",
local: bool = True,
) -> np.ndarray:
"""Get the grid of points from the bounding box
Expand Down Expand Up @@ -361,8 +398,8 @@ def regular_grid(
rng.shuffle(locs)
return locs

def cell_centers(self, order: str = "F") -> np.ndarray:
"""Get the cell centers of a regular grid
def cell_centres(self, order: str = "F") -> np.ndarray:
"""Get the cell centres of a regular grid

Parameters
----------
Expand All @@ -372,7 +409,7 @@ def cell_centers(self, order: str = "F") -> np.ndarray:
Returns
-------
np.ndarray
array of cell centers
array of cell centres
"""
locs = self.regular_grid(order=order, nsteps=self.nsteps - 1)

Expand Down Expand Up @@ -434,7 +471,7 @@ def structured_grid(
_cell_data = copy.deepcopy(cell_data)
_vertex_data = copy.deepcopy(vertex_data)
return StructuredGrid(
origin=self.global_origin,
origin=self.global_origin + self.origin,
step_vector=self.step_vector,
nsteps=self.nsteps,
cell_properties=_cell_data,
Expand All @@ -460,6 +497,9 @@ def project(self, xyz):
(self.global_maximum - self.global_origin)
) # np.clip(xyz, self.origin, self.maximum)

def scale_by_projection_factor(self, value):
return value / np.max((self.global_maximum - self.global_origin))

def reproject(self, xyz):
"""Reproject a point from the bounding box to the global space

Expand Down
15 changes: 11 additions & 4 deletions LoopStructural/datatypes/_point.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ def from_dict(self, d):
def vtk(
self,
geom='arrow',
scale=0.10,
scale=1.0,
scale_function=None,
normalise=True,
normalise=False,
tolerance=0.05,
bb=None,
scalars=None,
Expand All @@ -140,9 +140,15 @@ def vtk(

_projected = False
vectors = np.copy(self.vectors)

if normalise:
norm = np.linalg.norm(vectors, axis=1)
vectors[norm > 0, :] /= norm[norm > 0][:, None]
else:
norm = np.linalg.norm(vectors, axis=1)
vectors[norm > 0, :] /= norm[norm > 0][:, None]
norm = norm[norm > 0] / norm[norm > 0].max()
vectors *= norm[:, None]
if scale_function is not None:
# vectors /= np.linalg.norm(vectors, axis=1)[:, None]
vectors *= scale_function(self.locations)[:, None]
Expand All @@ -151,6 +157,7 @@ def vtk(
try:
locations = bb.project(locations)
_projected = True
scale = bb.scale_by_projection_factor(scale)
except Exception as e:
logger.error(f'Failed to project points to bounding box: {e}')
logger.error('Using unprojected points, this may cause issues with the glyphing')
Expand All @@ -161,10 +168,10 @@ def vtk(
if geom == 'arrow':
geom = pv.Arrow(scale=scale)
elif geom == 'disc':
geom = pv.Disc(inner=0, outer=scale).rotate_y(90)
geom = pv.Disc(inner=0, outer=scale * 0.5, c_res=50).rotate_y(90)

# Perform the glyph
glyphed = points.glyph(orient="vectors", geom=geom, tolerance=tolerance, scale=False)
glyphed = points.glyph(orient="vectors", geom=geom, tolerance=tolerance)
if _projected:
glyphed.points = bb.reproject(glyphed.points)
return glyphed
Expand Down
32 changes: 30 additions & 2 deletions LoopStructural/datatypes/_structured_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ def vtk(self):
z,
)
for name, data in self.properties.items():
grid[name] = data.flatten(order="F")
grid[name] = data.reshape((grid.n_points, -1), order="F")
for name, data in self.cell_properties.items():
grid.cell_data[name] = data.flatten(order="F")
grid.cell_data[name] = data.reshape((grid.n_cells, -1), order="F")
return grid

def plot(self, pyvista_kwargs={}):
Expand All @@ -63,6 +63,34 @@ def plot(self, pyvista_kwargs={}):
except ImportError:
logger.error("pyvista is required for vtk")

@property
def cell_centres(self):
x = np.linspace(
self.origin[0] + self.step_vector[0] * 0.5,
self.maximum[0] + self.step_vector[0] * 0.5,
self.nsteps[0] - 1,
)
y = np.linspace(
self.origin[1] + self.step_vector[1] * 0.5,
self.maximum[1] - self.step_vector[1] * 0.5,
self.nsteps[1] - 1,
)
z = np.linspace(
self.origin[2] + self.step_vector[2] * 0.5,
self.maximum[2] - self.step_vector[2] * 0.5,
self.nsteps[2] - 1,
)
x, y, z = np.meshgrid(x, y, z, indexing="ij")
return np.vstack([x.flatten(order='f'), y.flatten(order='f'), z.flatten(order='f')]).T

@property
def nodes(self):
x = np.linspace(self.origin[0], self.maximum[0], self.nsteps[0])
y = np.linspace(self.origin[1], self.maximum[1], self.nsteps[1])
z = np.linspace(self.origin[2], self.maximum[2], self.nsteps[2])
x, y, z = np.meshgrid(x, y, z, indexing="ij")
return np.vstack([x.flatten(order='f'), y.flatten(order='f'), z.flatten(order='f')]).T

def merge(self, other):
if not np.all(np.isclose(self.origin, other.origin)):
raise ValueError("Origin of grids must be the same")
Expand Down
18 changes: 18 additions & 0 deletions LoopStructural/interpolators/_discrete_interpolator.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,20 @@ def __init__(self, support, data={}, c=None, up_to_date=False):
logger.info("Creating discrete interpolator with {} degrees of freedom".format(self.nx))
self.type = InterpolatorType.BASE_DISCRETE

def set_nelements(self, nelements: int) -> int:
return self.support.set_nelements(nelements)

@property
def n_elements(self) -> int:
"""Number of elements in the interpolator

Returns
-------
int
number of elements, positive
"""
return self.support.n_elements

@property
def nx(self) -> int:
"""Number of degrees of freedom for the interpolator
Expand Down Expand Up @@ -161,6 +175,7 @@ def reset(self):
"""
self.constraints = {}
self.c_ = 0
self.regularisation_scale = np.ones(self.nx)
logger.info("Resetting interpolation constraints")

def add_constraints_to_least_squares(self, A, B, idc, w=1.0, name="undefined"):
Expand Down Expand Up @@ -737,3 +752,6 @@ def to_dict(self):
**super().to_dict(),
# 'region_function':self.region_function,
}

def vtk(self):
return self.support.vtk({'c': self.c})
Loading
Loading