Source code for ogstools.materiallib.core.components

# SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
# SPDX-License-Identifier: BSD-3-Clause

from pathlib import Path

import yaml  # type: ignore[import]

# from ogstools.definitions import MATERIALS_DIR
import ogstools.definitions as defs

from .component import Component
from .material import Material
from .property import MaterialProperty


[docs] class Components:
[docs] def __init__( self, phase_type: str, gas_component: Material, liquid_component: Material, process: str, Diffusion_coefficient: float | None = None, data_dir: Path | str | None = None, ): self.phase_type = phase_type self.gas_properties: list[MaterialProperty] = gas_component.properties self.liquid_properties: list[MaterialProperty] = ( liquid_component.properties ) self.process = process self.data_dir = ( Path(data_dir) if data_dir is not None else Path(defs.MATERIALS_DIR) ) self.gas_component = gas_component self.liquid_component = liquid_component if self.phase_type == "AqueousLiquid": gas_role = "Solute" liquid_role = "Solvent" elif self.phase_type == "Gas": gas_role = "Carrier" liquid_role = "Vapour" else: msg = f"Unsupported phase_type: {self.phase_type}" raise ValueError(msg) D = ( Diffusion_coefficient if Diffusion_coefficient is not None else self.get_binary_diffusion_coefficient(self.phase_type) ) self.gas_component_obj = self._create_component( self.gas_component, gas_role, D ) self.liquid_component_obj = self._create_component( self.liquid_component, liquid_role, D )
[docs] def get_binary_diffusion_coefficient(self, phase: str) -> float: """ Retrieves the binary diffusion coefficient D [m²/s] for a specific component pair in either the liquid or gas phase. In the liquid phase, this typically describes a gas (e.g. CO2) diffusing in a solvent like water. In the gas phase, it typically describes a vapor (e.g. H2O) diffusing in a carrier gas like CO₂. Binary diffusion coefficients depend on a pair of species rather than a single material, which is why they are treated differently from other material properties. Parameters: phase (str): Either "liquid" or "gas". Indicates the phase in which diffusion occurs. Returns: float: Binary diffusion coefficient in [m²/s]. Raises: ValueError: If the component pair is not found. """ if phase == "AqueousLiquid": # Solute (gas component) diffuses in solvent (liquid component) solvent = self.liquid_component.name solute = self.gas_component.name elif phase == "Gas": # Solute (liquid component) evaporates into solvent (gas component) solvent = self.gas_component.name solute = self.liquid_component.name else: msg = f"Invalid phase '{phase}'. Must be 'AqueousLiquid' or 'Gas'." raise ValueError(msg) file_path = self._diffusion_coefficients_path() with file_path.open() as f: data = yaml.safe_load(f) try: return float(data[phase][solvent][solute]) except KeyError: pass try: return float(data[phase][solute][solvent]) except KeyError as err: msg = ( f"No {phase}-phase diffusion coefficient found for the pair " f"'{solvent} / {solute}' in {file_path}" ) raise ValueError(msg) from err
def _diffusion_coefficients_path(self) -> Path: """ Resolve the diffusion coefficients file within the configured data directory. Prefers .yml but accepts .yaml as well so external libraries can supply either. If not found in the configured data directory, fall back to the built-in materials directory for backward compatibility. """ default_dir = Path(defs.MATERIALS_DIR) data_dir = self.data_dir names = ["diffusion_coefficients.yaml", "diffusion_coefficients.yml"] for dir_ in [data_dir, default_dir]: for name in names: candidate = dir_ / name if candidate.is_file(): return candidate # Default to the configured data_dir .yml path for error reporting/opening. return data_dir / names[-1] def _create_component( self, material: Material, role: str, D: float ) -> Component: return Component( material, self.phase_type, role, self.process, diffusion_coefficient=D, ) # ----------------------- # Representation # ----------------------- def __repr__(self) -> str: lines = [f"<Components for phase '{self.phase_type}'>"] for comp in [self.gas_component_obj, self.liquid_component_obj]: for line in repr(comp).splitlines(): lines.append(" " + line) return "\n".join(lines)