Coverage for lasso/dyna/test_d3plot.py: 96%
198 statements
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-28 18:42 +0100
« prev ^ index » next coverage.py v7.2.4, created at 2023-04-28 18:42 +0100
1import os
2import tempfile
3from lasso.io.binary_buffer import BinaryBuffer
4from lasso.femzip.femzip_api import FemzipAPI
5from unittest import TestCase
7import struct
8import numpy as np
10from lasso.dyna.d3plot import D3plot, FilterType, _negative_to_positive_state_indexes
11from lasso.dyna.array_type import ArrayType
14class D3plotTest(TestCase):
15 def test_init(self):
17 # settings
18 self.maxDiff = None
20 filepath = "test/simple_d3plot/d3plot"
22 geometry_array_shapes = {
23 "node_coordinates": (4915, 3),
24 "element_solid_part_indexes": (0,),
25 "element_solid_node_indexes": (0, 8),
26 "element_tshell_node_indexes": (0, 8),
27 "element_tshell_part_indexes": (0,),
28 "element_beam_part_indexes": (0,),
29 "element_beam_node_indexes": (0, 5),
30 "element_shell_node_indexes": (4696, 4),
31 "element_shell_part_indexes": (4696,),
32 "node_ids": (4915,),
33 "element_solid_ids": (0,),
34 "element_beam_ids": (0,),
35 "element_shell_ids": (4696,),
36 "element_tshell_ids": (0,),
37 "part_titles_ids": (1,),
38 "part_titles": (1,),
39 }
41 state_array_shapes = {
42 "timesteps": (1,),
43 "global_kinetic_energy": (1,),
44 "global_internal_energy": (1,),
45 "global_total_energy": (1,),
46 "global_velocity": (1, 3),
47 "part_internal_energy": (1, 1),
48 "part_kinetic_energy": (1, 1),
49 "part_velocity": (1, 1, 3),
50 "part_mass": (1, 1),
51 "part_hourglass_energy": (1, 1),
52 "node_displacement": (1, 4915, 3),
53 "node_velocity": (1, 4915, 3),
54 "node_acceleration": (1, 4915, 3),
55 "element_shell_stress": (1, 4696, 3, 6),
56 "element_shell_effective_plastic_strain": (1, 4696, 3),
57 "element_shell_history_vars": (1, 4696, 3, 19),
58 "element_shell_bending_moment": (1, 4696, 3),
59 "element_shell_shear_force": (1, 4696, 2),
60 "element_shell_normal_force": (1, 4696, 3),
61 "element_shell_thickness": (1, 4696),
62 "element_shell_unknown_variables": (1, 4696, 2),
63 "element_shell_strain": (1, 4696, 2, 6),
64 "element_shell_internal_energy": (1, 4696),
65 "element_shell_is_alive": (1, 4696),
66 }
68 all_array_shapes = {**geometry_array_shapes, **state_array_shapes}
70 # empty constructor
71 D3plot()
73 # file thing
74 d3plot = D3plot(filepath)
75 d3plot_shapes = {array_name: array.shape for array_name, array in d3plot.arrays.items()}
76 self.assertDictEqual(d3plot_shapes, all_array_shapes)
78 # limited buffer files
79 d3plot = D3plot(filepath, buffered_reading=True)
80 d3plot_shapes = {array_name: array.shape for array_name, array in d3plot.arrays.items()}
81 self.assertDictEqual(d3plot_shapes, all_array_shapes)
83 # test loading single state arrays
84 for array_name, array_shape in state_array_shapes.items():
85 d3plot = D3plot(filepath, state_array_filter=[array_name])
86 d3plot_shapes = {array_name: array.shape for array_name, array in d3plot.arrays.items()}
87 self.assertDictEqual(d3plot_shapes, {**geometry_array_shapes, array_name: array_shape})
89 # test loading individual states
90 d3plot = D3plot(filepath, state_filter={0})
91 d3plot2 = D3plot(filepath)
92 hdr_diff, array_diff = d3plot.compare(d3plot2)
93 self.assertEqual(d3plot.n_timesteps, 1)
94 self.assertDictEqual(hdr_diff, {})
95 self.assertDictEqual(array_diff, {})
97 def test_header(self):
99 test_header_data = {
100 "title": " ",
101 "runtime": 1472027823,
102 "filetype": 1,
103 "source_version": -971095103,
104 "release_version": "R712",
105 "version": 960.0,
106 "ndim": 4,
107 "numnp": 4915,
108 "it": 0,
109 "iu": 1,
110 "iv": 1,
111 "ia": 1,
112 "nel2": 0,
113 "nel4": 4696,
114 "nel8": 0,
115 "nelt": 0,
116 "nel20": 0,
117 "nel27": 0,
118 "nel48": 0,
119 "nv1d": 6,
120 "nv2d": 102,
121 "nv3d": 32,
122 "nv3dt": 90,
123 "nummat4": 1,
124 "nummat8": 0,
125 "nummat2": 0,
126 "nummatt": 0,
127 "icode": 6,
128 "nglbv": 13,
129 "numst": 0,
130 "numds": 0,
131 "neiph": 25,
132 "neips": 19,
133 "maxint": -10003,
134 "nmsph": 0,
135 "ngpsph": 0,
136 "narbs": 9624,
137 "ioshl1": 1000,
138 "ioshl2": 1000,
139 "ioshl3": 1000,
140 "ioshl4": 1000,
141 "ialemat": 0,
142 "ncfdv1": 0,
143 "ncfdv2": 0,
144 "nadapt": 0,
145 "nmmat": 1,
146 "numfluid": 0,
147 "inn": 1,
148 "npefg": 0,
149 "idtdt": 0,
150 "extra": 0,
151 "nt3d": 0,
152 "neipb": 0,
153 }
155 d3plot = D3plot("test/simple_d3plot/d3plot")
156 header = d3plot.header
158 for name, value in test_header_data.items():
159 self.assertEqual(header.raw_header[name], value, "Invalid var %s" % name)
161 def test_beam_integration_points(self):
163 self.maxDiff = None
165 filepath = "test/d3plot_beamip/d3plot"
166 maxmin_test_values = {
167 # "element_beam_shear_stress": (-0.007316963, 0.),
168 "element_beam_shear_stress": (0.0, 0.0056635854),
169 "element_beam_axial_stress": (-0.007316963, 0.0),
170 "element_beam_plastic_strain": (0.0, 0.0056297667),
171 "element_beam_axial_strain": (-0.0073745, 0.0),
172 }
174 d3plot = D3plot(filepath)
176 for array_name, minmax in maxmin_test_values.items():
177 array = d3plot.arrays[array_name]
178 self.assertAlmostEqual(
179 array.min(),
180 minmax[0],
181 msg="{0}: {1} != {2}".format(array_name, array.min(), minmax[0]),
182 )
183 self.assertAlmostEqual(
184 array.max(),
185 minmax[1],
186 msg="{0}: {1} != {2}".format(array_name, array.max(), minmax[1]),
187 )
189 def test_correct_sort_of_more_than_100_state_files(self):
191 filepath = "test/order_d3plot/d3plot"
193 d3plot = D3plot(filepath)
195 timesteps = d3plot.arrays[ArrayType.global_timesteps]
196 self.assertListEqual(timesteps.astype(int).tolist(), [1, 2, 10, 11, 12, 22, 100])
198 def test_femzip_basic(self):
200 self.maxDiff = None
202 filepath1 = "test/femzip/d3plot.fz"
203 filepath2 = "test/femzip/d3plot"
205 d3plot_kwargs_list = [{}, {"buffered_reading": True}, {"state_filter": [0]}]
207 D3plot.use_advanced_femzip_api = False
209 for d3plot_kwargs in d3plot_kwargs_list:
211 d3plot1 = D3plot(filepath1, **d3plot_kwargs)
212 d3plot2 = D3plot(filepath2, **d3plot_kwargs)
214 hdr_diff, array_diff = d3plot1.compare(d3plot2, array_eps=1e-2)
216 self.assertDictEqual(hdr_diff, {})
217 self.assertDictEqual(array_diff, {})
219 def test_femzip_extended(self):
221 self.maxDiff = None
223 filepath1 = "test/femzip/d3plot.fz"
224 filepath2 = "test/femzip/d3plot"
226 d3plot_kwargs_list = [{}, {"buffered_reading": True}, {"state_filter": [0]}]
228 api = FemzipAPI()
229 if not api.has_femunziplib_license():
230 return
232 D3plot.use_advanced_femzip_api = True
234 for d3plot_kwargs in d3plot_kwargs_list:
236 d3plot1 = D3plot(filepath1, **d3plot_kwargs)
237 d3plot2 = D3plot(filepath2, **d3plot_kwargs)
239 hdr_diff, array_diff = d3plot1.compare(d3plot2, array_eps=1e-2)
241 self.assertDictEqual(hdr_diff, {})
242 self.assertDictEqual(array_diff, {})
244 def test_part_filter(self):
246 self.maxDiff = None
248 filepath = "test/simple_d3plot/d3plot"
249 part_ids = [1]
251 d3plot = D3plot(filepath)
252 shell_filter = d3plot.get_part_filter(FilterType.SHELL, part_ids)
253 self.assertEqual(shell_filter.sum(), 4696)
255 part_filter = d3plot.get_part_filter(FilterType.PART, part_ids)
256 self.assertEqual(part_filter.sum(), 1)
258 node_filter = d3plot.get_part_filter(FilterType.NODE, part_ids)
259 self.assertEqual(len(node_filter), 4915)
261 def test_read_solid_integration_points(self):
263 filepath = "test/d3plot_solid_int/d3plot"
265 # data from META
266 stress_valid = np.array(
267 [
268 2.132084e02,
269 1.792203e02,
270 1.397527e02,
271 2.307352e02,
272 2.132105e02,
273 1.792210e02,
274 1.397558e02,
275 2.307304e02,
276 ]
277 )
278 pstrain_valid = np.array(
279 [
280 2.227418e-02,
281 2.576126e-03,
282 1.909884e-02,
283 3.695280e-02,
284 2.227416e-02,
285 2.576110e-03,
286 1.909888e-02,
287 3.695256e-02,
288 ]
289 )
290 last_timestep = -1
291 first_element = 0
292 stress_xx = 0
294 d3plot = D3plot(filepath)
296 stress = d3plot.arrays[ArrayType.element_solid_stress]
297 np.array_equal(stress[last_timestep, first_element, :, stress_xx], stress_valid)
299 pstrain = d3plot.arrays[ArrayType.element_solid_effective_plastic_strain]
300 np.array_equal(pstrain[last_timestep, first_element, :], pstrain_valid)
302 def test_negative_to_positive_state_indexes(self) -> None:
304 indexes = set()
305 new_indexes = _negative_to_positive_state_indexes(indexes, len(indexes))
306 self.assertSetEqual(indexes, new_indexes)
308 indexes = {0, 1, 2}
309 with self.assertRaises(ValueError):
310 new_indexes = _negative_to_positive_state_indexes(indexes, 2)
312 indexes = {0, -1}
313 new_indexes = _negative_to_positive_state_indexes(indexes, 8)
314 self.assertSetEqual(new_indexes, {0, 7})
316 def test_is_end_of_file_marker(self) -> None:
318 # -999999. in float32
319 bb = BinaryBuffer()
320 bb.memoryview = memoryview(struct.pack("<f", -999999.0))
322 result = D3plot._is_end_of_file_marker(bb, 0, np.float32)
323 self.assertEqual(result, True)
325 # -999999. in float64
326 bb = BinaryBuffer()
327 bb.memoryview = memoryview(struct.pack("<d", -999999.0))
329 result = D3plot._is_end_of_file_marker(bb, 0, np.float64)
330 self.assertEqual(result, True)
332 # some other number
333 bb = BinaryBuffer()
334 bb.memoryview = memoryview(struct.pack("<d", -41.0))
336 result = D3plot._is_end_of_file_marker(bb, 0, np.float64)
337 self.assertEqual(result, False)
339 # invalid dtype
340 with self.assertRaises(ValueError):
341 result = D3plot._is_end_of_file_marker(bb, 0, np.int32)
343 def test_write(self):
345 self.maxDiff = None
347 filepaths = [
348 "test/simple_d3plot/d3plot",
349 "test/d3plot_beamip/d3plot",
350 "test/d3plot_node_temperature/d3plot",
351 "test/d3plot_solid_int/d3plot",
352 ]
354 d3plot_kwargs_list = [
355 {},
356 {"buffered_reading": True},
357 ]
359 with tempfile.TemporaryDirectory() as dirpath:
361 for d3plot_kwargs in d3plot_kwargs_list:
362 for d3plot_filepath, _ in zip(filepaths, d3plot_kwargs_list):
364 print(d3plot_filepath)
366 # read d3plot
367 d3plot1 = D3plot(d3plot_filepath, **d3plot_kwargs)
369 # rewrite d3plot
370 out_filepath = os.path.join(dirpath, "yay.d3plot")
371 d3plot1.write_d3plot(out_filepath)
373 # read it in again and compare
374 d3plot2 = D3plot(out_filepath, **d3plot_kwargs)
375 hdr_diff, array_diff = d3plot1.compare(d3plot2)
377 err_msg = f"{d3plot_filepath}: {d3plot_kwargs}"
378 self.assertDictEqual(hdr_diff, {}, err_msg)
379 self.assertDictEqual(array_diff, {}, err_msg)
381 def test_write_new(self):
383 self.maxDiff = None
385 d3plot1 = D3plot()
386 d3plot1.arrays[ArrayType.node_coordinates] = np.array([[0, 0, 0], [1, 0, 0], [0, 1, 0]])
387 d3plot1.arrays[ArrayType.element_shell_node_indexes] = np.array([[0, 2, 1, 1]])
388 d3plot1.arrays[ArrayType.element_shell_part_indexes] = np.array([0])
389 d3plot1.arrays[ArrayType.node_displacement] = np.array([[[0, 0, 0], [1, 0, 0], [0, 1, 0]]])
391 with tempfile.TemporaryDirectory() as dirpath:
392 filepath = os.path.join(dirpath, "yay.d3plot")
394 # single file
395 d3plot1.write_d3plot(filepath)
396 d3plot2 = D3plot(filepath)
397 hdr_diff, array_diff = d3plot1.compare(d3plot2)
398 array_diff = {
399 name: reason
400 for name, reason in array_diff.items()
401 if "missing in original" not in reason
402 }
403 self.assertDictEqual(hdr_diff, {})
404 self.assertDictEqual(array_diff, {})
406 # multiple files
407 d3plot1.write_d3plot(filepath, single_file=False)
408 d3plot2 = D3plot(filepath)
409 hdr_diff, array_diff = d3plot1.compare(d3plot2)
410 array_diff = {
411 name: reason
412 for name, reason in array_diff.items()
413 if "missing in original" not in reason
414 }
415 self.assertDictEqual(hdr_diff, {})
416 self.assertDictEqual(array_diff, {})
417 self.assertTrue(os.path.isfile(filepath))
418 self.assertTrue(os.path.isfile(filepath + "01"))
420 def test_append_4_shell_hists_then_read_bug(self):
422 self.maxDiff = None
424 # we need some d3plot
425 d3plot = D3plot()
426 d3plot.arrays[ArrayType.node_coordinates] = np.array(
427 [[0, 0, 0], [1, 0, 0], [0, 1, 0]], dtype=np.float32
428 )
429 d3plot.arrays[ArrayType.element_shell_node_indexes] = np.array([[0, 2, 1, 1]])
430 d3plot.arrays[ArrayType.element_shell_part_indexes] = np.array([0])
431 d3plot.arrays[ArrayType.node_displacement] = np.array(
432 [[[0, 0, 0], [1, 0, 0], [0, 1, 0]]], dtype=np.float32
433 )
435 # there was a bug occurring if more than 3 history vars
436 # were added, these were written and read back in again
437 n_history_vars = 4
439 with tempfile.TemporaryDirectory() as dirpath:
440 filepath1 = os.path.join(dirpath, "original")
441 d3plot.write_d3plot(filepath1)
443 # open it again to have a safe copy
444 d3plot1 = D3plot(filepath1)
445 n_timesteps, n_shells, n_layers = 1, d3plot1.header.n_shells, 3
447 d3plot1.arrays[ArrayType.element_shell_history_vars] = np.random.random(
448 (n_timesteps, n_shells, n_layers, n_history_vars)
449 )
451 filepath2 = os.path.join(dirpath, "modified")
452 d3plot1.write_d3plot(filepath2)
454 d3plot_modif = D3plot(filepath2)
455 self.assertTrue(ArrayType.element_shell_internal_energy not in d3plot_modif.arrays)
457 def test_reading_selected_states(self):
459 # read all states
460 filepath = "test/d3plot_solid_int/d3plot"
462 d3plot = D3plot(filepath)
463 d3plot2 = D3plot(filepath, state_filter=np.arange(0, 22))
465 hdr_diff, array_diff = d3plot.compare(d3plot2)
467 self.assertDictEqual(hdr_diff, {})
468 self.assertDictEqual(array_diff, {})
470 # select first and last state
471 d3plot = D3plot(filepath, state_filter={0, -1})
473 node_id = 119
474 disp_node_real = np.array(
475 [[50.0, 70.0, 5.0], [47.50418, 70.0, -10.000001]], dtype=np.float32
476 )
478 node_index = d3plot.arrays[ArrayType.node_ids].tolist().index(node_id)
479 node_disp = d3plot.arrays[ArrayType.node_displacement][:, node_index]
481 np.testing.assert_allclose(node_disp, disp_node_real, rtol=1e-4)