From 2f6e06e624a14d55998927e2de4082cac2275c9e Mon Sep 17 00:00:00 2001 From: Norbert Grunwald <norbert.grunwald@ufz.de> Date: Mon, 31 Mar 2025 09:49:50 +0200 Subject: [PATCH] components now created in pairs for a certain phase --- ogstools/__init__.py | 2 + ogstools/materiallib/__init__.py | 2 + ogstools/materiallib/core/__init__.py | 2 + ogstools/materiallib/core/component.py | 135 +++++++++++++++++- ogstools/materiallib/core/components.py | 39 +++++ ogstools/materiallib/core/material_list.py | 83 +++++++++-- ogstools/materiallib/schema/process_schema.py | 14 +- 7 files changed, 258 insertions(+), 19 deletions(-) create mode 100644 ogstools/materiallib/core/components.py diff --git a/ogstools/__init__.py b/ogstools/__init__.py index 10aba63f..e6594142 100644 --- a/ogstools/__init__.py +++ b/ogstools/__init__.py @@ -13,6 +13,7 @@ from ._find_ogs import cli, status # from .materiallib import MaterialList, MaterialDB, MaterialLib, validate_medium from .materiallib import ( Component, + Components, MaterialDB, # MaterialLib, MaterialList, @@ -46,6 +47,7 @@ __all__ = [ "Medium", "Phase", "Component", + "Components", "Property", "validate_medium", ] diff --git a/ogstools/materiallib/__init__.py b/ogstools/materiallib/__init__.py index 1ef54aa1..98c03be9 100644 --- a/ogstools/materiallib/__init__.py +++ b/ogstools/materiallib/__init__.py @@ -7,6 +7,7 @@ from .core import ( Component, + Components, MaterialDB, # MaterialLib, MaterialList, @@ -23,6 +24,7 @@ __all__ = [ "Medium", "Phase", "Component", + "Components", "Property", "validate_medium", ] diff --git a/ogstools/materiallib/core/__init__.py b/ogstools/materiallib/core/__init__.py index d83b3045..2ad04c50 100644 --- a/ogstools/materiallib/core/__init__.py +++ b/ogstools/materiallib/core/__init__.py @@ -8,6 +8,7 @@ from .component import Component +from .components import Components from .material import Material from .material_db import MaterialDB from .material_list import MaterialList @@ -24,6 +25,7 @@ __all__ = [ "Medium", "Phase", "Component", + "Components", "Property", "MaterialLib", ] diff --git a/ogstools/materiallib/core/component.py b/ogstools/materiallib/core/component.py index c732be81..251a5bbb 100644 --- a/ogstools/materiallib/core/component.py +++ b/ogstools/materiallib/core/component.py @@ -1,10 +1,133 @@ -from typing import Any +from ogstools.materiallib.schema.process_schema import PROCESS_SCHEMAS + +from .material import Material +from .property import Property class Component: - def __init__(self, name: str, properties: list | None = None): - self.name = name - self.properties = properties or [] + def __init__( + self, + material: Material, + phase_type: str, + role: str, # This materials role in the phase, e.g. 'solute' or 'solvent, etc. + process: str, + ): + self.material = material + self.phase_type = phase_type + self.role = role + self.name = material.name + + self.schema = PROCESS_SCHEMAS.get(process) + if not self.schema: + msg = f"No process schema found for '{process}'." + raise ValueError(msg) + + self.properties: list[Property] = self._get_filtered_properties() + + def _get_filtered_properties(self) -> list[Property]: + """ + This method filters the material properties based on the process schema + and the role (gas or liquid). + """ + required_properties = set() + + # Process schema check and filter required properties + if self.schema: + print( + f"Processing schema for phase type: {self.phase_type}" + ) # Debug: Phase being processed + + # Iterate over the process schema to find the relevant properties + for key, value in self.schema.items(): + print( + f"Checking key: {key}, value: {value}" + ) # Debug: Output the schema's current key and value + + # Ensure 'value' is a dictionary and the phase matches the current phase type + if isinstance(value, dict) and key == self.phase_type: + # Debug output for components in the phase + if "Components" in value: + print( + f"Components found for {key}: {value['Components']}" + ) # Debug: Show components + + # Now check the role within the components + if self.role in value["Components"]: + # Debug: Role found, print properties + print( + f"Properties for role '{self.role}': {value['Components'][self.role]}" + ) + + # Add the properties for the current role (gas or liquid) + required_properties.update( + value["Components"][self.role] + ) + else: + print( + f"Role '{self.role}' not found in components for {key}" + ) # Debug: Role not found + else: + print( + f"No components found in schema for phase {key}" + ) # Debug: No components + else: + print( + f"Skipping phase {key} because it does not match {self.phase_type} or is not a dict" + ) # Debug: Phase mismatch + + # Debug: Print the required properties + print(f"Required properties: {required_properties}") + + # Now filter the material properties based on the schema + filtered_properties = [ + prop + for prop in self.material.get_properties() + if prop.name in required_properties + ] + + # Debug: Print filtered properties + print(f"Filtered properties: {filtered_properties}") + + return filtered_properties + + # def _get_filtered_properties(self) -> list[Property]: + # """ + # This method filters the material properties based on the process schema + # and the role (gas or liquid). + # """ + # required_properties = set() + + # # Process schema check and filter required properties + # if self.schema: + # # Here we filter the properties that are specified in the process schema + # # for this phase and role + # for key, value in self.schema.items(): + # # Make sure `value` is a dictionary and the role is in the components + # if ( + # isinstance(value, dict) + # and key == self.phase_type + # and "Components" in value + # and self.role in value["Components"] + # ): + # # Add all properties for the role (gas or liquid) + # required_properties.update(value["Components"][self.role]) + # print(value["Components"][self.role]) + # print("##############################") + + # # Now we filter the material properties based on the schema + # return [ + # prop + # for prop in self.material.get_properties() + # if prop.name in required_properties + # ] + + def __repr__(self) -> str: + # Sammle die Namen der Eigenschaften + property_names = [prop.name for prop in self.properties] - def add_property(self, prop: Any) -> None: - self.properties.append(prop) + # Gebe die Namen der Eigenschaften aus + return ( + f"<Component '{self.name}' (Role: {self.role}, Phase: {self.phase_type}) with " + f"{len(self.properties)} properties:\n" + f" ├─ {'\n ├─ '.join(property_names)}>" + ) diff --git a/ogstools/materiallib/core/components.py b/ogstools/materiallib/core/components.py new file mode 100644 index 00000000..ceb15fea --- /dev/null +++ b/ogstools/materiallib/core/components.py @@ -0,0 +1,39 @@ +from .component import Component +from .material import Material +from .property import Property + + +class Components: + def __init__( + self, + phase_type: str, + gas_component: Material, + liquid_component: Material, + process: str, + ): + self.phase_type = phase_type + self.gas_properties: list[Property] = gas_component.get_properties() + self.liquid_properties: list[Property] = ( + liquid_component.get_properties() + ) + self.process = process + + self.gas_component = gas_component + self.liquid_component = liquid_component + + self.gas_component_obj = self._create_component(self.gas_component, "A") + self.liquid_component_obj = self._create_component( + self.liquid_component, "W" + ) + + def _create_component(self, material: Material, role: str) -> Component: + return Component(material, self.phase_type, role, self.process) + + def __repr__(self) -> str: + return ( + f"<Components for phase '{self.phase_type}' with " + f"Gas Component: {self.gas_component_obj.name}, " + f"Liquid Component: {self.liquid_component_obj.name}.\n>" + f"{self.gas_component_obj.name}: {self.gas_component_obj},\n>" + f"{self.liquid_component_obj.name}: {self.liquid_component_obj},\n>" + ) diff --git a/ogstools/materiallib/core/material_list.py b/ogstools/materiallib/core/material_list.py index 6c125590..e178b3df 100644 --- a/ogstools/materiallib/core/material_list.py +++ b/ogstools/materiallib/core/material_list.py @@ -1,4 +1,4 @@ -from typing import cast +from typing import Any, cast from ogstools.materiallib.schema.process_schema import PROCESS_SCHEMAS @@ -40,7 +40,7 @@ class MaterialList: [2] matID="1", name="bentonit", properties: ["Density", "Permeability", ...] [3] matID="2", name="concrete", properties: ["Density", "Permeability", ...] [4] matID="3", name="concrete", properties: ["Density", "Permeability", ...] - [5] matID="4", name="water", properties: ["Density", "Viscosity", ...] + [5] matID="X", name="water", properties: ["Density", "Viscosity", ...] """ def __init__( @@ -106,10 +106,65 @@ class MaterialList: material = Material(name=mat_name, properties=filtered_props) self.fluid_materials[phase_type] = material + # def get_required_property_names(self) -> set[str]: + # """ + # Returns a set of all property names required by the current process schema. + + # Prints a structured overview of the required properties, grouped by hierarchy level): + # - Medium-level properties + # - Phase-level properties + # - Component-level properties (if fluid) + + # """ + # if self.schema is None: + # raise ValueError("Process schema not set. Cannot determine required properties.") + + # required = set() + + # # Print an overview of the required property structure + # print("=== Required Property Structure ===") + # print("===-----------------------------===") + + # # Medium-level properties + # medium_props = self.schema.get("properties", []) + # if medium_props: + # print("Medium-level properties:") + # for prop in medium_props: + # print(f" - {prop}") + # print() + # required.update(medium_props) + + # # Phases and their properties + # for phase in self.schema.get("phases", []): + # phase_type = phase.get("type") + + # # Phase-level + # phase_props = phase.get("properties", []) + # if phase_props: + # print(f"Phase-level properties for '{phase_type}':") + # for prop in phase_props: + # print(f" - {prop}") + # print() + # required.update(phase_props) + + # # Component-level (if fluid) + # if phase_type in ["AqueousLiquid", "Gas"]: + # components = phase.get("components", {}) + # for comp_name, comp_props in components.items(): + # if comp_props: + # print(f"Component-level properties for '{comp_name}' in phase '{phase_type}':") + # for prop in comp_props: + # print(f" - {prop}") + # print() + # required.update(comp_props) + # print("===-----------------------------===") + + # return required + def get_required_property_names(self) -> set[str]: """ Returns a set of all property names required by the current process schema. - This includes medium, solid, phase and component properties. + This includes medium-level, phase-level, and component-level properties. """ if self.schema is None: msg = ( @@ -117,16 +172,20 @@ class MaterialList: ) raise ValueError(msg) - required = set() + required = set[str]() + PHASES_WITH_COMPONENTS = {"AqueousLiquid", "Gas", "NonAqueousLiquid"} + + # Medium-level + medium_properties = cast(list[str], self.schema.get("properties", [])) + required.update(medium_properties) - for key, value in self.schema.items(): - if key == "_fluids": - fluid_defs = cast(dict[str, dict[str, list[str]]], value) - for fluid_def in fluid_defs.values(): - required.update(fluid_def.get("phase_properties", [])) - required.update(fluid_def.get("component_properties", [])) - else: - required.add(key) + # Phase-level and component-level + phases = cast(list[dict[str, Any]], self.schema.get("phases", [])) + for phase in phases: + required.update(phase.get("properties", [])) + if phase.get("type") in PHASES_WITH_COMPONENTS: + for component_props in phase.get("components", {}).values(): + required.update(component_props) return required diff --git a/ogstools/materiallib/schema/process_schema.py b/ogstools/materiallib/schema/process_schema.py index f71597f0..09e872e6 100644 --- a/ogstools/materiallib/schema/process_schema.py +++ b/ogstools/materiallib/schema/process_schema.py @@ -1,4 +1,16 @@ PROCESS_SCHEMAS = { + "SMALL_DEFORMATION": { + "phases": [{"type": "Solid", "properties": ["density"]}], + "properties": [], + }, + "HEAT_CONDUCTION": { + "phases": [], + "properties": [ + "thermal_conductivity", + "density", + "specific_heat_capacity", + ], + }, "TH2M_PT": { "phases": [ { @@ -54,7 +66,7 @@ PROCESS_SCHEMAS = { "thermal_conductivity", "bishops_effective_stress", ], - } + }, } -- GitLab