diff --git a/src/festim/__init__.py b/src/festim/__init__.py index ac904f92a..2046f9967 100644 --- a/src/festim/__init__.py +++ b/src/festim/__init__.py @@ -18,10 +18,11 @@ ) from .boundary_conditions.flux_bc import FluxBCBase, HeatFluxBC, ParticleFluxBC from .boundary_conditions.henrys_bc import HenrysBC -from .boundary_conditions.flux_bc import FluxBCBase, ParticleFluxBC, HeatFluxBC -from .boundary_conditions.surface_reaction import SurfaceReactionBC - from .boundary_conditions.sieverts_bc import SievertsBC +from .boundary_conditions.surface_reaction import SurfaceReactionBC +from .coupled_heat_hydrogen_problem import ( + CoupledtTransientHeatTransferHydrogenTransport, +) from .exports.average_surface import AverageSurface from .exports.average_volume import AverageVolume from .exports.maximum_surface import MaximumSurface @@ -45,20 +46,20 @@ ) from .hydrogen_transport_problem import ( HTransportProblemDiscontinuous, - HydrogenTransportProblem, HTransportProblemPenalty, + HydrogenTransportProblem, ) -from .problem_change_of_var import HydrogenTransportProblemDiscontinuousChangeVar from .initial_condition import InitialCondition, InitialTemperature from .material import Material from .mesh.mesh import Mesh from .mesh.mesh_1d import Mesh1D from .mesh.mesh_from_xdmf import MeshFromXDMF from .problem import ProblemBase +from .problem_change_of_var import HydrogenTransportProblemDiscontinuousChangeVar from .reaction import Reaction from .settings import Settings from .source import HeatSource, ParticleSource, SourceBase -from .species import ImplicitSpecies, Species, find_species_from_name, SpeciesChangeVar +from .species import ImplicitSpecies, Species, SpeciesChangeVar, find_species_from_name from .stepsize import Stepsize from .subdomain.interface import Interface from .subdomain.surface_subdomain import SurfaceSubdomain, find_surface_from_id diff --git a/src/festim/coupled_heat_hydrogen_problem.py b/src/festim/coupled_heat_hydrogen_problem.py new file mode 100644 index 000000000..21d491db2 --- /dev/null +++ b/src/festim/coupled_heat_hydrogen_problem.py @@ -0,0 +1,183 @@ +import tqdm.autonotebook +from dolfinx import fem + +from festim.heat_transfer_problem import HeatTransferProblem +from festim.helpers import as_fenics_constant, nmm_interpolate +from festim.hydrogen_transport_problem import ( + HTransportProblemDiscontinuous, + HTransportProblemPenalty, + HydrogenTransportProblem, +) +from festim.problem_change_of_var import HydrogenTransportProblemDiscontinuousChangeVar + + +class CoupledtTransientHeatTransferHydrogenTransport: + """ + Coupled heat transfer and hydrogen transport transient problem + + Args: + heat_problem: the heat transfer problem + hydrogen_problem: the hydrogen transport problem + + Attributes: + heat_problem: the heat transfer problem + hydrogen_problem: the hydrogen transport problem + non_matching_meshes: True if the meshes in the heat_problem and hydorgen_problem + are not matching + + Examples: + .. highlight:: python + .. code-block:: python + + import festim as F + + my_heat_transfer_model = F.HeatTransferProblem( + mesh=F.Mesh(...), + subdomains=[F.Subdomain(...)], + ... + ) + + my_h_transport_model = F.HydrogenTransportProblem( + mesh=F.Mesh(...), + subdomains=[F.Subdomain(...)], + species=[F.Species(name="H"), F.Species(name="Trap")], + ... + ) + + coupled_problem = F.CoupledHeatTransferHydrogenTransport( + heat_problem=my_heat_transfer_model, + hydrogen_problem=my_h_transport_model, + ) + + """ + + heat_problem: HeatTransferProblem + hydrogen_problem: HydrogenTransportProblem + + non_matching_meshes: bool + + def __init__( + self, + heat_problem: HeatTransferProblem, + hydrogen_problem: HydrogenTransportProblem, + ) -> None: + self.heat_problem = heat_problem + self.hydrogen_problem = hydrogen_problem + + if ( + not self.heat_problem.settings.transient + or not self.hydrogen_problem.settings.transient + ): + raise TypeError( + "Both the heat and hydrogen problems must be set to transient" + ) + + @property + def heat_problem(self): + return self._heat_problem + + @heat_problem.setter + def heat_problem(self, value): + if not isinstance(value, HeatTransferProblem): + raise TypeError("heat_problem must be a festim.HeatTransferProblem object") + self._heat_problem = value + + @property + def hydrogen_problem(self): + return self._hydrogen_problem + + @hydrogen_problem.setter + def hydrogen_problem(self, value): + if isinstance( + value, + HTransportProblemDiscontinuous + | HTransportProblemPenalty + | HydrogenTransportProblemDiscontinuousChangeVar, + ): + raise NotImplementedError( + "Coupled heat transfer - hydorgen transport simulations with " + "HydrogenTransportProblemDiscontinuousChangeVar, " + "HTransportProblemPenalty or" + "HydrogenTransportProblemDiscontinuousChangeVar, " + "not currently supported" + ) + elif not isinstance(value, HydrogenTransportProblem): + raise TypeError( + "hydrogen_problem must be a festim.HydrogenTransportProblem object" + ) + self._hydrogen_problem = value + + @property + def non_matching_meshes(self): + return self.heat_problem.mesh.mesh != self.hydrogen_problem.mesh.mesh + + def initialise(self): + # make sure both problems have the same initial time step and final time, + # use minimal initial value of the two and maximal final time of the two + min_initial_dt = min( + self.heat_problem.settings.stepsize.initial_value, + self.hydrogen_problem.settings.stepsize.initial_value, + ) + self.heat_problem.settings.stepsize.initial_value = min_initial_dt + self.hydrogen_problem.settings.stepsize.initial_value = min_initial_dt + + if ( + self.heat_problem.settings.final_time + != self.hydrogen_problem.settings.final_time + ): + raise ValueError( + "Final time values in the heat transfer and hydrogen transport " + "model must be the same" + ) + + self.heat_problem.initialise() + + self.heat_problem.show_progress_bar = False + + if self.non_matching_meshes: + V = fem.functionspace(self.hydrogen_problem.mesh.mesh, ("P", 1)) + T_func = fem.Function(V) + + nmm_interpolate(T_func, self.heat_problem.u) + + self.hydrogen_problem.temperature = T_func + else: + self.hydrogen_problem.temperature = self.heat_problem.u + + self.hydrogen_problem.initialise() + + def iterate(self): + self.heat_problem.iterate() + + if self.non_matching_meshes: + nmm_interpolate( + self.hydrogen_problem.temperature_fenics, self.heat_problem.u + ) + + self.hydrogen_problem.iterate() + + # use the same time step for both problems, use minimum of the two + next_dt_value = min( + float(self.hydrogen_problem.dt), float(self.heat_problem.dt) + ) + self.heat_problem.dt = as_fenics_constant( + value=next_dt_value, mesh=self.heat_problem.mesh.mesh + ) + self.hydrogen_problem.dt = as_fenics_constant( + value=next_dt_value, mesh=self.hydrogen_problem.mesh.mesh + ) + + def run(self): + if self.hydrogen_problem.show_progress_bar: + self.hydrogen_problem.progress_bar = tqdm.autonotebook.tqdm( + desc=f"Solving {self.__class__.__name__}", + total=self.hydrogen_problem.settings.final_time, + unit_scale=True, + ) + + while self.hydrogen_problem.t.value < self.hydrogen_problem.settings.final_time: + self.iterate() + + if self.hydrogen_problem.show_progress_bar: + self.hydrogen_problem.progress_bar.refresh() + self.hydrogen_problem.progress_bar.close() diff --git a/src/festim/helpers.py b/src/festim/helpers.py index 956d42ab0..53b0f1589 100644 --- a/src/festim/helpers.py +++ b/src/festim/helpers.py @@ -287,3 +287,25 @@ def update(self, t: float): get_interpolation_points = lambda element: element.interpolation_points else: get_interpolation_points = lambda element: element.interpolation_points() + + +def nmm_interpolate(f_out: fem.function, f_in: fem.function): + """Non Matching Mesh Interpolate: interpolate one function (f_in) from one mesh into + another function (f_out) with a mismatching mesh + + args: + f_out: function to interpolate into + f_in: function to interpolate from + + notes: + https://fenicsproject.discourse.group/t/gjk-error-in-interpolation-between-non-matching-second-ordered-3d-meshes/16086/6 + """ + + dim = f_out.function_space.mesh.topology.dim + index_map = f_out.function_space.mesh.topology.index_map(dim) + ncells = index_map.size_local + index_map.num_ghosts + cells = np.arange(ncells, dtype=np.int32) + interpolation_data = fem.create_interpolation_data( + f_out.function_space, f_in.function_space, cells, padding=1e-11 + ) + f_out.interpolate_nonmatching(f_in, cells, interpolation_data=interpolation_data) diff --git a/src/festim/hydrogen_transport_problem.py b/src/festim/hydrogen_transport_problem.py index 1898392f0..185f93d00 100644 --- a/src/festim/hydrogen_transport_problem.py +++ b/src/festim/hydrogen_transport_problem.py @@ -208,7 +208,7 @@ def temperature_fenics(self, value): def temperature_time_dependent(self): if self.temperature is None: return False - if isinstance(self.temperature, fem.Constant): + if isinstance(self.temperature, fem.Constant | fem.Function): return False if callable(self.temperature): arguments = self.temperature.__code__.co_varnames @@ -765,28 +765,28 @@ def update_time_dependent_values(self): if isinstance(reactant, _species.ImplicitSpecies): reactant.update_density(t=t) - if not self.temperature_time_dependent: - return - - if isinstance(self.temperature_fenics, fem.Constant): - self.temperature_fenics.value = self.temperature(t=t) - elif isinstance(self.temperature_fenics, fem.Function): - self.temperature_fenics.interpolate(self.temperature_expr) + if ( + isinstance(self.temperature, fem.Function) + or self.temperature_time_dependent + ): + for bc in self.boundary_conditions: + if isinstance( + bc, + boundary_conditions.FixedConcentrationBC + | boundary_conditions.ParticleFluxBC, + ): + if bc.temperature_dependent: + bc.update(t=t) - for bc in self.boundary_conditions: - if isinstance( - bc, - ( - boundary_conditions.FixedConcentrationBC, - boundary_conditions.ParticleFluxBC, - ), - ): - if bc.temperature_dependent: - bc.update(t=t) + for source in self.sources: + if source.value.temperature_dependent: + source.value.update(t=t) - for source in self.sources: - if source.value.temperature_dependent: - source.value.update(t=t) + if self.temperature_time_dependent: + if isinstance(self.temperature_fenics, fem.Constant): + self.temperature_fenics.value = self.temperature(t=t) + elif isinstance(self.temperature_fenics, fem.Function): + self.temperature_fenics.interpolate(self.temperature_expr) def post_processing(self): """Post processes the model""" diff --git a/src/festim/problem_change_of_var.py b/src/festim/problem_change_of_var.py index 643164af2..7e3c83e16 100644 --- a/src/festim/problem_change_of_var.py +++ b/src/festim/problem_change_of_var.py @@ -1,13 +1,14 @@ -import festim.boundary_conditions -from festim.hydrogen_transport_problem import HydrogenTransportProblem -from festim import boundary_conditions, as_fenics_constant -import festim -from festim.helpers import get_interpolation_points -import festim.species as _species +from typing import List + import ufl from dolfinx import fem -from typing import List +import festim +import festim.boundary_conditions +import festim.species as _species +from festim import boundary_conditions +from festim.helpers import as_fenics_constant, get_interpolation_points +from festim.hydrogen_transport_problem import HydrogenTransportProblem class HydrogenTransportProblemDiscontinuousChangeVar(HydrogenTransportProblem): @@ -79,6 +80,7 @@ def create_formulation(self): # hack enforce the concentration attribute of the species for all species # to be used in reaction.reaction_term + for spe in self.species: if spe.mobile: K_S = reaction.volume.material.get_solubility_coefficient( diff --git a/test/system_tests/test_coupled_heat_hydrogen.py b/test/system_tests/test_coupled_heat_hydrogen.py new file mode 100644 index 000000000..17004aefa --- /dev/null +++ b/test/system_tests/test_coupled_heat_hydrogen.py @@ -0,0 +1,239 @@ +import numpy as np +import ufl + +import festim as F + +from .tools import error_L2 + +test_mesh = F.Mesh1D(vertices=np.linspace(0, 1, 500)) +test_mat = F.Material(D_0=1, E_D=0, thermal_conductivity=1) +test_subdomains = [F.VolumeSubdomain1D(id=1, borders=[0, 1], material=test_mat)] +test_H = F.Species("H", mobile=True) + + +def test_MMS_coupled_problem(): + """MMS coupled heat and hydrogen test with 1 mobile species and 1 trap in a 1s + transient, the values of the temperature, mobile and trapped solutions at the last + time step is compared to an analytical solution""" + + # coupled simulation properties + density, heat_capacity = 1.2, 2.6 + thermal_conductivity = 4.2 + D_0, E_D = 1.2, 0.1 + k_0, E_k = 2.2, 0.2 + p_0, E_p = 0.5, 0.1 + n_trap = 5 + k_B = F.k_B + final_time = 1 + + # common festim objects + test_mesh = F.Mesh1D(vertices=np.linspace(0, 1, 2000)) + test_mat = F.Material( + D_0=D_0, + E_D=E_D, + thermal_conductivity=thermal_conductivity, + density=density, + heat_capacity=heat_capacity, + ) + test_vol_sub = F.VolumeSubdomain1D(id=1, borders=[0, 1], material=test_mat) + left_sub = F.SurfaceSubdomain1D(id=2, x=0) + right_sub = F.SurfaceSubdomain1D(id=3, x=1) + test_mobile = F.Species("mobile", mobile=True) + test_trapped = F.Species(name="trapped", mobile=False, subdomains=[test_vol_sub]) + test_traps = F.ImplicitSpecies(n=n_trap, others=[test_mobile, test_trapped]) + + # define temperature sim + exact_T_solution = lambda x, t: 3 * x[0] ** 2 + 10 * t + + dTdt = 10 + mms_T_source = ( + test_mat.density * test_mat.heat_capacity * dTdt + - test_mat.thermal_conductivity * 6 + ) + + test_heat_problem = F.HeatTransferProblem( + mesh=test_mesh, + subdomains=[test_vol_sub, left_sub, right_sub], + boundary_conditions=[ + F.FixedTemperatureBC(subdomain=left_sub, value=exact_T_solution), + F.FixedTemperatureBC(subdomain=right_sub, value=exact_T_solution), + ], + sources=[F.HeatSource(value=mms_T_source, volume=test_vol_sub)], + initial_condition=F.InitialTemperature(lambda x: exact_T_solution(x, 0)), + settings=F.Settings( + atol=1e-10, + rtol=1e-10, + transient=True, + stepsize=final_time / 20, + final_time=final_time, + ), + ) + + # define hydrogen problem + exact_mobile_solution = lambda x, t: 2 * x[0] ** 2 + 15 * t + exact_trapped_solution = lambda x, t: 4 * x[0] ** 2 + 12 * t + + exact_mobile_intial_cond = lambda x: 2 * x[0] ** 2 + exact_trapped_intial_cond = lambda x: 4 * x[0] ** 2 + + dmobiledt = 15 + dtrappeddt = 12 + + D = lambda x, t: D_0 * ufl.exp(-E_D / (k_B * exact_T_solution(x, t))) + k = lambda x, t: k_0 * ufl.exp(-E_k / (k_B * exact_T_solution(x, t))) + p = lambda x, t: p_0 * ufl.exp(-E_p / (k_B * exact_T_solution(x, t))) + + def mms_mobile_source(x, t): + return ( + dmobiledt + - ufl.div(D(x, t) * ufl.grad(exact_mobile_solution(x, t))) + + k(x, t) + * (exact_mobile_solution(x, t)) + * (n_trap - (exact_trapped_solution(x, t))) + - p(x, t) * (exact_trapped_solution(x, t)) + ) + + def mms_trapped_source(x, t): + return ( + dtrappeddt + + k(x, t) + * (exact_mobile_solution(x, t)) + * (n_trap - (exact_trapped_solution(x, t))) + - p(x, t) * (exact_trapped_solution(x, t)) + ) + + test_hydrogen_problem = F.HydrogenTransportProblem( + mesh=test_mesh, + subdomains=[test_vol_sub, left_sub, right_sub], + boundary_conditions=[ + F.FixedConcentrationBC( + subdomain=left_sub, + value=exact_mobile_solution, + species=test_mobile, + ), + F.FixedConcentrationBC( + subdomain=right_sub, + value=exact_mobile_solution, + species=test_mobile, + ), + F.FixedConcentrationBC( + subdomain=left_sub, + value=exact_trapped_solution, + species=test_trapped, + ), + F.FixedConcentrationBC( + subdomain=right_sub, + value=exact_trapped_solution, + species=test_trapped, + ), + ], + species=[test_mobile, test_trapped], + reactions=[ + F.Reaction( + reactant=[test_mobile, test_traps], + product=test_trapped, + k_0=k_0, + E_k=E_k, + p_0=p_0, + E_p=E_p, + volume=test_vol_sub, + ) + ], + initial_conditions=[ + F.InitialCondition(value=exact_mobile_intial_cond, species=test_mobile), + F.InitialCondition(value=exact_trapped_intial_cond, species=test_trapped), + ], + sources=[ + F.ParticleSource( + value=mms_mobile_source, volume=test_vol_sub, species=test_mobile + ), + F.ParticleSource( + value=mms_trapped_source, volume=test_vol_sub, species=test_trapped + ), + ], + settings=F.Settings( + atol=1e-10, + rtol=1e-10, + transient=True, + stepsize=final_time / 20, + final_time=final_time, + ), + ) + + # define coupled problem + test_coupled_problem = F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, + hydrogen_problem=test_hydrogen_problem, + ) + test_coupled_problem.initialise() + test_coupled_problem.run() + + # compare computed values with exact solutions + T_computed = test_coupled_problem.hydrogen_problem.temperature_fenics + mobile_computed = test_mobile.post_processing_solution + trapped_computed = test_trapped.post_processing_solution + + exact_T = lambda x: 3 * x[0] ** 2 + 10 * final_time + exact_mobile = lambda x: 2 * x[0] ** 2 + 15 * final_time + exact_trapped = lambda x: 4 * x[0] ** 2 + 12 * final_time + + L2_error_T = error_L2(T_computed, exact_T) + L2_error_mobile = error_L2(mobile_computed, exact_mobile) + L2_error_trapped = error_L2(trapped_computed, exact_trapped) + + # TEST ensure L2 error below 2e07 + assert L2_error_T < 2e-07 + assert L2_error_mobile < 2e-07 + assert L2_error_trapped < 2e-07 + + +def test_coupled_problem_non_matching_mesh(): + """Test that the function in the hydrogen problem which is temperature dependent + has the correct value at x=1, when temperature field is time and space dependent, + and the problems have mismatching meshes, in transient""" + + T_func = lambda x, t: 3 * x[0] + 5 + t + c_func = lambda T: 5 * T + + my_mat = F.Material( + D_0=1, E_D=0, thermal_conductivity=1, density=1, heat_capacity=1 + ) + test_vol_sub = F.VolumeSubdomain1D(id=1, borders=[0, 1], material=my_mat) + left_sub = F.SurfaceSubdomain1D(id=2, x=0) + right_sub = F.SurfaceSubdomain1D(id=3, x=1) + test_mesh_2 = F.Mesh1D(vertices=np.linspace(0, 1, 1000)) + + test_heat_problem = F.HeatTransferProblem( + mesh=test_mesh, + subdomains=[test_vol_sub, left_sub, right_sub], + boundary_conditions=[ + F.FixedTemperatureBC(subdomain=left_sub, value=0), + F.FixedTemperatureBC(subdomain=right_sub, value=T_func), + ], + settings=F.Settings( + atol=1e-10, rtol=1e-10, transient=True, final_time=5, stepsize=0.5 + ), + ) + + test_hydrogen_problem = F.HydrogenTransportProblem( + mesh=test_mesh_2, + subdomains=[test_vol_sub, left_sub, right_sub], + boundary_conditions=[ + F.FixedConcentrationBC(subdomain=left_sub, value=0, species=test_H), + F.FixedConcentrationBC(subdomain=right_sub, value=c_func, species=test_H), + ], + species=[test_H], + settings=F.Settings( + atol=1, rtol=1e-10, transient=True, final_time=5, stepsize=0.5 + ), + ) + + test_coupled_problem = F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, + hydrogen_problem=test_hydrogen_problem, + ) + + test_coupled_problem.initialise() + test_coupled_problem.run() + + assert np.isclose(test_coupled_problem.hydrogen_problem.u.x.array[-1], 65) diff --git a/test/test_coupled_heat_hydrogen_problem.py b/test/test_coupled_heat_hydrogen_problem.py new file mode 100644 index 000000000..4c34cfb73 --- /dev/null +++ b/test/test_coupled_heat_hydrogen_problem.py @@ -0,0 +1,156 @@ +import numpy as np +import pytest + +import festim as F + +test_mesh = F.Mesh1D(vertices=np.linspace(0, 1, 100)) +test_mat = F.Material(D_0=1, E_D=0, thermal_conductivity=1, heat_capacity=1, density=1) +test_subdomains = [F.VolumeSubdomain1D(id=1, borders=[0, 1], material=test_mat)] +test_H = F.Species("H") + + +@pytest.mark.parametrize( + "object", + ["coucou", 1, 1.0], +) +def test_error_raised_when_wrong_hydrogen_problem_given(object): + """Test TypeError is raised when an object that isnt a + festim.HydrogenTransportProblem object is given to hydrogen_problem""" + + test_heat_problem = F.HeatTransferProblem() + + with pytest.raises( + TypeError, + match="hydrogen_problem must be a festim.HydrogenTransportProblem object", + ): + F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, hydrogen_problem=object + ) + + +@pytest.mark.parametrize( + "object", + [ + F.HTransportProblemDiscontinuous(), + F.HTransportProblemPenalty(), + F.HydrogenTransportProblemDiscontinuousChangeVar(), + ], +) +def test_error_raised_when_wrong_type_hydrogen_problem_given(object): + """Test TypeError is raised when an object that isnt a + festim.HydrogenTransportProblem object is given to hydrogen_problem""" + + test_heat_problem = F.HeatTransferProblem() + + with pytest.raises( + NotImplementedError, + match="Coupled heat transfer - hydorgen transport simulations with " + "HydrogenTransportProblemDiscontinuousChangeVar, " + "HTransportProblemPenalty or" + "HydrogenTransportProblemDiscontinuousChangeVar, " + "not currently supported", + ): + F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, hydrogen_problem=object + ) + + +@pytest.mark.parametrize( + "object", + ["coucou", 1, 1.0], +) +def test_error_raised_when_wrong_heat_problem_given(object): + """Test TypeError is raised when an object that isnt a + festim.HeatTransferProblem object is given to heat_problem""" + + test_hydrogen_problem = F.HydrogenTransportProblem() + + with pytest.raises( + TypeError, + match="heat_problem must be a festim.HeatTransferProblem object", + ): + F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=object, hydrogen_problem=test_hydrogen_problem + ) + + +def test_initial_dt_values_are_the_same(): + """Test that the smallest of the stepsize intial_value values given is used in both + the heat_problem and the hydorgen_problem""" + + test_heat_problem = F.HeatTransferProblem( + mesh=test_mesh, + subdomains=test_subdomains, + settings=F.Settings(atol=1, rtol=1, transient=True, stepsize=0.5, final_time=5), + ) + + test_hydrogen_problem = F.HydrogenTransportProblem( + mesh=test_mesh, + subdomains=test_subdomains, + species=[test_H], + settings=F.Settings(atol=1, rtol=1, transient=True, stepsize=1.5, final_time=5), + ) + + test_coupled_problem = F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, + hydrogen_problem=test_hydrogen_problem, + ) + + test_coupled_problem.initialise() + + assert np.isclose( + float(test_coupled_problem.heat_problem.dt), + float(test_coupled_problem.hydrogen_problem.dt), + ) + + +def test_error_raised_when_final_times_not_the_same(): + """Test that an error is raised when the final time values given in the heat_problem + and the hydorgen_problem are not the same""" + + test_heat_problem = F.HeatTransferProblem( + mesh=test_mesh, + subdomains=test_subdomains, + settings=F.Settings(atol=1, rtol=1, transient=True, stepsize=1, final_time=10), + ) + + test_hydrogen_problem = F.HydrogenTransportProblem( + mesh=test_mesh, + subdomains=test_subdomains, + species=[test_H], + settings=F.Settings(atol=1, rtol=1, transient=True, stepsize=1, final_time=5), + ) + + test_coupled_problem = F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, + hydrogen_problem=test_hydrogen_problem, + ) + + with pytest.raises( + ValueError, + match="Final time values in the heat transfer and hydrogen transport model" + " must be the same", + ): + test_coupled_problem.initialise() + + +def test_error_raised_when_both_problems_not_transient(): + """Test that an error is raised when the heat_problem and hydorgen_problem are not + set to transient""" + + test_heat_problem = F.HeatTransferProblem( + settings=F.Settings(atol=1, rtol=1, transient=False), + ) + + test_hydrogen_problem = F.HydrogenTransportProblem( + settings=F.Settings(atol=1, rtol=1, transient=True, stepsize=1, final_time=4), + ) + + with pytest.raises( + TypeError, + match="Both the heat and hydrogen problems must be set to transient", + ): + F.CoupledtTransientHeatTransferHydrogenTransport( + heat_problem=test_heat_problem, + hydrogen_problem=test_hydrogen_problem, + )