Repository: joergbuchwald/ogs6py

Issue:
extending  add_property for new MPL models 
Hi Joerg, 
we want to use the following MPL model with OGS6PY. But it is not considered yet.
```python
model.media.add_property(medium_id="0",                                                                                   
                    name="permeability",
                    type="GasPressureDependentPermeability",
                    initial_permeability="1e-15",
                    a1="0.125",
                    a2="152",
                    pressure_threshold="3.2e6",
                    minimum_permeability="0.0"	,												   
                    maximum_permeability="8.0e-16")	
```
THis is actually how the model is translated 
```xml
                <property>
                    <name>permeability</name>
                    <type>GasPressureDependentPermeability</type>
                </property>
```

and here is how the model should be: 

```xml
                <property>
                    <name>permeability</name>
                    <type>GasPressureDependentPermeability</type>
                    <initial_permeability>permeability0</initial_permeability>
                    <a1>0.125</a1>
                    <a2>152</a2>
                    <pressure_threshold>3.2e6</pressure_threshold>
                 <minimum_permeability>0.0</minimum_permeability>													   
                    <maximum_permeability>8.0e-16</maximum_permeability>										  
                </property>
```

Could you please add this model into ogs6py ? 
Thanks, Eric 

Discussion:

Patch:
diff --git a/examples/example_THM.py b/examples/example_THM.py
index 6430ec3..632afcc 100644
--- a/examples/example_THM.py
+++ b/examples/example_THM.py
@@ -39,9 +39,32 @@ model.media.add_property(medium_id="0",
                             name="density",
                             type="Linear",
                             reference_value="999.1",
-                            variable_name="temperature",
-                            reference_condition="273.15",
-                            slope="-4e-4")
+                            independent_variables={"temperature": {
+                                "reference_condition":273.15,
+                                "slope":-4e-4},
+                                "phase_pressure": {
+                                "reference_condition": 1e5,
+                                "slope": 1e-20
+                                }})
+# Alternative density models using property type Exponential or Function
+#model.media.add_property(medium_id="0",
+#                            phase_type="AqueousLiquid",
+#                            name="density",
+#                            type="Exponential",
+#                            reference_value="999.1",
+#                            offset="0.0",
+#                            exponent={"variable_name": "temperature",
+#                                "reference_condition":273.15,
+#                                "factor":-4e-4})
+#model.media.add_property(medium_id="0",
+#                            phase_type="AqueousLiquid",
+#                            name="density",
+#                            type="Function",
+#                            expression="999.1",
+#                            dvalues={"temperature": {
+#                                "expression":0.0},
+#                                "phase_pressure": {
+#                                "expression": 0.0}})
 model.media.add_property(medium_id="0",
                             phase_type="AqueousLiquid",
                             name="thermal_expansivity",
diff --git a/ogs6py/classes/media.py b/ogs6py/classes/media.py
index 0aa7e8f..a12ab79 100644
--- a/ogs6py/classes/media.py
+++ b/ogs6py/classes/media.py
@@ -22,6 +22,239 @@ class Media(build_tree.BuildTree):
                 'children': {}
             }
         }
