Skip to content

Commit d30f07c

Browse files
support for mixed domain
1 parent 78e491a commit d30f07c

3 files changed

Lines changed: 127 additions & 10 deletions

File tree

src/festim/exports/vtx.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,14 +216,28 @@ def set_dolfinx_expression(
216216
x = ufl.SpatialCoordinate(time._ufl_domain)
217217
kwargs["x"] = x
218218
if "T" in arguments:
219-
kwargs["T"] = temperature
219+
if (
220+
isinstance(temperature, fem.Function)
221+
and self.subdomain.sub_T is not None
222+
):
223+
kwargs["T"] = (
224+
self.subdomain.sub_T
225+
) # NOTE I'm not sure that sub_T is updated at every time step
226+
else:
227+
kwargs["T"] = temperature
220228
# check if there are other arguments and if they are in species_dependent_value
221229
for arg in arguments:
222230
if arg in self.species_dependent_value:
223231
spe = self.species_dependent_value[arg]
224-
kwargs[arg] = spe.post_processing_solution
225-
# TODO handle discontinuous case and use self.subdomain
226-
232+
if spe.subdomain_to_post_processing_solution:
233+
kwargs[arg] = spe.subdomain_to_post_processing_solution[
234+
self.subdomain
235+
]
236+
else:
237+
kwargs[arg] = spe.post_processing_solution
238+
assert kwargs[arg] is not None, (
239+
f"Argument {arg} not found in species_dependent_value"
240+
)
227241
self.dolfinx_expression = fem.Expression(
228242
self.expression(**kwargs),
229243
get_interpolation_points(self.function.function_space.element),

src/festim/hydrogen_transport_problem.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,7 @@ def __init__(
11171117
self.interfaces = interfaces or []
11181118
self.surface_to_volume = surface_to_volume or {}
11191119
self.subdomain_to_species = {} # maps subdomain to species defined in it
1120+
self.subdomain_to_V_CG1 = {}
11201121

11211122
@property
11221123
def method_interface(self):
@@ -1357,6 +1358,10 @@ def define_function_spaces(
13571358
u = dolfinx.fem.Function(V)
13581359
u_n = dolfinx.fem.Function(V)
13591360

1361+
self.subdomain_to_V_CG1[subdomain] = dolfinx.fem.functionspace(
1362+
subdomain.submesh, ("CG", 1)
1363+
)
1364+
13601365
# store attributes in the subdomain object
13611366
subdomain.u = u
13621367
subdomain.u_n = u_n
@@ -1684,7 +1689,22 @@ def initialise_exports(self):
16841689
export.filename,
16851690
mesh=functions[0].function_space.mesh,
16861691
)
1692+
elif isinstance(export, exports.CustomField):
1693+
# need to find an appropriate function space on the right submesh
1694+
V = self.subdomain_to_V_CG1[export.subdomain]
1695+
export.function = fem.Function(V)
1696+
export.set_dolfinx_expression(
1697+
temperature=self.temperature_fenics, # need to pass the right temperature
1698+
species=self.species,
1699+
time=self.t,
1700+
)
16871701

1702+
export.writer = dolfinx.io.VTXWriter(
1703+
comm=export.function.function_space.mesh.comm,
1704+
filename=export.filename,
1705+
output=export.function,
1706+
engine="BP5",
1707+
)
16881708
# compute diffusivity function for surface fluxes
16891709
# for the discontinuous case, we don't use D_global as in
16901710
# HydrogenTransportProblem
@@ -1726,11 +1746,11 @@ def post_processing(self):
17261746
continue
17271747
# handle VTX exports
17281748
if isinstance(export, exports.ExportBaseClass):
1729-
if not isinstance(export, exports.VTXSpeciesExport):
1730-
raise NotImplementedError(
1731-
f"Export type {type(export)} not implemented"
1732-
)
1733-
if isinstance(export, exports.VTXSpeciesExport):
1749+
if isinstance(export, exports.CustomField):
1750+
# update internal function
1751+
export.function.interpolate(export.dolfinx_expression)
1752+
export.writer.write(float(self.t))
1753+
elif isinstance(export, exports.VTXSpeciesExport):
17341754
if export._checkpoint:
17351755
for species in export.field:
17361756
post_processing_solution = (
@@ -1746,7 +1766,10 @@ def post_processing(self):
17461766
)
17471767
else:
17481768
export.writer.write(float(self.t))
1749-
1769+
else:
1770+
raise NotImplementedError(
1771+
f"Export type {type(export)} not implemented"
1772+
)
17501773
# handle derived quantities
17511774
if isinstance(export, exports.SurfaceQuantity):
17521775
if isinstance(

test/test_vtx.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,83 @@ def test_custom_field(tmp_path, expression):
260260
my_model.initialise()
261261

262262
my_model.run()
263+
264+
265+
@pytest.mark.parametrize(
266+
"expression",
267+
[
268+
lambda x: x[0] + x[1] * 2,
269+
lambda T: T + 1,
270+
lambda c_A, c_B: c_A + c_B,
271+
lambda c_A, T: c_A * T,
272+
# FIXME: the following don't work (easily) because of https://github.com/FEniCS/dolfinx/issues/3207
273+
# lambda c_A, c_B, x: c_A * c_B + x[0],
274+
# lambda c_A, T, x: c_A * T + x[0],
275+
# lambda T, x: T + x[0],
276+
],
277+
)
278+
def test_custom_field_discontinuous(tmp_path, expression):
279+
"""
280+
Test custom field export functionality.
281+
This test checks that a custom field can be created with various types of
282+
expressions.
283+
"""
284+
285+
my_model = F.HydrogenTransportProblemDiscontinuous()
286+
287+
mat = F.Material(D_0=1, E_D=0, K_S_0=1, E_K_S=0)
288+
289+
vol = F.VolumeSubdomain(id=1, material=mat)
290+
291+
top = F.SurfaceSubdomain(id=1, locator=lambda x: np.isclose(x[1], 1))
292+
bottom = F.SurfaceSubdomain(id=2, locator=lambda x: np.isclose(x[1], 0))
293+
left = F.SurfaceSubdomain(id=3, locator=lambda x: np.isclose(x[0], 0))
294+
right = F.SurfaceSubdomain(id=4, locator=lambda x: np.isclose(x[0], 1))
295+
296+
my_model.subdomains = [vol, top, bottom, left, right]
297+
298+
my_model.surface_to_volume = {top: vol, bottom: vol, left: vol, right: vol}
299+
300+
dolfinx_mesh = dolfinx.mesh.create_unit_square(MPI.COMM_WORLD, 10, 10)
301+
my_model.mesh = F.Mesh(dolfinx_mesh)
302+
303+
A = F.Species("A", subdomains=my_model.volume_subdomains)
304+
B = F.Species("B", subdomains=my_model.volume_subdomains)
305+
C = F.Species("C", subdomains=my_model.volume_subdomains)
306+
D = F.Species("D", subdomains=my_model.volume_subdomains)
307+
308+
my_model.species = [A, B, C, D]
309+
310+
my_model.boundary_conditions = (
311+
[
312+
F.FixedConcentrationBC(species=A, subdomain=top, value=1),
313+
F.FixedConcentrationBC(species=B, subdomain=left, value=1),
314+
]
315+
+ [
316+
F.FixedConcentrationBC(species=C, subdomain=surf, value=0)
317+
for surf in [top, bottom, left, right]
318+
]
319+
+ [
320+
F.FixedConcentrationBC(species=D, subdomain=surf, value=0)
321+
for surf in [top, bottom, left, right]
322+
]
323+
)
324+
325+
my_model.temperature = lambda x: 300 + 100 * x[0]
326+
327+
my_model.settings = F.Settings(transient=False, atol=1e-9, rtol=1e-9)
328+
329+
custom_field = F.CustomField(
330+
filename=tmp_path / "custom_field.bp",
331+
expression=expression,
332+
species_dependent_value={"c_A": A, "c_B": B},
333+
subdomain=vol,
334+
)
335+
336+
my_model.exports = [
337+
custom_field,
338+
]
339+
340+
my_model.initialise()
341+
342+
my_model.run()

0 commit comments

Comments
 (0)