From ab6b43c1b56af241aef9831a730a1ecbc1c2e90e Mon Sep 17 00:00:00 2001 From: FZill <florian.zill@ufz.de> Date: Wed, 6 Mar 2024 13:32:31 +0100 Subject: [PATCH] [propertylib] improved depth calculation + tests --- .../howto_meshplotlib/plot_solid_mechanics.py | 2 +- ogstools/propertylib/mesh_dependent.py | 56 ++++++++++++++----- tests/test_propertylib.py | 17 ++++++ 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/docs/examples/howto_meshplotlib/plot_solid_mechanics.py b/docs/examples/howto_meshplotlib/plot_solid_mechanics.py index 0a717d109..22fd4b904 100644 --- a/docs/examples/howto_meshplotlib/plot_solid_mechanics.py +++ b/docs/examples/howto_meshplotlib/plot_solid_mechanics.py @@ -108,7 +108,7 @@ fig = plot(mesh, presets.pressure) # %% del mesh.point_data["pressure"] -mesh["depth"] = mesh_dependent.depth(mesh) + 450 # in m +mesh["depth"] = mesh_dependent.depth(mesh, use_coords=True) fig = plot(mesh, "depth") mesh["pressure"] = mesh_dependent.p_fluid(mesh) fig = plot(mesh, presets.pressure) diff --git a/ogstools/propertylib/mesh_dependent.py b/ogstools/propertylib/mesh_dependent.py index d586feaf6..c26938ba0 100644 --- a/ogstools/propertylib/mesh_dependent.py +++ b/ogstools/propertylib/mesh_dependent.py @@ -14,23 +14,51 @@ from .unit_registry import u_reg ValType = Union[PlainQuantity, np.ndarray] -# TODO: find smart way to choose between y values and depth -def depth(mesh: pv.UnstructuredGrid) -> np.ndarray: +def depth(mesh: pv.UnstructuredGrid, use_coords: bool = False) -> np.ndarray: """Return the depth values of the mesh. - This assumes the y-axes is the vertical axis. For each point, the shortest - distance to the top boundary is returned. + For 2D, the last non-flat dimension is used as the vertical axis, for 3D, + the z-axes is used. + If `use_coords` is True, returns the negative coordinate value of the + vertical axis. Otherwise, the vertical distance to the top facing edges / + surfaces are returned. """ - # return -mesh.points[:, 1] - edges = mesh.extract_feature_edges() - cell_bounds = np.asarray([cell.bounds for cell in edges.cell]) - cell_vec = np.abs(cell_bounds[..., 1:2] - cell_bounds[..., :2]) - ids = np.argmax(cell_vec, axis=-1) == 0 - ids2 = edges.cell_centers().points[:, 1] > edges.center[1] - top = edges.extract_cells(ids & ids2) - - vert_diff = mesh.points[:, 1, None] - top.points[:, 1] - return np.abs(np.min(vert_diff, axis=-1)) + if mesh.volume > 0: + # prevents inner edge (from holes) to be detected as a top boundary + edges = mesh.extract_surface().connectivity("point_seed", point_ids=[0]) + top_id = 2 + if use_coords: + return -mesh.points[:, top_id] + point_upwards = edges.cell_normals[..., top_id] > 0 + top_cells = point_upwards + else: + mean_normal = np.abs( + np.mean(mesh.extract_surface().cell_normals, axis=0) + ) + top_id = np.delete([0, 1, 2], int(np.argmax(mean_normal)))[-1] + if use_coords: + return -mesh.points[:, top_id] + # prevents inner edge (from holes) to be detected as a top boundary + edges = mesh.extract_feature_edges().connectivity( + "point_seed", point_ids=[0] + ) + edge_horizontal_extent = [ + np.diff(np.reshape(cell.bounds, (-1, 2))).ravel()[0] + for cell in edges.cell + ] + edge_centers = edges.cell_centers().points + adj_cells = [mesh.find_closest_cell(point) for point in edge_centers] + adj_cell_centers = mesh.extract_cells(adj_cells).cell_centers().points + are_above = edge_centers[..., top_id] > adj_cell_centers[..., top_id] + are_non_vertical = np.asarray(edge_horizontal_extent) > 1e-12 + top_cells = are_above & are_non_vertical + top = edges.extract_cells(top_cells) + eucl_distance_projected_top_points = np.sum( + np.abs(np.delete(mesh.points[:, None] - top.points, top_id, axis=-1)), + axis=-1, + ) + matching_top = np.argmin(eucl_distance_projected_top_points, axis=-1) + return np.abs(mesh.points[..., top_id] - top.points[matching_top, top_id]) def p_fluid(mesh: pv.UnstructuredGrid) -> PlainQuantity: diff --git a/tests/test_propertylib.py b/tests/test_propertylib.py index c94b2565f..2a203cf73 100644 --- a/tests/test_propertylib.py +++ b/tests/test_propertylib.py @@ -3,11 +3,13 @@ import unittest import numpy as np +import pyvista as pv from pint.facets.plain import PlainQuantity from ogstools.meshplotlib.examples import mesh_mechanics from ogstools.propertylib import presets as pp from ogstools.propertylib.matrix import Matrix +from ogstools.propertylib.mesh_dependent import depth from ogstools.propertylib.property import Scalar, u_reg from ogstools.propertylib.vector import Vector @@ -97,6 +99,21 @@ class PhysicalPropertyTest(unittest.TestCase): self.equality(pp.stress.qp_ratio, sig, qty(-100, "percent")) self.equality(pp.stress.qp_ratio, [sig] * 2, qty([-100] * 2, "percent")) + def test_depth_2D(self): + mesh = mesh_mechanics + mesh["depth"] = depth(mesh, use_coords=True) + # y Axis is vertical axis + self.assertTrue(np.all(mesh["depth"] == -mesh.points[..., 1])) + mesh["depth"] = depth(mesh) + self.assertTrue(np.all(mesh["depth"] < -mesh.points[..., 1])) + + def test_depth_3D(self): + mesh = pv.SolidSphere(100, center=(0, 0, -101)) + mesh["depth"] = depth(mesh, use_coords=True) + self.assertTrue(np.all(mesh["depth"] == -mesh.points[..., -1])) + mesh["depth"] = depth(mesh) + self.assertTrue(np.all(mesh["depth"] < -mesh.points[..., -1])) + def test_integrity_criteria(self): """Test integrity criteria.""" sig = np.array([4, 1, 2, 1, 1, 1]) * 1e6 -- GitLab