micress-micpy 0.3.2b0__py3-none-any.whl → 0.4.0a1__py3-none-any.whl
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.
- micpy/MicressBinaryReader copy.py +130 -0
- micpy/MicressBinaryReader.py +263 -0
- micpy/__init__.py +0 -3
- micpy/bin copy.py +865 -0
- micpy/bin.py +535 -550
- micpy/paraview_plugin.py +221 -0
- micpy/paraview_plugin_stub.py +1 -0
- micpy/version.py +1 -1
- micpy/vtk.py +60 -0
- {micress_micpy-0.3.2b0.dist-info → micress_micpy-0.4.0a1.dist-info}/METADATA +16 -3
- micress_micpy-0.4.0a1.dist-info/RECORD +18 -0
- {micress_micpy-0.3.2b0.dist-info → micress_micpy-0.4.0a1.dist-info}/WHEEL +1 -1
- micress_micpy-0.3.2b0.dist-info/RECORD +0 -12
- {micress_micpy-0.3.2b0.dist-info → micress_micpy-0.4.0a1.dist-info/licenses}/LICENSE +0 -0
- {micress_micpy-0.3.2b0.dist-info → micress_micpy-0.4.0a1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import vtk
|
|
5
|
+
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
|
|
6
|
+
from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase
|
|
7
|
+
|
|
8
|
+
from micpy import bin as micpy_bin
|
|
9
|
+
from micpy import vtk as micpy_vtk
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
CONVERT_CELL_TO_POINT = False # keep it identical to your .vti baseline for now
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MicressBinaryReaderLastOnly(VTKPythonAlgorithmBase):
|
|
16
|
+
def __init__(self):
|
|
17
|
+
super().__init__(nInputPorts=0, nOutputPorts=1, outputType="vtkImageData")
|
|
18
|
+
self._filename: Optional[str] = None
|
|
19
|
+
self._file: Optional[micpy_bin.File] = None
|
|
20
|
+
self._array_name = "values"
|
|
21
|
+
self._cached_meta = None # (extent6, spacing3, origin3)
|
|
22
|
+
|
|
23
|
+
def SetFileName(self, filename: str) -> None:
|
|
24
|
+
filename = str(filename)
|
|
25
|
+
if filename == self._filename:
|
|
26
|
+
return
|
|
27
|
+
self._filename = filename
|
|
28
|
+
self._close()
|
|
29
|
+
self._cached_meta = None
|
|
30
|
+
self.Modified()
|
|
31
|
+
|
|
32
|
+
def _close(self):
|
|
33
|
+
if self._file is not None:
|
|
34
|
+
try:
|
|
35
|
+
self._file.close()
|
|
36
|
+
except Exception:
|
|
37
|
+
pass
|
|
38
|
+
self._file = None
|
|
39
|
+
|
|
40
|
+
def _open(self):
|
|
41
|
+
if self._file is None:
|
|
42
|
+
if not self._filename:
|
|
43
|
+
raise RuntimeError("No filename set.")
|
|
44
|
+
self._file = micpy_bin.File(self._filename).open()
|
|
45
|
+
|
|
46
|
+
def _get_image_and_meta(self):
|
|
47
|
+
"""Read last field and return (vtkImageData, meta)."""
|
|
48
|
+
self._open()
|
|
49
|
+
field = self._file._read_field(-1)
|
|
50
|
+
image = micpy_vtk.to_image(field, name=self._array_name)
|
|
51
|
+
|
|
52
|
+
if CONVERT_CELL_TO_POINT:
|
|
53
|
+
c2p = vtk.vtkCellDataToPointData()
|
|
54
|
+
c2p.SetInputData(image)
|
|
55
|
+
c2p.PassCellDataOn()
|
|
56
|
+
c2p.Update()
|
|
57
|
+
image = c2p.GetOutput()
|
|
58
|
+
|
|
59
|
+
extent = image.GetExtent() # 6-tuple
|
|
60
|
+
spacing = image.GetSpacing() # 3-tuple
|
|
61
|
+
origin = image.GetOrigin() # 3-tuple
|
|
62
|
+
meta = (
|
|
63
|
+
tuple(int(x) for x in extent),
|
|
64
|
+
tuple(float(x) for x in spacing),
|
|
65
|
+
tuple(float(x) for x in origin),
|
|
66
|
+
)
|
|
67
|
+
return image, meta
|
|
68
|
+
|
|
69
|
+
def RequestInformation(self, request, inInfo, outInfo):
|
|
70
|
+
if not self._filename:
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
# Cache metadata once so ParaView knows bounds/extents BEFORE RequestData renders.
|
|
74
|
+
if self._cached_meta is None:
|
|
75
|
+
_, self._cached_meta = self._get_image_and_meta()
|
|
76
|
+
|
|
77
|
+
extent, spacing, origin = self._cached_meta
|
|
78
|
+
info = outInfo.GetInformationObject(0)
|
|
79
|
+
|
|
80
|
+
# This is the critical piece for vtkImageData visibility in ParaView.
|
|
81
|
+
info.Set(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT(), extent, 6)
|
|
82
|
+
|
|
83
|
+
# These help ParaView compute bounds correctly.
|
|
84
|
+
info.Set(vtk.vtkDataObject.SPACING(), spacing, 3)
|
|
85
|
+
info.Set(vtk.vtkDataObject.ORIGIN(), origin, 3)
|
|
86
|
+
|
|
87
|
+
return 1
|
|
88
|
+
|
|
89
|
+
def RequestData(self, request, inInfo, outInfo):
|
|
90
|
+
image, meta = self._get_image_and_meta()
|
|
91
|
+
extent, spacing, origin = meta
|
|
92
|
+
|
|
93
|
+
output = vtk.vtkImageData.GetData(outInfo, 0)
|
|
94
|
+
|
|
95
|
+
# Copy structure & arrays explicitly (ParaView-friendly)
|
|
96
|
+
output.SetExtent(extent)
|
|
97
|
+
output.SetSpacing(spacing)
|
|
98
|
+
output.SetOrigin(origin)
|
|
99
|
+
output.AllocateScalars(image.GetScalarType(), 0) # no default scalars
|
|
100
|
+
|
|
101
|
+
output.GetPointData().ShallowCopy(image.GetPointData())
|
|
102
|
+
output.GetCellData().ShallowCopy(image.GetCellData())
|
|
103
|
+
output.Modified()
|
|
104
|
+
return 1
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# --- ParaView registration ---
|
|
108
|
+
try:
|
|
109
|
+
from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain
|
|
110
|
+
except Exception:
|
|
111
|
+
smproxy = smproperty = smdomain = None
|
|
112
|
+
|
|
113
|
+
if smproxy is not None:
|
|
114
|
+
|
|
115
|
+
@smproxy.reader(
|
|
116
|
+
name="MicressBinaryReaderLastOnly",
|
|
117
|
+
label="Micress Binary Reader (last only)",
|
|
118
|
+
extensions="mcr mcr.gz conc1",
|
|
119
|
+
file_description="MICRESS binary files",
|
|
120
|
+
)
|
|
121
|
+
class _R(MicressBinaryReaderLastOnly):
|
|
122
|
+
@smproperty.stringvector(
|
|
123
|
+
name="FileName",
|
|
124
|
+
command="SetFileName",
|
|
125
|
+
number_of_elements=1,
|
|
126
|
+
panel_visibility="default",
|
|
127
|
+
)
|
|
128
|
+
@smdomain.filelist()
|
|
129
|
+
def FileName(self):
|
|
130
|
+
return [self._filename or ""]
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
import vtk
|
|
6
|
+
from vtkmodules.vtkCommonExecutionModel import vtkStreamingDemandDrivenPipeline
|
|
7
|
+
from vtkmodules.util.vtkAlgorithm import VTKPythonAlgorithmBase
|
|
8
|
+
|
|
9
|
+
from micpy import bin as micpy_bin
|
|
10
|
+
from micpy import vtk as micpy_vtk
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
CONVERT_CELL_TO_POINT = False # keep identical to your .vti behavior for now
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MicressBinaryReader(VTKPythonAlgorithmBase):
|
|
17
|
+
"""ParaView reader for MICRESS files via micpy with timestepping."""
|
|
18
|
+
|
|
19
|
+
def __init__(self):
|
|
20
|
+
super().__init__(nInputPorts=0, nOutputPorts=1, outputType="vtkImageData")
|
|
21
|
+
|
|
22
|
+
self._filename: Optional[str] = None
|
|
23
|
+
self._file: Optional[micpy_bin.File] = None
|
|
24
|
+
|
|
25
|
+
self._array_name: str = "values"
|
|
26
|
+
|
|
27
|
+
# Cached metadata for ParaView (critical for vtkImageData)
|
|
28
|
+
self._cached_meta: Optional[
|
|
29
|
+
Tuple[
|
|
30
|
+
Tuple[int, int, int, int, int, int],
|
|
31
|
+
Tuple[float, float, float],
|
|
32
|
+
Tuple[float, float, float],
|
|
33
|
+
]
|
|
34
|
+
] = None # (extent6, spacing3, origin3)
|
|
35
|
+
|
|
36
|
+
# Time indexing
|
|
37
|
+
self._times: List[float] = []
|
|
38
|
+
self._time_to_index: Dict[float, int] = {}
|
|
39
|
+
|
|
40
|
+
# --- ParaView properties ---
|
|
41
|
+
|
|
42
|
+
def SetFileName(self, filename: str) -> None:
|
|
43
|
+
filename = str(filename)
|
|
44
|
+
if filename == self._filename:
|
|
45
|
+
return
|
|
46
|
+
self._filename = filename
|
|
47
|
+
self._reset()
|
|
48
|
+
self.Modified()
|
|
49
|
+
|
|
50
|
+
def SetArrayName(self, name: str) -> None:
|
|
51
|
+
name = str(name) if name else "values"
|
|
52
|
+
if name == self._array_name:
|
|
53
|
+
return
|
|
54
|
+
self._array_name = name
|
|
55
|
+
self.Modified()
|
|
56
|
+
|
|
57
|
+
# --- internal ---
|
|
58
|
+
|
|
59
|
+
def _reset(self) -> None:
|
|
60
|
+
if self._file is not None:
|
|
61
|
+
try:
|
|
62
|
+
self._file.close()
|
|
63
|
+
except Exception:
|
|
64
|
+
pass
|
|
65
|
+
self._file = None
|
|
66
|
+
self._cached_meta = None
|
|
67
|
+
self._times = []
|
|
68
|
+
self._time_to_index = {}
|
|
69
|
+
|
|
70
|
+
def _open(self) -> None:
|
|
71
|
+
if self._file is None:
|
|
72
|
+
if not self._filename:
|
|
73
|
+
raise RuntimeError("No filename set.")
|
|
74
|
+
self._file = micpy_bin.File(self._filename).open()
|
|
75
|
+
|
|
76
|
+
def _ensure_meta(self) -> None:
|
|
77
|
+
"""Cache extent/spacing/origin once (needed by ParaView to render ImageData)."""
|
|
78
|
+
if self._cached_meta is not None:
|
|
79
|
+
return
|
|
80
|
+
self._open()
|
|
81
|
+
|
|
82
|
+
# Use a single field to compute the image geometry.
|
|
83
|
+
# (Assumes geometry is constant across timesteps, which is typical for MICRESS.)
|
|
84
|
+
field0 = self._file._read_field(0)
|
|
85
|
+
image0 = micpy_vtk.to_image(field0, name=self._array_name)
|
|
86
|
+
|
|
87
|
+
if CONVERT_CELL_TO_POINT:
|
|
88
|
+
c2p = vtk.vtkCellDataToPointData()
|
|
89
|
+
c2p.SetInputData(image0)
|
|
90
|
+
c2p.PassCellDataOn()
|
|
91
|
+
c2p.Update()
|
|
92
|
+
image0 = c2p.GetOutput()
|
|
93
|
+
|
|
94
|
+
extent = tuple(int(x) for x in image0.GetExtent())
|
|
95
|
+
spacing = tuple(float(x) for x in image0.GetSpacing())
|
|
96
|
+
origin = tuple(float(x) for x in image0.GetOrigin())
|
|
97
|
+
|
|
98
|
+
self._cached_meta = (extent, spacing, origin)
|
|
99
|
+
|
|
100
|
+
def _ensure_times(self) -> None:
|
|
101
|
+
if self._times:
|
|
102
|
+
return
|
|
103
|
+
self._open()
|
|
104
|
+
|
|
105
|
+
times: List[float] = []
|
|
106
|
+
time_to_index: Dict[float, int] = {}
|
|
107
|
+
|
|
108
|
+
i = 0
|
|
109
|
+
while True:
|
|
110
|
+
try:
|
|
111
|
+
fld = self._file._read_field(i)
|
|
112
|
+
except (IndexError, EOFError):
|
|
113
|
+
break
|
|
114
|
+
|
|
115
|
+
tt = float(fld.time)
|
|
116
|
+
times.append(tt)
|
|
117
|
+
if tt not in time_to_index:
|
|
118
|
+
time_to_index[tt] = i
|
|
119
|
+
i += 1
|
|
120
|
+
|
|
121
|
+
self._times = times
|
|
122
|
+
self._time_to_index = time_to_index
|
|
123
|
+
|
|
124
|
+
# Temporary debug (prints to ParaView output console)
|
|
125
|
+
print(
|
|
126
|
+
"MicressBinaryReader: discovered",
|
|
127
|
+
len(self._times),
|
|
128
|
+
"timesteps:",
|
|
129
|
+
self._times,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def _get_index_for_requested_time(
|
|
133
|
+
self, req_t: Optional[float]
|
|
134
|
+
) -> Tuple[int, float]:
|
|
135
|
+
"""Return (index, chosen_time) where chosen_time is exactly one of self._times."""
|
|
136
|
+
self._ensure_times()
|
|
137
|
+
|
|
138
|
+
if not self._times:
|
|
139
|
+
return 0, 0.0
|
|
140
|
+
|
|
141
|
+
if req_t is None:
|
|
142
|
+
chosen_t = self._times[0]
|
|
143
|
+
return 0, float(chosen_t)
|
|
144
|
+
|
|
145
|
+
# Choose nearest actual timestep
|
|
146
|
+
req_t = float(req_t)
|
|
147
|
+
chosen_t = min(self._times, key=lambda x: abs(x - req_t))
|
|
148
|
+
idx = self._time_to_index.get(chosen_t)
|
|
149
|
+
if idx is None:
|
|
150
|
+
# Fallback if mapping policy changed / duplicates: linear search
|
|
151
|
+
idx = self._times.index(chosen_t)
|
|
152
|
+
return int(idx), float(chosen_t)
|
|
153
|
+
|
|
154
|
+
def _read_image_at_index(self, idx: int) -> vtk.vtkImageData:
|
|
155
|
+
"""Read field idx and convert to vtkImageData."""
|
|
156
|
+
self._open()
|
|
157
|
+
field = self._file._read_field(idx)
|
|
158
|
+
image = micpy_vtk.to_image(field, name=self._array_name)
|
|
159
|
+
|
|
160
|
+
if CONVERT_CELL_TO_POINT:
|
|
161
|
+
c2p = vtk.vtkCellDataToPointData()
|
|
162
|
+
c2p.SetInputData(image)
|
|
163
|
+
c2p.PassCellDataOn()
|
|
164
|
+
c2p.Update()
|
|
165
|
+
image = c2p.GetOutput()
|
|
166
|
+
|
|
167
|
+
return image
|
|
168
|
+
|
|
169
|
+
# --- VTK pipeline ---
|
|
170
|
+
|
|
171
|
+
def RequestInformation(self, request, inInfo, outInfo):
|
|
172
|
+
if not self._filename:
|
|
173
|
+
return 1
|
|
174
|
+
|
|
175
|
+
self._ensure_meta()
|
|
176
|
+
self._ensure_times()
|
|
177
|
+
|
|
178
|
+
info = outInfo.GetInformationObject(0)
|
|
179
|
+
|
|
180
|
+
# Required for ImageData to render correctly in ParaView
|
|
181
|
+
extent, spacing, origin = self._cached_meta
|
|
182
|
+
info.Set(vtkStreamingDemandDrivenPipeline.WHOLE_EXTENT(), extent, 6)
|
|
183
|
+
info.Set(vtk.vtkDataObject.SPACING(), spacing, 3)
|
|
184
|
+
info.Set(vtk.vtkDataObject.ORIGIN(), origin, 3)
|
|
185
|
+
|
|
186
|
+
# Timesteps
|
|
187
|
+
if self._times:
|
|
188
|
+
ts = [float(x) for x in self._times]
|
|
189
|
+
vtkStreamingDemandDrivenPipeline.TIME_STEPS().Set(info, ts, len(ts))
|
|
190
|
+
|
|
191
|
+
tr = [float(min(ts)), float(max(ts))]
|
|
192
|
+
vtkStreamingDemandDrivenPipeline.TIME_RANGE().Set(info, tr, 2)
|
|
193
|
+
|
|
194
|
+
return 1
|
|
195
|
+
|
|
196
|
+
def RequestData(self, request, inInfo, outInfo):
|
|
197
|
+
if not self._filename:
|
|
198
|
+
raise RuntimeError("No filename set.")
|
|
199
|
+
|
|
200
|
+
self._ensure_meta()
|
|
201
|
+
|
|
202
|
+
info = outInfo.GetInformationObject(0)
|
|
203
|
+
output = vtk.vtkImageData.GetData(outInfo, 0)
|
|
204
|
+
|
|
205
|
+
req_t = None
|
|
206
|
+
if info.Has(vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()):
|
|
207
|
+
req_t = float(info.Get(vtkStreamingDemandDrivenPipeline.UPDATE_TIME_STEP()))
|
|
208
|
+
|
|
209
|
+
idx, chosen_t = self._get_index_for_requested_time(req_t)
|
|
210
|
+
image = self._read_image_at_index(idx)
|
|
211
|
+
|
|
212
|
+
# Copy structure + arrays explicitly (ParaView-friendly)
|
|
213
|
+
extent, spacing, origin = self._cached_meta
|
|
214
|
+
output.SetExtent(extent)
|
|
215
|
+
output.SetSpacing(spacing)
|
|
216
|
+
output.SetOrigin(origin)
|
|
217
|
+
output.AllocateScalars(image.GetScalarType(), 0) # no default scalars
|
|
218
|
+
|
|
219
|
+
output.GetPointData().ShallowCopy(image.GetPointData())
|
|
220
|
+
output.GetCellData().ShallowCopy(image.GetCellData())
|
|
221
|
+
output.Modified()
|
|
222
|
+
|
|
223
|
+
# Crucial: report the exact time step we produced (avoids "0.00 (?)")
|
|
224
|
+
output.GetInformation().Set(vtk.vtkDataObject.DATA_TIME_STEP(), float(chosen_t))
|
|
225
|
+
|
|
226
|
+
return 1
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ---- ParaView registration ----
|
|
230
|
+
try:
|
|
231
|
+
from paraview.util.vtkAlgorithm import smproxy, smproperty, smdomain
|
|
232
|
+
except Exception:
|
|
233
|
+
smproxy = smproperty = smdomain = None
|
|
234
|
+
|
|
235
|
+
if smproxy is not None:
|
|
236
|
+
|
|
237
|
+
@smproxy.reader(
|
|
238
|
+
name="MicressBinaryReader",
|
|
239
|
+
label="Micress Binary Reader (micpy)",
|
|
240
|
+
extensions="mcr mcr.gz conc1",
|
|
241
|
+
file_description="MICRESS binary files",
|
|
242
|
+
)
|
|
243
|
+
class _MicressBinaryReader(MicressBinaryReader):
|
|
244
|
+
|
|
245
|
+
@smproperty.stringvector(
|
|
246
|
+
name="FileName",
|
|
247
|
+
command="SetFileName",
|
|
248
|
+
number_of_elements=1,
|
|
249
|
+
panel_visibility="default",
|
|
250
|
+
)
|
|
251
|
+
@smdomain.filelist()
|
|
252
|
+
def FileName(self):
|
|
253
|
+
return [self._filename or ""]
|
|
254
|
+
|
|
255
|
+
# Optional; remove if you want it even more minimal
|
|
256
|
+
@smproperty.stringvector(
|
|
257
|
+
name="ArrayName",
|
|
258
|
+
command="SetArrayName",
|
|
259
|
+
number_of_elements=1,
|
|
260
|
+
default_values=["values"],
|
|
261
|
+
)
|
|
262
|
+
def ArrayName(self):
|
|
263
|
+
return [self._array_name]
|