From 96a75232c8dda9e7cc5d51b33a49df8abcfedeb1 Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 23 Jan 2025 15:20:11 +0100 Subject: [PATCH 01/10] MOD: Changing inheritance in models in examples --- .../model_boundary_conditions.py | 8 ++-- .../examples/flow_benchmark_2d_case_4.py | 7 ++- .../examples/flow_benchmark_3d_case_3.py | 8 ++-- src/porepy/examples/mandel_biot.py | 43 ++++++------------- src/porepy/models/protocol.py | 3 ++ src/porepy/utils/porepy_types.py | 6 ++- 6 files changed, 30 insertions(+), 45 deletions(-) diff --git a/src/porepy/applications/boundary_conditions/model_boundary_conditions.py b/src/porepy/applications/boundary_conditions/model_boundary_conditions.py index f631e6740e..4ab48ded1b 100644 --- a/src/porepy/applications/boundary_conditions/model_boundary_conditions.py +++ b/src/porepy/applications/boundary_conditions/model_boundary_conditions.py @@ -9,7 +9,7 @@ import porepy as pp -class BoundaryConditionsMassDirWestEast(pp.BoundaryConditionMixin): +class BoundaryConditionsMassDirWestEast(pp.PorePyModel): """Boundary conditions for the flow problem. Dirichlet boundary conditions are defined on the west and east boundaries. Some @@ -73,7 +73,7 @@ def bc_type_fluid_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: return pp.BoundaryCondition(sd, domain_sides.west + domain_sides.east, "dir") -class BoundaryConditionsMassDirNorthSouth(pp.BoundaryConditionMixin): +class BoundaryConditionsMassDirNorthSouth(pp.PorePyModel): """Boundary conditions for the flow problem. Dirichlet boundary conditions are defined on the north and south boundaries. Some @@ -137,7 +137,7 @@ def bc_type_fluid_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: return pp.BoundaryCondition(sd, domain_sides.north + domain_sides.south, "dir") -class BoundaryConditionsEnergyDirNorthSouth(pp.BoundaryConditionMixin): +class BoundaryConditionsEnergyDirNorthSouth(pp.PorePyModel): """Boundary conditions for the thermal problem. Dirichlet boundary conditions are defined on the north and south boundaries. Some @@ -183,7 +183,7 @@ def bc_type_enthalpy_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: return pp.BoundaryCondition(sd, domain_sides.north + domain_sides.south, "dir") -class BoundaryConditionsMechanicsDirNorthSouth(pp.BoundaryConditionMixin): +class BoundaryConditionsMechanicsDirNorthSouth(pp.PorePyModel): """Boundary conditions for the mechanics with Dirichlet conditions on north and south boundaries. diff --git a/src/porepy/examples/flow_benchmark_2d_case_4.py b/src/porepy/examples/flow_benchmark_2d_case_4.py index 5799841b87..4b5def2fbf 100644 --- a/src/porepy/examples/flow_benchmark_2d_case_4.py +++ b/src/porepy/examples/flow_benchmark_2d_case_4.py @@ -20,7 +20,6 @@ import porepy as pp from porepy.examples.flow_benchmark_2d_case_1 import FractureSolidConstants from porepy.models.constitutive_laws import DimensionDependentPermeability -from porepy.models.protocol import PorePyModel solid_constants = FractureSolidConstants( residual_aperture=1e-2, # m @@ -30,7 +29,7 @@ ) -class Geometry(PorePyModel): +class Geometry(pp.PorePyModel): """Geometry specification.""" def set_fractures(self) -> None: @@ -43,7 +42,7 @@ def domain(self) -> pp.Domain: return pp.Domain({"xmax": 700, "ymax": 600}) -class BoundaryConditions(PorePyModel): +class BoundaryConditions(pp.PorePyModel): """Boundary conditions for Case 4 of the 2D flow benchmark. Inflow on west (left) and prescribed pressure on east (right). @@ -114,6 +113,6 @@ class FlowBenchmark2dCase4Model( # type: ignore[misc] Geometry, BoundaryConditions, Permeability, - pp.fluid_mass_balance.SinglePhaseFlow, + pp.SinglePhaseFlow, ): """Mixer class for case 4 from the 2d flow benchmark.""" diff --git a/src/porepy/examples/flow_benchmark_3d_case_3.py b/src/porepy/examples/flow_benchmark_3d_case_3.py index 07a8fc84a0..88de040a56 100644 --- a/src/porepy/examples/flow_benchmark_3d_case_3.py +++ b/src/porepy/examples/flow_benchmark_3d_case_3.py @@ -36,12 +36,9 @@ ) -class Geometry(pp.ModelGeometry): +class Geometry(pp.PorePyModel): """Define Geometry as specified in Section 5.3 of the benchmark study [1].""" - params: dict - """User-defined model parameters.""" - def set_geometry(self) -> None: """Create mixed-dimensional grid and fracture network.""" @@ -71,6 +68,7 @@ def set_geometry(self) -> None: class IntersectionPermeability(Permeability): + def intersection_permeability(self, subdomains: list[pp.Grid]) -> pp.ad.Operator: """Constant intersection permeability. @@ -90,7 +88,7 @@ def intersection_permeability(self, subdomains: list[pp.Grid]) -> pp.ad.Operator return self.isotropic_second_order_tensor(subdomains, permeability) -class BoundaryConditions(pp.BoundaryConditionMixin): +class BoundaryConditions(pp.PorePyModel): """Define inlet and outlet boundary conditions as specified by the benchmark.""" def bc_type_darcy_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: diff --git a/src/porepy/examples/mandel_biot.py b/src/porepy/examples/mandel_biot.py index f7ab778b28..bcc3b7e3d7 100644 --- a/src/porepy/examples/mandel_biot.py +++ b/src/porepy/examples/mandel_biot.py @@ -33,7 +33,6 @@ import scipy.optimize as opt import porepy as pp -import porepy.models.fluid_mass_balance as mass import porepy.models.poromechanics as poromechanics from porepy.applications.convergence_analysis import ConvergenceAnalysis from porepy.models.derived_models.biot import BiotPoromechanics @@ -1230,12 +1229,9 @@ def _plot_consolidation_degree(self): # -----> Geometry -class MandelGeometry(pp.ModelGeometry): +class MandelGeometry(pp.PorePyModel): """Class for setting up the rectangular geometry.""" - params: dict - """Simulation model parameters.""" - def set_domain(self) -> None: """Set the domain.""" ls = self.units.convert_units(1, "m") # length scaling @@ -1255,15 +1251,11 @@ def grid_type(self) -> Literal["simplex", "cartesian", "tensor_grid"]: # -----> Boundary conditions -class MandelBoundaryConditionsMechanicsTimeDependent( - pp.momentum_balance.BoundaryConditionsMomentumBalance, -): +class MandelBoundaryConditionsMechanicsTimeDependent(pp.PorePyModel): + exact_sol: MandelExactSolution """Exact solution object.""" - params: dict - """Parameter dictionary of the verification setup.""" - def vertical_load(self): """Retrieve and scale applied force. @@ -1288,8 +1280,11 @@ def bc_type_mechanics(self, sd: pp.Grid) -> pp.BoundaryConditionVectorial: Vectorial boundary condition representation. """ - # Inherit bc from parent class. This sets all bc faces as Dirichlet. - bc = super().bc_type_mechanics(sd=sd) + + # NOTE see BC for momentum balance + boundary_faces = self.domain_boundary_sides(sd).all_bf + bc = pp.BoundaryConditionVectorial(sd, boundary_faces, "dir") + bc.internal_to_dirichlet(sd) # Get boundary sides, retrieve data dict, and bc object sides = self.domain_boundary_sides(sd) @@ -1323,7 +1318,8 @@ def bc_values_displacement(self, boundary_grid: pp.BoundaryGrid) -> np.ndarray: the North side of the domain. """ - bc_vals = super().bc_values_displacement(boundary_grid) + + bc_vals = np.zeros((self.nd, boundary_grid.num_cells)).ravel("F") sides = self.domain_boundary_sides(boundary_grid) # Cells of the boundary grid are faces of the parent subdomain. @@ -1337,7 +1333,8 @@ def bc_values_displacement(self, boundary_grid: pp.BoundaryGrid) -> np.ndarray: return bc_vals -class MandelBoundaryConditionsSinglePhaseFlow(mass.BoundaryConditionsSinglePhaseFlow): +class MandelBoundaryConditionsSinglePhaseFlow(pp.PorePyModel): + def bc_type_darcy_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: """Define boundary condition types for the Darcy flux. @@ -1379,21 +1376,6 @@ class MandelSolutionStrategy(poromechanics.SolutionStrategyPoromechanics): """ - def __init__(self, params: dict) -> None: - """Constructor of the class. - - Parameters: - params: Parameters of the verification setup. - - """ - super().__init__(params) - - self.exact_sol: MandelExactSolution - """Exact solution object.""" - - self.results: list[MandelSaveData] = [] - """List of stored results from the verification.""" - def set_materials(self): """Set material parameters. @@ -1402,6 +1384,7 @@ def set_materials(self): """ super().set_materials() self.exact_sol = MandelExactSolution(self) + self.results = [] # Biot's coefficient must be one assert self.solid.biot_coefficient == 1 diff --git a/src/porepy/models/protocol.py b/src/porepy/models/protocol.py index 80fdec80eb..9685ec7479 100644 --- a/src/porepy/models/protocol.py +++ b/src/porepy/models/protocol.py @@ -76,6 +76,9 @@ def set_geometry(self) -> None: """ + def set_well_network(self) -> None: + """Assign well network class.""" + def is_well(self, grid: pp.Grid | pp.MortarGrid) -> bool: """Check if a subdomain is a well. diff --git a/src/porepy/utils/porepy_types.py b/src/porepy/utils/porepy_types.py index ae30d9ea22..1adc093417 100644 --- a/src/porepy/utils/porepy_types.py +++ b/src/porepy/utils/porepy_types.py @@ -5,6 +5,8 @@ from typing import Callable, Sequence, Union import porepy as pp +from porepy.fracs.fracture_network_2d import FractureNetwork2d +from porepy.fracs.fracture_network_3d import FractureNetwork3d __all__ = [ "number", @@ -34,8 +36,8 @@ ] fracture_network = Union[ - "pp.fracs.fracture_network_2d.FractureNetwork2d", - "pp.fracs.fracture_network_3d.FractureNetwork3d", + "FractureNetwork2d", + "FractureNetwork3d", ] DomainFunctionType = Callable[[SubdomainsOrBoundaries], "pp.ad.Operator"] From 399f324fc5cda04022668f25eef7410420ca398b Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 30 Jan 2025 10:37:38 +0100 Subject: [PATCH 02/10] MOD: Turning classes in pp.applications into proper mixins. --- .../applications/md_grids/model_geometries.py | 6 +++--- src/porepy/applications/test_utils/models.py | 20 ++++++------------- .../applications/test_utils/well_models.py | 15 ++++++-------- src/porepy/models/protocol.py | 6 ++++++ 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/porepy/applications/md_grids/model_geometries.py b/src/porepy/applications/md_grids/model_geometries.py index 47b36eb5e3..9fb79f2dd1 100644 --- a/src/porepy/applications/md_grids/model_geometries.py +++ b/src/porepy/applications/md_grids/model_geometries.py @@ -7,7 +7,7 @@ from . import domains, fracture_sets -class SquareDomainOrthogonalFractures(pp.ModelGeometry): +class SquareDomainOrthogonalFractures(pp.PorePyModel): """Create a mixed-dimensional grid for a square domain with up to two orthogonal fractures. @@ -52,7 +52,7 @@ def set_domain(self) -> None: self._domain = domains.nd_cube_domain(2, self.domain_size) -class CubeDomainOrthogonalFractures(pp.ModelGeometry): +class CubeDomainOrthogonalFractures(pp.PorePyModel): """Create a mixed-dimensional grid for a cube domain with up to three orthogonal fractures. @@ -78,7 +78,7 @@ def set_domain(self) -> None: self._domain = domains.nd_cube_domain(3, self.domain_size) -class RectangularDomainThreeFractures(pp.ModelGeometry): +class RectangularDomainThreeFractures(pp.PorePyModel): """A rectangular domain with up to three fractures. The domain is `[0, 2] x [0, 1]`. diff --git a/src/porepy/applications/test_utils/models.py b/src/porepy/applications/test_utils/models.py index cadecfb50e..6d66688a05 100644 --- a/src/porepy/applications/test_utils/models.py +++ b/src/porepy/applications/test_utils/models.py @@ -3,7 +3,7 @@ from __future__ import annotations import inspect -from typing import Any, Callable +from typing import Any, Callable, cast import numpy as np @@ -72,9 +72,7 @@ class Thermoporomechanics( # type: ignore[misc] """Combine components needed for poromechanics simulation.""" -def model( - model_type: str, dim: int, num_fracs: int = 1 -) -> MassBalance | MomentumBalance | MassAndEnergyBalance | Poromechanics: +def model(model_type: str, dim: int, num_fracs: int = 1) -> pp.PorePyModel: """Setup for tests.""" # Suppress output for tests fracture_indices = [i for i in range(num_fracs)] @@ -114,7 +112,7 @@ class Model(geometry, model_class): pass # Create an instance of the combined class - model = Model(params) + model: pp.PorePyModel = cast(pp.PorePyModel, Model(params)) # Prepare the simulation # (create grids, variables, equations, discretize, etc.) @@ -122,16 +120,10 @@ class Model(geometry, model_class): return model -class RobinDirichletNeumannConditions: +class RobinDirichletNeumannConditions(pp.PorePyModel): """Mixin for applying Neumann, Dirichlet and Robin conditions for a thermoporomechanics model.""" - params: dict - - domain_boundary_sides: Callable[[pp.GridLike], pp.domain.DomainSides] - - nd: int - def bc_values_pressure(self, boundary_grid: pp.BoundaryGrid) -> np.ndarray: """Assigns pressure values on the north and south boundary.""" p_north = self.params.get("pressure_north", 1) @@ -259,7 +251,7 @@ def subdomains_or_interfaces_from_method_name( return domains -def _add_mixin(mixin, parent): +def _add_mixin(mixin: type, parent: type) -> type: """Helper method to dynamically construct a class by adding a mixin. Multiple mixins can be added by nested calls to this method. @@ -378,7 +370,7 @@ def compare_values( assert np.isclose(np.sum(values_0 - values_1), 0, atol=1e-10 + rtol) -def get_model_methods_returning_ad_operator(model_setup) -> list[str]: +def get_model_methods_returning_ad_operator(model_setup: pp.PorePyModel) -> list[str]: """Get all possible testable methods to be used in test_ad_operator_methods_xx. A testable method is one that: diff --git a/src/porepy/applications/test_utils/well_models.py b/src/porepy/applications/test_utils/well_models.py index 6332e6a827..cc9884dbd3 100644 --- a/src/porepy/applications/test_utils/well_models.py +++ b/src/porepy/applications/test_utils/well_models.py @@ -1,18 +1,14 @@ """Contains code for setting up a simple but non-trivial model with a well. """ +from typing import Literal + import numpy as np import porepy as pp -class OneVerticalWell: - - domain: pp.Domain - """Domain for the model.""" - - units: pp.Units - """Simulation units provided by the solution strategy mixin.""" +class OneVerticalWell(pp.PorePyModel): def set_well_network(self) -> None: """Assign well network class.""" @@ -36,11 +32,11 @@ def meshing_arguments(self) -> dict: return mesh_sizes - def grid_type(self) -> str: + def grid_type(self) -> Literal["simplex"]: return "simplex" -class BoundaryConditionsWellSetup(pp.BoundaryConditionMixin): +class BoundaryConditionsWellSetup(pp.PorePyModel): """Boundary conditions for the well setup.""" def _bc_type(self, sd: pp.Grid, well_cond: str) -> pp.BoundaryCondition: @@ -158,6 +154,7 @@ def bc_type_fourier_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: class WellPermeability(pp.constitutive_laws.CubicLawPermeability): + def permeability(self, subdomains: list[pp.Grid]) -> pp.ad.Operator: """Permeability [m^2]. diff --git a/src/porepy/models/protocol.py b/src/porepy/models/protocol.py index 9685ec7479..e12973934d 100644 --- a/src/porepy/models/protocol.py +++ b/src/porepy/models/protocol.py @@ -561,6 +561,12 @@ def iterate_indices(self) -> np.ndarray: """ + def prepare_simulation(self) -> None: + """Run at the start of simulation. Used for initialization etc.""" + + def after_simulation(self) -> None: + """Run at the end of simulation. Can be used for cleanup etc.""" + def _is_time_dependent(self) -> bool: """Specifies whether the Model problem is time-dependent. From 179510f29d40e4500d17dfce119497b47a3f45a1 Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 30 Jan 2025 11:51:06 +0100 Subject: [PATCH 03/10] MOD: Modifying models in tests to be proper mixins. --- src/porepy/examples/terzaghi_biot.py | 5 +---- src/porepy/models/protocol.py | 19 +++++++++++++++++++ .../setups/manu_thermoporomech_nofrac_2d.py | 5 +---- .../setups/manu_thermoporomech_nofrac_3d.py | 5 +---- tests/models/test_boundary_condition.py | 2 +- tests/models/test_geometry.py | 6 +++++- 6 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/porepy/examples/terzaghi_biot.py b/src/porepy/examples/terzaghi_biot.py index 1e5ba7f196..31dd999075 100644 --- a/src/porepy/examples/terzaghi_biot.py +++ b/src/porepy/examples/terzaghi_biot.py @@ -499,12 +499,9 @@ def _consolidation_degree_plot(self, color_map: mcolors.ListedColormap) -> None: # -----> Geometry -class PseudoOneDimensionalColumn(pp.ModelGeometry): +class PseudoOneDimensionalColumn(pp.PorePyModel): """Define geometry of the verification setup.""" - params: dict - """Simulation model parameters.""" - def height(self) -> pp.number: """Retrieve height of the domain, in scaled [m].""" ls = self.units.convert_units(1, "m") # length scaling diff --git a/src/porepy/models/protocol.py b/src/porepy/models/protocol.py index e12973934d..47326cf377 100644 --- a/src/porepy/models/protocol.py +++ b/src/porepy/models/protocol.py @@ -708,6 +708,25 @@ def update_all_boundary_conditions(self) -> None: """ + def update_boundary_condition( + self, + name: str, + function: Callable[[pp.BoundaryGrid], np.ndarray], + ) -> None: + """This method is the unified procedure of updating a boundary condition. + + It shifts the boundary condition values in time and stores the current + iterate data (current time step) as the most recent previous time step data. + Next, it evaluates the boundary condition values for the new time step and + stores them in the iterate data. + + Parameters: + name: Name of the operator defined on the boundary. + function: A callable that provides the boundary condition values on a + given boundary grid. + + """ + class EquationProtocol(Protocol): """This protocol provides declarations of methods and properties related to equations. diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py index 77c1d24bf9..9411b6cdca 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py @@ -895,7 +895,7 @@ def energy_source(self, sd: pp.Grid, time: float) -> np.ndarray: # -----> Geometry -class UnitSquareGrid(pp.ModelGeometry): +class UnitSquareGrid(pp.PorePyModel): """Class for setting up the geometry of the unit square domain. The domain may be assigned different material parameters in the region x > 0.5 and y @@ -917,9 +917,6 @@ class UnitSquareGrid(pp.ModelGeometry): """ - params: dict - """Simulation model parameters.""" - def set_geometry(self) -> None: super().set_geometry() diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_3d.py b/tests/functional/setups/manu_thermoporomech_nofrac_3d.py index 4b988c2cf5..2e76deb1fc 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_3d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_3d.py @@ -791,7 +791,7 @@ def energy_source(self, sd: pp.Grid, time: float) -> np.ndarray: # -----> Geometry -class UnitCubeGrid(pp.ModelGeometry): +class UnitCubeGrid(pp.PorePyModel): """Class for setting up the geometry of the unit cube domain. The domain may be assigned different material parameters in the region x > 0.5, y > @@ -812,9 +812,6 @@ class UnitCubeGrid(pp.ModelGeometry): """ - params: dict - """Simulation model parameters.""" - def set_geometry(self) -> None: super().set_geometry() diff --git a/tests/models/test_boundary_condition.py b/tests/models/test_boundary_condition.py index ce11a43295..a8a77e6829 100644 --- a/tests/models/test_boundary_condition.py +++ b/tests/models/test_boundary_condition.py @@ -15,7 +15,7 @@ from porepy.models.momentum_balance import MomentumBalance -class CustomBoundaryCondition(pp.BoundaryConditionMixin): +class CustomBoundaryCondition(pp.PorePyModel): """We define a custom dummy boundary condition. Neumann values are explicitly set, they are time dependent. diff --git a/tests/models/test_geometry.py b/tests/models/test_geometry.py index 4014793ef3..5e5015eacc 100644 --- a/tests/models/test_geometry.py +++ b/tests/models/test_geometry.py @@ -27,8 +27,12 @@ # List of geometry classes to test. +# Turn mixins of specific grids into proper model geometries. geometry_list: list[pp.ModelGeometry] = [ - porepy.applications.md_grids.model_geometries.RectangularDomainThreeFractures, + models._add_mixin( + porepy.applications.md_grids.model_geometries.RectangularDomainThreeFractures, + pp.ModelGeometry, + ), models._add_mixin( porepy.applications.md_grids.model_geometries.OrthogonalFractures3d, pp.ModelGeometry, From f927ca781b1555cf70ef539ca17e4f5ec90463b2 Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 30 Jan 2025 15:55:55 +0100 Subject: [PATCH 04/10] MOD: Purging VerificationDataSaving from porepy. --- src/porepy/examples/mandel_biot.py | 3 +- src/porepy/examples/terzaghi_biot.py | 3 +- src/porepy/models/derived_models/biot.py | 19 +++-- src/porepy/models/protocol.py | 5 +- src/porepy/models/solution_strategy.py | 3 + src/porepy/viz/data_saving_model_mixin.py | 74 +++++++++++-------- .../applications/test_convergence_analysis.py | 5 +- .../setups/manu_flow_comp_2d_frac.py | 3 +- .../setups/manu_flow_incomp_frac_2d.py | 3 +- .../setups/manu_poromech_nofrac_2d.py | 3 +- .../setups/manu_thermoporomech_nofrac_2d.py | 9 ++- tests/functional/test_mandel.py | 4 +- tests/functional/test_manu_flow_comp_frac.py | 4 +- .../functional/test_manu_flow_incomp_frac.py | 6 +- tests/functional/test_manu_poromech_nofrac.py | 4 +- .../test_manu_thermoporomech_nofrac.py | 4 +- 16 files changed, 93 insertions(+), 59 deletions(-) diff --git a/src/porepy/examples/mandel_biot.py b/src/porepy/examples/mandel_biot.py index bcc3b7e3d7..d48ec2a6bf 100644 --- a/src/porepy/examples/mandel_biot.py +++ b/src/porepy/examples/mandel_biot.py @@ -38,7 +38,6 @@ from porepy.models.derived_models.biot import BiotPoromechanics from porepy.numerics.linalg.matrix_operations import sparse_array_to_row_col_data from porepy.utils.examples_utils import VerificationUtils -from porepy.viz.data_saving_model_mixin import VerificationDataSaving # PorePy typings number = pp.number @@ -116,7 +115,7 @@ class MandelSaveData: """Current simulation time.""" -class MandelDataSaving(VerificationDataSaving): +class MandelDataSaving(pp.PorePyModel): """Mixin class to save relevant data.""" darcy_flux: Callable[[list[pp.Grid]], pp.ad.Operator] diff --git a/src/porepy/examples/terzaghi_biot.py b/src/porepy/examples/terzaghi_biot.py index 31dd999075..2fb805afae 100644 --- a/src/porepy/examples/terzaghi_biot.py +++ b/src/porepy/examples/terzaghi_biot.py @@ -50,7 +50,6 @@ from porepy.applications.convergence_analysis import ConvergenceAnalysis from porepy.models.derived_models.biot import BiotPoromechanics from porepy.utils.examples_utils import VerificationUtils -from porepy.viz.data_saving_model_mixin import VerificationDataSaving # PorePy typings number = pp.number @@ -100,7 +99,7 @@ class TerzaghiSaveData: """Current simulation time.""" -class TerzaghiDataSaving(VerificationDataSaving): +class TerzaghiDataSaving(pp.PorePyModel): """Mixin class to save relevant data.""" exact_sol: TerzaghiExactSolution diff --git a/src/porepy/models/derived_models/biot.py b/src/porepy/models/derived_models/biot.py index 305e0afd47..cb61a259e0 100644 --- a/src/porepy/models/derived_models/biot.py +++ b/src/porepy/models/derived_models/biot.py @@ -77,11 +77,7 @@ """ import porepy as pp -from porepy.models.poromechanics import ( - ConstitutiveLawsPoromechanics, - Poromechanics, - SolutionStrategyPoromechanics, -) +from porepy.models.poromechanics import Poromechanics, SolutionStrategyPoromechanics class SolutionStrategyBiot(SolutionStrategyPoromechanics): @@ -100,8 +96,17 @@ def set_materials(self): class ConstitutiveLawsBiot( pp.constitutive_laws.SpecificStorage, pp.constitutive_laws.BiotPoroMechanicsPorosity, - ConstitutiveLawsPoromechanics, -): ... +): + """Additional constitutive laws required for the Biot-Poromechanics model. + + Note: + These are additions and do not contain everything a poromechanical model needs. + Intention behind this choice include a cleaner MRO in the + :class:`BiotPoromechanics`. + + """ + + ... class BiotPoromechanics( # type: ignore[misc] diff --git a/src/porepy/models/protocol.py b/src/porepy/models/protocol.py index 47326cf377..e6cdec136f 100644 --- a/src/porepy/models/protocol.py +++ b/src/porepy/models/protocol.py @@ -16,7 +16,7 @@ """ from pathlib import Path -from typing import TYPE_CHECKING, Callable, Literal, Optional, Protocol, Sequence +from typing import TYPE_CHECKING, Any, Callable, Literal, Optional, Protocol, Sequence import numpy as np import scipy.sparse as sps @@ -537,6 +537,9 @@ class SolutionStrategyProtocol(Protocol): """Time step as an automatic differentiation scalar.""" nonlinear_solver_statistics: pp.SolverStatistics """Solver statistics for the nonlinear solver.""" + results: list[Any] + """A list of results collected by the data saving mixin in + :meth:`~porepy.viz.data_saving_model_mixin.DataSavingMixin.collect_data`.""" @property def time_step_indices(self) -> np.ndarray: diff --git a/src/porepy/models/solution_strategy.py b/src/porepy/models/solution_strategy.py index ef0466ab53..8c3a5eae50 100644 --- a/src/porepy/models/solution_strategy.py +++ b/src/porepy/models/solution_strategy.py @@ -114,6 +114,9 @@ def __init__(self, params: Optional[dict] = None): """Restart options. The template is provided in `SolutionStrategy.__init__`.""" self.ad_time_step = pp.ad.Scalar(self.time_manager.dt) """Time step as an automatic differentiation scalar.""" + self.results: list[Any] = [] + """A list of results collected by the data saving mixin in + :meth:`~porepy.viz.data_saving_model_mixin.DataSavingMixin.collect_data`.""" self.set_solver_statistics() diff --git a/src/porepy/viz/data_saving_model_mixin.py b/src/porepy/viz/data_saving_model_mixin.py index b04a067ebd..6d7c1d79a3 100644 --- a/src/porepy/viz/data_saving_model_mixin.py +++ b/src/porepy/viz/data_saving_model_mixin.py @@ -10,7 +10,7 @@ from __future__ import annotations from pathlib import Path -from typing import Optional, Union +from typing import Any, Optional, Union import numpy as np @@ -29,14 +29,19 @@ class DataSavingMixin(pp.PorePyModel): def save_data_time_step(self) -> None: """Export the model state at a given time step and log time. - The options for exporting times are: - * `None`: All time steps are exported - * `list`: Export if time is in the list. If the list is empty, then no - times are exported. + The options for exporting times can be given as ``params['times_to_export']``: + + - ``None``: All time steps are exported. + - ``list``: Export if time is in the list. If the list is empty, then no + times are exported. In addition, save the solver statistics to file if the option is set. + Finally, :meth:`collect_data` is called and stored in :attr:`results` for + data collection and verification in runtime. + """ + # Fetching the desired times to export. times_to_export = self.params.get("times_to_export", None) if times_to_export is None: @@ -55,6 +60,39 @@ def save_data_time_step(self) -> None: # Save solver statistics to file. self.nonlinear_solver_statistics.save() + # Collecting and storing data in runtime for analysis. + if not self._is_time_dependent(): # stationary problem + if ( + self.nonlinear_solver_statistics.num_iteration > 0 + ): # avoid saving initial condition + collected_data = self.collect_data() + if collected_data is not None: + self.results.append(collected_data) + else: # time-dependent problem + t = self.time_manager.time # current time + scheduled = self.time_manager.schedule[1:] # scheduled times except t_init + if any(np.isclose(t, scheduled)): + collected_data = self.collect_data() + if collected_data is not None: + self.results.append(collected_data) + + def collect_data(self) -> Any: + """Collect relevant simulation data to be stored in attr:`results`. + + Override to collect data respectively. By default, this method returns None and + nothing is stored. + + For stationary problems, this method is called in every iteration. For time + dependent problems, it is called after convergence of a time step which is + scheduled by the time manager. + + Returns: + Any data structure relevant for future verification. By default, None. + If it is not None, it is stored in :attr:`results`. + + """ + return None + def write_pvd_and_vtu(self) -> None: """Helper function for writing the .vtu and .pvd files and time information.""" self.exporter.write_vtu(self.data_to_export(), time_dependent=True) @@ -237,29 +275,3 @@ def load_data_from_pvd( self.time_manager.load_time_information(times_file) self.time_manager.set_time_and_dt_from_exported_steps(time_index) self.exporter._time_step_counter = time_index - - -class VerificationDataSaving(DataSavingMixin): - """Class to store relevant data for a generic verification setup.""" - - results: list - """List of objects containing the results of the verification.""" - - def save_data_time_step(self) -> None: - """Save data to the `results` list.""" - if not self._is_time_dependent(): # stationary problem - if ( - self.nonlinear_solver_statistics.num_iteration > 0 - ): # avoid saving initial condition - collected_data = self.collect_data() - self.results.append(collected_data) - else: # time-dependent problem - t = self.time_manager.time # current time - scheduled = self.time_manager.schedule[1:] # scheduled times except t_init - if any(np.isclose(t, scheduled)): - collected_data = self.collect_data() - self.results.append(collected_data) - - def collect_data(self): - """Collect relevant data for the verification setup.""" - raise NotImplementedError() diff --git a/tests/applications/test_convergence_analysis.py b/tests/applications/test_convergence_analysis.py index 44516eb837..708dea4cf3 100644 --- a/tests/applications/test_convergence_analysis.py +++ b/tests/applications/test_convergence_analysis.py @@ -26,7 +26,6 @@ ) from porepy.models.fluid_mass_balance import SinglePhaseFlow from porepy.utils.txt_io import read_data_from_txt -from porepy.viz.data_saving_model_mixin import VerificationDataSaving # -----> Fixtures that are required on a module level. @@ -461,7 +460,7 @@ class StationaryModelSaveData: error_var_0: float # error associated with variable 0 error_var_1: float # error associated with variable 1 - class StationaryModelDataSaving(VerificationDataSaving): + class StationaryModelDataSaving(pp.PorePyModel): """Class that collects and store data.""" def collect_data(self) -> StationaryModelSaveData: @@ -528,7 +527,7 @@ class TimeDependentModelSaveData: error_var_0: float # error associated with variable 0 error_var_1: float # error associated with variable 1 - class TimeDependentModelDataSaving(VerificationDataSaving): + class TimeDependentModelDataSaving(pp.PorePyModel): """Class that collects and store data.""" def collect_data(self) -> TimeDependentModelSaveData: diff --git a/tests/functional/setups/manu_flow_comp_2d_frac.py b/tests/functional/setups/manu_flow_comp_2d_frac.py index 3bffc2a4e6..9dba5e6760 100644 --- a/tests/functional/setups/manu_flow_comp_2d_frac.py +++ b/tests/functional/setups/manu_flow_comp_2d_frac.py @@ -34,7 +34,6 @@ import porepy as pp from porepy.applications.convergence_analysis import ConvergenceAnalysis -from porepy.viz.data_saving_model_mixin import VerificationDataSaving from tests.functional.setups.manu_flow_incomp_frac_2d import ( ManuIncompSaveData, ManuIncompUtils, @@ -75,7 +74,7 @@ class ManuCompSaveData(ManuIncompSaveData): """Current simulation time.""" -class ManuCompDataSaving(VerificationDataSaving): +class ManuCompDataSaving(pp.PorePyModel): """Mixin class to store relevant data.""" darcy_flux: Callable[[list[pp.Grid]], pp.ad.Operator] diff --git a/tests/functional/setups/manu_flow_incomp_frac_2d.py b/tests/functional/setups/manu_flow_incomp_frac_2d.py index b45bd9c67e..183d432585 100644 --- a/tests/functional/setups/manu_flow_incomp_frac_2d.py +++ b/tests/functional/setups/manu_flow_incomp_frac_2d.py @@ -25,7 +25,6 @@ from porepy.applications.convergence_analysis import ConvergenceAnalysis from porepy.applications.md_grids.domains import nd_cube_domain from porepy.utils.examples_utils import VerificationUtils -from porepy.viz.data_saving_model_mixin import VerificationDataSaving # PorePy typings number = pp.number @@ -97,7 +96,7 @@ class ManuIncompSaveData: """Exact pressure in the matrix.""" -class ManuIncompDataSaving(VerificationDataSaving): +class ManuIncompDataSaving(pp.PorePyModel): """Mixin class to save relevant data.""" darcy_flux: Callable[[list[pp.Grid]], pp.ad.Operator] diff --git a/tests/functional/setups/manu_poromech_nofrac_2d.py b/tests/functional/setups/manu_poromech_nofrac_2d.py index a508814da0..6cadf599a7 100644 --- a/tests/functional/setups/manu_poromech_nofrac_2d.py +++ b/tests/functional/setups/manu_poromech_nofrac_2d.py @@ -60,7 +60,6 @@ from porepy.applications.convergence_analysis import ConvergenceAnalysis from porepy.applications.md_grids.domains import nd_cube_domain from porepy.utils.examples_utils import VerificationUtils -from porepy.viz.data_saving_model_mixin import VerificationDataSaving # PorePy typings number = pp.number @@ -112,7 +111,7 @@ class ManuPoroMechSaveData: """Current simulation time.""" -class ManuPoroMechDataSaving(VerificationDataSaving): +class ManuPoroMechDataSaving(pp.PorePyModel): """Mixin class to save relevant data.""" darcy_flux: Callable[[list[pp.Grid]], pp.ad.Operator] diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py index 9411b6cdca..e1d9ace525 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py @@ -47,7 +47,6 @@ import porepy as pp from porepy.applications.convergence_analysis import ConvergenceAnalysis from porepy.applications.md_grids.domains import nd_cube_domain -from porepy.viz.data_saving_model_mixin import VerificationDataSaving # PorePy typings number = pp.number @@ -116,7 +115,7 @@ class ManuThermoPoroMechSaveData: time: number -class ManuThermoPoroMechDataSaving(VerificationDataSaving): +class ManuThermoPoroMechDataSaving(pp.PorePyModel): """Mixin class to save relevant data.""" exact_sol: ManuThermoPoroMechExactSolution2d @@ -138,6 +137,12 @@ class ManuThermoPoroMechDataSaving(VerificationDataSaving): """ + energy_flux: Callable[[list[pp.Grid]], pp.ad.Operator] + """Method that returns the energy fluxes in the form of an Ad operator. Usually + provided by the mixin class + :class:`porepy.models.energy_balance.EnergyBalanceEquations`. + + """ darcy_flux: Callable[[list[pp.Grid]], pp.ad.Operator] """Method that returns the Darcy fluxes in the form of an Ad operator. Usually provided by the mixin class :class:`porepy.models.constitutive_laws.DarcysLaw`. diff --git a/tests/functional/test_mandel.py b/tests/functional/test_mandel.py index db612d1ded..3eb08c72c0 100644 --- a/tests/functional/test_mandel.py +++ b/tests/functional/test_mandel.py @@ -78,7 +78,9 @@ def results() -> list[MandelSaveData]: @pytest.mark.parametrize("time_index", [0, 1]) -def test_error_primary_and_secondary_variables(time_index: int, results): +def test_error_primary_and_secondary_variables( + time_index: int, results: list[MandelSaveData] +): """Checks error for pressure, displacement, flux, force, and consolidation degree. Physical parameters used in this test have been adapted from [1]. diff --git a/tests/functional/test_manu_flow_comp_frac.py b/tests/functional/test_manu_flow_comp_frac.py index 8b5858f5d9..844c990c67 100644 --- a/tests/functional/test_manu_flow_comp_frac.py +++ b/tests/functional/test_manu_flow_comp_frac.py @@ -109,6 +109,7 @@ def actual_l2_errors( "reference_variable_values": reference_values, "meshing_arguments": {"cell_size": 0.125}, "time_manager": pp.TimeManager([0, 0.5, 1.0], 0.5, True), + "times_to_export": [], # Suppress output for tests } # Retrieve actual L2-relative errors. @@ -116,7 +117,7 @@ def actual_l2_errors( # Loop through models, i.e., 2d and 3d. for model in [ManuCompFlowSetup2d, ManuCompFlowSetup3d]: # Make deep copy of params to avoid nasty bugs. - setup = model(deepcopy(model_params)) + setup: pp.PorePyModel = model(deepcopy(model_params)) pp.run_time_dependent_model(setup, {}) errors_setup: list[dict[str, float]] = [] # Loop through results, i.e., results for each scheduled time. @@ -279,6 +280,7 @@ def actual_ooc( "material_constants": material_constants, "reference_variable_values": reference_values, "meshing_arguments": {"cell_size": 0.125}, + "times_to_export": [], # Suppress output for tests } # Use 4 levels of refinement for 2d and 3 levels for 3d if model_idx == 0: diff --git a/tests/functional/test_manu_flow_incomp_frac.py b/tests/functional/test_manu_flow_incomp_frac.py index a9282a9618..bf3322900e 100644 --- a/tests/functional/test_manu_flow_incomp_frac.py +++ b/tests/functional/test_manu_flow_incomp_frac.py @@ -82,6 +82,7 @@ def actual_l2_errors(material_constants: dict) -> list[dict[str, float]]: "grid_type": "cartesian", "material_constants": material_constants, "meshing_arguments": {"cell_size": 0.125}, + "times_to_export": [], # Suppress output for tests } # Retrieve actual L2-relative errors @@ -89,7 +90,9 @@ def actual_l2_errors(material_constants: dict) -> list[dict[str, float]]: # Loop through models, i.e., 2d and 3d for model in [ManuIncompFlowSetup2d, ManuIncompFlowSetup3d]: # Make deep copy of params to avoid nasty bugs. - setup = model(deepcopy(model_params)) + setup: ManuIncompFlowSetup2d | ManuIncompFlowSetup3d = model( + deepcopy(model_params) + ) pp.run_time_dependent_model(setup) errors.append( { @@ -217,6 +220,7 @@ def actual_ooc(material_constants: dict) -> list[list[dict[str, float]]]: "grid_type": grid_type, "material_constants": material_constants, "meshing_arguments": {"cell_size": 0.125}, + "times_to_export": [], # Suppress output for tests } # Use 4 levels of refinement for 2d and 3 levels for 3d if model_idx == 0: diff --git a/tests/functional/test_manu_poromech_nofrac.py b/tests/functional/test_manu_poromech_nofrac.py index a1f3dbf7b0..6db8fa0b37 100644 --- a/tests/functional/test_manu_poromech_nofrac.py +++ b/tests/functional/test_manu_poromech_nofrac.py @@ -96,6 +96,7 @@ def actual_l2_errors(material_constants: dict) -> list[list[dict[str, float]]]: "meshing_arguments": {"cell_size": 0.25}, "manufactured_solution": "nordbotten_2016", "time_manager": pp.TimeManager([0, 0.5, 1.0], 0.5, True), + "times_to_export": [], # Suppress output for tests } # Retrieve actual L2-relative errors. @@ -103,7 +104,7 @@ def actual_l2_errors(material_constants: dict) -> list[list[dict[str, float]]]: # Loop through models, i.e., 2d and 3d. for model in [ManuPoroMechSetup2d, ManuPoroMechSetup3d]: # Make deep copy of params to avoid nasty bugs. - setup = model(deepcopy(model_params)) + setup: pp.PorePyModel = model(deepcopy(model_params)) pp.run_time_dependent_model(setup) errors_setup: list[dict[str, float]] = [] # Loop through results, i.e., results for each scheduled time. @@ -251,6 +252,7 @@ def actual_ooc(material_constants: dict) -> list[list[dict[str, float]]]: "grid_type": grid_type, "material_constants": material_constants, "meshing_arguments": {"cell_size": 0.25}, + "times_to_export": [], # Suppress output for tests } # Use 4 levels of refinement for 2d and 3 levels for 3d. if model_idx == 0: diff --git a/tests/functional/test_manu_thermoporomech_nofrac.py b/tests/functional/test_manu_thermoporomech_nofrac.py index da4f4bc0f4..6e89f9e043 100644 --- a/tests/functional/test_manu_thermoporomech_nofrac.py +++ b/tests/functional/test_manu_thermoporomech_nofrac.py @@ -82,6 +82,7 @@ def actual_l2_errors(material_constants) -> list[list[dict[str, float]]]: "meshing_arguments": {"cell_size": 0.25}, "time_manager": pp.TimeManager([0, 0.5, 1.0], 0.5, True), "heterogeneity": 10.0, + "times_to_export": [], # Suppress output for tests } # Retrieve actual L2-relative errors. @@ -89,7 +90,7 @@ def actual_l2_errors(material_constants) -> list[list[dict[str, float]]]: # Loop through models, i.e., 2d and 3d. for model in [ManuThermoPoroMechSetup2d, ManuThermoPoroMechSetup3d]: # Make deep copy of params to avoid nasty bugs. - setup = model(deepcopy(model_params)) + setup: pp.PorePyModel = model(deepcopy(model_params)) pp.run_time_dependent_model(setup, {}) errors_setup: list[dict[str, float]] = [] # Loop through results, i.e., results for each scheduled time. @@ -253,6 +254,7 @@ def actual_ooc(material_constants: dict) -> list[list[dict[str, float]]]: "meshing_arguments": {"cell_size": 0.25}, "perturbation": 0.3, "heterogeneity": 10.0, + "times_to_export": [], # Suppress output for tests } # Use 4 levels of refinement for 2d and 3 levels for 3d. if model_idx == 0: From a8f894d30cb75f5a81dc4a26969f33c78445ef18 Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 30 Jan 2025 16:29:12 +0100 Subject: [PATCH 05/10] MOD: Optimizing class structure of some existing models. --- src/porepy/examples/terzaghi_biot.py | 30 ++++---------- src/porepy/models/constitutive_laws.py | 2 +- src/porepy/models/derived_models/biot.py | 41 +++++++------------ .../applications/test_convergence_analysis.py | 8 +--- .../setups/manu_flow_comp_2d_frac.py | 6 --- .../setups/manu_flow_incomp_frac_2d.py | 11 ----- .../setups/manu_poromech_nofrac_2d.py | 6 --- .../setups/manu_thermoporomech_nofrac_2d.py | 12 +++--- .../setups/manu_thermoporomech_nofrac_3d.py | 14 +++---- 9 files changed, 37 insertions(+), 93 deletions(-) diff --git a/src/porepy/examples/terzaghi_biot.py b/src/porepy/examples/terzaghi_biot.py index 2fb805afae..5e242da5aa 100644 --- a/src/porepy/examples/terzaghi_biot.py +++ b/src/porepy/examples/terzaghi_biot.py @@ -44,8 +44,6 @@ import numpy as np import porepy as pp -import porepy.models.fluid_mass_balance as mass -import porepy.models.momentum_balance as mechanics import porepy.models.poromechanics as poromechanics from porepy.applications.convergence_analysis import ConvergenceAnalysis from porepy.models.derived_models.biot import BiotPoromechanics @@ -523,7 +521,7 @@ def meshing_arguments(self) -> dict: # -----> Boundary conditions -class TerzaghiBoundaryConditionsMechanics(mechanics.BoundaryConditionsMomentumBalance): +class TerzaghiBoundaryConditionsMechanics(pp.PorePyModel): def applied_load(self) -> pp.number: """Obtain vertical load in scaled [Pa].""" @@ -541,8 +539,10 @@ def bc_type_mechanics(self, sd: pp.Grid) -> pp.BoundaryConditionVectorial: the South, and rollers on the sides. """ - # Inherit bc from parent class. This sets all bc faces as Dirichlet. - bc = super().bc_type_mechanics(sd=sd) + # Start with all faces as Dirichlet faces, analogous to base mechanics set-up. + boundary_faces = self.domain_boundary_sides(sd).all_bf + bc = pp.BoundaryConditionVectorial(sd, boundary_faces, "dir") + bc.internal_to_dirichlet(sd) # Get boundary sides, retrieve data dict, and bc object _, east, west, north, *_ = self.domain_boundary_sides(sd) @@ -581,9 +581,8 @@ def bc_values_stress(self, boundary_grid: pp.BoundaryGrid) -> np.ndarray: return bc_values.ravel("F") -class TerzaghiBoundaryConditionsFlow( - mass.BoundaryConditionsSinglePhaseFlow, -): +class TerzaghiBoundaryConditionsFlow(pp.PorePyModel): + def bc_type_darcy_flux(self, sd: pp.Grid) -> pp.BoundaryCondition: """Define boundary condition types for the Darcy flux. @@ -637,21 +636,6 @@ class TerzaghiSolutionStrategy(poromechanics.SolutionStrategyPoromechanics): """ - def __init__(self, params: dict) -> None: - """Constructor of the class. - - Parameters: - params: Parameters of the verification setup. - - """ - super().__init__(params) - - self.exact_sol: TerzaghiExactSolution - """Exact solution object.""" - - self.results: list[TerzaghiSaveData] = [] - """List of stored results from the verification.""" - def set_materials(self): """Set material parameters. diff --git a/src/porepy/models/constitutive_laws.py b/src/porepy/models/constitutive_laws.py index 03f798c74d..102b0bbdea 100644 --- a/src/porepy/models/constitutive_laws.py +++ b/src/porepy/models/constitutive_laws.py @@ -3943,7 +3943,7 @@ def _mpsa_consistency( return consistency -class BiotPoroMechanicsPorosity(PoroMechanicsPorosity): +class BiotPoroMechanicsPorosity(pp.PorePyModel): """Porosity for poromechanical models following classical Biot's theory. The porosity is defined such that, after the chain rule is applied to the diff --git a/src/porepy/models/derived_models/biot.py b/src/porepy/models/derived_models/biot.py index cb61a259e0..9bb36c92de 100644 --- a/src/porepy/models/derived_models/biot.py +++ b/src/porepy/models/derived_models/biot.py @@ -77,40 +77,27 @@ """ import porepy as pp -from porepy.models.poromechanics import Poromechanics, SolutionStrategyPoromechanics +from porepy.models.poromechanics import Poromechanics -class SolutionStrategyBiot(SolutionStrategyPoromechanics): - """Modified solution strategy for the Biot class""" +class BiotPoromechanics( # type: ignore[misc] + pp.constitutive_laws.SpecificStorage, + pp.constitutive_laws.BiotPoroMechanicsPorosity, + Poromechanics, +): + """Biot-Poromechanics model with special constitutive laws for specific storage and + porosity. + + The model is valid for single-phase, single-component and incompressible flow only. + A respective check is performed via overload of :meth:`set_materials`. + + """ def set_materials(self): """Set the material constants.""" super().set_materials() - # Check that fluid compressibility is zero, otherwise Biot class doesn't hold + # Check that fluid compressibility is zero, otherwise Biot class doesn't hold. # NOTE Biot is tested for 1 phase 1 component. assert self.fluid.num_components == 1 assert self.fluid.num_phases == 1 assert self.fluid.reference_component.compressibility == 0 - - -class ConstitutiveLawsBiot( - pp.constitutive_laws.SpecificStorage, - pp.constitutive_laws.BiotPoroMechanicsPorosity, -): - """Additional constitutive laws required for the Biot-Poromechanics model. - - Note: - These are additions and do not contain everything a poromechanical model needs. - Intention behind this choice include a cleaner MRO in the - :class:`BiotPoromechanics`. - - """ - - ... - - -class BiotPoromechanics( # type: ignore[misc] - ConstitutiveLawsBiot, - SolutionStrategyBiot, - Poromechanics, -): ... diff --git a/tests/applications/test_convergence_analysis.py b/tests/applications/test_convergence_analysis.py index 708dea4cf3..81fd5b5cd1 100644 --- a/tests/applications/test_convergence_analysis.py +++ b/tests/applications/test_convergence_analysis.py @@ -484,9 +484,7 @@ def collect_data(self) -> StationaryModelSaveData: class StationaryModelSolutionStrategy(pp.SolutionStrategy): """Solution strategy for the stationary flow model.""" - def __init__(self, params: dict): - super().__init__(params) - self.results: list[StationaryModelSaveData] = [] + results: list[StationaryModelSaveData] def _is_nonlinear_problem(self) -> bool: """Whether the model is non-linear.""" @@ -551,9 +549,7 @@ def collect_data(self) -> TimeDependentModelSaveData: class TimeDependentModelSolutionStrategy(pp.SolutionStrategy): """Solution strategy for the time-dependent flow model.""" - def __init__(self, params: dict): - super().__init__(params) - self.results: list[TimeDependentModelSaveData] = [] + results: list[TimeDependentModelSaveData] def _is_nonlinear_problem(self) -> bool: """Whether the problem is non-linear.""" diff --git a/tests/functional/setups/manu_flow_comp_2d_frac.py b/tests/functional/setups/manu_flow_comp_2d_frac.py index 9dba5e6760..361c806748 100644 --- a/tests/functional/setups/manu_flow_comp_2d_frac.py +++ b/tests/functional/setups/manu_flow_comp_2d_frac.py @@ -702,12 +702,6 @@ def __init__(self, params: dict): """Constructor of the class.""" super().__init__(params) - self.exact_sol: ManuCompExactSolution2d - """Exact solution object.""" - - self.results: list[ManuCompSaveData] = [] - """Object that stores exact and approximated solutions and L2 errors.""" - self.subdomain_darcy_flux_variable: str = "darcy_flux" """Keyword to access the subdomain Darcy fluxes.""" diff --git a/tests/functional/setups/manu_flow_incomp_frac_2d.py b/tests/functional/setups/manu_flow_incomp_frac_2d.py index 183d432585..10ada3b8b5 100644 --- a/tests/functional/setups/manu_flow_incomp_frac_2d.py +++ b/tests/functional/setups/manu_flow_incomp_frac_2d.py @@ -754,17 +754,6 @@ class ManuIncompSolutionStrategy2d( results: list[ManuIncompSaveData] """List of SaveData objects.""" - def __init__(self, params: dict): - """Constructor for the class.""" - - super().__init__(params) - - self.exact_sol: ManuIncompExactSolution2d - """Exact solution object.""" - - self.results: list[ManuIncompSaveData] = [] - """Results object that stores exact and approximated solutions and errors.""" - def set_materials(self): """Set material constants for the verification setup.""" super().set_materials() diff --git a/tests/functional/setups/manu_poromech_nofrac_2d.py b/tests/functional/setups/manu_poromech_nofrac_2d.py index 6cadf599a7..ce74efb6f9 100644 --- a/tests/functional/setups/manu_poromech_nofrac_2d.py +++ b/tests/functional/setups/manu_poromech_nofrac_2d.py @@ -716,12 +716,6 @@ def __init__(self, params: dict): """Constructor for the class.""" super().__init__(params) - self.exact_sol: ManuPoroMechExactSolution2d - """Exact solution object.""" - - self.results: list[ManuPoroMechSaveData] = [] - """Results object that stores exact and approximated solutions and errors.""" - self.flux_variable: str = "darcy_flux" """Keyword to access the Darcy fluxes.""" diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py index e1d9ace525..23585b2d22 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py @@ -1036,16 +1036,16 @@ class ManuThermoPoroMechSolutionStrategy2d( ): """Solution strategy for the verification setup.""" + exact_sol: ManuThermoPoroMechExactSolution2d + """Exact solution object.""" + + results: list[ManuThermoPoroMechSaveData] = [] + """Results object that stores exact and approximated solutions and errors.""" + def __init__(self, params: dict): """Constructor for the class.""" super().__init__(params) - self.exact_sol: ManuThermoPoroMechExactSolution2d - """Exact solution object.""" - - self.results: list[ManuThermoPoroMechSaveData] = [] - """Results object that stores exact and approximated solutions and errors.""" - self.flux_variable: str = "darcy_flux" """Keyword to access the Darcy fluxes.""" diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_3d.py b/tests/functional/setups/manu_thermoporomech_nofrac_3d.py index 2e76deb1fc..ff10fb4900 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_3d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_3d.py @@ -873,18 +873,18 @@ class ManuThermoPoroMechSolutionStrategy3d( ): """Solution strategy for the verification setup.""" + exact_sol: ManuThermoPoroMechExactSolution3d + """Exact solution object.""" + + results: list[ManuThermoPoroMechSaveData] = [] + """Results object that stores exact and approximated solutions and errors.""" + def __init__(self, params: dict): """Constructor for the class.""" super().__init__(params) - self.exact_sol: ManuThermoPoroMechExactSolution3d - """Exact solution object.""" - self.stress_variable: str = "thermoporoelastic_force" - """Keyword to access the thermoporoelastic force.""" - - self.results: list[ManuThermoPoroMechSaveData] = [] - """Results object that stores exact and approximated solutions and errors.""" + """Keyword to access the thermoporoelastic force.""" def set_materials(self): """Set material parameters.""" From ae23a6297044c09eb02165ccc5867f746521d796 Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Sat, 1 Feb 2025 22:23:11 +0100 Subject: [PATCH 06/10] MOD: Suggestions from review. --- src/porepy/applications/test_utils/models.py | 2 +- src/porepy/examples/mandel_biot.py | 1 - src/porepy/models/solution_strategy.py | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/porepy/applications/test_utils/models.py b/src/porepy/applications/test_utils/models.py index 6d66688a05..9d8962272f 100644 --- a/src/porepy/applications/test_utils/models.py +++ b/src/porepy/applications/test_utils/models.py @@ -112,7 +112,7 @@ class Model(geometry, model_class): pass # Create an instance of the combined class - model: pp.PorePyModel = cast(pp.PorePyModel, Model(params)) + model = cast(pp.PorePyModel, Model(params)) # Prepare the simulation # (create grids, variables, equations, discretize, etc.) diff --git a/src/porepy/examples/mandel_biot.py b/src/porepy/examples/mandel_biot.py index d48ec2a6bf..7d530a47c5 100644 --- a/src/porepy/examples/mandel_biot.py +++ b/src/porepy/examples/mandel_biot.py @@ -1383,7 +1383,6 @@ def set_materials(self): """ super().set_materials() self.exact_sol = MandelExactSolution(self) - self.results = [] # Biot's coefficient must be one assert self.solid.biot_coefficient == 1 diff --git a/src/porepy/models/solution_strategy.py b/src/porepy/models/solution_strategy.py index 8c3a5eae50..0f07cd5578 100644 --- a/src/porepy/models/solution_strategy.py +++ b/src/porepy/models/solution_strategy.py @@ -7,7 +7,6 @@ from __future__ import annotations -import abc import logging import time import warnings @@ -22,7 +21,7 @@ logger = logging.getLogger(__name__) -class SolutionStrategy(abc.ABC, pp.PorePyModel): +class SolutionStrategy(pp.PorePyModel): """This is a class that specifies methods that a model must implement to be compatible with the linearization and time stepping methods. From 9356347e286aeb3cf4192d71cf2b731da4abdd1a Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Sat, 1 Feb 2025 23:01:37 +0100 Subject: [PATCH 07/10] TEST: Adding suppression of file IO to some tests of porepy models. --- src/porepy/viz/data_saving_model_mixin.py | 3 ++- tests/functional/test_benchmark_2d_case_3.py | 1 + tests/functional/test_benchmark_3d_case_3.py | 1 + tests/functional/test_mandel.py | 3 +++ tests/functional/test_terzaghi.py | 14 ++++++++++++-- tests/models/test_boundary_condition.py | 7 ++++++- tests/models/test_constitutive_laws.py | 1 + tests/models/test_energy_balance.py | 3 ++- 8 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/porepy/viz/data_saving_model_mixin.py b/src/porepy/viz/data_saving_model_mixin.py index 6d7c1d79a3..afbcd90692 100644 --- a/src/porepy/viz/data_saving_model_mixin.py +++ b/src/porepy/viz/data_saving_model_mixin.py @@ -60,7 +60,8 @@ def save_data_time_step(self) -> None: # Save solver statistics to file. self.nonlinear_solver_statistics.save() - # Collecting and storing data in runtime for analysis. + # Collecting and storing data in runtime for analysis. If default value of None + # is returned, nothing is stored to not burden memory. if not self._is_time_dependent(): # stationary problem if ( self.nonlinear_solver_statistics.num_iteration > 0 diff --git a/tests/functional/test_benchmark_2d_case_3.py b/tests/functional/test_benchmark_2d_case_3.py index 55b1d0224c..54c625a931 100644 --- a/tests/functional/test_benchmark_2d_case_3.py +++ b/tests/functional/test_benchmark_2d_case_3.py @@ -71,6 +71,7 @@ def model( "grid_type": "simplex", "meshing_arguments": {"cell_size": 0.1}, "flux_discretization": flux_discretization, + "times_to_export": [], # Suppress output for tests } if case == "a": model = Model3aWithEffectivePermeability(model_params) diff --git a/tests/functional/test_benchmark_3d_case_3.py b/tests/functional/test_benchmark_3d_case_3.py index 4e7dad4586..60a95e51c2 100644 --- a/tests/functional/test_benchmark_3d_case_3.py +++ b/tests/functional/test_benchmark_3d_case_3.py @@ -53,6 +53,7 @@ def model( model_params = { "material_constants": {"solid": solid_constants}, "flux_discretization": flux_discretization, + "times_to_export": [], # Suppress output for tests } model = ModelWithEffectivePermeability(model_params) pp.run_time_dependent_model(model) diff --git a/tests/functional/test_mandel.py b/tests/functional/test_mandel.py index 3eb08c72c0..d5566c93d6 100644 --- a/tests/functional/test_mandel.py +++ b/tests/functional/test_mandel.py @@ -41,6 +41,7 @@ def results() -> list[MandelSaveData]: model_params = { "material_constants": material_constants, "time_manager": time_manager, + "times_to_export": [], # Suppress output for tests } setup = MandelSetup(model_params) pp.run_time_dependent_model(setup) @@ -155,6 +156,7 @@ def test_scaled_vs_unscaled_systems(): model_params_unscaled = { "material_constants": material_constants_unscaled, "time_manager": time_manager_unscaled, + "times_to_export": [], # Suppress output for tests } model_unscaled = MandelSetup(params=model_params_unscaled) pp.run_time_dependent_model(model_unscaled) @@ -171,6 +173,7 @@ def test_scaled_vs_unscaled_systems(): "material_constants": material_constants_scaled, "time_manager": time_manager_scaled, "units": units, + "times_to_export": [], # Suppress output for tests } scaled_model = MandelSetup(params=model_params_scaled) pp.run_time_dependent_model(model=scaled_model) diff --git a/tests/functional/test_terzaghi.py b/tests/functional/test_terzaghi.py index e2b06402d4..fe909e9777 100644 --- a/tests/functional/test_terzaghi.py +++ b/tests/functional/test_terzaghi.py @@ -56,6 +56,7 @@ def test_biot_equal_to_incompressible_poromechanics(): "fluid": pp.FluidComponent(**terzaghi_fluid_constants), }, "num_cells": 10, + "times_to_export": [], # Suppress output for tests } setup_poromech = TerzaghiSetupPoromechanics(model_params_poromech) pp.run_time_dependent_model(model=setup_poromech) @@ -69,6 +70,7 @@ def test_biot_equal_to_incompressible_poromechanics(): "fluid": pp.FluidComponent(**terzaghi_fluid_constants), }, "num_cells": 10, + "times_to_export": [], # Suppress output for tests } setup_biot = TerzaghiSetup(model_params_biot) pp.run_time_dependent_model(model=setup_biot) @@ -114,6 +116,7 @@ def test_pressure_and_consolidation_degree_errors(): }, "time_manager": pp.TimeManager([0, 0.15, 0.3], 0.15, True), "num_cells": 10, + "times_to_export": [], # Suppress output for tests } setup = TerzaghiSetup(model_params) pp.run_time_dependent_model(setup) @@ -144,7 +147,10 @@ def test_scaled_vs_unscaled_systems(): "fluid": pp.FluidComponent(**terzaghi_fluid_constants), "solid": pp.SolidConstants(**terzaghi_solid_constants), } - model_params_unscaled = {"material_constants": material_constants_unscaled} + model_params_unscaled = { + "material_constants": material_constants_unscaled, + "times_to_export": [], # Suppress output for tests + } unscaled = TerzaghiSetup(params=model_params_unscaled) pp.run_time_dependent_model(model=unscaled) @@ -155,7 +161,11 @@ def test_scaled_vs_unscaled_systems(): } scaling = {"m": 0.001, "kg": 0.001} # length in millimeters and mass in grams units = pp.Units(**scaling) - model_params_scaled = {"material_constants": material_constants_scaled, "units": units} + model_params_scaled = { + "material_constants": material_constants_scaled, + "units": units, + "times_to_export": [], # Suppress output for tests + } scaled = TerzaghiSetup(params=model_params_scaled) pp.run_time_dependent_model(model=scaled) diff --git a/tests/models/test_boundary_condition.py b/tests/models/test_boundary_condition.py index a8a77e6829..8da495fec1 100644 --- a/tests/models/test_boundary_condition.py +++ b/tests/models/test_boundary_condition.py @@ -76,7 +76,11 @@ def test_boundary_condition_mixin(t_end: int): 3) Previous timestep values are set correctly for the time dependent Neumann. """ - setup = MassBalance() + setup = MassBalance( + { + "times_to_export": [], # Suppress output for tests + } + ) setup.time_manager.dt = 1 setup.time_manager.time_final = t_end pp.run_time_dependent_model(setup) @@ -385,6 +389,7 @@ def run_model(balance_class, alpha): "times_to_export": [], "fracture_indices": [], "meshing_arguments": {"cell_size": 0.5}, + "times_to_export": [], # Suppress output for tests } params["alpha"] = alpha diff --git a/tests/models/test_constitutive_laws.py b/tests/models/test_constitutive_laws.py index 0f23cdd372..1abd1a77a7 100644 --- a/tests/models/test_constitutive_laws.py +++ b/tests/models/test_constitutive_laws.py @@ -348,6 +348,7 @@ def test_evaluated_values( params = { "material_constants": {"solid": solid, "fluid": fluid}, "fracture_indices": [0, 1], + "times_to_export": [], # Suppress output for tests } setup = model(params) diff --git a/tests/models/test_energy_balance.py b/tests/models/test_energy_balance.py index 78fa264828..a71ccbba5e 100644 --- a/tests/models/test_energy_balance.py +++ b/tests/models/test_energy_balance.py @@ -231,7 +231,7 @@ def bc_values_pressure(self, boundary_grid: pp.BoundaryGrid) -> np.ndarray: # Non-unitary time step needed for convergence dt = 1e5 model_params = { - "times_to_export": [], + "times_to_export": [], # Suppress output for tests "fracture_indices": [0, 1], "cartesian": True, "material_constants": {"solid": solid, "fluid": fluid, "numerical": numerical}, @@ -308,6 +308,7 @@ def test_energy_conservation(): "fracture_indices": [2], "time_manager": pp.TimeManager(schedule=[0, dt], dt_init=dt, constant_dt=True), "grid_type": "cartesian", + "times_to_export": [], # Suppress output for tests } setup = MassAndEnergyWellModel(model_params) From af0c9ee03d0dd6ec83d12f7c9ebcea78af05f6e8 Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Mon, 3 Feb 2025 23:05:31 +0100 Subject: [PATCH 08/10] MIN: Removing redundant declaration in some tests. --- tests/functional/setups/manu_thermoporomech_nofrac_2d.py | 3 --- tests/functional/setups/manu_thermoporomech_nofrac_3d.py | 3 --- 2 files changed, 6 deletions(-) diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py index 23585b2d22..93c7ea9232 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_2d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_2d.py @@ -1039,9 +1039,6 @@ class ManuThermoPoroMechSolutionStrategy2d( exact_sol: ManuThermoPoroMechExactSolution2d """Exact solution object.""" - results: list[ManuThermoPoroMechSaveData] = [] - """Results object that stores exact and approximated solutions and errors.""" - def __init__(self, params: dict): """Constructor for the class.""" super().__init__(params) diff --git a/tests/functional/setups/manu_thermoporomech_nofrac_3d.py b/tests/functional/setups/manu_thermoporomech_nofrac_3d.py index ff10fb4900..9a02da7a06 100644 --- a/tests/functional/setups/manu_thermoporomech_nofrac_3d.py +++ b/tests/functional/setups/manu_thermoporomech_nofrac_3d.py @@ -876,9 +876,6 @@ class ManuThermoPoroMechSolutionStrategy3d( exact_sol: ManuThermoPoroMechExactSolution3d """Exact solution object.""" - results: list[ManuThermoPoroMechSaveData] = [] - """Results object that stores exact and approximated solutions and errors.""" - def __init__(self, params: dict): """Constructor for the class.""" super().__init__(params) From 31578607b8b57b8fe8c608ef4b4a6bd683b25a7f Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 6 Feb 2025 22:32:02 +0100 Subject: [PATCH 09/10] Reverting removal of SolutionStrategyBiot. --- src/porepy/models/derived_models/biot.py | 26 +++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/porepy/models/derived_models/biot.py b/src/porepy/models/derived_models/biot.py index 9bb36c92de..5083d1ca99 100644 --- a/src/porepy/models/derived_models/biot.py +++ b/src/porepy/models/derived_models/biot.py @@ -77,27 +77,25 @@ """ import porepy as pp -from porepy.models.poromechanics import Poromechanics +from porepy.models.poromechanics import Poromechanics, SolutionStrategyPoromechanics -class BiotPoromechanics( # type: ignore[misc] - pp.constitutive_laws.SpecificStorage, - pp.constitutive_laws.BiotPoroMechanicsPorosity, - Poromechanics, -): - """Biot-Poromechanics model with special constitutive laws for specific storage and - porosity. - - The model is valid for single-phase, single-component and incompressible flow only. - A respective check is performed via overload of :meth:`set_materials`. - - """ +class SolutionStrategyBiot(SolutionStrategyPoromechanics): + """Modified solution strategy for the Biot class""" def set_materials(self): """Set the material constants.""" super().set_materials() - # Check that fluid compressibility is zero, otherwise Biot class doesn't hold. + # Check that fluid compressibility is zero, otherwise Biot class doesn't hold # NOTE Biot is tested for 1 phase 1 component. assert self.fluid.num_components == 1 assert self.fluid.num_phases == 1 assert self.fluid.reference_component.compressibility == 0 + + +class BiotPoromechanics( # type: ignore[misc] + pp.constitutive_laws.SpecificStorage, + pp.constitutive_laws.BiotPoroMechanicsPorosity, + SolutionStrategyBiot, + Poromechanics, +): ... From d0f1a7ed006cfd99e5d71d5ce5bb6516fbefea6d Mon Sep 17 00:00:00 2001 From: Veljko Lipovac Date: Thu, 6 Feb 2025 23:06:57 +0100 Subject: [PATCH 10/10] MIN: Purge file IO from tests where not tested explicitly. --- tests/models/test_constitutive_laws.py | 11 +++++++++-- tests/models/test_fluid_mass_balance.py | 7 +++++++ tests/models/test_poromechanics.py | 4 +++- tests/models/test_solution_strategy.py | 1 + tests/models/test_thermoporomechanics.py | 4 +++- .../test_fracture_propagation.py | 2 +- tests/numerics/fv/test_tpfa.py | 12 ++++++++++-- tests/numerics/nonlinear/test_line_search.py | 2 +- tests/numerics/nonlinear/test_nonlinear_solvers.py | 2 +- 9 files changed, 36 insertions(+), 9 deletions(-) diff --git a/tests/models/test_constitutive_laws.py b/tests/models/test_constitutive_laws.py index 1abd1a77a7..3573d5db42 100644 --- a/tests/models/test_constitutive_laws.py +++ b/tests/models/test_constitutive_laws.py @@ -420,6 +420,7 @@ def test_perturbation_from_reference(model: type[models.MassAndEnergyBalance], q "reference_variable_values": pp.ReferenceVariableValues( pressure=1, temperature=2 ), + "times_to_export": [], } setup = model(params) @@ -467,7 +468,11 @@ def test_dimension_reduction_values( """ # Assign non-trivial values to the parameters to avoid masking errors. solid = pp.SolidConstants(residual_aperture=0.02) - params = {"material_constants": {"solid": solid}, "num_fracs": 3} + params = { + "material_constants": {"solid": solid}, + "num_fracs": 3, + "times_to_export": [], + } if geometry is models.RectangularDomainThreeFractures: params["fracture_indices"] = [0, 1] @@ -636,7 +641,9 @@ def test_derivatives_darcy_flux_potential_trace(base_discr: str): """ # Set up and discretize model - model = PoromechanicalTestDiffTpfa({"darcy_flux_discretization": base_discr}) + model = PoromechanicalTestDiffTpfa( + {"darcy_flux_discretization": base_discr, "times_to_export": []} + ) model.prepare_simulation() model.discretize() diff --git a/tests/models/test_fluid_mass_balance.py b/tests/models/test_fluid_mass_balance.py index 09d508d635..13ad862937 100644 --- a/tests/models/test_fluid_mass_balance.py +++ b/tests/models/test_fluid_mass_balance.py @@ -74,6 +74,7 @@ class Model(SquareDomainOrthogonalFractures, SinglePhaseFlow): "fracture_indices": [0, 1], "grid_type": "cartesian", "meshing_arguments": {"cell_size_x": 0.5, "cell_size_y": 0.5}, + "times_to_export": [], } # Instantiate the model setup @@ -550,6 +551,7 @@ def test_well_incompressible_pressure_values(): }, # Use only the horizontal fracture of OrthogonalFractures3d "fracture_indices": [2], + "times_to_export": [], } setup = WellModel(params) @@ -670,6 +672,7 @@ def model_setup_gravity( "fracture_indices": [-1], # Constant y and z coordinates in 2d and 3d, resp. "meshing_arguments": {"cell_size": 0.5}, "darcy_flux_discretization": discretization_method, + "times_to_export": [], } params.update(model_params) params["material_constants"] = { @@ -893,6 +896,7 @@ def test_no_flow_neumann( params = { "meshing_arguments": {"cell_size": 1 / 3, "cell_size_y": 1 / 2}, "grid_type": grid_type, + "times_to_export": [], } self.model = model_setup_gravity( dimension=2, @@ -931,6 +935,7 @@ def test_no_flow_rotate_gravity(self, discretization, num_nodes_mortar, angle): params = { "meshing_arguments": {"cell_size": 1.0, "cell_size_y": 1 / 2}, "grid_type": "cartesian", + "times_to_export": [], } num_nodes_1d = 2 self.model = model_setup_gravity( @@ -965,6 +970,7 @@ def test_no_flow_dirichlet( params = { "meshing_arguments": {"cell_size": 1 / 3, "cell_size_y": 1 / 2}, "grid_type": grid_type, + "times_to_export": [], } self.model = model_setup_gravity( @@ -993,6 +999,7 @@ def test_inflow_top( params = { "meshing_arguments": {"cell_size": 1 / 2}, "grid_type": grid_type, + "times_to_export": [], } a = 1e-2 self.model = model_setup_gravity( diff --git a/tests/models/test_poromechanics.py b/tests/models/test_poromechanics.py index 2689b0f262..364ab9f2d8 100644 --- a/tests/models/test_poromechanics.py +++ b/tests/models/test_poromechanics.py @@ -317,7 +317,7 @@ def test_poromechanics_model_no_modification(): Failure of this test would signify rather fundamental problems in the model. """ - mod = pp.Poromechanics({}) + mod = pp.Poromechanics({"times_to_export": []}) pp.run_stationary_model(mod, {}) @@ -330,6 +330,7 @@ def test_without_fracture(biot_coefficient): "material_constants": {"fluid": fluid, "solid": solid}, "u_north": [0.0, 0.001], "cartesian": True, + "times_to_export": [], } m = TailoredPoromechanics(params) pp.run_time_dependent_model(m) @@ -539,6 +540,7 @@ def test_poromechanics_well(): model_params = { "fracture_indices": [2], "well_flux": -1e-2, + "times_to_export": [], } setup = PoromechanicsWell(model_params) pp.run_time_dependent_model(setup) diff --git a/tests/models/test_solution_strategy.py b/tests/models/test_solution_strategy.py index c16f724786..1dc89f6323 100644 --- a/tests/models/test_solution_strategy.py +++ b/tests/models/test_solution_strategy.py @@ -264,6 +264,7 @@ def test_targeted_rediscretization(model_class): "cartesian": True, # Make flow problem non-linear: "material_constants": {"fluid": pp.FluidComponent(compressibility=1.0)}, + "times_to_export": [], } # Finalize the model class by adding the rediscretization mixin. rediscretization_model_class = models._add_mixin(RediscretizationTest, model_class) diff --git a/tests/models/test_thermoporomechanics.py b/tests/models/test_thermoporomechanics.py index 344b8a9d3e..7ef751cf17 100644 --- a/tests/models/test_thermoporomechanics.py +++ b/tests/models/test_thermoporomechanics.py @@ -178,7 +178,7 @@ def test_thermoporomechanics_model_no_modification(): Failure of this test would signify rather fundamental problems in the model. """ - mod = pp.Thermoporomechanics({}) + mod = pp.Thermoporomechanics({"times_to_export": []}) pp.run_stationary_model(mod, {}) @@ -304,6 +304,7 @@ def set_domain(self) -> None: "fourier_flux_east": -2e-2, "mechanical_stress_west": 3e-2, "mechanical_stress_east": -3e-2, + "times_to_export": [], } model = TailoredPoromechanicsRobin(model_params) @@ -463,6 +464,7 @@ def test_thermoporomechanics_well(): model_params = { "fracture_indices": [2], "well_flux": -1e-2, + "times_to_export": [], } setup = ThermoporomechanicsWell(model_params) pp.run_time_dependent_model(setup) diff --git a/tests/numerics/fracture_deformation/test_fracture_propagation.py b/tests/numerics/fracture_deformation/test_fracture_propagation.py index 976dbe2121..5356b037c8 100644 --- a/tests/numerics/fracture_deformation/test_fracture_propagation.py +++ b/tests/numerics/fracture_deformation/test_fracture_propagation.py @@ -166,7 +166,7 @@ def test_pick_propagation_face_conforming_propagation(case): data_primary[pp.TIME_STEP_SOLUTIONS] = {} # Propagation model; assign this some necessary fields. - model = pp.ConformingFracturePropagation({}) + model = pp.ConformingFracturePropagation({"times_to_export": []}) model.mechanics_parameter_key = mech_key model.nd = mdg.dim_max() diff --git a/tests/numerics/fv/test_tpfa.py b/tests/numerics/fv/test_tpfa.py index 4f46ee29ae..db384fcca2 100644 --- a/tests/numerics/fv/test_tpfa.py +++ b/tests/numerics/fv/test_tpfa.py @@ -333,6 +333,7 @@ def test_transmissibility_calculation(vector_source: bool, base_discr: str): model_params = { "darcy_flux_discretization": base_discr, "vector_source": vector_source_array, + "times_to_export": [], } model = UnitTestAdTpfaFlux(model_params) @@ -671,7 +672,11 @@ def test_diff_tpfa_on_grid_with_all_dimensions(base_discr: str, grid_type: str): """ model = DiffTpfaGridsOfAllDimensions( - {"darcy_flux_discretization": base_discr, "grid_type": grid_type} + { + "darcy_flux_discretization": base_discr, + "grid_type": grid_type, + "times_to_export": [] + } ) model.prepare_simulation() @@ -762,6 +767,7 @@ def test_diff_tpfa_and_standard_tpfa_give_same_linear_system(base_discr: str): params = { "darcy_flux_discretization": base_discr, "fourier_flux_discretization": base_discr, + "times_to_export": [], } model_without_diff = WithoutDiffTpfa(params.copy()) model_with_diff = WithDiffTpfa(params) @@ -838,7 +844,9 @@ def test_flux_potential_trace_on_tips_and_internal_boundaries(base_discr: str): trace is equal to the pressure in the adjacent cell. """ - model = DiffTpfaFractureTipsInternalBoundaries({"base_discr": base_discr}) + model = DiffTpfaFractureTipsInternalBoundaries( + {"base_discr": base_discr, "times_to_export": []} + ) model.prepare_simulation() mdg = model.mdg diff --git a/tests/numerics/nonlinear/test_line_search.py b/tests/numerics/nonlinear/test_line_search.py index c38500202d..0a677cb492 100644 --- a/tests/numerics/nonlinear/test_line_search.py +++ b/tests/numerics/nonlinear/test_line_search.py @@ -70,7 +70,7 @@ def test_line_search(): checks are made on the results or that the line search does what it is supposed to. """ - model = ConstraintFunctionsMomentumBalance() + model = ConstraintFunctionsMomentumBalance({"times_to_export": []}) solver_params = { "nonlinear_solver": ConstraintLineSearchNonlinearSolver, "Global_line_search": True, diff --git a/tests/numerics/nonlinear/test_nonlinear_solvers.py b/tests/numerics/nonlinear/test_nonlinear_solvers.py index dc9652f195..44969e8977 100644 --- a/tests/numerics/nonlinear/test_nonlinear_solvers.py +++ b/tests/numerics/nonlinear/test_nonlinear_solvers.py @@ -40,7 +40,7 @@ def test_nonlinear_iteration_count(): iteration count matches the pre set value after convergence is obtained. """ - model = NonlinearSinglePhaseFlow() + model = NonlinearSinglePhaseFlow({"times_to_export": []}) model.expected_number_of_iterations = 3 pp.run_time_dependent_model(model, {})