solidipes-solid-mech-plugin 0.0.14__tar.gz → 0.0.16__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/PKG-INFO +6 -3
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/pyproject.toml +5 -2
- solidipes_solid_mech_plugin-0.0.16/solidipes_solid_mech_plugin/config.py +8 -0
- solidipes_solid_mech_plugin-0.0.16/solidipes_solid_mech_plugin/loaders/exodus.py +184 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/viewers/pyvista_plotter.py +3 -3
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/PKG-INFO +6 -3
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/SOURCES.txt +2 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/requires.txt +5 -2
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/scm_file_list.json +27 -25
- solidipes_solid_mech_plugin-0.0.16/solidipes_solid_mech_plugin.egg-info/scm_version.json +8 -0
- solidipes_solid_mech_plugin-0.0.16/tests/test_exodus_mesh.py +88 -0
- solidipes_solid_mech_plugin-0.0.16/tests/test_pyvista_plotter.py +59 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/test_streamlit_pyvista.py +109 -7
- solidipes_solid_mech_plugin-0.0.16/uv.lock +4464 -0
- solidipes_solid_mech_plugin-0.0.14/solidipes_solid_mech_plugin/config.py +0 -5
- solidipes_solid_mech_plugin-0.0.14/solidipes_solid_mech_plugin.egg-info/scm_version.json +0 -8
- solidipes_solid_mech_plugin-0.0.14/tests/test_pyvista_plotter.py +0 -29
- solidipes_solid_mech_plugin-0.0.14/uv.lock +0 -3519
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/.flake8 +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/.gitignore +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/.gitlab-ci.yml +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/.pre-commit-config.yaml +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/CHANGELOG.md +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/LICENSE +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/README.md +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/setup.cfg +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/__init__.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/__init__.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/abaqus.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/abaqus_python_code.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/geo_msh.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/geof_mesh.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/meshio.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/parse_inp.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/pyvista_mesh.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/loaders/xdmf.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/ontologies/solidipes_solid_mech.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/viewers/__init__.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin/viewers/xdmf.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/dependency_links.txt +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/entry_points.txt +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/solidipes_solid_mech_plugin.egg-info/top_level.txt +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/generate_assets.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/pyvista_mesh.vtu +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/pyvista_mesh_no_fields.vtu +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/pyvista_plot.png +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/test_mesh_loaded.png +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/xdmf_mesh.h5 +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/assets/xdmf_mesh.xdmf +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/conftest.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/helpers/utils.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/test_load_modules.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/test_ontologies.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/test_pyvista_mesh.py +0 -0
- {solidipes_solid_mech_plugin-0.0.14 → solidipes_solid_mech_plugin-0.0.16}/tests/test_xdmf_mesh.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solidipes-solid-mech-plugin
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.16
|
|
4
4
|
Summary: Plugin for Solidipes with solid mechanics components
|
|
5
5
|
Author-email: Son Pham-Ba <son.phamba@epfl.ch>, Guillaume Anciaux <guillaume.anciaux@epfl.ch>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -12,9 +12,12 @@ Requires-Dist: ipython~=8.18.1
|
|
|
12
12
|
Requires-Dist: mergedeep~=1.3.4
|
|
13
13
|
Requires-Dist: meshio[all]~=5.3.5
|
|
14
14
|
Requires-Dist: streamlit~=1.45.0
|
|
15
|
-
Requires-Dist: trame
|
|
16
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: trame<4,>=3.13.2
|
|
16
|
+
Requires-Dist: trame-pyvista>=0.2.1
|
|
17
|
+
Requires-Dist: trame-vtk>=2.11.15
|
|
18
|
+
Requires-Dist: streamlit-pyvista~=0.0.24
|
|
17
19
|
Requires-Dist: h5py~=3.13.0
|
|
20
|
+
Requires-Dist: netCDF4~=1.7.0
|
|
18
21
|
Provides-Extra: dev
|
|
19
22
|
Requires-Dist: pillow~=10.4.0; extra == "dev"
|
|
20
23
|
Requires-Dist: pixelmatch~=0.3.0; extra == "dev"
|
|
@@ -16,9 +16,12 @@ dependencies = [
|
|
|
16
16
|
"mergedeep~=1.3.4",
|
|
17
17
|
"meshio[all]~=5.3.5",
|
|
18
18
|
"streamlit~=1.45.0",
|
|
19
|
-
"trame
|
|
20
|
-
"
|
|
19
|
+
"trame>=3.13.2,<4",
|
|
20
|
+
"trame-pyvista>=0.2.1",
|
|
21
|
+
"trame-vtk>=2.11.15",
|
|
22
|
+
"streamlit-pyvista~=0.0.24",
|
|
21
23
|
"h5py~=3.13.0",
|
|
24
|
+
"netCDF4~=1.7.0",
|
|
22
25
|
]
|
|
23
26
|
|
|
24
27
|
[project.optional-dependencies]
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from solidipes.loaders.file import File
|
|
5
|
+
from solidipes.loaders.sequence import Sequence
|
|
6
|
+
from solidipes_core_plugin.loaders.netcdf import NetCDF
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _decode_exodus_string(raw) -> str:
|
|
10
|
+
"""Decode a character array row from an Exodus II netCDF file."""
|
|
11
|
+
if isinstance(raw, np.ndarray):
|
|
12
|
+
raw = raw.tobytes()
|
|
13
|
+
|
|
14
|
+
if isinstance(raw, (bytes, np.bytes_)):
|
|
15
|
+
return raw.decode("utf-8", errors="ignore").strip("\x00").strip()
|
|
16
|
+
|
|
17
|
+
return str(raw)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Exodus(Sequence, NetCDF):
|
|
21
|
+
"""Exodus II finite element database file, read with meshio and netCDF4.
|
|
22
|
+
|
|
23
|
+
Every Exodus file is treated as a sequence of time steps (a mesh-only
|
|
24
|
+
database counts as a single frame). Nodal and elemental result variables
|
|
25
|
+
are exposed as point and cell data for each available time step, and the
|
|
26
|
+
mesh can be viewed with the PyvistaPlotter.
|
|
27
|
+
|
|
28
|
+
The `netcdf` loadable and the netCDF metadata (dimensions, variables,
|
|
29
|
+
global attributes) are inherited from the NetCDF loader.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from ..viewers.pyvista_plotter import PyvistaPlotter
|
|
33
|
+
|
|
34
|
+
supported_mime_types = {"application/x-netcdf": ["e", "exo", "gen"]}
|
|
35
|
+
_compatible_viewers = [PyvistaPlotter]
|
|
36
|
+
|
|
37
|
+
@File.loadable
|
|
38
|
+
def meshio_mesh(self):
|
|
39
|
+
"""Static mesh (connectivity and coordinates) read with meshio"""
|
|
40
|
+
import meshio
|
|
41
|
+
|
|
42
|
+
return meshio.read(self.file_info.path)
|
|
43
|
+
|
|
44
|
+
@File.rocrate_metadata
|
|
45
|
+
def title(self):
|
|
46
|
+
return self.netcdf.title
|
|
47
|
+
|
|
48
|
+
@File.rocrate_metadata
|
|
49
|
+
def n_time_steps(self):
|
|
50
|
+
if "time_step" not in self.netcdf.dimensions:
|
|
51
|
+
return 1
|
|
52
|
+
|
|
53
|
+
# A mesh-only database has an empty (or missing) time_step dimension:
|
|
54
|
+
# the mesh itself counts as a single frame
|
|
55
|
+
return max(1, len(self.netcdf.dimensions["time_step"]))
|
|
56
|
+
|
|
57
|
+
@File.rocrate_metadata
|
|
58
|
+
def n_nodes(self):
|
|
59
|
+
return len(self.netcdf.dimensions["num_nodes"])
|
|
60
|
+
|
|
61
|
+
@File.rocrate_metadata
|
|
62
|
+
def n_elements(self):
|
|
63
|
+
return len(self.netcdf.dimensions["num_elem"])
|
|
64
|
+
|
|
65
|
+
@File.rocrate_metadata
|
|
66
|
+
def nodal_variable_names(self):
|
|
67
|
+
if "name_nod_var" not in self.netcdf.variables:
|
|
68
|
+
return []
|
|
69
|
+
|
|
70
|
+
return [_decode_exodus_string(raw) for raw in self.netcdf.variables["name_nod_var"][:]]
|
|
71
|
+
|
|
72
|
+
@File.rocrate_metadata
|
|
73
|
+
def element_block_names(self):
|
|
74
|
+
if "eb_names" not in self.netcdf.variables:
|
|
75
|
+
return {}
|
|
76
|
+
|
|
77
|
+
return {i + 1: _decode_exodus_string(raw) for i, raw in enumerate(self.netcdf.variables["eb_names"][:])}
|
|
78
|
+
|
|
79
|
+
@File.rocrate_metadata
|
|
80
|
+
def elemental_variable_names(self):
|
|
81
|
+
"""List of (element_block_number, variable_number, name) triples"""
|
|
82
|
+
names = []
|
|
83
|
+
|
|
84
|
+
for key, var in self.netcdf.variables.items():
|
|
85
|
+
if not key.startswith("name_elem_var"):
|
|
86
|
+
continue
|
|
87
|
+
|
|
88
|
+
block = int(key[len("name_elem_var") :]) if len(key) > len("name_elem_var") else 1
|
|
89
|
+
|
|
90
|
+
for i, raw in enumerate(var[:]):
|
|
91
|
+
name = _decode_exodus_string(raw)
|
|
92
|
+
|
|
93
|
+
if name:
|
|
94
|
+
names.append((block, i + 1, name))
|
|
95
|
+
|
|
96
|
+
return names
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def _element_count(self):
|
|
100
|
+
return self.n_time_steps
|
|
101
|
+
|
|
102
|
+
def select_frame(self, frame):
|
|
103
|
+
self.select_element(frame)
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def mesh(self):
|
|
107
|
+
"""Pyvista mesh of the currently selected time step"""
|
|
108
|
+
return self._current_element
|
|
109
|
+
|
|
110
|
+
def _load_element(self, n):
|
|
111
|
+
"""Load the mesh with nodal and elemental data at time step n"""
|
|
112
|
+
import pyvista as pv
|
|
113
|
+
from meshio import Mesh
|
|
114
|
+
|
|
115
|
+
meshio_mesh = self.meshio_mesh
|
|
116
|
+
point_data = self._point_data_at_step(n)
|
|
117
|
+
cell_data = self._cell_data_at_step(n)
|
|
118
|
+
mesh = Mesh(meshio_mesh.points, meshio_mesh.cells, point_data=point_data, cell_data=cell_data)
|
|
119
|
+
return pv.from_meshio(mesh)
|
|
120
|
+
|
|
121
|
+
def _point_data_at_step(self, n):
|
|
122
|
+
"""Nodal variable values at time step n"""
|
|
123
|
+
nc = self.netcdf
|
|
124
|
+
names = self.nodal_variable_names
|
|
125
|
+
data = {}
|
|
126
|
+
|
|
127
|
+
if "vals_nod_var" in nc.variables:
|
|
128
|
+
# Standard layout: single variable with dimensions
|
|
129
|
+
# (time_step, num_nod_var, num_nodes)
|
|
130
|
+
all_data = np.asarray(nc.variables["vals_nod_var"][n])
|
|
131
|
+
|
|
132
|
+
for i, name in enumerate(names):
|
|
133
|
+
data[name] = all_data[i]
|
|
134
|
+
|
|
135
|
+
else:
|
|
136
|
+
# Alternative layout: one variable per nodal variable
|
|
137
|
+
for i, name in enumerate(names):
|
|
138
|
+
var_name = f"vals_nod_var{i + 1}"
|
|
139
|
+
|
|
140
|
+
if var_name in nc.variables:
|
|
141
|
+
data[name] = np.asarray(nc.variables[var_name][n])
|
|
142
|
+
|
|
143
|
+
return data
|
|
144
|
+
|
|
145
|
+
def _cell_data_at_step(self, n):
|
|
146
|
+
"""Elemental variable values at time step n, per meshio cell block"""
|
|
147
|
+
nc = self.netcdf
|
|
148
|
+
names = self.elemental_variable_names
|
|
149
|
+
|
|
150
|
+
if len(names) == 0:
|
|
151
|
+
return {}
|
|
152
|
+
|
|
153
|
+
# Collect data per (variable index, element block)
|
|
154
|
+
raw: dict[int, dict[int, np.ndarray]] = {}
|
|
155
|
+
|
|
156
|
+
for key, var in nc.variables.items():
|
|
157
|
+
match = re.fullmatch(r"vals_elem_var(\d+)?(?:eb(\d+))?", key)
|
|
158
|
+
|
|
159
|
+
if match is None:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
var_index = 0 if match.group(1) is None else int(match.group(1)) - 1
|
|
163
|
+
block = 0 if match.group(2) is None else int(match.group(2)) - 1
|
|
164
|
+
raw.setdefault(var_index, {})[block] = np.asarray(var[n])
|
|
165
|
+
|
|
166
|
+
cell_sizes = [len(c.data) for c in self.meshio_mesh.cells]
|
|
167
|
+
offsets = np.cumsum([0, *cell_sizes])
|
|
168
|
+
|
|
169
|
+
cell_data = {}
|
|
170
|
+
|
|
171
|
+
for block, var_index, name in names:
|
|
172
|
+
if var_index - 1 not in raw:
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
by_block = raw[var_index - 1]
|
|
176
|
+
concatenated = np.concatenate([by_block[b] for b in sorted(by_block)])
|
|
177
|
+
|
|
178
|
+
if len(concatenated) != offsets[-1]:
|
|
179
|
+
# Element blocks without data for this variable: skip
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
cell_data[name] = [concatenated[start:stop] for start, stop in zip(offsets[:-1], offsets[1:])]
|
|
183
|
+
|
|
184
|
+
return cell_data
|
|
@@ -95,7 +95,7 @@ class PyvistaPlotter(Viewer):
|
|
|
95
95
|
method
|
|
96
96
|
"""
|
|
97
97
|
from streamlit_pyvista.mesh_viewer_component import MeshViewerComponent # noqa: E402
|
|
98
|
-
from streamlit_pyvista.server_managers import
|
|
98
|
+
from streamlit_pyvista.server_managers import ServerManager # noqa: E402
|
|
99
99
|
from streamlit_pyvista.trame_viewers import get_advanced_viewer_path # noqa: E402
|
|
100
100
|
|
|
101
101
|
max_frames = 500
|
|
@@ -133,7 +133,7 @@ class PyvistaPlotter(Viewer):
|
|
|
133
133
|
MeshViewerComponent(
|
|
134
134
|
paths,
|
|
135
135
|
trame_viewer_class=get_advanced_viewer_path(),
|
|
136
|
-
server_manager_class=
|
|
136
|
+
server_manager_class=ServerManager,
|
|
137
137
|
).show()
|
|
138
138
|
elif viewer_backends.current_backend == "python":
|
|
139
139
|
self.shown = True
|
|
@@ -143,7 +143,7 @@ class PyvistaPlotter(Viewer):
|
|
|
143
143
|
MeshViewerComponent(
|
|
144
144
|
paths,
|
|
145
145
|
trame_viewer_class=get_advanced_viewer_path(),
|
|
146
|
-
server_manager_class=
|
|
146
|
+
server_manager_class=ServerManager,
|
|
147
147
|
).show()
|
|
148
148
|
|
|
149
149
|
def save(self, path, **kwargs):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: solidipes-solid-mech-plugin
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.16
|
|
4
4
|
Summary: Plugin for Solidipes with solid mechanics components
|
|
5
5
|
Author-email: Son Pham-Ba <son.phamba@epfl.ch>, Guillaume Anciaux <guillaume.anciaux@epfl.ch>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -12,9 +12,12 @@ Requires-Dist: ipython~=8.18.1
|
|
|
12
12
|
Requires-Dist: mergedeep~=1.3.4
|
|
13
13
|
Requires-Dist: meshio[all]~=5.3.5
|
|
14
14
|
Requires-Dist: streamlit~=1.45.0
|
|
15
|
-
Requires-Dist: trame
|
|
16
|
-
Requires-Dist:
|
|
15
|
+
Requires-Dist: trame<4,>=3.13.2
|
|
16
|
+
Requires-Dist: trame-pyvista>=0.2.1
|
|
17
|
+
Requires-Dist: trame-vtk>=2.11.15
|
|
18
|
+
Requires-Dist: streamlit-pyvista~=0.0.24
|
|
17
19
|
Requires-Dist: h5py~=3.13.0
|
|
20
|
+
Requires-Dist: netCDF4~=1.7.0
|
|
18
21
|
Provides-Extra: dev
|
|
19
22
|
Requires-Dist: pillow~=10.4.0; extra == "dev"
|
|
20
23
|
Requires-Dist: pixelmatch~=0.3.0; extra == "dev"
|
|
@@ -20,6 +20,7 @@ solidipes_solid_mech_plugin.egg-info/top_level.txt
|
|
|
20
20
|
solidipes_solid_mech_plugin/loaders/__init__.py
|
|
21
21
|
solidipes_solid_mech_plugin/loaders/abaqus.py
|
|
22
22
|
solidipes_solid_mech_plugin/loaders/abaqus_python_code.py
|
|
23
|
+
solidipes_solid_mech_plugin/loaders/exodus.py
|
|
23
24
|
solidipes_solid_mech_plugin/loaders/geo_msh.py
|
|
24
25
|
solidipes_solid_mech_plugin/loaders/geof_mesh.py
|
|
25
26
|
solidipes_solid_mech_plugin/loaders/meshio.py
|
|
@@ -31,6 +32,7 @@ solidipes_solid_mech_plugin/viewers/__init__.py
|
|
|
31
32
|
solidipes_solid_mech_plugin/viewers/pyvista_plotter.py
|
|
32
33
|
solidipes_solid_mech_plugin/viewers/xdmf.py
|
|
33
34
|
tests/conftest.py
|
|
35
|
+
tests/test_exodus_mesh.py
|
|
34
36
|
tests/test_load_modules.py
|
|
35
37
|
tests/test_ontologies.py
|
|
36
38
|
tests/test_pyvista_mesh.py
|
|
@@ -3,9 +3,12 @@ ipython~=8.18.1
|
|
|
3
3
|
mergedeep~=1.3.4
|
|
4
4
|
meshio[all]~=5.3.5
|
|
5
5
|
streamlit~=1.45.0
|
|
6
|
-
trame
|
|
7
|
-
|
|
6
|
+
trame<4,>=3.13.2
|
|
7
|
+
trame-pyvista>=0.2.1
|
|
8
|
+
trame-vtk>=2.11.15
|
|
9
|
+
streamlit-pyvista~=0.0.24
|
|
8
10
|
h5py~=3.13.0
|
|
11
|
+
netCDF4~=1.7.0
|
|
9
12
|
|
|
10
13
|
[dev]
|
|
11
14
|
pillow~=10.4.0
|
|
@@ -1,43 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
+
".flake8",
|
|
3
4
|
".gitignore",
|
|
4
5
|
".gitlab-ci.yml",
|
|
5
6
|
".pre-commit-config.yaml",
|
|
6
7
|
"CHANGELOG.md",
|
|
7
|
-
"
|
|
8
|
+
"LICENSE",
|
|
8
9
|
"README.md",
|
|
9
|
-
"uv.lock",
|
|
10
10
|
"pyproject.toml",
|
|
11
|
-
"LICENSE",
|
|
12
|
-
"tests/test_pyvista_mesh.py",
|
|
13
|
-
"tests/conftest.py",
|
|
14
|
-
"tests/test_ontologies.py",
|
|
15
|
-
"tests/test_load_modules.py",
|
|
16
|
-
"tests/test_streamlit_pyvista.py",
|
|
17
|
-
"tests/test_pyvista_plotter.py",
|
|
18
|
-
"tests/test_xdmf_mesh.py",
|
|
19
|
-
"tests/helpers/utils.py",
|
|
20
|
-
"tests/assets/xdmf_mesh.h5",
|
|
21
|
-
"tests/assets/test_mesh_loaded.png",
|
|
22
|
-
"tests/assets/pyvista_mesh.vtu",
|
|
23
|
-
"tests/assets/generate_assets.py",
|
|
24
|
-
"tests/assets/pyvista_plot.png",
|
|
25
|
-
"tests/assets/pyvista_mesh_no_fields.vtu",
|
|
26
|
-
"tests/assets/xdmf_mesh.xdmf",
|
|
27
11
|
"solidipes_solid_mech_plugin/__init__.py",
|
|
28
12
|
"solidipes_solid_mech_plugin/config.py",
|
|
29
|
-
"solidipes_solid_mech_plugin/
|
|
30
|
-
"solidipes_solid_mech_plugin/loaders/parse_inp.py",
|
|
13
|
+
"solidipes_solid_mech_plugin/loaders/__init__.py",
|
|
31
14
|
"solidipes_solid_mech_plugin/loaders/abaqus.py",
|
|
15
|
+
"solidipes_solid_mech_plugin/loaders/abaqus_python_code.py",
|
|
16
|
+
"solidipes_solid_mech_plugin/loaders/exodus.py",
|
|
32
17
|
"solidipes_solid_mech_plugin/loaders/geo_msh.py",
|
|
33
|
-
"solidipes_solid_mech_plugin/loaders/pyvista_mesh.py",
|
|
34
|
-
"solidipes_solid_mech_plugin/loaders/__init__.py",
|
|
35
18
|
"solidipes_solid_mech_plugin/loaders/geof_mesh.py",
|
|
36
|
-
"solidipes_solid_mech_plugin/loaders/xdmf.py",
|
|
37
19
|
"solidipes_solid_mech_plugin/loaders/meshio.py",
|
|
38
|
-
"solidipes_solid_mech_plugin/loaders/
|
|
20
|
+
"solidipes_solid_mech_plugin/loaders/parse_inp.py",
|
|
21
|
+
"solidipes_solid_mech_plugin/loaders/pyvista_mesh.py",
|
|
22
|
+
"solidipes_solid_mech_plugin/loaders/xdmf.py",
|
|
23
|
+
"solidipes_solid_mech_plugin/ontologies/solidipes_solid_mech.py",
|
|
39
24
|
"solidipes_solid_mech_plugin/viewers/__init__.py",
|
|
25
|
+
"solidipes_solid_mech_plugin/viewers/pyvista_plotter.py",
|
|
40
26
|
"solidipes_solid_mech_plugin/viewers/xdmf.py",
|
|
41
|
-
"
|
|
27
|
+
"tests/assets/generate_assets.py",
|
|
28
|
+
"tests/assets/pyvista_mesh.vtu",
|
|
29
|
+
"tests/assets/pyvista_mesh_no_fields.vtu",
|
|
30
|
+
"tests/assets/pyvista_plot.png",
|
|
31
|
+
"tests/assets/test_mesh_loaded.png",
|
|
32
|
+
"tests/assets/xdmf_mesh.h5",
|
|
33
|
+
"tests/assets/xdmf_mesh.xdmf",
|
|
34
|
+
"tests/conftest.py",
|
|
35
|
+
"tests/helpers/utils.py",
|
|
36
|
+
"tests/test_exodus_mesh.py",
|
|
37
|
+
"tests/test_load_modules.py",
|
|
38
|
+
"tests/test_ontologies.py",
|
|
39
|
+
"tests/test_pyvista_mesh.py",
|
|
40
|
+
"tests/test_pyvista_plotter.py",
|
|
41
|
+
"tests/test_streamlit_pyvista.py",
|
|
42
|
+
"tests/test_xdmf_mesh.py",
|
|
43
|
+
"uv.lock"
|
|
42
44
|
]
|
|
43
45
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
from solidipes.loaders import load_file
|
|
5
|
+
|
|
6
|
+
meshio = pytest.importorskip("meshio")
|
|
7
|
+
netCDF4 = pytest.importorskip("netCDF4")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.fixture
|
|
11
|
+
def exodus_file_path(tmp_path, study_dir):
|
|
12
|
+
"""Create a small Exodus II file with a nodal variable and return its path."""
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
points = [[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]]
|
|
16
|
+
cells = [("quad", np.array([[0, 1, 2, 3]]))]
|
|
17
|
+
mesh = meshio.Mesh(points, cells, point_data={"v": np.array([0.1, 0.2, 0.3, 0.4])})
|
|
18
|
+
|
|
19
|
+
path = os.path.join(tmp_path, "test.e")
|
|
20
|
+
meshio.write(path, mesh, file_format="exodus")
|
|
21
|
+
return path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture
|
|
25
|
+
def exodus_file(exodus_file_path):
|
|
26
|
+
file = load_file(exodus_file_path)
|
|
27
|
+
return file
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_file_type(exodus_file):
|
|
31
|
+
"""The Exodus loader is selected for .e files"""
|
|
32
|
+
from solidipes.loaders.file import File
|
|
33
|
+
|
|
34
|
+
assert isinstance(exodus_file, File)
|
|
35
|
+
assert type(exodus_file).__name__ == "Exodus"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_metadata(exodus_file):
|
|
39
|
+
assert exodus_file.n_nodes == 4
|
|
40
|
+
assert exodus_file.n_elements == 1
|
|
41
|
+
assert exodus_file.n_time_steps == 1
|
|
42
|
+
assert exodus_file.nodal_variable_names == ["v"]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_mesh(exodus_file):
|
|
46
|
+
import numpy as np
|
|
47
|
+
import pyvista as pv
|
|
48
|
+
|
|
49
|
+
mesh = exodus_file.mesh
|
|
50
|
+
assert isinstance(mesh, pv.UnstructuredGrid)
|
|
51
|
+
assert mesh.n_points == 4
|
|
52
|
+
assert mesh.n_cells == 1
|
|
53
|
+
assert "v" in mesh.array_names
|
|
54
|
+
assert np.allclose(mesh.point_data["v"], [0.1, 0.2, 0.3, 0.4])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_time_steps(exodus_file_path):
|
|
58
|
+
"""Add a second time step and check that it can be selected"""
|
|
59
|
+
import numpy as np
|
|
60
|
+
|
|
61
|
+
with netCDF4.Dataset(exodus_file_path, "a") as nc:
|
|
62
|
+
var = nc.variables["vals_nod_var1"]
|
|
63
|
+
var[1] = np.array([0.5, 0.6, 0.7, 0.8])
|
|
64
|
+
nc.variables["time_whole"][1] = 1.0
|
|
65
|
+
|
|
66
|
+
exodus_file = load_file(exodus_file_path)
|
|
67
|
+
assert exodus_file.n_time_steps == 2
|
|
68
|
+
|
|
69
|
+
mesh_0 = exodus_file.mesh
|
|
70
|
+
assert np.allclose(mesh_0.point_data["v"], [0.1, 0.2, 0.3, 0.4])
|
|
71
|
+
|
|
72
|
+
exodus_file.select_frame(1)
|
|
73
|
+
mesh_1 = exodus_file.mesh
|
|
74
|
+
assert np.allclose(mesh_1.point_data["v"], [0.5, 0.6, 0.7, 0.8])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_validation(exodus_file):
|
|
78
|
+
assert exodus_file.is_valid
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_inherited_netcdf(exodus_file):
|
|
82
|
+
"""The netCDF loadable and metadata are inherited from the NetCDF loader"""
|
|
83
|
+
import netCDF4
|
|
84
|
+
|
|
85
|
+
assert isinstance(exodus_file.netcdf, netCDF4.Dataset)
|
|
86
|
+
assert exodus_file.dimensions["num_nodes"] == 4
|
|
87
|
+
assert exodus_file.global_attributes["title"].startswith("Created by meshio")
|
|
88
|
+
assert "coord" in exodus_file.variables
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import solidipes as sp
|
|
2
|
+
import utils
|
|
3
|
+
from PIL import Image
|
|
4
|
+
from pixelmatch.contrib.PIL import pixelmatch
|
|
5
|
+
|
|
6
|
+
image_name = "pyvista_plot.png"
|
|
7
|
+
image_size = (1024, 768)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def start_xvfb(wait=3, window_size=None):
|
|
11
|
+
"""Start the virtual framebuffer Xvfb.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
wait : float, optional
|
|
16
|
+
Time to wait for the virtual framebuffer to start. Set to 0
|
|
17
|
+
to disable wait.
|
|
18
|
+
|
|
19
|
+
window_size : list, optional
|
|
20
|
+
Window size of the virtual frame buffer. Defaults to ``[1024, 768]``.
|
|
21
|
+
"""
|
|
22
|
+
import os
|
|
23
|
+
import time
|
|
24
|
+
|
|
25
|
+
if os.name != "posix":
|
|
26
|
+
raise OSError("`start_xvfb` is only supported on Linux")
|
|
27
|
+
|
|
28
|
+
if os.system("which Xvfb > /dev/null"):
|
|
29
|
+
raise OSError("Xvfb is not installed. Install it with: apt-get install xvfb libgl1-mesa-glx")
|
|
30
|
+
|
|
31
|
+
if window_size is None:
|
|
32
|
+
window_size = [1024, 768]
|
|
33
|
+
window_size_parm = f"{window_size[0]:d}x{window_size[1]:d}x24"
|
|
34
|
+
display_num = ":99"
|
|
35
|
+
os.system(f"Xvfb {display_num} -screen 0 {window_size_parm} > /dev/null 2>&1 &")
|
|
36
|
+
os.environ["DISPLAY"] = display_num
|
|
37
|
+
if wait:
|
|
38
|
+
time.sleep(wait)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_plotter(study_dir):
|
|
42
|
+
pyvista_file = sp.load_file(utils.get_asset_path("pyvista_mesh.vtu"))
|
|
43
|
+
test_image_path = study_dir / image_name
|
|
44
|
+
reference_image_path = utils.get_asset_path(image_name)
|
|
45
|
+
|
|
46
|
+
# Start virtual frame buffer, otherwise segfault
|
|
47
|
+
start_xvfb()
|
|
48
|
+
|
|
49
|
+
# Create a plotter and save the image
|
|
50
|
+
plotter = sp.viewer.PyvistaPlotter(off_screen=True)
|
|
51
|
+
plotter.add_mesh(pyvista_file)
|
|
52
|
+
plotter.show() # Not shown because off_screen=True
|
|
53
|
+
plotter.save(test_image_path)
|
|
54
|
+
|
|
55
|
+
test_image = Image.open(test_image_path)
|
|
56
|
+
reference_image = Image.open(reference_image_path)
|
|
57
|
+
diff = Image.new("RGB", image_size)
|
|
58
|
+
mismatch = pixelmatch(test_image, reference_image, diff)
|
|
59
|
+
assert mismatch == 0
|