diff --git a/docs/examples/3_point_bending.py b/docs/examples/3_point_bending.py index a87d60c..4a08d88 100644 --- a/docs/examples/3_point_bending.py +++ b/docs/examples/3_point_bending.py @@ -17,7 +17,7 @@ # Setting up the Beam # ------------------- # First, initialize the setup for the simply supported beam with a distributed load. -# Define the geometry, the mesh, and the load. +# Define the geometry, the mesh, and the load. Not defined parameters are set to default values (from class function default_parameters). # Note, all parameters must be pint objects: parameters = {} @@ -36,6 +36,7 @@ # Initializing the Linear Elasticity Problem # ------------------------------------------ # Second, initialize the linear elastic problem using the setup object and further material parameters: +# Again not defined parameters are set to default values (from class function default_parameters). parameters["rho"] = 7750 * ureg("kg/m^3") parameters["E"] = 210e9 * ureg("N/m^2") diff --git a/docs/examples/cylinder.py b/docs/examples/cylinder.py index 0f96e8a..b50e384 100644 --- a/docs/examples/cylinder.py +++ b/docs/examples/cylinder.py @@ -21,6 +21,7 @@ # * `mesh_density` (mesh density) in 1/m # # The parameters must be defined as `pint` objects +# Parameters required but not defined are set to default values (from class function default_parameters). # # Example code # ------------ diff --git a/src/fenicsxconcrete/experimental_setup/am_multiple_layers.py b/src/fenicsxconcrete/experimental_setup/am_multiple_layers.py index 949ee66..3c0d439 100644 --- a/src/fenicsxconcrete/experimental_setup/am_multiple_layers.py +++ b/src/fenicsxconcrete/experimental_setup/am_multiple_layers.py @@ -34,13 +34,7 @@ def __init__(self, parameters: dict[str, pint.Quantity]): """ - # initialize default parameters for the setup - default_p = Parameters() - # default_p['dummy'] = 'example' * ureg('') # example default parameter for this class - - # updating parameters, overriding defaults - default_p.update(parameters) - super().__init__(default_p) + super().__init__(parameters) @staticmethod def default_parameters() -> dict[str, pint.Quantity]: @@ -52,6 +46,7 @@ def default_parameters() -> dict[str, pint.Quantity]: """ setup_parameters = {} + setup_parameters["degree"] = 2 * ureg("") # polynomial degree # geometry setup_parameters["dim"] = 2 * ureg("") setup_parameters["num_layers"] = 10 * ureg("") # number of layers in y diff --git a/src/fenicsxconcrete/experimental_setup/base_experiment.py b/src/fenicsxconcrete/experimental_setup/base_experiment.py index c6ebeb2..5167603 100644 --- a/src/fenicsxconcrete/experimental_setup/base_experiment.py +++ b/src/fenicsxconcrete/experimental_setup/base_experiment.py @@ -30,14 +30,33 @@ def __init__(self, parameters: dict[str, pint.Quantity]) -> None: """ # initialize parameter attributes - default_setup_parameters = Parameters() - # setting up default setup parameters - default_setup_parameters["degree"] = 2 * ureg("") # polynomial degree + setup_parameters = Parameters() + # setting up default setup parameters defined in each child + default_p = self.default_parameters() + setup_parameters.update(default_p) # update with input parameters - default_setup_parameters.update(parameters) + setup_parameters.update(parameters) + + # get logger info which parameters are set to default values + # plus check dimensionality of input parameters + keys_set_default = [] + for key in dict(default_p): + if key not in parameters: + keys_set_default.append(key) + else: + # check if units are compatible + dim_given = parameters[key].dimensionality + dim_default = default_p[key].dimensionality + if dim_given != dim_default: + raise ValueError( + f"given units for {key} are not compatible with default units: {dim_given} != {dim_default}" + ) + self.logger.info(f"for the following parameters, the default values are used: {keys_set_default}") + # as attribute - self.parameters = default_setup_parameters + self.parameters = setup_parameters + # remove units for use in fem model self.p = self.parameters.to_magnitude() @@ -59,6 +78,8 @@ def default_parameters() -> dict[str, pint.Quantity]: """ + pass + @abstractmethod def create_displacement_boundary(self, V: df.fem.FunctionSpace) -> list[df.fem.bcs.DirichletBCMetaClass] | None: """defines empty displacement boundary conditions (to be done in child) diff --git a/src/fenicsxconcrete/experimental_setup/cantilever_beam.py b/src/fenicsxconcrete/experimental_setup/cantilever_beam.py index 6cf1daf..7b06b38 100644 --- a/src/fenicsxconcrete/experimental_setup/cantilever_beam.py +++ b/src/fenicsxconcrete/experimental_setup/cantilever_beam.py @@ -27,14 +27,7 @@ def __init__(self, parameters: dict[str, pint.Quantity] | None = None): """ - # initialize default parameters for the setup - default_p = Parameters() - # default_p['dummy'] = 'example' * ureg('') # example default parameter for this class - - # updating parameters, overriding defaults - default_p.update(parameters) - - super().__init__(default_p) + super().__init__(parameters) def setup(self) -> None: """defines the mesh for 2D or 3D @@ -77,6 +70,7 @@ def default_parameters() -> dict[str, pint.Quantity]: """ setup_parameters = {} + setup_parameters["length"] = 1 * ureg("m") setup_parameters["height"] = 0.3 * ureg("m") setup_parameters["width"] = 0.3 * ureg("m") # only relevant for 3D case diff --git a/src/fenicsxconcrete/experimental_setup/compression_cylinder.py b/src/fenicsxconcrete/experimental_setup/compression_cylinder.py index f94ebfa..a58c2d2 100644 --- a/src/fenicsxconcrete/experimental_setup/compression_cylinder.py +++ b/src/fenicsxconcrete/experimental_setup/compression_cylinder.py @@ -90,12 +90,7 @@ def __init__(self, parameters: dict[str, pint.Quantity] | None = None) -> None: """ - # initialize a set of default parameters - p = Parameters() - - p.update(parameters) - - super().__init__(p) + super().__init__(parameters) # initialize variable top_displacement self.top_displacement = df.fem.Constant(domain=self.mesh, c=0.0) # applied via fkt: apply_displ_load(...) @@ -127,7 +122,7 @@ def setup(self) -> None: # until the bottom surface area matches that of a circle with the initially defined radius def create_cylinder_mesh(radius, p): # generate cylinder mesh using gmsh - mesh = generate_cylinder_mesh(radius, p["height"], p["mesh_density"], p["degree"]) + mesh = generate_cylinder_mesh(radius, p["height"], p["mesh_density"], p["element_order"]) facets = df.mesh.locate_entities_boundary(mesh, 2, plane_at(0.0, 2)) tdim = mesh.topology.dim f_v = mesh.topology.connectivity(tdim - 1, 0).array.reshape(-1, 3) @@ -169,6 +164,7 @@ def default_parameters() -> dict[str, pint.Quantity]: """ default_parameters = {} + default_parameters["element_order"] = 2 * ureg("") # polynomial degree # boundary setting default_parameters["bc_setting"] = "free" * ureg("") # boundary setting, two options available: fixed and free diff --git a/src/fenicsxconcrete/experimental_setup/simple_beam.py b/src/fenicsxconcrete/experimental_setup/simple_beam.py index c9733bf..edfecdb 100644 --- a/src/fenicsxconcrete/experimental_setup/simple_beam.py +++ b/src/fenicsxconcrete/experimental_setup/simple_beam.py @@ -31,14 +31,7 @@ def __init__(self, parameters: dict[str, pint.Quantity]) -> None: """ - # initialize default parameters for the setup - default_p = Parameters() - default_p["degree"] = 2 * ureg("") # polynomial degree - - # updating parameters, overriding defaults - default_p.update(parameters) - - super().__init__(default_p) + super().__init__(parameters) def setup(self): """defines the mesh for 2D or 3D @@ -81,6 +74,7 @@ def default_parameters() -> dict[str, pint.Quantity]: """ setup_parameters = {} + setup_parameters["load"] = 10000 * ureg("N/m^2") setup_parameters["length"] = 1 * ureg("m") setup_parameters["height"] = 0.3 * ureg("m") diff --git a/src/fenicsxconcrete/experimental_setup/simple_cube.py b/src/fenicsxconcrete/experimental_setup/simple_cube.py index f48ad05..33e0fca 100644 --- a/src/fenicsxconcrete/experimental_setup/simple_cube.py +++ b/src/fenicsxconcrete/experimental_setup/simple_cube.py @@ -9,7 +9,7 @@ from fenicsxconcrete.boundary_conditions.bcs import BoundaryConditions from fenicsxconcrete.experimental_setup.base_experiment import Experiment -from fenicsxconcrete.util import Parameters, ureg +from fenicsxconcrete.util import LogMixin, Parameters, ureg class SimpleCube(Experiment): @@ -33,17 +33,7 @@ def __init__(self, parameters: dict[str, pint.Quantity] | None = None) -> None: see default_parameters for a first guess """ - # initialize a set of default parameters - default_p = Parameters() - default_p["height"] = 1 * ureg("m") - default_p["width"] = 1 * ureg("m") - default_p["length"] = 1 * ureg("m") - default_p["T_0"] = ureg.Quantity(20.0, ureg.degC) - default_p["T_bc"] = ureg.Quantity(20.0, ureg.degC) - - default_p.update(parameters) - - super().__init__(default_p) + super().__init__(parameters) @staticmethod def default_parameters() -> dict[str, pint.Quantity]: @@ -56,6 +46,11 @@ def default_parameters() -> dict[str, pint.Quantity]: setup_parameters = {} + setup_parameters["height"] = 1 * ureg("m") + setup_parameters["width"] = 1 * ureg("m") + setup_parameters["length"] = 1 * ureg("m") + setup_parameters["T_0"] = ureg.Quantity(20.0, ureg.degC) + setup_parameters["T_bc"] = ureg.Quantity(20.0, ureg.degC) setup_parameters["dim"] = 3 * ureg("") setup_parameters["num_elements_length"] = 2 * ureg("") setup_parameters["num_elements_width"] = 2 * ureg("") diff --git a/src/fenicsxconcrete/experimental_setup/tensile_beam.py b/src/fenicsxconcrete/experimental_setup/tensile_beam.py index 50b2980..1567ef3 100644 --- a/src/fenicsxconcrete/experimental_setup/tensile_beam.py +++ b/src/fenicsxconcrete/experimental_setup/tensile_beam.py @@ -26,14 +26,8 @@ def __init__(self, parameters: dict[str, pint.Quantity] | None = None) -> None: see default_parameters for a first guess """ - # initialize a set of "basic parameters" - default_p = Parameters() - # default_p['dummy'] = 'example' * ureg('') # example default parameter for this class - # updating parameters, overriding defaults - default_p.update(parameters) - - super().__init__(default_p) + super().__init__(parameters) def setup(self) -> None: """defines the mesh for 2D or 3D @@ -76,6 +70,7 @@ def default_parameters() -> dict[str, pint.Quantity]: """ setup_parameters = {} + setup_parameters["length"] = 1 * ureg("m") setup_parameters["height"] = 0.3 * ureg("m") setup_parameters["width"] = 0.3 * ureg("m") # only relevant for 3D case diff --git a/src/fenicsxconcrete/finite_element_problem/base_material.py b/src/fenicsxconcrete/finite_element_problem/base_material.py index 6411b40..7a2f9b7 100644 --- a/src/fenicsxconcrete/finite_element_problem/base_material.py +++ b/src/fenicsxconcrete/finite_element_problem/base_material.py @@ -92,16 +92,34 @@ def __init__( self.experiment = experiment self.mesh = self.experiment.mesh - # setting up default material parameters - default_fem_parameters = Parameters() - default_fem_parameters["g"] = 9.81 * ureg("m/s^2") - default_fem_parameters["dt"] = 1.0 * ureg("s") - - # adding experimental parameters to dictionary to combine to one - default_fem_parameters.update(self.experiment.parameters) + # initialize parameter attributes + setup_parameters = Parameters() + # setting up default setup parameters defined in each child + _, default_p = self.default_parameters() + setup_parameters.update(default_p) + # update with experiment parameters + setup_parameters.update(self.experiment.parameters) # update with input parameters - default_fem_parameters.update(parameters) - self.parameters = default_fem_parameters + setup_parameters.update(parameters) + + # get logger info which input parameters are set to default values + # plus check dimensionality of input parameters + keys_set_default = [] + for key in dict(default_p): + if key not in parameters: + keys_set_default.append(key) + else: + # check if units are compatible + dim_given = parameters[key].dimensionality + dim_default = default_p[key].dimensionality + if dim_given != dim_default: + raise ValueError( + f"given units for {key} are not compatible with default units: {dim_given} != {dim_default}" + ) + self.logger.info(f"for the following parameters, the default values are used: {keys_set_default}") + + # set parameters as attribute + self.parameters = setup_parameters # remove units for use in fem model self.p = self.parameters.to_magnitude() self.experiment.p = self.p # update experimental parameter list for use in e.g. boundary definition @@ -135,6 +153,8 @@ def default_parameters() -> tuple[Experiment, dict[str, pint.Quantity]]: """returns a dictionary with required parameters and a set of working values as example""" # this must de defined in each setup class + pass + @abstractmethod def setup(self) -> None: # initialization of this specific problem diff --git a/src/fenicsxconcrete/finite_element_problem/concrete_am.py b/src/fenicsxconcrete/finite_element_problem/concrete_am.py index a168871..6c5a437 100644 --- a/src/fenicsxconcrete/finite_element_problem/concrete_am.py +++ b/src/fenicsxconcrete/finite_element_problem/concrete_am.py @@ -48,19 +48,19 @@ def __init__( """ - # adding default material parameter, will be overridden by outside input - default_p = Parameters() - default_p["stress_state"] = "plane_strain" * ureg("") # default stress state for 2D optional "plane_stress" - - # updating parameters, overriding defaults - default_p.update(parameters) + # # adding default material parameter, will be overridden by outside input + # default_p = Parameters() + # # default stress state for 2D optional "plane_stress" + # + # # updating parameters, overriding defaults + # default_p.update(parameters) if nonlinear_problem: self.nonlinear_problem = nonlinear_problem else: self.nonlinear_problem = ConcreteThixElasticModel # default material - super().__init__(experiment, default_p, pv_name, pv_path) + super().__init__(experiment, parameters, pv_name, pv_path) @staticmethod def parameter_description() -> dict[str, str]: @@ -73,12 +73,13 @@ def parameter_description() -> dict[str, str]: description = { "general parameters": { "rho": "density of fresh concrete", + "g": "gravity", "nu": "Poissons Ratio", "degree": "Polynomial degree for the FEM model", "q_degree": "Polynomial degree for which the quadrature rule integrates correctly", "load_time": "time in which the load is applied", "stress_state": "for 2D plain stress or plane strain", - "dt": "time step", # default set in material base class + "dt": "time step", }, "ThixElasticModel": { "E_0": "Youngs Modulus at age=0", @@ -112,10 +113,13 @@ def default_parameters( joined_parameters = { # Material parameter for concrete model with structural build-up "rho": 2070 * ureg("kg/m^3"), # density of fresh concrete + "g": 9.81 * ureg("m/s^2"), # gravity "nu": 0.3 * ureg(""), # Poissons Ratio # other model parameters - # "degree": 2 * ureg(""), # polynomial degree --> default defined in base_experiment!! + "degree": 2 * ureg(""), # polynomial degree "q_degree": 2 * ureg(""), # quadrature rule + "stress_state": "plane_strain" * ureg(""), # for 2D stress state + "dt": 1.0 * ureg("s"), # time step "load_time": 60 * ureg("s"), # body force load applied in s } if not non_linear_problem or non_linear_problem == ConcreteThixElasticModel: diff --git a/src/fenicsxconcrete/finite_element_problem/concrete_thermo_mechanical.py b/src/fenicsxconcrete/finite_element_problem/concrete_thermo_mechanical.py index 2f53779..86891e2 100644 --- a/src/fenicsxconcrete/finite_element_problem/concrete_thermo_mechanical.py +++ b/src/fenicsxconcrete/finite_element_problem/concrete_thermo_mechanical.py @@ -32,22 +32,23 @@ def __init__( pv_path: str | None = None, ) -> None: - # adding default material parameter, will be overridden by outside input - default_p = Parameters() - # default_p['dummy'] = 'example' * ureg('') # example default parameter for this class + # # adding default material parameter, will be overridden by outside input + # default_p = Parameters() + # # default_p['dummy'] = 'example' * ureg('') # example default parameter for this class + # + # # updating parameters, overriding defaults + # default_p.update(parameters) - # updating parameters, overriding defaults - default_p.update(parameters) - - super().__init__(experiment, default_p, pv_name, pv_path) + super().__init__(experiment, parameters, pv_name, pv_path) @staticmethod def parameter_description() -> dict[str, str]: description = { "igc": "Ideal gas constant", "rho": "Density of concrete", - "themal_cond": "Thermal conductivity", - "vol_heat_cap": "TODO", + "g": "Gravitational acceleration", + "thermal_cond": "effective thermal conductivity", + "vol_heat_cap": "volumetric heat capacity", "Q_pot": "potential heat per weight of binder", "Q_inf": "potential heat per concrete volume", "B1": "numerical shape parameter for heat release function", @@ -68,6 +69,7 @@ def parameter_description() -> dict[str, str]: "ft_inf": "reference value for the tensile strength, default infinity, otherwise at alpha_tx", "a_ft": "exponential parameter to change the shape of the function ft(DOH)", "evolution_ft": "flag to turn off the evolution of the tensile strength", + "dt": "time step", } return description @@ -85,6 +87,7 @@ def default_parameters() -> tuple[Experiment, dict[str, pint.Quantity]]: default_parameters = { "igc": 8.3145 * ureg("J/K/mol"), "rho": 2350.0 * ureg("kg/m^3"), + "g": 9.81 * ureg("m/s^2"), "thermal_cond": 2.0 * ureg("W/(m*K)"), "vol_heat_cap": 2.4e6 * ureg("J/(m^3 * K)"), # "Q_pot": 500e3 * ureg("J/kg"), only needed for postprocessing @@ -95,6 +98,7 @@ def default_parameters() -> tuple[Experiment, dict[str, pint.Quantity]]: "alpha_max": 0.875 * ureg(""), "alpha_tx": 0.68 * ureg(""), "T_ref": ureg.Quantity(25.0, ureg.degC), + "degree": 2 * ureg(""), "q_degree": 2 * ureg(""), "E_28": 15 * ureg("MPa"), "nu": 0.2 * ureg(""), @@ -106,16 +110,16 @@ def default_parameters() -> tuple[Experiment, dict[str, pint.Quantity]]: "ft_inf": 467000 * ureg(""), "a_ft": 1.0 * ureg(""), "evolution_ft": "True" * ureg(""), + "dt": 1.0 * ureg("s"), } - default_parameters["E_act"] = 5653.0 * default_parameters["igc"] * ureg("J/mol") + default_parameters["E_act"] = 5653.0 * ureg("K") * default_parameters["igc"] + return experiment, default_parameters def compute_residuals(self) -> None: pass def setup(self) -> None: - # TODO: the next line only makes a test pass. I don't like it either - _ = self.p["rho"] self.t = 0.0 self.rule = QuadratureRule(cell_type=self.mesh.ufl_cell(), degree=self.p["q_degree"]) @@ -193,7 +197,6 @@ def solve(self) -> None: # set current DOH for computation of Young's modulus self.mechanics_problem.q_array_alpha[:] = self.temperature_problem.q_alpha.vector.array - # print('Solving: u') # TODO ouput only a certain log level INFO # mechanics paroblem is not required for temperature, could crash in frist time steps but then be useful try: diff --git a/src/fenicsxconcrete/finite_element_problem/linear_elasticity.py b/src/fenicsxconcrete/finite_element_problem/linear_elasticity.py index bd32428..e2c3fef 100644 --- a/src/fenicsxconcrete/finite_element_problem/linear_elasticity.py +++ b/src/fenicsxconcrete/finite_element_problem/linear_elasticity.py @@ -21,14 +21,14 @@ def __init__( ) -> None: """defines default parameters, for the rest, see base class""" - # adding default material parameter, will be overridden by outside input - default_p = Parameters() - default_p["stress_state"] = "plane_strain" * ureg("") # default stress state in 2D, optional "plane_stress" + # # adding default material parameter, will be overridden by outside input + # default_p = Parameters() + # default_p["stress_state"] = "plane_strain" * ureg("") # default stress state in 2D, optional "plane_stress" + # + # # updating parameters, overriding defaults + # default_p.update(parameters) - # updating parameters, overriding defaults - default_p.update(parameters) - - super().__init__(experiment, default_p, pv_name, pv_path) + super().__init__(experiment, parameters, pv_name, pv_path) def setup(self) -> None: # compute different set of elastic moduli @@ -84,6 +84,27 @@ def setup(self) -> None: petsc_options={"ksp_type": "preonly", "pc_type": "lu"}, ) + @staticmethod + def parameter_description() -> dict[str, str]: + """static method returning a description dictionary for required parameters + + Returns: + description dictionary + + """ + description = { + "g": "gravity", + "dt": "time step", + "rho": "density of fresh concrete", + "E": "Young's Modulus", + "nu": "Poissons Ratio", + "stress_state": "for 2D plain stress or plane strain", + "degree": "Polynomial degree for the FEM model", + "dt": "time step", + } + + return description + @staticmethod def default_parameters() -> tuple[Experiment, dict[str, pint.Quantity]]: """returns a dictionary with required parameters and a set of working values as example""" @@ -91,10 +112,17 @@ def default_parameters() -> tuple[Experiment, dict[str, pint.Quantity]]: experiment = CantileverBeam(CantileverBeam.default_parameters()) model_parameters = {} + model_parameters["g"] = 9.81 * ureg("m/s^2") + model_parameters["dt"] = 1.0 * ureg("s") + model_parameters["rho"] = 7750 * ureg("kg/m^3") model_parameters["E"] = 210e9 * ureg("N/m^2") model_parameters["nu"] = 0.28 * ureg("") + model_parameters["stress_state"] = "plane_strain" * ureg("") + model_parameters["degree"] = 2 * ureg("") # polynomial degree + model_parameters["dt"] = 1.0 * ureg("s") + return experiment, model_parameters # Stress computation for linear elastic problem diff --git a/tests/experimental_setup/test_experimental_setups.py b/tests/experimental_setup/test_experimental_setups.py index a491a41..82e5981 100644 --- a/tests/experimental_setup/test_experimental_setups.py +++ b/tests/experimental_setup/test_experimental_setups.py @@ -1,5 +1,6 @@ import copy +import pint import pytest from fenicsxconcrete.experimental_setup.am_multiple_layers import AmMultipleLayers @@ -12,6 +13,39 @@ from fenicsxconcrete.finite_element_problem.linear_elasticity import LinearElasticity from fenicsxconcrete.util import ureg +# # makes no sense anymore since the default parameter are used in each experiment or material problem +# # plus was not tested since twice the same names!! +# @pytest.mark.parametrize( +# "setup", +# [ +# CantileverBeam, +# TensileBeam, +# SimpleBeam, +# CompressionCylinder, +# AmMultipleLayers, +# SimpleCube, +# ], +# ) +# def test_default_parameters(setup: Experiment) -> None: +# """This function creates experimental setups with the respective default dictionaries +# +# This makes sure all relevant values are included""" +# default_material = LinearElasticity +# +# setup_parameters = setup.default_parameters() +# +# # initialize with default parameters +# experiment = setup(setup_parameters) +# +# # test that each parameter is truly required +# for key in setup_parameters: +# with pytest.raises(KeyError): +# less_parameters = copy.deepcopy(setup_parameters) +# less_parameters.pop(key) +# experiment = setup(less_parameters) +# fem_problem = default_material(experiment, default_material.default_parameters()[1]) +# fem_problem.solve() + @pytest.mark.parametrize( "setup", @@ -25,24 +59,16 @@ ], ) def test_default_parameters(setup: Experiment) -> None: - """This function creates experimental setups with the respective default dictionaries - - This makes sure all relevant values are included""" - default_material = LinearElasticity + """This function tests if the default_parameters are complete""" + # default_material = LinearElasticity setup_parameters = setup.default_parameters() - # initialize with default parameters - experiment = setup(setup_parameters) - - # test that each parameter is truly required - for key in setup_parameters: - with pytest.raises(KeyError): - less_parameters = copy.deepcopy(setup_parameters) - less_parameters.pop(key) - experiment = setup(less_parameters) - fem_problem = default_material(experiment, default_material.default_parameters()[1]) - fem_problem.solve() + try: + experiment = setup(setup_parameters) + except KeyError: + print("default parameter dictionary is wrong") + raise ValueError # to imporve coverage, I want to test the error messages @@ -63,3 +89,22 @@ def test_default_parameters(setup: Experiment) -> None: with pytest.raises(ValueError): setup_parameters["dim"] = 4 * ureg("") # there is no 4D setup test_setup = setup(setup_parameters) + + +@pytest.mark.parametrize( + "setup", + [ + CantileverBeam, + TensileBeam, + SimpleBeam, + CompressionCylinder, + AmMultipleLayers, + SimpleCube, + ], +) +def test_dimensionality_check(setup: Experiment) -> None: + setup_parameters = setup.default_parameters() + + with pytest.raises(ValueError): + setup_parameters["dim"] = 3 * ureg("s") # dimension should be dimensionless + test_setup = setup(setup_parameters) diff --git a/tests/finite_element_problem/test_am_layers.py b/tests/finite_element_problem/test_am_layers.py index 477c932..74dddde 100644 --- a/tests/finite_element_problem/test_am_layers.py +++ b/tests/finite_element_problem/test_am_layers.py @@ -38,13 +38,8 @@ def set_test_parameters(dim: int, mat_type: str = "thix") -> Parameters: if dim == 2: setup_parameters["stress_state"] = "plane_stress" * ureg("") - # default material parameters as start - if mat_type == "thix": - _, default_params = ConcreteAM.default_parameters(ConcreteThixElasticModel) - else: - raise ValueError(f"Unknown material type {mat_type}") + # default material parameters from material problem - setup_parameters.update(default_params) if dim == 3: setup_parameters["q_degree"] = 4 * ureg("") diff --git a/tests/finite_element_problem/test_default_dictionaries.py b/tests/finite_element_problem/test_default_dictionaries.py index da4d680..0e9bfa8 100644 --- a/tests/finite_element_problem/test_default_dictionaries.py +++ b/tests/finite_element_problem/test_default_dictionaries.py @@ -6,26 +6,51 @@ from fenicsxconcrete.finite_element_problem.concrete_am import ConcreteAM from fenicsxconcrete.finite_element_problem.concrete_thermo_mechanical import ConcreteThermoMechanical from fenicsxconcrete.finite_element_problem.linear_elasticity import LinearElasticity +from fenicsxconcrete.util import ureg + + +# @pytest.mark.parametrize("material_model", [LinearElasticity, ConcreteAM, ConcreteThermoMechanical]) +# def test_default_dictionaries(material_model: MaterialProblem) -> None: +# """This function creates experimental setups with the respective default dictionaries +# +# This makes sure all relevant values are included""" +# +# default_setup, default_parameters = material_model.default_parameters() +# +# fem_problem = material_model(default_setup, default_parameters) +# fem_problem.solve() +# +# # test that each parameter is truly required +# # a loop over all default parameters removes each on in turn and expects a key error from the initialized problem +# for key in default_parameters: +# with pytest.raises(KeyError) as ex: +# less_parameters = copy.deepcopy(default_parameters) +# less_parameters.pop(key) +# fem_problem = material_model(default_setup, less_parameters) +# fem_problem.solve() +# print(key, "seems to be an unneccessary key in the default dictionary") +# print(ex) +# since the default parameter are used in each experiment or material problem this test makes no sence anymore +@pytest.mark.parametrize("material_model", [LinearElasticity, ConcreteAM, ConcreteThermoMechanical]) +def test_dimensionality_check(material_model: MaterialProblem) -> None: + default_setup, default_parameters = material_model.default_parameters() -@pytest.mark.parametrize("material_model", [LinearElasticity, ConcreteAM, ConcreteThermoMechanical]) -def test_default_dictionaries(material_model: MaterialProblem) -> None: - """This function creates experimental setups with the respective default dictionaries + with pytest.raises(ValueError): + default_parameters["g"] = 3 * ureg("m") # gravity should be m/s² + fem_problem = material_model(default_setup, default_parameters) - This makes sure all relevant values are included""" + +@pytest.mark.parametrize("material_model", [LinearElasticity, ConcreteAM, ConcreteThermoMechanical]) +def test_default_parameters(material_model: MaterialProblem) -> None: + """This function tests if the default_parameters are complete""" + # default_material = LinearElasticity default_setup, default_parameters = material_model.default_parameters() - fem_problem = material_model(default_setup, default_parameters) - fem_problem.solve() - - # test that each parameter is truly required - # a loop over all default parameters removes each on in turn and expects a key error from the initialized problem - for key in default_parameters: - with pytest.raises(KeyError) as ex: - less_parameters = copy.deepcopy(default_parameters) - less_parameters.pop(key) - fem_problem = material_model(default_setup, less_parameters) - fem_problem.solve() - print(key, "seems to be an unneccessary key in the default dictionary") - print(ex) + try: + fem_problem = material_model(default_setup, default_parameters) + fem_problem.solve() + except KeyError: + print("default parameter dictionary is wrong") + raise ValueError diff --git a/tests/finite_element_problem/test_linear_cylinder.py b/tests/finite_element_problem/test_linear_cylinder.py index 441b060..4835015 100644 --- a/tests/finite_element_problem/test_linear_cylinder.py +++ b/tests/finite_element_problem/test_linear_cylinder.py @@ -21,6 +21,7 @@ def simple_setup( parameters["height"] = 0.012 * ureg("m") parameters["dim"] = 3 * ureg("") parameters["bc_setting"] = bc_setting * ureg("") + parameters["element_order"] = 2 * ureg("") parameters["degree"] = 2 * ureg("") parameters.update(p) diff --git a/tests/finite_element_problem/test_linear_simple_cube.py b/tests/finite_element_problem/test_linear_simple_cube.py index 3b621f3..5a8ea2a 100644 --- a/tests/finite_element_problem/test_linear_simple_cube.py +++ b/tests/finite_element_problem/test_linear_simple_cube.py @@ -105,7 +105,8 @@ def test_disp(dim: int) -> None: @pytest.mark.parametrize("dim", [2, 3]) def test_strain_state_error(dim: int) -> None: - setup_parameters = SimpleCube.default_parameters() + # setup_parameters = SimpleCube.default_parameters() + setup_parameters = {} # use default parameters setup_parameters["dim"] = dim * ureg("") setup_parameters["strain_state"] = "wrong" * ureg("") setup = SimpleCube(setup_parameters) @@ -117,7 +118,8 @@ def test_strain_state_error(dim: int) -> None: @pytest.mark.parametrize("dim", [2, 3]) @pytest.mark.parametrize("degree", [1, 2]) def test_multiaxial_strain(dim: int, degree: int) -> None: - setup_parameters = SimpleCube.default_parameters() + # setup_parameters = SimpleCube.default_parameters() + setup_parameters = {} # use default parameters setup_parameters["dim"] = dim * ureg("") setup_parameters["degree"] = degree * ureg("") setup_parameters["strain_state"] = "multiaxial" * ureg("") diff --git a/tests/finite_element_problem/test_thermo_mechanical_cube.py b/tests/finite_element_problem/test_thermo_mechanical_cube.py index 56e8da2..cb1cefa 100644 --- a/tests/finite_element_problem/test_thermo_mechanical_cube.py +++ b/tests/finite_element_problem/test_thermo_mechanical_cube.py @@ -158,9 +158,7 @@ def test_hydration_with_body_forces(dim: int): parameters["rho"] = 2350 * ureg("kg/m^3") # in kg/m^3 density of concrete parameters["density_binder"] = 1440 * ureg("kg/m^3") # in kg/m^3 density of the binder - parameters["thermal_cond"] = 2.0 * ureg( - "W/(m^3*K)" - ) # effective thermal conductivity, approx in Wm^-3K^-1, concrete! + parameters["thermal_cond"] = 2.0 * ureg("W/(m*K)") # effective thermal conductivity, approx in W(mK)^-1, concrete! # self.specific_heat_capacity = 9000 # effective specific heat capacity in J kg⁻1 K⁻1 parameters["vol_heat_cap"] = 2.4e6 * ureg("J/(m^3 * K)") # volumetric heat cap J/(m3 K) # parameters["b_ratio"] = 0.2 # volume percentage of binder diff --git a/tests/finite_element_problem/test_thixotropy_uniaxial.py b/tests/finite_element_problem/test_thixotropy_uniaxial.py index 72fd0d6..532c60e 100644 --- a/tests/finite_element_problem/test_thixotropy_uniaxial.py +++ b/tests/finite_element_problem/test_thixotropy_uniaxial.py @@ -64,12 +64,11 @@ def test_disp(dim: int, degree: int): # setting up the problem experiment = SimpleCube(parameters) - # get default parameters and change accordingly to cases + # get description of parameters des = ConcreteAM.parameter_description() print(des) - _, default_params = ConcreteAM.default_parameters(ConcreteThixElasticModel) - parameters.update(default_params) + # use default parameters (default) and change accordingly to cases parameters["degree"] = degree * ureg("") if dim == 3: parameters["q_degree"] = 4 * ureg("") @@ -139,6 +138,7 @@ def check_disp_case(problem: ConcreteAM, dt: pint.Quantity, E_o_time: list[float if problem.p["dim"] == 2: # standard uniaxial checks for last time step + print("analytic_eps", analytic_eps, problem.p["nu"] * analytic_eps) # strain in yy direction assert problem.sensors["StrainSensor"].data[-1][-1] == pytest.approx(analytic_eps) # strain in xx direction @@ -198,5 +198,5 @@ def check_disp_case(problem: ConcreteAM, dt: pint.Quantity, E_o_time: list[float # if __name__ == "__main__": # # test_disp(2, 2) -# -# # test_disp(3, 2) + +# test_disp(3, 1)