Source code for ogstools.variables.mesh_dependent
# Copyright (c) 2012-2025, OpenGeoSys Community (http://www.opengeosys.org)
# Distributed under a Modified BSD License.
# See accompanying file LICENSE.txt or
# http://www.opengeosys.org/project/license
#
"Functions related to stress analysis which can be only applied to a mesh."
from collections.abc import Sequence
import numpy as np
import pyvista as pv
from .tensor_math import eigenvalues, mean, octahedral_shear
[docs]
def fluid_pressure_criterion(mesh: pv.UnstructuredGrid) -> np.ndarray:
"""Compute the fluid pressure criterion.
Requires "sigma" and "pressure" to be in the mesh and having the same units.
The criterion is defined as:
fluid pressure - minimal principal stress (compression positive).
.. math::
F_{p} = p_{fl} - \\sigma_{min}
"""
sig_min = eigenvalues(-mesh["sigma"])[..., 0]
return mesh["pressure"] - sig_min
[docs]
def dilatancy_critescu(
mesh: pv.UnstructuredGrid,
a: float = -0.01697,
b: float = 0.8996,
effective: bool = False,
) -> np.ndarray:
"""Compute the dilatancy criterion.
Requires "sigma" and "pressure" to be in the mesh (in Pa).
For total stresses it is defined as:
.. math::
F_{dil} = \\frac{\\tau_{oct}}{\\sigma_0} - a \\left( \\frac{\\sigma_m}{\\sigma_0} \\right)^2 - b \\frac{\\sigma_m}{\\sigma_0}
For effective stresses it is defined as:
.. math::
F'_{dil} = \\frac{\\tau_{oct}}{\\sigma_0} - a \\left( \\frac{\\sigma'_m}{\\sigma_0} \\right)^2 - b \\frac{\\sigma'_m}{\\sigma_0}
<https://www.sciencedirect.com/science/article/pii/S0360544222000512?via%3Dihub>
"""
sigma = -mesh["sigma"]
sigma_0 = 1e6
sigma_m = mean(sigma)
if effective:
sigma_m -= mesh["pressure"]
tau_oct = octahedral_shear(sigma)
return (
tau_oct / sigma_0 - a * (sigma_m / sigma_0) ** 2 - b * sigma_m / sigma_0
)
[docs]
def dilatancy_alkan(
mesh: pv.UnstructuredGrid,
b: float = 0.04,
tau_max: float = 33e6,
effective: bool = False,
) -> np.ndarray:
"""Compute the dilatancy criterion.
Requires "sigma" and "pressure" to be in the mesh (in Pa).
For total stresses it is defined as:
.. math::
F_{dil} = \\tau_{oct} - \\tau_{max} \\cdot b \\frac{\\sigma'_m}{\\sigma_0 + b \\cdot \\sigma'_m}
For effective stresses it is defined as:
.. math::
F_{dil} = \\tau_{oct} - \\tau_{max} \\cdot b \\frac{\\sigma'_m}{\\sigma_0 + b \\cdot \\sigma'_m}
<https://www.sciencedirect.com/science/article/pii/S1365160906000979>
"""
sigma = -mesh["sigma"]
sigma_m = mean(sigma)
if effective:
sigma_m -= mesh["pressure"]
tau = octahedral_shear(sigma)
return tau - tau_max * (b * sigma_m / (1e6 + b * sigma_m))
[docs]
def angles(
mesh: pv.UnstructuredGrid,
center: Sequence = (0.0, 0.0, 0.0),
normal: Sequence = (0.0, 0.0, 1.0),
) -> np.ndarray:
"""Compute the angles of the mesh's points around a normal and center.
:param mesh: For the points of this mesh the angles are computed.
:param center: Center of rotation.
:param normal: Normal axis of rotation.
"""
n = np.asarray(normal, dtype=float)
assert n.shape == (3,), "normal must be length-3"
n_unit = n / np.linalg.norm(n)
vecs = mesh.points - np.asarray(center, dtype=float)
# project each vector into the plane
v_dot_n = np.dot(vecs, n_unit) # (N,)
v_proj = vecs - np.outer(v_dot_n, n_unit) # (N,3)
trial_axis = np.array([1.0, 0.0, 0.0])
if abs(np.dot(trial_axis, n_unit)) > 0.9:
trial_axis = np.array([0.0, 1.0, 0.0])
u_axis = trial_axis - np.dot(trial_axis, n_unit) * n_unit
u_unit = u_axis / np.linalg.norm(u_axis)
v_axis = np.cross(n_unit, u_unit)
v_unit = v_axis / np.linalg.norm(v_axis)
# coordinates in plane
x = np.dot(v_proj, u_unit)
y = np.dot(v_proj, v_unit)
result = np.arctan2(y, x) # in radians, range (-pi, pi]
return np.where(np.hypot(x, y) > 1e-12, result, 0.0)