+        self.properties = {"AverageMolarMass": [],
+            "BishopsSaturationCutoff": ["cutoff_value"],
+            "BishopsPowerLaw": ["exponent"],
+            "CapillaryPressureRegularizedVanGenuchten": ["exponent",
+                    "p_b",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "CapillaryPressureVanGenuchten": ["exponent",
+                    "maximum_capillary_pressure"
+                    "p_b",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "ClausiusClapeyron": ["critical_pressure",
+                    "critical_temperature",
+                    "reference_pressure",
+                    "reference_temperature",
+                    "triple_pressure",
+                    "triple_temperature"],
+            "Constant": ["value"],
+            "Curve" : ["curve", "independent_variable"],
+            "DupuitPermeability": ["parameter_name"],
+            "EffectiveThermalConductivityPorosityMixing": [],
+            "EmbeddedFracturePermeability": ["intrinsic_permeability",
+                    "initial_aperture",
+                    "mean_frac_distance",
+                    "threshold_strain",
+                    "fracture_normal",
+                    "fracture_rotation_xy",
+                    "fracture_rotation_yz"],
+            "Function": ["value"],
+            "Exponential": ["offset","reference_value"],
+            "GasPressureDependentPermeability": ["initial_permeability",
+                    "a1", "a2",
+                    "pressure_threshold",
+                    "minimum_permeability",
+                    "maximum_permeability"],
+            "IdealGasLaw": [],
+            "IdealGasLawBinaryMixture": [],
+            "KozenyCarmanModel": ["intitial_permeability", "initial_prosity"],
+            "Linear": ["reference_value"],
+            "LinearSaturationSwellingStress" : ["coefficient", "reference_saturation"],
+            "LinearWaterVapourLatentHeat" : [],
+            "OrthotropicEmbeddedFracturePermeability": ["intrinsic_permeability",
+                    "mean_frac_distances",
+                    "threshold_strains",
+                    "fracture_normals",
+                    "fracture_rotation_xy",
+                    "fracture_rotation_yz",
+                    "jacobian_factor"],
+            "Parameter": ["parameter_name"],
+            "PermeabilityMohrCoulombFailureIndexModel": ["cohesion",
+                    "fitting_factor",
+                    "friction_angle",
+                    "initial_ppermeability",
+                    "maximum_permeability",
+                    "reference_permeability",
+                    "tensile_strength_parameter"],
+            "PermeabilityOrthotropicPowerLaw": ["exponents",
+                    "intrinsic_permeabilities"],
+            "PorosityFromMassBalance": ["initial_porosity",
+                    "maximal_porosity",
+                    "minimal_porosity"],
+            "RelPermBrooksCorey": ["lambda",
+                    "min_relative_permeability"
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "RelPermBrooksCoreyNonwettingPhase": ["lambda",
+                    "min_relative_permeability"
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "RelPermLiakopoulos": [],
+            "RelativePermeabilityNonWettingVanGenuchten": ["exponent",
+                    "minimum_relative_permeability",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "RelativePermeabilityUdell": ["min_relative_permeability",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "RelativePermeabilityUdellNonwettingPhase": ["min_relative_permeability",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "RelativePermeabilityVanGenuchten": ["exponent",
+                    "minimum_relative_permeability_liquid",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "SaturationBrooksCorey": ["entry_pressure",
+                    "lambda",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "SaturationDependentSwelling": ["exponents",
+                    "lower_saturation_limit",
+                    "swelling_pressures",
+                    "upper_saturation_limit"],
+            "SaturationDependentThermalConductivity": ["dry","wet"],
+            "SaturationExponential": ["exponent",
+                    "maximum_capillary_pressure",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "SaturationLiakopoulos": [],
+            "SaturationVanGenuchten": ["exponent",
+                    "p_b",
+                    "residual_gas_saturation",
+                    "residual_liquid_saturation"],
+            "SoilThermalConductivitySomerton": ["dry_thermal_conductivity",
+                    "wet_thermal_conductivity"],
+            "StrainDependentPermeability": ["initial_permeability",
+                    "b1", "b2", "b3",
+                    "minimum_permeability",
+                    "maximum_permeability"],
+            "TemperatureDependentDiffusion": ["activation_energy",
+                    "reference_diffusion",
+                    "reference_temperature"],
+            "TransportPorosityFromMassBalance": ["initial_porosity",
+                    "maximal_porosity",
+                    "minimal_porosity"],
+            "VapourDiffusionFEBEX": ["tortuosity"],
+            "VapourDiffusionPMQ": [],
+            "VermaPruessModel": ["critical_porosity",
+                    "exponent",
+                    "initial_permeability",
+                    "initial_porosity"],
+            "WaterVapourDensity": [],
+            "WaterVapourLatentHeatWithCriticalTemperature": []
+            }
+
+    def _generate_generic_property(self, args):
+        property_parameters = {}
+        for parameter in self.properties[args["type"]]:
+            property_parameters[parameter] = {
+                    'tag': parameter,
+                    'text': args[parameter],
+                    'attr': {},
+                    'children': {}
+                }
+        return property_parameters
+    def _generate_linear_property(self, args):
+        property_parameters = {}
+        for parameter in self.properties[args["type"]]:
+            property_parameters[parameter] = {
+                    'tag': parameter,
+                    'text': args[parameter],
+                    'attr': {},
+                    'children': {}
+                }
+        for var, param in args["independent_variables"].items():
+            property_parameters[f"independent_variable{var}"] = {
+                    'tag': 'independent_variable',
+                    'text': '',
+                    'attr': {},
+                    'children': {}
+                }
+            indep_var = property_parameters[f"independent_variable{var}"]['children']
+            indep_var['variable_name'] = {
+                    'tag': 'variable_name',
+                    'text': var,
+                    'attr': {},
+                    'children': {}
+                }
+            attributes = ['reference_condition','slope']
+            for attrib in attributes:
+                indep_var[attrib] = {
+                    'tag': attrib,
+                    'text': str(param[attrib]),
+                    'attr': {},
+                    'children': {}
+                }
+        return property_parameters
+    def _generate_function_property(self, args):
+        property_parameters = {}
+        for parameter in self.properties[args["type"]]:
+            property_parameters[parameter] = {
+                    'tag': parameter,
+                    'text': "",
+                    'attr': {},
+                    'children': {}
+                }
+        property_parameters["value"]["children"]["expression"] = {
+                    'tag': "expression",
+                    'text': args["expression"],
+                    'attr': {},
+                    'children': {}
+                }
+        for dvar in args["dvalues"]:
+            property_parameters[f"dvalue{dvar}"] = {
+                    'tag': "dvalue",
+                    'text': "",
+                    'attr': {},
+                    'children': {}
+                }
+            property_parameters[f"dvalue{dvar}"]["children"]["variable_name"] = {
+                    'tag': "variable_name",
+                    'text': dvar,
+                    'attr': {},
+                    'children': {}
+                }
+            property_parameters[f"dvalue{dvar}"]["children"]["expression"] = {
+                    'tag': "expression",
+                    'text': args["dvalues"][dvar]["expression"],
+                    'attr': {},
+                    'children': {}
+                }
+        return property_parameters
+    def _generate_exponential_property(self, args):
+        property_parameters = {}
+        for parameter in self.properties[args["type"]]:
+            property_parameters[parameter] = {
+                    'tag': parameter,
+                    'text': args[parameter],
+                    'attr': {},
+                    'children': {}
+                }
+        property_parameters["exponent"] = {
+                    'tag': 'exponent',
+                    'text': '',
+                    'attr': {},
+                    'children': {}
+                }
+        indep_var = property_parameters["exponent"]['children']
+        indep_var['variable_name'] = {
+                    'tag': 'variable_name',
+                    'text': args["exponent"]["variable_name"],
+                    'attr': {},
+                    'children': {}
+                }
+        attributes = ['reference_condition','factor']
+        for attrib in attributes:
+            indep_var[attrib] = {
+                    'tag': attrib,
+                    'text': str(args["exponent"][attrib]),
+                    'attr': {},
+                    'children': {}
+                }
+        return property_parameters
 
     def add_property(self, **args):
         """
@@ -106,76 +339,26 @@ class Media(build_tree.BuildTree):
                 'attr': {},
                 'children': {}
             }
-            phase[args['name']]['children']['name'] = {
-                'tag': 'name',
-                'text': args['name'],
-                'attr': {},
-                'children': {}
-            }
-            phase[args['name']]['children']['type'] = {
-                'tag': 'type',
-                'text': args['type'],
-                'attr': {},
-                'children': {}
-            }
-            if args['type'] == "Constant":
-                phase[args['name']]['children']['value'] = {
-                    'tag': 'value',
-                    'text': args['value'],
-                    'attr': {},
-                    'children': {}
-                }
-            elif args['type'] == "Linear":
-                phase[args['name']]['children']['reference_value'] = {
-                    'tag': 'reference_value',
-                    'text': args['reference_value'],
+            base_property_param = ["name", "type"]
+            for param in base_property_param:
+                phase[args['name']]['children'][param] = {
+                    'tag': param,
+                    'text': args[param],
                     'attr': {},
                     'children': {}
-                }
-                phase[args['name']]['children']['independent_variable'] = {
-                    'tag': 'independent_variable',
-                    'text': '',
-                    'attr': {},
-                    'children': {}
-                }
-                indep_var = phase[args['name']]['children']['independent_variable']['children']
-                indep_var['variable_name'] = {
-                    'tag': 'variable_name',
-                    'text': args['variable_name'],
-                    'attr': {},
-                    'children': {}
-                }
-                indep_var['reference_condition'] = {
-                    'tag': 'reference_condition',
-                    'text': args['reference_condition'],
-                    'attr': {},
-                    'children': {}
-                }
-                indep_var['slope'] = {
-                    'tag': 'slope',
-                    'text': args['slope'],
-                    'attr': {},
-                    'children': {}
-                }
-            elif args['type'] == "Parameter":
-                phase[args['name']]['children']['parameter'] = {
-                      'tag': 'parameter_name',
-                      'text': args['parameter_name'],
-                      'attr': {},
-                      'children': {}
-                  }
-            elif args['type'] == "BishopsSaturationCutoff":
-                phase[args['name']]['children']['cutoff_value'] = {
-                    'tag': 'cutoff_value',
-                    'text': args['cutoff_value'],
-                    'attr': {},
-                    'children': {}
-                }
-            elif args['type'] == "BishopsPowerLaw":
-                phase[args['name']]['children']['exponent'] = {
-                    'tag': 'exponent',
-                    'text': args['exponent'],
-                    'attr': {},
-                    'children': {}
-                }
-
+            }
+            try:
+                if args['type'] == "Linear":
+                    phase[args['name']]['children'].update(self._generate_linear_property(args))
+                elif args['type'] == "Exponential":
+                    phase[args['name']]['children'].update(self._generate_exponential_property(args))
+                elif args['type'] == "Function":
+                    phase[args['name']]['children'].update(self._generate_function_property(args))
+                else:
+                    phase[args['name']]['children'].update(self._generate_generic_property(args))
+            except KeyError:
+                print("Material property parameters incomplete for")
+                if "phase_type" in args:
+                    print(f"Medium {args['medium_id']}->{args['phase_type']}->{args['name']}[{args['type']}]")
+                else:
+                    print(f"Medium {args['medium_id']}->{args['name']}[{args['type']}]")


Test patch:
diff --git a/tests/test_ogs6py.py b/tests/test_ogs6py.py
index ee957e9..4f80bdc 100644
--- a/tests/test_ogs6py.py
+++ b/tests/test_ogs6py.py
@@ -68,13 +68,14 @@ class TestiOGS(unittest.TestCase):
                                 type="Constant",
                                 value="0.6")
         model.media.add_property(medium_id="0",
-                                phase_type="AqueousLiquid",
-                                name="density",
-                                type="Linear",
-                                reference_value="999.1",
-                                variable_name="phase_pressure",
-                                reference_condition="1e5",
-                                slope="4.5999999999999996e-10")
+                            phase_type="AqueousLiquid",
+                            name="density",
+                            type="Linear",
+                            reference_value="999.1",
+                            independent_variables={"phase_pressure": {
+                                "reference_condition": "1e5",
+                                "slope": "4.5999999999999996e-10"
+                                }})
         model.media.add_property(medium_id="0",
                                 phase_type="AqueousLiquid",
                                 name="thermal_expansivity",