3from .Visualiser
import Visualiser
10from concurrent.futures
import ProcessPoolExecutor
17 Patch filter (@see PatchFileParser.patch_filter) which accepts only
18 patches whose closed bounding box contains a given point
20 It is a class rather than a lambda, as it has to be picklable when
21 we farm snapshots out to worker processes.
32 <= offset[d] + size[d] + self.
tolerance * size[d]
33 for d
in range(len(offset))
39 Probe the data within a Peano patch file at one point over time
41 This is not a visualiser but a postprocessing tool, but it reuses the
42 whole meta file and snapshot logic of the Visualiser. Per snapshot,
43 we parse only those patches which contain the probe point (@see
44 PatchContainsPoint), pick the finest one of them, and take the value
45 of the cell (for cell data) or of the nearest dof (for vertex data)
46 which contains the point. There is no interpolation.
48 The result is a csv file with one row per snapshot. The first column
49 is the snapshot's time stamp, followed by all unknowns of all data
50 sets found in the file. If the point is not covered by any patch in a
51 snapshot, we write NaNs.
53 If the point sits exactly on the face between two patches of the same
54 size, we take the patch to the right/top/front, i.e. we use half-open
55 intervals [offset,offset+size). Only if no such patch exists (domain
56 boundary), we fall back to the patch to the left.
61 def __init__(self, file_name, point, output_file, start, end, verbose=False):
62 super(PointProbe, self).
__init__(file_name, start, end, verbose)
63 self.
_point = tuple(float(x)
for x
in point)
68 Return list of unknowns at point or None if point is not covered
70 dimensions = patch_file_data._dimensions
71 point = point[:dimensions]
72 dof = patch_file_data._dof
73 unknowns = patch_file_data._unknowns
77 for patch
in patch_file_data._cell_data
79 patch.offset[d] - self.
Tolerance * patch.size[d]
81 <= patch.offset[d] + patch.size[d] * (1.0 + self.
Tolerance)
82 for d
in range(dimensions)
88 finest_size = min(patch.size[0]
for patch
in candidates)
91 for patch
in candidates
92 if math.isclose(patch.size[0], finest_size, rel_tol=1e-6)
96 def distance_to_upper_faces(patch):
99 for d
in range(dimensions)
100 if point[d] >= patch.offset[d] + patch.size[d] * (1.0 - self.
Tolerance)
103 patch = min(candidates, key=distance_to_upper_faces)
105 relative_position = [
106 min(max((point[d] - patch.offset[d]) / patch.size[d], 0.0), 1.0)
107 for d
in range(dimensions)
110 if patch_file_data._is_data_associated_to_cell:
113 for d
in reversed(range(dimensions)):
114 index_along_axis = min(int(relative_position[d] * dof), dof - 1)
115 index = index * dof + index_along_axis
117 dof_positions = np.asarray(patch_file_data._mapping, dtype=float).reshape(
122 np.sum((dof_positions - np.asarray(relative_position)) ** 2, axis=1)
127 float(x)
for x
in patch.values[index * unknowns : (index + 1) * unknowns]
132 Cell data with a user-defined (distorted) mapping is not supported
134 dimensions = patch_file_data._dimensions
135 vertices_per_axis = patch_file_data._dof + 1
136 axis = np.linspace(0.0, 1.0, vertices_per_axis)
138 default_mapping = np.asarray(
141 for vertex
in itertools.product(axis, repeat=dimensions)
142 for coordinate
in reversed(vertex)
145 mapping = np.asarray(patch_file_data._mapping, dtype=float)
146 if mapping.size != default_mapping.size
or not np.allclose(
147 mapping, default_mapping
150 "Probing cell data with a non-default mapping is not supported yet ("
151 + patch_file_data._unknown_name
157 Return (timestamp, dict from unknown name onto (number of unknowns,
158 description, values or None))
161 print(f
"Probe snapshot {i} (t={snapshot.timestamp})")
164 for unknown_name, patch_file_data
in data.items():
165 result[unknown_name] = (
166 patch_file_data._unknowns,
167 patch_file_data._description,
170 return snapshot.timestamp, result
183 with ProcessPoolExecutor(
184 max_workers=max_workers,
185 mp_context=multiprocessing.get_context(
"spawn"),
191 results = [future.result()
for future
in futures]
193 self.
_write_csv(sorted(results, key=
lambda result: result[0]))
198 One column name per unknown of each data set
200 If a data set names its unknowns (@see unknown_names_from_description()),
201 we use these names, e.g. h, hu, hv, b. If two data sets use the same
202 name, we prefix the names with the data set, e.g. SWEQ_h. Data sets
203 without names yield SWEQ_0, SWEQ_1, ...
207 unknown_name: unknown_names_from_description(
208 descriptions.get(unknown_name,
""), unknowns
210 for unknown_name, unknowns
in columns.items()
212 all_names = [name
for n
in names.values()
if n
is not None for name
in n]
213 unique = len(all_names) == len(set(all_names))
215 for unknown_name, unknowns
in columns.items():
216 if names[unknown_name]
is None:
217 header += [f
"{unknown_name}_{i}" for i
in range(unknowns)]
219 header += names[unknown_name]
221 header += [f
"{unknown_name}_{name}" for name
in names[unknown_name]]
227 for _, result
in results:
228 for unknown_name, (unknowns, description, _)
in result.items():
229 columns[unknown_name] = max(unknowns, columns.get(unknown_name, 0))
230 descriptions.setdefault(unknown_name, description)
232 uncovered_snapshots = 0
233 with open(self.
_output_file,
"w", newline=
"")
as csv_file:
234 writer = csv.writer(csv_file, delimiter=
",")
235 writer.writerow([
"time"] + self.
_column_names(columns, descriptions))
237 for timestamp, result
in results:
239 for unknown_name, unknowns
in columns.items():
240 values = result.get(unknown_name, (unknowns,
"",
None))[2]
242 values = [float(
"nan")] * unknowns
243 uncovered_snapshots += 1
247 if uncovered_snapshots > 0:
249 f
"Warning: point {self._point} not covered by data in {uncovered_snapshots} (snapshot,data set) pair(s), wrote NaN"
251 print(f
"Wrote {len(results)} snapshots into {self._output_file}")
254 super(PointProbe, self).
display()
255 self.
_run(max_workers)
__init__(self, point, tolerance)
__call__(self, offset, size)
Probe the data within a Peano patch file at one point over time.
_probe_patch_data(self, patch_file_data, point)
Return list of unknowns at point or None if point is not covered.
__init__(self, file_name, point, output_file, start, end, verbose=False)
Abstract superclass of a visualiser.
display(self, max_workers=1)
This is the one routine the visualisers typically do overwrite.
process_snapshot(self, snapshot, i)
Return (timestamp, dict from unknown name onto (number of unknowns, description, values or None))
display_single_file(self)
If we are here we've supplied a single file which is itself a snapshot.
_assert_default_cell_mapping(self, patch_file_data)
Cell data with a user-defined (distorted) mapping is not supported.
_column_names(self, columns, descriptions)
One column name per unknown of each data set.
_write_csv(self, results)
The visualiser is first and foremost a persistency layer around datasets.
parse_snapshot(self, snapshot)
We rewrite "reload" from previous version of this file.