# Copyright (c) 2012-2024, OpenGeoSys Community (http://www.opengeosys.org)
# Distributed under a Modified BSD License.
# See accompanying file LICENSE.txt or
# http://www.opengeosys.org/project/license
#
# flake8: noqa: E501
"""Predefined properties.
".. seealso:: :py:mod:`ogstools.propertylib.tensor_math`"
"""
from functools import partial
from typing import Union
import pandas as pd
import pyvista as pv
from . import mesh_dependent, tensor_math
from .custom_colormaps import integrity_cmap, temperature_cmap
from .matrix import Matrix
from .property import Property, Scalar
from .tensor_math import identity
from .vector import Vector
T_MASK = "temperature_active"
H_MASK = "pressure_active"
M_MASK = "displacement_active"
# general
material_id = Scalar(data_name="MaterialIDs", categoric=True, cmap="tab20")
# thermal
temperature = Scalar(
data_name="temperature",
data_unit="K",
output_name="temperature",
output_unit="°C",
mask=T_MASK,
cmap=temperature_cmap,
bilinear_cmap=True,
)
heatflowrate = Scalar(data_name="HeatFlowRate", mask=T_MASK)
# hydraulic
pressure = Scalar(
data_name="pressure",
data_unit="Pa",
output_unit="MPa",
output_name="pore_pressure",
mask=H_MASK,
cmap="Blues",
)
hydraulic_height = Scalar(
data_name="pressure",
data_unit="m",
output_unit="m",
output_name="hydraulic_height",
mask=H_MASK,
cmap="Blues",
)
velocity = Vector(
data_name="velocity",
data_unit="m/s",
output_unit="m/s",
output_name="darcy_velocity",
mask=H_MASK,
)
massflowrate = Scalar(data_name="MassFlowRate", mask=H_MASK)
# mechanical
displacement = Vector(
data_name="displacement",
data_unit="m",
output_unit="m",
mask=M_MASK,
cmap="PRGn",
bilinear_cmap=True,
)
strain = Matrix(
data_name="epsilon",
data_unit="",
output_unit="percent",
output_name="strain",
mask=M_MASK,
)
stress = Matrix(
data_name="sigma",
data_unit="Pa",
output_unit="MPa",
output_name="stress",
mask=M_MASK,
)
effective_pressure = Scalar(
data_name="sigma",
data_unit="Pa",
output_unit="MPa",
output_name="effective_pressure",
mask=M_MASK,
func=tensor_math.effective_pressure,
)
dilatancy_critescu_tot = Scalar(
data_name="sigma",
data_unit="Pa",
output_unit="",
output_name="dilatancy_criterion",
mask=M_MASK,
func=mesh_dependent.dilatancy_critescu,
mesh_dependent=True,
cmap=integrity_cmap,
bilinear_cmap=True,
)
dilatancy_critescu_eff = dilatancy_critescu_tot.replace(
output_name="effective_dilatancy_criterion",
func=partial(mesh_dependent.dilatancy_critescu, effective=True),
)
dilatancy_alkan = Scalar(
data_name="sigma",
data_unit="Pa",
output_unit="MPa",
output_name="dilatancy_criterion",
mask=M_MASK,
func=mesh_dependent.dilatancy_alkan,
mesh_dependent=True,
cmap=integrity_cmap,
bilinear_cmap=True,
)
dilatancy_alkan_eff = dilatancy_alkan.replace(
output_name="effective_dilatancy_criterion",
func=partial(mesh_dependent.dilatancy_alkan, effective=True),
)
fluid_pressure_crit = Scalar(
data_name="sigma",
data_unit="Pa",
output_unit="MPa",
output_name="fluid_pressure_criterion",
mask=M_MASK,
func=mesh_dependent.fluid_pressure_criterion,
mesh_dependent=True,
cmap=integrity_cmap,
bilinear_cmap=True,
)
nodal_forces = Vector(data_name="NodalForces", mask=M_MASK)
all_properties = [v for v in locals().values() if isinstance(v, Property)]
[docs]
def get_preset(
mesh_property: Union[Property, str], mesh: pv.UnstructuredGrid
) -> Property:
"""
Returns a Property preset or creates one with correct type.
Searches for presets by data_name and output_name and returns if found.
If 'mesh_property' is given as type Property this will also look for
derived properties (difference, aggregate).
Otherwise create Scalar, Vector, or Matrix Property depending on the shape
of data in mesh.
:param mesh_property: The property to retrieve or its name if a string.
:param mesh: The mesh containing the property data.
:returns: A corresponding Property preset or a new Property of correct type.
"""
data_keys: list[str] = list(set().union(mesh.point_data, mesh.cell_data))
error_msg = (
f"Data not found in mesh. Available data names are {data_keys}. "
)
if isinstance(mesh_property, Property):
if mesh_property.data_name in data_keys:
return mesh_property
matches = [
mesh_property.output_name in data_key for data_key in data_keys
]
if not any(matches):
raise KeyError(error_msg)
data_key = data_keys[matches.index(True)]
if data_key == f"{mesh_property.output_name}_difference":
return mesh_property.difference
return mesh_property.replace(
data_name=data_key,
data_unit=mesh_property.output_unit,
output_unit=mesh_property.output_unit,
output_name=data_key,
func=identity,
mesh_dependent=False,
)
for prop in all_properties:
if prop.output_name == mesh_property:
return prop
for prop in all_properties:
if prop.data_name == mesh_property:
return prop
matches = [mesh_property in data_key for data_key in data_keys]
if not any(matches):
raise KeyError(error_msg)
data_shape = mesh[mesh_property].shape
if len(data_shape) == 1:
return Scalar(mesh_property)
if data_shape[1] in [2, 3]:
return Vector(mesh_property)
return Matrix(mesh_property)
[docs]
def get_dataframe() -> pd.DataFrame:
data = [
"preset,data_name,data_unit,output_unit,output_name,type".split(",")
]
for preset_name, preset_value in globals().items():
if isinstance(preset := preset_value, Property):
data += [
[
preset_name,
preset.data_name,
preset.data_unit,
preset.output_unit,
preset.output_name,
preset.type_name,
]
]
return (
pd.DataFrame(data[1:], columns=data[0])
.sort_values(["data_name", "preset"])
.set_index("preset")
)