From 387832428a19e25fcc8a43ebd0697b591280e11c Mon Sep 17 00:00:00 2001 From: J Dark Date: Sat, 4 Nov 2023 10:43:05 -0400 Subject: [PATCH 01/10] test post processing --- test/test_h_transport_problem.py | 47 ++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/test_h_transport_problem.py b/test/test_h_transport_problem.py index 36a3df1ce..9b22598f8 100644 --- a/test/test_h_transport_problem.py +++ b/test/test_h_transport_problem.py @@ -343,3 +343,50 @@ def test_initialise_exports_multiple_exports_same_species(): Ds.append(export.D) assert np.isclose(Ds[0].x.array[0], Ds[1].x.array[0]) + + +def test_post_processing_update_D_global(): + """Test that the D_global attribute is updated at each time + step when temperture is time dependent""" + my_mesh = F.Mesh1D(np.linspace(0, 1, num=11)) + my_mat = F.Material(D_0=1.5, E_D=0.1, name="my_mat") + surf = F.SurfaceSubdomain1D(id=1, x=1) + + # create species and interpolate solution + H = F.Species("H") + V = fem.FunctionSpace(my_mesh.mesh, ("CG", 1)) + u = fem.Function(V) + u.interpolate(lambda x: 2 * x[0] ** 2 + 1) + H.solution = u + + my_export = F.SurfaceFlux( + field=H, + surface=surf, + ) + + # Build the model + my_model = F.HydrogenTransportProblem( + mesh=my_mesh, + subdomains=[F.VolumeSubdomain1D(id=1, borders=[0, 1], material=my_mat), surf], + species=[H], + temperature=lambda t: 500 * t, + exports=[my_export], + ) + + my_model.define_function_spaces() + my_model.define_markers_and_measures() + my_model.t = fem.Constant(my_model.mesh.mesh, 1.0) + my_model.define_temperature() + my_model.initialise_exports() + + # RUN + my_model.post_processing() + value_t_1 = my_export.D.x.array[-1] + + my_model.t = fem.Constant(my_model.mesh.mesh, 2.0) + my_model.update_time_dependent_values() + my_model.post_processing() + value_t_2 = my_export.D.x.array[-1] + + # TEST + assert value_t_1 != value_t_2 From bda7ad845b077ad6dcd31edbe82b320e47e5e548 Mon Sep 17 00:00:00 2001 From: James Dark <65899899+jhdark@users.noreply.github.com> Date: Sat, 4 Nov 2023 10:44:05 -0400 Subject: [PATCH 02/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémi Delaporte-Mathurin <40028739+RemDelaporteMathurin@users.noreply.github.com> --- test/test_h_transport_problem.py | 4 +--- test/test_surface_quantity.py | 13 +++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/test/test_h_transport_problem.py b/test/test_h_transport_problem.py index 9b22598f8..045c9b529 100644 --- a/test/test_h_transport_problem.py +++ b/test/test_h_transport_problem.py @@ -338,9 +338,7 @@ def test_initialise_exports_multiple_exports_same_species(): my_model.define_temperature() my_model.initialise_exports() - Ds = [] - for export in my_model.exports: - Ds.append(export.D) + Ds = [export.D for export in my_model_exports] assert np.isclose(Ds[0].x.array[0], Ds[1].x.array[0]) diff --git a/test/test_surface_quantity.py b/test/test_surface_quantity.py index 0ff67f19e..3d0420342 100644 --- a/test/test_surface_quantity.py +++ b/test/test_surface_quantity.py @@ -30,11 +30,11 @@ def surface_flux_export_compute(): # give function to species V = fem.FunctionSpace(my_mesh.mesh, ("CG", 1)) - u = fem.Function(V) - u.interpolate(lambda x: 2 * x[0] ** 2 + 1) + c = fem.Function(V) + c.interpolate(lambda x: 2 * x[0] ** 2 + 1) my_species = F.Species("H") - my_species.solution = u + my_species.solution = c my_export = F.SurfaceFlux( filename="my_surface_flux.csv", @@ -47,6 +47,7 @@ def surface_flux_export_compute(): my_export.compute(n=my_mesh.n, ds=ds) # TEST + # flux = -D grad(c)_ \cdot n = -D dc/dx = -D * 4 * x expected_value = -D * 4 * dummy_surface.x computed_value = my_export.value @@ -108,11 +109,11 @@ def test_writer(tmp_path): for i in range(10): my_export.write(i) - computed_value = len(np.genfromtxt(my_export.filename, delimiter=",")) + file_length = len(np.genfromtxt(my_export.filename, delimiter=",")) - expected_value = 10 + expected_length = i + 1 - assert computed_value == expected_value + assert file_length == expected_length def test_surface_setter_raises_TypeError(): From 52da3d007267bd31477da0dafd82b6edb721cbae Mon Sep 17 00:00:00 2001 From: J Dark Date: Sat, 4 Nov 2023 10:59:22 -0400 Subject: [PATCH 03/10] fixes --- test/test_h_transport_problem.py | 2 +- test/test_surface_quantity.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/test_h_transport_problem.py b/test/test_h_transport_problem.py index 045c9b529..985e2d713 100644 --- a/test/test_h_transport_problem.py +++ b/test/test_h_transport_problem.py @@ -338,7 +338,7 @@ def test_initialise_exports_multiple_exports_same_species(): my_model.define_temperature() my_model.initialise_exports() - Ds = [export.D for export in my_model_exports] + Ds = [export.D for export in my_model.exports] assert np.isclose(Ds[0].x.array[0], Ds[1].x.array[0]) diff --git a/test/test_surface_quantity.py b/test/test_surface_quantity.py index 3d0420342..4bcc192b6 100644 --- a/test/test_surface_quantity.py +++ b/test/test_surface_quantity.py @@ -99,19 +99,19 @@ def test_field_setter_raises_TypeError(): def test_writer(tmp_path): """Test that the writes values at each timestep""" - my_export = F.SurfaceQuantity( + my_export = F.SurfaceFlux( filename=os.path.join(tmp_path, "my_export.csv"), field=F.Species("test"), surface=F.SurfaceSubdomain1D(id=1, x=0), ) my_export.value = 2.0 + my_export.initialise_export() for i in range(10): my_export.write(i) - file_length = len(np.genfromtxt(my_export.filename, delimiter=",")) - expected_length = i + 1 + expected_length = i + 2 assert file_length == expected_length From 96d47e03d52d193a287cf72deed8f2b77dd2eafc Mon Sep 17 00:00:00 2001 From: J Dark Date: Sun, 5 Nov 2023 11:28:19 -0500 Subject: [PATCH 04/10] new test and doc strings --- test/test_h_transport_problem.py | 56 ++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/test_h_transport_problem.py b/test/test_h_transport_problem.py index 985e2d713..13eb718bd 100644 --- a/test/test_h_transport_problem.py +++ b/test/test_h_transport_problem.py @@ -229,6 +229,8 @@ def test_initialise_exports_find_species_with_one_field(): def test_define_D_global_different_temperatures(): + """Test that the D_global attribute is correctly defined when the temperature + is different in the volume subdomains""" D_0, E_D = 1.5, 0.1 my_mat = F.Material(D_0=D_0, E_D=E_D, name="my_mat") surf = F.SurfaceSubdomain1D(id=1, x=0) @@ -267,6 +269,8 @@ def test_define_D_global_different_temperatures(): def test_define_D_global_different_materials(): + """Test that the D_global attribute is correctly defined when the material + is different in the volume subdomains""" D_0_left, E_D_left = 1.0, 0.1 D_0_right, E_D_right = 2.0, 0.2 my_mat_L = F.Material(D_0=D_0_left, E_D=E_D_left, name="my_mat_L") @@ -307,6 +311,8 @@ def test_define_D_global_different_materials(): def test_initialise_exports_multiple_exports_same_species(): + """Test that the D attribute is the same for multiple exports of the same species, + and that D_global is only created once per species""" D_0, E_D = 1.5, 0.1 my_mat = F.Material(D_0=D_0, E_D=E_D, name="my_mat") surf_1 = F.SurfaceSubdomain1D(id=1, x=0) @@ -343,6 +349,56 @@ def test_initialise_exports_multiple_exports_same_species(): assert np.isclose(Ds[0].x.array[0], Ds[1].x.array[0]) +def test_define_D_global_multispecies(): + """Test that the D_global attribute is correctly defined when there are multiple + species in one subdomain""" + A = F.Species("A") + B = F.Species("B") + + D_0_A, D_0_B = 1.0, 2.0 + E_D_A, E_D_B = 0.1, 0.2 + + my_mat = F.Material( + D_0={A: D_0_A, B: D_0_B}, E_D={A: E_D_A, B: E_D_B}, name="my_mat" + ) + surf = F.SurfaceSubdomain1D(id=1, x=1) + + my_model = F.HydrogenTransportProblem( + mesh=F.Mesh1D(np.linspace(0, 1, num=101)), + subdomains=[ + F.VolumeSubdomain1D(id=1, borders=[0, 1], material=my_mat), + ], + species=[F.Species("A"), F.Species("B")], + temperature=500, + exports=[ + F.SurfaceFlux( + field=A, + surface=surf, + ), + F.SurfaceFlux( + field=A, + surface=surf, + ), + ], + ) + + my_model.define_function_spaces() + my_model.define_markers_and_measures() + my_model.define_temperature() + + D_A_computed, D_A_expr = my_model.define_D_global(A) + D_B_computed, D_B_expr = my_model.define_D_global(B) + + computed_values = [D_A_computed.x.array[-1], D_B_computed.x.array[-1]] + + D_analytical_A = D_0_A * np.exp(-E_D_A / (F.k_B * my_model.temperature)) + D_analytical_B = D_0_B * np.exp(-E_D_B / (F.k_B * my_model.temperature)) + + expected_values = [D_analytical_A, D_analytical_B] + + assert np.isclose(computed_values, expected_values).all() + + def test_post_processing_update_D_global(): """Test that the D_global attribute is updated at each time step when temperture is time dependent""" From cf41ff464ee6def4657b608a162f28869e0c8955 Mon Sep 17 00:00:00 2001 From: James Dark <65899899+jhdark@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:38:17 -0500 Subject: [PATCH 05/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Rémi Delaporte-Mathurin <40028739+RemDelaporteMathurin@users.noreply.github.com> --- festim/exports/surface_flux.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/festim/exports/surface_flux.py b/festim/exports/surface_flux.py index 1a3fdad88..3585344de 100644 --- a/festim/exports/surface_flux.py +++ b/festim/exports/surface_flux.py @@ -18,12 +18,12 @@ class SurfaceFlux(F.SurfaceQuantity): filename (str): name of the file to which the surface flux is exported t (list): list of time values data (list): list of surface flux values - title (str): title of the exported data + title (str): title of the exported data in the csv file """ def __init__( self, - field, + field: F.Species, surface: F.SurfaceSubdomain1D, filename: str = None, ) -> None: From e0d568a7ed63be28679bccb6a3d1558170162acf Mon Sep 17 00:00:00 2001 From: J Dark Date: Sun, 5 Nov 2023 16:24:57 -0500 Subject: [PATCH 06/10] changes from reveiw --- festim/exports/surface_flux.py | 16 ++++----- festim/exports/surface_quantity.py | 7 ++-- test/test_h_transport_problem.py | 53 +++++++++--------------------- test/test_permeation_problem.py | 1 + test/test_surface_quantity.py | 23 ++++++++----- 5 files changed, 44 insertions(+), 56 deletions(-) diff --git a/festim/exports/surface_flux.py b/festim/exports/surface_flux.py index 1a3fdad88..c0710948d 100644 --- a/festim/exports/surface_flux.py +++ b/festim/exports/surface_flux.py @@ -16,9 +16,6 @@ class SurfaceFlux(F.SurfaceQuantity): field (festim.Species): species for which the surface flux is computed surface (festim.SurfaceSubdomain1D): surface subdomain filename (str): name of the file to which the surface flux is exported - t (list): list of time values - data (list): list of surface flux values - title (str): title of the exported data """ def __init__( @@ -29,10 +26,13 @@ def __init__( ) -> None: super().__init__(field, surface, filename) - self.t = [] - self.data = [] - def compute(self, n, ds): + """Computes the value of the surface flux at the surface + + Args: + n (ufl.geometry.FacetNormal): normal vector to the surface + ds (ufl.Measure): surface measure of the model + """ self.value = fem.assemble_scalar( fem.form( -self.D @@ -43,9 +43,9 @@ def compute(self, n, ds): self.data.append(self.value) def initialise_export(self): - self.title = "Flux surface {}: {}".format(self.surface.id, self.field.name) + title = "Flux surface {}: {}".format(self.surface.id, self.field.name) if self.filename is not None: with open(self.filename, mode="w", newline="") as file: writer = csv.writer(file) - writer.writerow(["t(s)", f"{self.title}"]) + writer.writerow(["t(s)", f"{title}"]) diff --git a/festim/exports/surface_quantity.py b/festim/exports/surface_quantity.py index 3045d6bab..c03e2bfc2 100644 --- a/festim/exports/surface_quantity.py +++ b/festim/exports/surface_quantity.py @@ -21,6 +21,9 @@ def __init__(self, field, surface, filename: str = None) -> None: self.surface = surface self.filename = filename + self.t = [] + self.data = [] + @property def filename(self): return self._filename @@ -31,8 +34,8 @@ def filename(self, value): self._filename = None elif not isinstance(value, str): raise TypeError("filename must be of type str") - elif not value.endswith(".csv"): - raise ValueError("filename must end with .csv") + elif not value.endswith(".csv") and not value.endswith(".txt"): + raise ValueError("filename must end with .csv or .txt") self._filename = value @property diff --git a/test/test_h_transport_problem.py b/test/test_h_transport_problem.py index 13eb718bd..e3fc55073 100644 --- a/test/test_h_transport_problem.py +++ b/test/test_h_transport_problem.py @@ -229,7 +229,7 @@ def test_initialise_exports_find_species_with_one_field(): def test_define_D_global_different_temperatures(): - """Test that the D_global attribute is correctly defined when the temperature + """Test that the D_global object is correctly defined when the temperature is different in the volume subdomains""" D_0, E_D = 1.5, 0.1 my_mat = F.Material(D_0=D_0, E_D=E_D, name="my_mat") @@ -244,12 +244,6 @@ def test_define_D_global_different_temperatures(): ], species=[H], temperature=lambda x: 100.0 * x[0] + 50, - exports=[ - F.SurfaceFlux( - field=H, - surface=surf, - ), - ], ) my_model.define_function_spaces() @@ -269,13 +263,12 @@ def test_define_D_global_different_temperatures(): def test_define_D_global_different_materials(): - """Test that the D_global attribute is correctly defined when the material + """Test that the D_global object is correctly defined when the material is different in the volume subdomains""" D_0_left, E_D_left = 1.0, 0.1 D_0_right, E_D_right = 2.0, 0.2 my_mat_L = F.Material(D_0=D_0_left, E_D=E_D_left, name="my_mat_L") my_mat_R = F.Material(D_0=D_0_right, E_D=E_D_right, name="my_mat_R") - surf = F.SurfaceSubdomain1D(id=1, x=0) H = F.Species("H") my_model = F.HydrogenTransportProblem( @@ -284,14 +277,8 @@ def test_define_D_global_different_materials(): F.VolumeSubdomain1D(id=1, borders=[0, 2], material=my_mat_L), F.VolumeSubdomain1D(id=2, borders=[2, 4], material=my_mat_R), ], - species=[F.Species("H"), F.Species("D")], + species=[H], temperature=500, - exports=[ - F.SurfaceFlux( - field=H, - surface=surf, - ), - ], ) my_model.define_function_spaces() @@ -302,17 +289,19 @@ def test_define_D_global_different_materials(): computed_values = [D_computed.x.array[0], D_computed.x.array[-1]] - D_analytical_left = D_0_left * np.exp(-E_D_left / (F.k_B * my_model.temperature)) - D_analytical_right = D_0_right * np.exp(-E_D_right / (F.k_B * my_model.temperature)) + D_expected_left = D_0_left * np.exp(-E_D_left / (F.k_B * my_model.temperature)) + D_expected_right = D_0_right * np.exp(-E_D_right / (F.k_B * my_model.temperature)) - expected_values = [D_analytical_left, D_analytical_right] + expected_values = [D_expected_left, D_expected_right] assert np.isclose(computed_values, expected_values).all() def test_initialise_exports_multiple_exports_same_species(): - """Test that the D attribute is the same for multiple exports of the same species, - and that D_global is only created once per species""" + """Test that the diffusion coefficient within the D_global object function is the same + for multiple exports of the same species, and that D_global object is only + created once per species""" + D_0, E_D = 1.5, 0.1 my_mat = F.Material(D_0=D_0, E_D=E_D, name="my_mat") surf_1 = F.SurfaceSubdomain1D(id=1, x=0) @@ -346,11 +335,11 @@ def test_initialise_exports_multiple_exports_same_species(): Ds = [export.D for export in my_model.exports] - assert np.isclose(Ds[0].x.array[0], Ds[1].x.array[0]) + assert Ds[0].x.array[0] == Ds[1].x.array[0] def test_define_D_global_multispecies(): - """Test that the D_global attribute is correctly defined when there are multiple + """Test that the D_global object is correctly defined when there are multiple species in one subdomain""" A = F.Species("A") B = F.Species("B") @@ -370,16 +359,6 @@ def test_define_D_global_multispecies(): ], species=[F.Species("A"), F.Species("B")], temperature=500, - exports=[ - F.SurfaceFlux( - field=A, - surface=surf, - ), - F.SurfaceFlux( - field=A, - surface=surf, - ), - ], ) my_model.define_function_spaces() @@ -391,16 +370,16 @@ def test_define_D_global_multispecies(): computed_values = [D_A_computed.x.array[-1], D_B_computed.x.array[-1]] - D_analytical_A = D_0_A * np.exp(-E_D_A / (F.k_B * my_model.temperature)) - D_analytical_B = D_0_B * np.exp(-E_D_B / (F.k_B * my_model.temperature)) + D_expected_A = D_0_A * np.exp(-E_D_A / (F.k_B * my_model.temperature)) + D_expected_B = D_0_B * np.exp(-E_D_B / (F.k_B * my_model.temperature)) - expected_values = [D_analytical_A, D_analytical_B] + expected_values = [D_expected_A, D_expected_B] assert np.isclose(computed_values, expected_values).all() def test_post_processing_update_D_global(): - """Test that the D_global attribute is updated at each time + """Test that the D_global object is updated at each time step when temperture is time dependent""" my_mesh = F.Mesh1D(np.linspace(0, 1, num=11)) my_mat = F.Material(D_0=1.5, E_D=0.1, name="my_mat") diff --git a/test/test_permeation_problem.py b/test/test_permeation_problem.py index eb90c22c0..624ce635e 100644 --- a/test/test_permeation_problem.py +++ b/test/test_permeation_problem.py @@ -62,6 +62,7 @@ def test_permeation_problem(mesh_size=1001): ), ] outgassing_flux = F.SurfaceFlux( + filename="outgassing_flux.txt", field=mobile_H, surface=right_surface, ) diff --git a/test/test_surface_quantity.py b/test/test_surface_quantity.py index 4bcc192b6..dee0ea5f1 100644 --- a/test/test_surface_quantity.py +++ b/test/test_surface_quantity.py @@ -37,7 +37,6 @@ def surface_flux_export_compute(): my_species.solution = c my_export = F.SurfaceFlux( - filename="my_surface_flux.csv", field=my_species, surface=dummy_surface, ) @@ -54,15 +53,20 @@ def surface_flux_export_compute(): assert np.isclose(computed_value, expected_value, rtol=1e-2) -def test_title_generation(tmp_path): - """Test that the title is made to be written to the header""" +@pytest.mark.parametrize("value", ["my_export.csv", "my_export.txt"]) +def test_title_generation(tmp_path, value): + """Test that the title is made to be written to the header in a csv or txt file""" my_export = F.SurfaceFlux( - filename=os.path.join(tmp_path, "my_export.csv"), + filename=os.path.join(tmp_path, f"{value}"), field=F.Species("TEST"), surface=F.SurfaceSubdomain1D(id=35, x=1), ) my_export.initialise_export() - assert my_export.title == "Flux surface 35: TEST" + title = np.genfromtxt(my_export.filename, delimiter=",", max_rows=1, dtype=str) + + expected_title = "Flux surface 35: TEST" + + assert title[1] == expected_title def test_filename_setter_raises_TypeError(): @@ -77,7 +81,7 @@ def test_filename_setter_raises_TypeError(): def test_filename_setter_raises_ValueError(tmp_path): - """Test that a ValueError is raised when the filename does not end with .csv""" + """Test that a ValueError is raised when the filename does not end with .csv or .txt""" with pytest.raises(ValueError): F.SurfaceQuantity( @@ -97,10 +101,11 @@ def test_field_setter_raises_TypeError(): ) -def test_writer(tmp_path): - """Test that the writes values at each timestep""" +@pytest.mark.parametrize("value", ["my_export.csv", "my_export.txt"]) +def test_writer(tmp_path, value): + """Test that the writes values at each timestep to either a csv or txt file""" my_export = F.SurfaceFlux( - filename=os.path.join(tmp_path, "my_export.csv"), + filename=os.path.join(tmp_path, f"{value}"), field=F.Species("test"), surface=F.SurfaceSubdomain1D(id=1, x=0), ) From 4b20b0542b51308ebe1059ecf41544cd9f7fa707 Mon Sep 17 00:00:00 2001 From: J Dark Date: Sun, 5 Nov 2023 16:25:11 -0500 Subject: [PATCH 07/10] test new materials methods --- festim/material.py | 4 +-- test/test_material.py | 69 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) diff --git a/festim/material.py b/festim/material.py index 057760434..401baa01f 100644 --- a/festim/material.py +++ b/festim/material.py @@ -59,7 +59,7 @@ def get_D_0(self, species=None): raise ValueError(f"{species} is not in D_0 keys") else: - raise ValueError("D_0 must be either a float or a dict") + raise TypeError("D_0 must be either a float, int or a dict") def get_E_D(self, species=None): """Returns the activation energy of the diffusion coefficient @@ -85,7 +85,7 @@ def get_E_D(self, species=None): raise ValueError(f"{species} is not in E_D keys") else: - raise ValueError("E_D must be either a float or a dict") + raise TypeError("E_D must be either a float, int or a dict") def get_diffusion_coefficient(self, mesh, temperature, species=None): """Defines the diffusion coefficient diff --git a/test/test_material.py b/test/test_material.py index 265bd9afb..c897a7daa 100644 --- a/test/test_material.py +++ b/test/test_material.py @@ -98,6 +98,7 @@ def test_multispecies_dict_different_keys(): def test_D_0_type_raises_error(): """Test that a value error is raised in the get_diffusion_coefficient function""" + # TODO remove this when material class is updated A = F.Species("A") my_mat = F.Material(D_0=[1, 1], E_D=0.1) @@ -128,3 +129,71 @@ def test_error_raised_when_species_not_not_in_D_0_dict(): with pytest.raises(ValueError, match="J is not in D_0 keys"): my_mat.get_diffusion_coefficient(test_mesh.mesh, 500, species=J) + + +def test_D_0_raises_ValueError_if_species_not_provided_in_dict(): + """Test that a value error is raised in the get_diffusion_coefficient + function""" + # TODO remove this when material class is updated + A = F.Species("A") + B = F.Species("B") + my_mat = F.Material(D_0={A: 1, B: 2}, E_D=1) + + with pytest.raises(ValueError, match="species must be provided if D_0 is a dict"): + my_mat.get_D_0() + + +def test_D_0_raises_ValueError_if_species_given_not_in_dict_keys(): + """Test that a value error is raised in the get_diffusion_coefficient + function""" + # TODO remove this when material class is updated + A = F.Species("A") + B = F.Species("B") + J = F.Species("J") + my_mat = F.Material(D_0={A: 1, B: 2}, E_D=1) + + with pytest.raises(ValueError, match="J is not in D_0 keys"): + my_mat.get_D_0(species=J) + + +def test_raises_TypeError_when_D_0_is_not_correct_type(): + """Test that a TypeError is raised when D_0 is not a float or a dict""" + + my_mat = F.Material(D_0=[1, 2], E_D=1) + + with pytest.raises(TypeError, match="D_0 must be either a float, int or a dict"): + my_mat.get_D_0() + + +def test_E_D_raises_ValueError_if_species_not_provided_in_dict(): + """Test that a value error is raised in the get_diffusion_coefficient + function""" + # TODO remove this when material class is updated + A = F.Species("A") + B = F.Species("B") + my_mat = F.Material(D_0=1, E_D={A: 1, B: 2}) + + with pytest.raises(ValueError, match="species must be provided if E_D is a dict"): + my_mat.get_E_D() + + +def test_E_D_raises_ValueError_if_species_given_not_in_dict_keys(): + """Test that a value error is raised in the get_diffusion_coefficient + function""" + # TODO remove this when material class is updated + A = F.Species("A") + B = F.Species("B") + J = F.Species("J") + my_mat = F.Material(D_0=1, E_D={A: 1, B: 2}) + + with pytest.raises(ValueError, match="J is not in E_D keys"): + my_mat.get_E_D(species=J) + + +def test_raises_TypeError_when_E_D_is_not_correct_type(): + """Test that a TypeError is raised when E_D is not a float or a dict""" + + my_mat = F.Material(D_0=1, E_D=[1, 2]) + + with pytest.raises(TypeError, match="E_D must be either a float, int or a dict"): + my_mat.get_E_D() From ee155e91498c4dec2984105a7a5208f887115514 Mon Sep 17 00:00:00 2001 From: J Dark Date: Sun, 5 Nov 2023 19:04:22 -0500 Subject: [PATCH 08/10] export one first write --- festim/exports/surface_flux.py | 8 -------- festim/exports/surface_quantity.py | 9 +++++++++ festim/hydrogen_transport_problem.py | 2 -- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/festim/exports/surface_flux.py b/festim/exports/surface_flux.py index 96403b8c6..c21db46ba 100644 --- a/festim/exports/surface_flux.py +++ b/festim/exports/surface_flux.py @@ -41,11 +41,3 @@ def compute(self, n, ds): ) ) self.data.append(self.value) - - def initialise_export(self): - title = "Flux surface {}: {}".format(self.surface.id, self.field.name) - - if self.filename is not None: - with open(self.filename, mode="w", newline="") as file: - writer = csv.writer(file) - writer.writerow(["t(s)", f"{title}"]) diff --git a/festim/exports/surface_quantity.py b/festim/exports/surface_quantity.py index c03e2bfc2..3ff9b6a56 100644 --- a/festim/exports/surface_quantity.py +++ b/festim/exports/surface_quantity.py @@ -1,5 +1,6 @@ import festim as F import csv +import os class SurfaceQuantity: @@ -63,6 +64,14 @@ def field(self, value): self._field = value def write(self, t): + if not os.path.isfile(self.filename): + title = "Flux surface {}: {}".format(self.surface.id, self.field.name) + + if self.filename is not None: + with open(self.filename, mode="w", newline="") as file: + writer = csv.writer(file) + writer.writerow(["t(s)", f"{title}"]) + with open(self.filename, mode="a", newline="") as file: writer = csv.writer(file) writer.writerow([t, self.value]) diff --git a/festim/hydrogen_transport_problem.py b/festim/hydrogen_transport_problem.py index 5681a9e7e..7307ab7f1 100644 --- a/festim/hydrogen_transport_problem.py +++ b/festim/hydrogen_transport_problem.py @@ -250,8 +250,6 @@ def initialise_exports(self): export.define_writer(MPI.COMM_WORLD) if isinstance(export, F.XDMFExport): export.writer.write_mesh(self.mesh.mesh) - elif isinstance(export, F.SurfaceQuantity): - export.initialise_export() # compute diffusivity function for surface fluxes From 849de0419cf2a707df5322d6f9891e2c0a5d3de6 Mon Sep 17 00:00:00 2001 From: J Dark Date: Sun, 5 Nov 2023 19:04:33 -0500 Subject: [PATCH 09/10] update test --- test/test_surface_quantity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_surface_quantity.py b/test/test_surface_quantity.py index dee0ea5f1..b0801f862 100644 --- a/test/test_surface_quantity.py +++ b/test/test_surface_quantity.py @@ -61,7 +61,8 @@ def test_title_generation(tmp_path, value): field=F.Species("TEST"), surface=F.SurfaceSubdomain1D(id=35, x=1), ) - my_export.initialise_export() + my_export.value = 2.0 + my_export.write(0) title = np.genfromtxt(my_export.filename, delimiter=",", max_rows=1, dtype=str) expected_title = "Flux surface 35: TEST" @@ -110,7 +111,6 @@ def test_writer(tmp_path, value): surface=F.SurfaceSubdomain1D(id=1, x=0), ) my_export.value = 2.0 - my_export.initialise_export() for i in range(10): my_export.write(i) From 3f818283ad56962755188ce33cc786896de81340 Mon Sep 17 00:00:00 2001 From: J Dark Date: Sun, 5 Nov 2023 19:26:15 -0500 Subject: [PATCH 10/10] updated docs --- festim/exports/surface_flux.py | 1 - festim/exports/surface_quantity.py | 5 +++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/festim/exports/surface_flux.py b/festim/exports/surface_flux.py index c21db46ba..93831d490 100644 --- a/festim/exports/surface_flux.py +++ b/festim/exports/surface_flux.py @@ -1,7 +1,6 @@ from dolfinx import fem import festim as F import ufl -import csv class SurfaceFlux(F.SurfaceQuantity): diff --git a/festim/exports/surface_quantity.py b/festim/exports/surface_quantity.py index 3ff9b6a56..148cb0ba4 100644 --- a/festim/exports/surface_quantity.py +++ b/festim/exports/surface_quantity.py @@ -15,6 +15,8 @@ class SurfaceQuantity: field (festim.Species): species for which the surface flux is computed surface (festim.SurfaceSubdomain1D): surface subdomain filename (str): name of the file to which the surface flux is exported + t (list): list of time values + data (list): list of values of the surface quantity """ def __init__(self, field, surface, filename: str = None) -> None: @@ -64,6 +66,9 @@ def field(self, value): self._field = value def write(self, t): + """If the filename doesnt exist yet, create it and write the header, + then append the time and value to the file""" + if not os.path.isfile(self.filename): title = "Flux surface {}: {}".format(self.surface.id, self.field.name)