Skip to content

Commit bedbb20

Browse files
authored
Merge pull request #990 from jhdark/milestones_from_exports
Export at specific times
2 parents 61bd43d + ade86fa commit bedbb20

File tree

7 files changed

+332
-123
lines changed

7 files changed

+332
-123
lines changed

src/festim/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .exports.total_surface import TotalSurface
3535
from .exports.total_volume import TotalVolume
3636
from .exports.volume_quantity import VolumeQuantity
37-
from .exports.vtx import VTXSpeciesExport, VTXTemperatureExport
37+
from .exports.vtx import VTXSpeciesExport, VTXTemperatureExport, ExportBaseClass
3838
from .exports.xdmf import XDMFExport
3939
from .heat_transfer_problem import HeatTransferProblem
4040
from .helpers import (

src/festim/exports/vtx.py

Lines changed: 106 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,37 @@
11
import warnings
22
from pathlib import Path
3+
from typing import Optional
34

4-
from dolfinx import fem
5+
import numpy as np
6+
from dolfinx import fem, io
57

6-
from festim.species import Species as _Species
7-
from festim.subdomain.volume_subdomain import (
8-
VolumeSubdomain as _VolumeSubdomain,
9-
)
8+
from festim.species import Species
9+
from festim.subdomain.volume_subdomain import VolumeSubdomain
1010

1111

1212
class ExportBaseClass:
13+
"""Export functions to VTX file
14+
15+
Args:
16+
filename: The name of the output file
17+
times: if provided, the field will be exported at these timesteps. Otherwise
18+
exports at all timesteps. Defaults to None.
19+
20+
Attributes:
21+
filename: The name of the output file
22+
times: if provided, the field will be exported at these timesteps. Otherwise
23+
exports at all timesteps. Defaults to None.
24+
"""
25+
1326
_filename: Path | str
27+
writer: io.VTXWriter
1428

15-
def __init__(self, filename: str | Path, ext: str) -> None:
29+
def __init__(
30+
self,
31+
filename: str | Path,
32+
ext: str,
33+
times: Optional[list[float] | list[int] | None] = None,
34+
):
1635
name = Path(filename)
1736
if name.suffix != ext:
1837
warnings.warn(
@@ -21,86 +40,141 @@ def __init__(self, filename: str | Path, ext: str) -> None:
2140
name = name.with_suffix(ext)
2241

2342
self._filename = name
43+
if times:
44+
self.times = sorted(times)
45+
else:
46+
self.times = times
2447

2548
@property
2649
def filename(self):
2750
return self._filename
2851

52+
def is_it_time_to_export(self, current_time: float) -> bool:
53+
"""
54+
Checks if the exported field should be written to a file or not based on the
55+
current time and the times in `export.times`
56+
57+
Args:
58+
current_time: the current simulation time
59+
60+
Returns:
61+
bool: True if the exported field should be written to a file, else False
62+
"""
63+
64+
if self.times is None:
65+
return True
66+
67+
for time in self.times:
68+
if np.isclose(time, current_time, atol=0):
69+
return True
70+
71+
return False
72+
2973

3074
class VTXTemperatureExport(ExportBaseClass):
31-
def __init__(self, filename: str | Path):
32-
super().__init__(filename, ".bp")
75+
"""Export temperature field functions to VTX file
76+
77+
Args:
78+
filename: The name of the output file
79+
times: if provided, the field will be exported at these timesteps. Otherwise
80+
exports at all timesteps. Defaults to None.
81+
82+
Attributes:
83+
filename: The name of the output file
84+
times: if provided, the field will be exported at these timesteps. Otherwise
85+
exports at all timesteps. Defaults to None.
86+
writer: The VTXWriter object used to write the file
87+
"""
88+
89+
writer: io.VTXWriter
90+
91+
def __init__(
92+
self,
93+
filename: str | Path,
94+
times: Optional[list[float] | list[int] | None] = None,
95+
):
96+
super().__init__(filename, ".bp", times)
3397

3498

3599
class VTXSpeciesExport(ExportBaseClass):
36-
"""Export functions to VTX file
100+
"""Export species field functions to VTX file
37101
38102
Args:
39103
filename: The name of the output file
40104
field: Set of species to export
41-
subdomain: A field can be defined on multiple domains.
42-
This arguments specifies what subdomains we export on.
43-
If `None` we export on all domains.
44-
checkpoint: If True, the export will be a checkpoint file
45-
using adios4dolfinx and won't be readable by ParaView.
46-
Default is False.
105+
subdomain: A field can be defined on multiple domains. This arguments specifies
106+
what subdomains we export on. If `None` we export on all domains.
107+
checkpoint: If True, the export will be a checkpoint file using adios4dolfinx
108+
and won't be readable by ParaView. Default is False.
109+
times: if provided, the field will be exported at these timesteps. Otherwise
110+
exports at all timesteps. Defaults to None.
111+
112+
Attributes:
113+
filename: The name of the output file
114+
field: Set of species to export
115+
times: if provided, the field will be exported at these timesteps. Otherwise
116+
exports at all timesteps. Defaults to None.
117+
writer: The VTXWriter object used to write the file
47118
"""
48119

49-
field: list[_Species]
50-
_subdomain: _VolumeSubdomain
120+
_subdomain: VolumeSubdomain
51121
_checkpoint: bool
122+
writer: io.VTXWriter
52123

53124
def __init__(
54125
self,
55126
filename: str | Path,
56-
field: _Species | list[_Species],
57-
subdomain: _VolumeSubdomain = None,
127+
field: Species | list[Species],
128+
subdomain: VolumeSubdomain = None,
58129
checkpoint: bool = False,
59-
) -> None:
60-
super().__init__(filename, ".bp")
130+
times: Optional[list[float] | list[int] | None] = None,
131+
):
132+
super().__init__(filename, ".bp", times)
61133
self.field = field
62134
self._subdomain = subdomain
63135
self._checkpoint = checkpoint
64136

65137
@property
66-
def field(self) -> list[_Species]:
138+
def field(self) -> list[Species]:
67139
return self._field
68140

69141
@field.setter
70-
def field(self, value: _Species | list[_Species]):
142+
def field(self, value: Species | list[Species]):
71143
"""
72144
Update the field to export.
73145
74-
Note:
75-
This also creates a new writer with the updated field.
76-
77146
Args:
78147
value: The species to export
79148
80149
Raises:
81150
TypeError: If input field is not a Species or a list of Species
151+
152+
Note:
153+
This also creates a new writer with the updated field.
82154
"""
83155
# check that all elements of list are festim.Species
84156
if isinstance(value, list):
85157
for element in value:
86-
if not isinstance(element, (_Species, str)):
158+
if not isinstance(element, Species | str):
87159
raise TypeError(
88-
"field must be of type festim.Species or a list of festim.Species or str"
160+
"field must be of type festim.Species or a list of "
161+
"festim.Species or str"
89162
)
90163
val = value
91-
elif isinstance(value, _Species):
164+
elif isinstance(value, Species):
92165
val = [value]
93166
else:
94167
raise TypeError(
95-
"field must be of type festim.Species or a list of festim.Species or str",
168+
"field must be of type festim.Species or a list of festim.Species or "
169+
"str",
96170
f"got {type(value)}.",
97171
)
98172
self._field = val
99173

100174
def get_functions(self) -> list[fem.Function]:
101175
"""
102-
Returns list of species for a given subdomain.
103-
If using legacy mode, return the whole species.
176+
Returns list of species for a given subdomain. If using legacy mode, return the
177+
whole species.
104178
"""
105179

106180
legacy_output: bool = False

src/festim/heat_transfer_problem.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ def __init__(
2828
)
2929

3030
self.initial_condition = initial_condition
31-
self._vtxfile: VTXWriter | None = None
3231

3332
@property
3433
def sources(self):
@@ -49,15 +48,13 @@ def boundary_conditions(self, value):
4948
if not all(
5049
isinstance(
5150
bc,
52-
(
53-
boundary_conditions.FixedTemperatureBC,
54-
boundary_conditions.HeatFluxBC,
55-
),
51+
boundary_conditions.FixedTemperatureBC | boundary_conditions.HeatFluxBC,
5652
)
5753
for bc in value
5854
):
5955
raise TypeError(
60-
"boundary_conditions must be a list of festim.FixedTemperatureBC or festim.HeatFluxBC objects"
56+
"boundary_conditions must be a list of festim.FixedTemperatureBC "
57+
"or festim.HeatFluxBC objects"
6158
)
6259
self._boundary_conditions = value
6360

@@ -228,7 +225,7 @@ def initialise_exports(self):
228225
"XDMF export is not implemented yet for heat transfer problems"
229226
)
230227
if isinstance(export, exports.VTXTemperatureExport):
231-
self._vtxfile = VTXWriter(
228+
export.writer = VTXWriter(
232229
self.u.function_space.mesh.comm,
233230
export.filename,
234231
[self.u],
@@ -242,7 +239,8 @@ def post_processing(self):
242239
# TODO if export type derived quantity
243240
if isinstance(export, exports.SurfaceQuantity):
244241
raise NotImplementedError(
245-
"SurfaceQuantity export is not implemented yet for heat transfer problems"
242+
"SurfaceQuantity export is not implemented yet "
243+
"for heat transfer problems"
246244
)
247245
export.compute(
248246
self.mesh.n,
@@ -257,9 +255,11 @@ def post_processing(self):
257255
if isinstance(export, exports.XDMFExport):
258256
export.write(float(self.t))
259257

260-
if self._vtxfile is not None:
261-
self._vtxfile.write(float(self.t))
258+
if isinstance(export, exports.VTXTemperatureExport):
259+
if export.is_it_time_to_export(float(self.t)):
260+
export.writer.write(float(self.t))
262261

263262
def __del__(self):
264-
if self._vtxfile is not None:
265-
self._vtxfile.close()
263+
for export in self.exports:
264+
if isinstance(export, exports.VTXTemperatureExport):
265+
export.writer.close()

0 commit comments

Comments
 (0)