Peano
Loading...
Searching...
No Matches
test_point_probe.py
Go to the documentation of this file.
1import csv
2import os
3import math
4
5import pytest
6
7from tarch.plotter.api.input.PatchFileParser import PatchFileParser
8from tarch.plotter.api.output.PointProbe import PointProbe, PatchContainsPoint
9
10
11# ---------------------------------------------------------------------------
12# Helpers writing small synthetic patch files
13#
14# Each value encodes where it comes from: 100*patch_id + 10*dof_index + unknown,
15# so a probed value tells us which patch and which dof have been picked.
16# ---------------------------------------------------------------------------
17
18
19def patch_file(
20 dimensions,
21 metadata_kind,
22 name,
23 dof,
24 unknowns,
25 patches,
26 mapping=None,
27 description="test data",
28):
29 """
30 patches: list of (patch_id, offset, size)
31 metadata_kind: "cell" or "vertex"
32 """
33 lines = [
34 "# ",
35 "# Peano patch file ",
36 "# Version 0.2 ",
37 "# ",
38 "format ASCII",
39 f"dimensions {dimensions}",
40 "",
41 f'begin {metadata_kind}-metadata "{name}"',
42 f" number-of-unknowns {unknowns}",
43 f" number-of-dofs-per-axis {dof}",
44 f" description {description}",
45 ]
46 if mapping is not None:
47 lines.append(" mapping " + " ".join(str(x) for x in mapping))
48 lines.append(f"end {metadata_kind}-metadata")
49 lines.append("")
50 for patch_id, offset, size in patches:
51 lines += [
52 "begin patch",
53 " offset " + " ".join(str(x) for x in offset),
54 " size " + " ".join(str(x) for x in size),
55 f' begin {metadata_kind}-values "{name}"',
56 " "
57 + " ".join(
58 str(100 * patch_id + 10 * k + u)
59 for k in range(dof**dimensions)
60 for u in range(unknowns)
61 ),
62 f" end {metadata_kind}-values",
63 "end patch",
64 "",
65 ]
66 return "\n".join(lines) + "\n"
67
68
69def meta_file(snapshots):
70 """
71 snapshots: list of (timestamp, [file names])
72 """
73 lines = ["# ", "# Peano patch file ", "# Version 0.2 ", "# ", "format ASCII", ""]
74 for timestamp, file_names in snapshots:
75 lines += ["begin dataset", f" timestamp {timestamp:e}", ""]
76 lines += [f' include "{file_name}"' for file_name in file_names]
77 lines += ["end dataset", ""]
78 return "\n".join(lines) + "\n"
79
80
81def read_csv(file_name):
82 with open(file_name) as f:
83 rows = list(csv.reader(f))
84 return rows[0], [[float(x) for x in row] for row in rows[1:]]
85
86
87def probe_single_file(tmp_path, monkeypatch, content, point):
88 monkeypatch.chdir(tmp_path)
89 (tmp_path / "data.peano-patch-file").write_text(content)
90 probe = PointProbe("data.peano-patch-file", point, "probe.csv", 0, 0)
91 probe.display_single_file()
92 return read_csv("probe.csv")
93
94
95# ---------------------------------------------------------------------------
96# Tests
97# ---------------------------------------------------------------------------
98
99TWO_PATCHES_2D = [(1, (0.0, 0.0), (0.5, 0.5)), (2, (0.5, 0.0), (0.5, 0.5))]
100
101
102def test_cell_data_2d_picks_right_cell(tmp_path, monkeypatch):
103 content = patch_file(2, "cell", "Q", 3, 2, TWO_PATCHES_2D)
104 # Point in patch 2, cell (x=1,y=2) -> dof index 2*3+1=7
105 header, rows = probe_single_file(tmp_path, monkeypatch, content, (0.75, 0.45))
106 assert header == ["time", "Q_0", "Q_1"]
107 assert rows == [[0.0, 270.0, 271.0]]
108
109
110def test_point_on_face_takes_upper_patch(tmp_path, monkeypatch):
111 content = patch_file(2, "cell", "Q", 3, 1, TWO_PATCHES_2D)
112 # x=0.5 is the face between patch 1 and 2 -> patch 2, cell x=0
113 _, rows = probe_single_file(tmp_path, monkeypatch, content, (0.5, 0.1))
114 assert rows == [[0.0, 200.0]]
115
116
118 content = patch_file(2, "cell", "Q", 3, 1, TWO_PATCHES_2D)
119 # x=1.0, y=0.5 is the upper right corner of patch 2 -> last cell
120 _, rows = probe_single_file(tmp_path, monkeypatch, content, (1.0, 0.5))
121 assert rows == [[0.0, 280.0]]
122
123
124def test_adaptive_mesh_takes_finest_patch(tmp_path, monkeypatch):
125 patches = [(1, (0.0, 0.0), (1.0, 1.0)), (2, (0.0, 0.0), (0.5, 0.5))]
126 content = patch_file(2, "cell", "Q", 2, 1, patches)
127 _, rows = probe_single_file(tmp_path, monkeypatch, content, (0.3, 0.1))
128 assert rows == [[0.0, 210.0]]
129
130
131def test_cell_data_3d(tmp_path, monkeypatch):
132 patches = [(1, (0.0, 0.0, 0.0), (1.0, 1.0, 1.0))]
133 content = patch_file(3, "cell", "Q", 2, 1, patches)
134 # cell (x=1,y=0,z=1) -> 1*4+0*2+1 = 5
135 _, rows = probe_single_file(tmp_path, monkeypatch, content, (0.9, 0.1, 0.6))
136 assert rows == [[0.0, 150.0]]
137
138
139def test_2d_data_ignores_z(tmp_path, monkeypatch):
140 content = patch_file(2, "cell", "Q", 3, 1, TWO_PATCHES_2D)
141 _, rows = probe_single_file(tmp_path, monkeypatch, content, (0.75, 0.45, 42.0))
142 assert rows == [[0.0, 270.0]]
143
144
146 # Non-uniform 3x3 dof layout along each axis: 0, 0.1, 1
147 axis = [0.0, 0.1, 1.0]
148 mapping = [c for y in axis for x in axis for c in (x, y)]
149 patches = [(1, (0.0, 0.0), (1.0, 1.0))]
150 content = patch_file(2, "vertex", "u", 3, 1, patches, mapping)
151 # x=0.4 is closer to 0.1 than to 1.0, y=0.6 closer to 1.0 -> dof (1,2) = 7
152 _, rows = probe_single_file(tmp_path, monkeypatch, content, (0.4, 0.6))
153 assert rows == [[0.0, 170.0]]
154
155
156def test_point_outside_domain_yields_nan(tmp_path, monkeypatch):
157 content = patch_file(2, "cell", "Q", 3, 1, TWO_PATCHES_2D)
158 _, rows = probe_single_file(tmp_path, monkeypatch, content, (2.0, 0.1))
159 assert rows[0][0] == 0.0 and math.isnan(rows[0][1])
160
161
163 mapping = [0.0, 0.0, 0.7, 0.0, 0.0, 1.0, 1.0, 1.0]
164 patches = [(1, (0.0, 0.0), (1.0, 1.0))]
165 content = patch_file(2, "cell", "Q", 1, 1, patches, mapping)
166 with pytest.raises(Exception, match="non-default mapping"):
167 probe_single_file(tmp_path, monkeypatch, content, (0.5, 0.5))
168
169
171 monkeypatch.chdir(tmp_path)
172 # Each snapshot is split over two partial files (two ranks)
173 for snapshot in range(2):
174 for rank, (patch_id, offset, size) in enumerate(TWO_PATCHES_2D):
175 (tmp_path / f"s{snapshot}-r{rank}.peano-patch-file").write_text(
176 patch_file(
177 2, "cell", "Q", 3, 1, [(patch_id + 10 * snapshot, offset, size)]
178 )
179 )
180 # Snapshots deliberately written in reverse order to check sorting
181 (tmp_path / "data.peano-patch-file").write_text(
182 meta_file(
183 [
184 (0.5, ["s1-r0.peano-patch-file", "s1-r1.peano-patch-file"]),
185 (0.0, ["s0-r0.peano-patch-file", "s0-r1.peano-patch-file"]),
186 ]
187 )
188 )
189 probe = PointProbe("data.peano-patch-file", (0.75, 0.45), "probe.csv", 0, 10)
190 probe.display(1)
191 header, rows = read_csv("probe.csv")
192 assert header == ["time", "Q_0"]
193 assert rows == [[0.0, 270.0], [0.5, 1270.0]]
194
195
196def test_meta_file_in_other_directory(tmp_path, monkeypatch):
197 # The meta file includes its snapshots relative to its own directory, so
198 # the probe has to work from any working directory.
199 monkeypatch.chdir(tmp_path)
200 output = tmp_path / "output"
201 output.mkdir()
202 for snapshot in range(2):
203 (output / f"s{snapshot}.peano-patch-file").write_text(
204 patch_file(2, "cell", "Q", 3, 1, [(10 * snapshot, (0.0, 0.0), (1.0, 1.0))])
205 )
206 (output / "data.peano-patch-file").write_text(
207 meta_file([(0.0, ["./s0.peano-patch-file"]), (0.5, ["s1.peano-patch-file"])])
208 )
209 probe = PointProbe(
210 os.path.join("output", "data.peano-patch-file"), (0.5, 0.5), "probe.csv", 0, 10
211 )
212 probe.display(1)
213 _, rows = read_csv("probe.csv")
214 assert [row[0] for row in rows] == [0.0, 0.5]
215 assert rows[0][1] != rows[1][1]
216
217
219 content = patch_file(2, "cell", "Q", 3, 2, TWO_PATCHES_2D, description="h, hu")
220 header, rows = probe_single_file(tmp_path, monkeypatch, content, (0.75, 0.45))
221 assert header == ["time", "h", "hu"]
222 assert rows == [[0.0, 270.0, 271.0]]
223
224
226 monkeypatch.chdir(tmp_path)
227 for name, (patch_id, offset, size) in zip(["A", "B"], TWO_PATCHES_2D):
228 (tmp_path / f"{name}.peano-patch-file").write_text(
229 patch_file(
230 2, "cell", name, 3, 1, [(1, (0.0, 0.0), (1.0, 1.0))], description="h"
231 )
232 )
233 (tmp_path / "data.peano-patch-file").write_text(
234 meta_file([(0.0, ["A.peano-patch-file", "B.peano-patch-file"])])
235 )
236 probe = PointProbe("data.peano-patch-file", (0.5, 0.5), "probe.csv", 0, 10)
237 probe.display(1)
238 header, _ = read_csv("probe.csv")
239 assert header == ["time", "A_h", "B_h"]
240
241
243 content = patch_file(
244 2, "cell", "Q", 3, 1, TWO_PATCHES_2D + [(3, (0.0, 0.5), (0.5, 0.5))]
245 )
246 (tmp_path / "data.peano-patch-file").write_text(content)
247 parser = PatchFileParser(
248 str(tmp_path / "data.peano-patch-file"),
249 "",
250 0,
251 patch_filter=PatchContainsPoint((0.75, 0.25), 1e-8),
252 )
253 parser.parse_file()
254 assert parser.parsed
255 patches = parser.unknown_attributes["Q"].cell_data
256 assert [patch.offset for patch in patches] == [(0.5, 0.0)]
Probe the data within a Peano patch file at one point over time.
Definition PointProbe.py:37
test_cell_data_2d_picks_right_cell(tmp_path, monkeypatch)
test_named_unknowns_become_column_names(tmp_path, monkeypatch)
test_adaptive_mesh_takes_finest_patch(tmp_path, monkeypatch)
test_meta_file_in_other_directory(tmp_path, monkeypatch)
probe_single_file(tmp_path, monkeypatch, content, point)
test_meta_file_with_partial_files_and_multiple_snapshots(tmp_path, monkeypatch)
meta_file(snapshots)
snapshots: list of (timestamp, [file names])
test_point_outside_domain_yields_nan(tmp_path, monkeypatch)
test_vertex_data_nearest_dof_with_mapping(tmp_path, monkeypatch)
test_cell_data_3d(tmp_path, monkeypatch)
test_same_names_in_two_data_sets_are_prefixed(tmp_path, monkeypatch)
test_point_on_domain_boundary_takes_lower_patch(tmp_path, monkeypatch)
test_point_on_face_takes_upper_patch(tmp_path, monkeypatch)
test_cell_data_with_non_default_mapping_is_rejected(tmp_path, monkeypatch)
test_2d_data_ignores_z(tmp_path, monkeypatch)
test_patch_filter_skips_patches(tmp_path)