py-pluto 1.1.4__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.
- pyPLUTO/__init__.py +22 -0
- pyPLUTO/amr.py +745 -0
- pyPLUTO/baseloadmixin.py +258 -0
- pyPLUTO/baseloadstate.py +45 -0
- pyPLUTO/codes/echo_load.py +161 -0
- pyPLUTO/configure.py +261 -0
- pyPLUTO/gui/config.py +174 -0
- pyPLUTO/gui/custom_var.py +435 -0
- pyPLUTO/gui/globals.py +108 -0
- pyPLUTO/gui/main.py +17 -0
- pyPLUTO/gui/main_window.py +177 -0
- pyPLUTO/gui/panels.py +66 -0
- pyPLUTO/gui/utils.py +273 -0
- pyPLUTO/h_pypluto.py +84 -0
- pyPLUTO/image.py +302 -0
- pyPLUTO/imagefuncs/colorbar.py +240 -0
- pyPLUTO/imagefuncs/contour.py +254 -0
- pyPLUTO/imagefuncs/create_axes.py +464 -0
- pyPLUTO/imagefuncs/display.py +306 -0
- pyPLUTO/imagefuncs/figure.py +395 -0
- pyPLUTO/imagefuncs/imagetools.py +487 -0
- pyPLUTO/imagefuncs/interactive.py +403 -0
- pyPLUTO/imagefuncs/legend.py +250 -0
- pyPLUTO/imagefuncs/plot.py +311 -0
- pyPLUTO/imagefuncs/range.py +242 -0
- pyPLUTO/imagefuncs/scatter.py +270 -0
- pyPLUTO/imagefuncs/set_axis.py +497 -0
- pyPLUTO/imagefuncs/streamplot.py +297 -0
- pyPLUTO/imagefuncs/zoom.py +428 -0
- pyPLUTO/imagemixin.py +259 -0
- pyPLUTO/imagestate.py +45 -0
- pyPLUTO/load.py +447 -0
- pyPLUTO/loadfuncs/baseloadtools.py +71 -0
- pyPLUTO/loadfuncs/codeselection.py +48 -0
- pyPLUTO/loadfuncs/defpluto.py +123 -0
- pyPLUTO/loadfuncs/descriptor.py +102 -0
- pyPLUTO/loadfuncs/findfiles.py +182 -0
- pyPLUTO/loadfuncs/findformat.py +245 -0
- pyPLUTO/loadfuncs/initload.py +203 -0
- pyPLUTO/loadfuncs/loadvars.py +227 -0
- pyPLUTO/loadfuncs/offsetdata.py +87 -0
- pyPLUTO/loadfuncs/offsetfluid.py +408 -0
- pyPLUTO/loadfuncs/read_files.py +213 -0
- pyPLUTO/loadfuncs/readdata.py +619 -0
- pyPLUTO/loadfuncs/readdata_old.py +567 -0
- pyPLUTO/loadfuncs/readdefplini.py +101 -0
- pyPLUTO/loadfuncs/readfluid.py +479 -0
- pyPLUTO/loadfuncs/readformat.py +277 -0
- pyPLUTO/loadfuncs/readgridalone.py +224 -0
- pyPLUTO/loadfuncs/readgridfile.py +255 -0
- pyPLUTO/loadfuncs/readgridout.py +451 -0
- pyPLUTO/loadfuncs/readpart.py +419 -0
- pyPLUTO/loadfuncs/readtab.py +105 -0
- pyPLUTO/loadfuncs/write_files.py +283 -0
- pyPLUTO/loadmixin.py +419 -0
- pyPLUTO/loadpart.py +233 -0
- pyPLUTO/loadstate.py +68 -0
- pyPLUTO/newload.py +81 -0
- pyPLUTO/pytools.py +145 -0
- pyPLUTO/toolfuncs/findlines.py +551 -0
- pyPLUTO/toolfuncs/fourier.py +149 -0
- pyPLUTO/toolfuncs/nabla.py +676 -0
- pyPLUTO/toolfuncs/parttools.py +152 -0
- pyPLUTO/toolfuncs/transform.py +638 -0
- pyPLUTO/utils/annotator.py +27 -0
- pyPLUTO/utils/inspector.py +145 -0
- pyPLUTO/utils/make_docstrings.py +3 -0
- py_pluto-1.1.4.dist-info/METADATA +218 -0
- py_pluto-1.1.4.dist-info/RECORD +73 -0
- py_pluto-1.1.4.dist-info/WHEEL +5 -0
- py_pluto-1.1.4.dist-info/entry_points.txt +2 -0
- py_pluto-1.1.4.dist-info/licenses/LICENSE +27 -0
- py_pluto-1.1.4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
from ..h_pypluto import makelist
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _load_variables(
|
|
10
|
+
self,
|
|
11
|
+
vars: str | list[str] | bool | None,
|
|
12
|
+
i: int,
|
|
13
|
+
exout: int,
|
|
14
|
+
endian: str | None,
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Loads the variables in the class. The function checks if the
|
|
17
|
+
variables to be loaded are valid and then loads them. If the
|
|
18
|
+
variables are not valid, an error is raised. If the variables are
|
|
19
|
+
valid, the function loads them in the class through memory mapping.
|
|
20
|
+
The offset and shape of each variable is computed depenging on the
|
|
21
|
+
format and typefile characteristics. In case the files are
|
|
22
|
+
standalone, the relevand time and grid information is loaded.
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
- None
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
- endian (not optional): bool
|
|
31
|
+
The endianess of the files. If True the endianess is big, otherwise it
|
|
32
|
+
is little.
|
|
33
|
+
- exout (not optional): int
|
|
34
|
+
The index of the output to be loaded.
|
|
35
|
+
- i (not optional): int
|
|
36
|
+
The index of the file to be loaded.
|
|
37
|
+
- vars (not optional): str | list[str] | bool | None, default True
|
|
38
|
+
If True all the variables are loaded, otherwise just a selection is
|
|
39
|
+
loaded.
|
|
40
|
+
|
|
41
|
+
----
|
|
42
|
+
|
|
43
|
+
Examples
|
|
44
|
+
--------
|
|
45
|
+
- Example #1: Load all the variables
|
|
46
|
+
|
|
47
|
+
>>> _load_variables(True, 0, 0, True)
|
|
48
|
+
|
|
49
|
+
- Example #2: Load only the selected variables
|
|
50
|
+
|
|
51
|
+
>>> _load_variables(["rho", "vx1"], 0, 0, True)
|
|
52
|
+
|
|
53
|
+
- Example #3: Load all the variables (little endian)
|
|
54
|
+
|
|
55
|
+
>>> _load_variables(True, 0, 0, False)
|
|
56
|
+
|
|
57
|
+
- Example #4: Load all the variables from a specific output file
|
|
58
|
+
|
|
59
|
+
>>> _load_variables(True, 0, 1, True)
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
# Find the class name and find the single_file filepath
|
|
63
|
+
class_name: str = self.__class__.__name__
|
|
64
|
+
if class_name == "Load":
|
|
65
|
+
# If the class name is Load (single file), the filepath is data
|
|
66
|
+
self._filepath = self.pathdir / ("data" + self._d_info["endpath"][i])
|
|
67
|
+
elif class_name == "LoadPart":
|
|
68
|
+
# If the class name is LoadPart, the filepath is particles
|
|
69
|
+
self._filepath = self.pathdir / (
|
|
70
|
+
"particles" + self._d_info["endpath"][i]
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
# If the class name is not recognized, raise an error
|
|
74
|
+
raise NameError("Invalid class name.")
|
|
75
|
+
|
|
76
|
+
# If files in single_file format, inspect the file
|
|
77
|
+
# or compute the offset and shape
|
|
78
|
+
if self._d_info["typefile"][i] == "single_file":
|
|
79
|
+
self._compute_offset(i, endian, exout, None)
|
|
80
|
+
if self.format == "hdf5":
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
# Check if only specific variables should be loaded
|
|
84
|
+
if vars is True:
|
|
85
|
+
# If all the variables are to be loaded, the load_vars
|
|
86
|
+
# is set to the variables list
|
|
87
|
+
self._load_vars = self._d_info["varslist"][i]
|
|
88
|
+
elif vars is not None:
|
|
89
|
+
# If only specific variables are to be loaded, the load_vars
|
|
90
|
+
# becomes the list of the selected variables
|
|
91
|
+
self._load_vars = makelist(vars)
|
|
92
|
+
else:
|
|
93
|
+
# If no variables are to be loaded, return None (WIP)
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
# If the format is tab, the data have been already loaded, so
|
|
97
|
+
# the function returns None
|
|
98
|
+
if self.format == "tab":
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
# ERROR: TOO MANY OPEN FILES!!!
|
|
102
|
+
# This chunk of code is here simply to show how the variables were loaded
|
|
103
|
+
# in a preliminary version of the memory mapping procedure.
|
|
104
|
+
"""
|
|
105
|
+
# Loop over the variables to be loaded
|
|
106
|
+
for j in self._load_vars:
|
|
107
|
+
|
|
108
|
+
# Change filepath, offset and shape in case of multiple_files
|
|
109
|
+
if self._d_info['typefile'][i] == 'multiple_files':
|
|
110
|
+
# If the files are multiple, the filepath is changed for each
|
|
111
|
+
# variable and the offset and shape are computed
|
|
112
|
+
self._filepath = self.pathdir / (j + self._d_info['endpath'][i])
|
|
113
|
+
self._compute_offset(i, endian, exout, j)
|
|
114
|
+
|
|
115
|
+
# Initialize the variables dictionary
|
|
116
|
+
self._init_vardict(j) if self._lennout != 1 else None
|
|
117
|
+
|
|
118
|
+
# Load the variable through memory mapping and store them in the class
|
|
119
|
+
scrh = np.memmap(self._filepath,self._d_info['binformat'][i],mode="r+",
|
|
120
|
+
offset=self._offset[j], shape = self._shape[j]).T
|
|
121
|
+
self._assign_var(exout, j, scrh)
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
# Compute the byte range for all variables in the current loop
|
|
126
|
+
if self._d_info["typefile"][i] == "single_file" and class_name == "Load":
|
|
127
|
+
start_byte = min(self._offset[j] for j in self._load_vars)
|
|
128
|
+
end_byte = max(
|
|
129
|
+
self._offset[j]
|
|
130
|
+
+ np.prod(self._shape[j])
|
|
131
|
+
* np.dtype(self._d_info["binformat"][i]).itemsize
|
|
132
|
+
for j in self._load_vars
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Create a single memmap spanning the required byte range
|
|
136
|
+
# This is the original code that was used to load the variables
|
|
137
|
+
|
|
138
|
+
# file_memmap = np.memmap(
|
|
139
|
+
# self._filepath,
|
|
140
|
+
# dtype=self._d_info["binformat"][i],
|
|
141
|
+
# mode="r+",
|
|
142
|
+
# offset=start_byte,
|
|
143
|
+
# shape=(end_byte - start_byte),
|
|
144
|
+
# )
|
|
145
|
+
|
|
146
|
+
file_memmap = np.memmap(
|
|
147
|
+
self._filepath,
|
|
148
|
+
dtype="uint8", # Raw byte access for all data types
|
|
149
|
+
mode="r+",
|
|
150
|
+
offset=start_byte,
|
|
151
|
+
shape=(end_byte - start_byte),
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Loop over the variables to extract slices
|
|
155
|
+
for j in self._load_vars:
|
|
156
|
+
if self._d_info["typefile"][i] == "multiple_files":
|
|
157
|
+
self._filepath = self.pathdir / (j + self._d_info["endpath"][i])
|
|
158
|
+
self._compute_offset(i, endian, exout, j)
|
|
159
|
+
start_byte = self._offset[j]
|
|
160
|
+
# Reload memmap for the new file
|
|
161
|
+
file_memmap = np.memmap(
|
|
162
|
+
self._filepath,
|
|
163
|
+
self._d_info["binformat"][i],
|
|
164
|
+
mode="r+",
|
|
165
|
+
offset=self._offset[j],
|
|
166
|
+
shape=self._shape[j],
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Initialize the variables dictionary
|
|
170
|
+
self._init_vardict(j) if self._lennout != 1 else None
|
|
171
|
+
|
|
172
|
+
# Extract the relevant slice and reshape
|
|
173
|
+
if class_name == "Load":
|
|
174
|
+
# Calculate the relative start and end for this variable
|
|
175
|
+
rel_start = (
|
|
176
|
+
self._offset[j] - start_byte
|
|
177
|
+
) # Offset relative to the memory-mapped file
|
|
178
|
+
rel_end = (
|
|
179
|
+
rel_start
|
|
180
|
+
+ np.prod(self._shape[j])
|
|
181
|
+
* np.dtype(self._d_info["binformat"][i]).itemsize
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
# Step 3: Extract the raw data slice from the memory map
|
|
185
|
+
raw_data = file_memmap[rel_start:rel_end]
|
|
186
|
+
|
|
187
|
+
# View the slice with the desired dtype and reshape
|
|
188
|
+
scrh = np.ndarray(
|
|
189
|
+
shape=self._shape[j],
|
|
190
|
+
dtype=self._d_info["binformat"][i],
|
|
191
|
+
buffer=raw_data,
|
|
192
|
+
).T
|
|
193
|
+
|
|
194
|
+
# Calculate the relative offset within the mapped range
|
|
195
|
+
# rel_start = (self._offset[j] - start_byte) // file_memmap.itemsize
|
|
196
|
+
# rel_end = rel_start + np.prod(self._shape[j])
|
|
197
|
+
# scrh = file_memmap[rel_start:rel_end].reshape(self._shape[j]).T
|
|
198
|
+
|
|
199
|
+
elif class_name == "LoadPart":
|
|
200
|
+
scrh = np.memmap(
|
|
201
|
+
self._filepath,
|
|
202
|
+
self._d_info["binformat"][i],
|
|
203
|
+
mode="r+",
|
|
204
|
+
offset=self._offset[j],
|
|
205
|
+
shape=self._shape[j],
|
|
206
|
+
).T
|
|
207
|
+
|
|
208
|
+
# Assign the variable
|
|
209
|
+
self._assign_var(exout, j, scrh)
|
|
210
|
+
|
|
211
|
+
# End of function
|
|
212
|
+
return None
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def _check_nout(self, nout: int | str | list[int | str]) -> None:
|
|
216
|
+
"""Finds the number of datafile to be loaded. If nout is a list, the
|
|
217
|
+
function checks if the list contains the keyword 'last' or -1. If
|
|
218
|
+
so, the keyword is replaced with the last file number. If nout is a
|
|
219
|
+
string, the function checks if the string contains the keyword
|
|
220
|
+
'last' or -1. If so, the keyword is replaced with the last file
|
|
221
|
+
number. If nout is an integer, the function returns a list
|
|
222
|
+
containing the integer. If nout is 'all', the function returns a
|
|
223
|
+
list containing all the file numbers.
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
- None
|
|
228
|
+
|
|
229
|
+
Parameters
|
|
230
|
+
----------
|
|
231
|
+
- nout (not optional): int | str | list[int|str]
|
|
232
|
+
The output file to be loaded.
|
|
233
|
+
|
|
234
|
+
----
|
|
235
|
+
|
|
236
|
+
Examples
|
|
237
|
+
--------
|
|
238
|
+
- Example #1: Load the last file
|
|
239
|
+
|
|
240
|
+
>>> _check_nout("last")
|
|
241
|
+
|
|
242
|
+
- Example #2: Load the first file
|
|
243
|
+
|
|
244
|
+
>>> _check_nout(0)
|
|
245
|
+
|
|
246
|
+
- Example #3: Load all the files
|
|
247
|
+
|
|
248
|
+
>>> _check_nout("all")
|
|
249
|
+
|
|
250
|
+
- Example #4: Load multiple specific files
|
|
251
|
+
|
|
252
|
+
>>> _check_nout([0, 1, 2, 3])
|
|
253
|
+
|
|
254
|
+
"""
|
|
255
|
+
# Assign the last possible output file
|
|
256
|
+
last: int = self.outlist.tolist()[-1]
|
|
257
|
+
|
|
258
|
+
# Check if nout is a list and change the keywords
|
|
259
|
+
if not isinstance(nout, list):
|
|
260
|
+
# If nout is a string, get the keywords
|
|
261
|
+
Dnout = {nout: nout, "last": last, -1: last, "all": self.outlist}[nout]
|
|
262
|
+
else:
|
|
263
|
+
# If nout is a list, replace the keywords
|
|
264
|
+
Dnout = [last if i in {"last", -1} else i for i in nout]
|
|
265
|
+
|
|
266
|
+
# Sort the list, compute the corresponding time and store its length
|
|
267
|
+
self.nout = np.sort(np.unique(np.atleast_1d(Dnout)))
|
|
268
|
+
|
|
269
|
+
# Check if the output files are in the list
|
|
270
|
+
if np.any(~np.isin(self.nout, self.outlist)):
|
|
271
|
+
raise ValueError(
|
|
272
|
+
f"Error: Wrong output file(s) {self.nout} \
|
|
273
|
+
in path {self.pathdir}."
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
# End of the function
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _findfiles(self, nout: int | str | list[int | str]) -> None:
|
|
280
|
+
"""Finds the files to be loaded. If nout is a list, the function
|
|
281
|
+
loops over the list and finds the corresponding files. If nout is an
|
|
282
|
+
integer, the function finds the corresponding file. If nout is
|
|
283
|
+
'last', the function finds the last file. If nout is 'all', the
|
|
284
|
+
function finds all the files. Then, the function stores the relevant
|
|
285
|
+
information in a dictionary _d_info.
|
|
286
|
+
|
|
287
|
+
Returns
|
|
288
|
+
-------
|
|
289
|
+
- None
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
- nout (not optional): int | str | list[int|str]
|
|
294
|
+
The output file to be loaded
|
|
295
|
+
|
|
296
|
+
----
|
|
297
|
+
|
|
298
|
+
Examples
|
|
299
|
+
--------
|
|
300
|
+
- Example #1: Load the last file
|
|
301
|
+
|
|
302
|
+
>>> _findfiles("last")
|
|
303
|
+
|
|
304
|
+
- Example #2: Load the first file
|
|
305
|
+
|
|
306
|
+
>>> _findfiles(0)
|
|
307
|
+
|
|
308
|
+
- Example #3: Load all the files
|
|
309
|
+
|
|
310
|
+
>>> _findfiles("all")
|
|
311
|
+
|
|
312
|
+
- Example #4: Load multiple specific files
|
|
313
|
+
|
|
314
|
+
>>> _findfiles([0, 1, 2, 3])
|
|
315
|
+
|
|
316
|
+
"""
|
|
317
|
+
# Initialization or declaration of variables
|
|
318
|
+
class_name = self.__class__.__name__ # The class name
|
|
319
|
+
self.set_vars = set()
|
|
320
|
+
self.set_outs = set()
|
|
321
|
+
|
|
322
|
+
# Loop over the matching files and call the functions
|
|
323
|
+
for elem in self._matching_files:
|
|
324
|
+
# varsouts[condition](self, elem)
|
|
325
|
+
_varsouts(self, elem, class_name)
|
|
326
|
+
|
|
327
|
+
# Check if the files are present
|
|
328
|
+
if len(self.set_vars) == 0:
|
|
329
|
+
raise FileNotFoundError(f"No files found in {self.pathdir}!")
|
|
330
|
+
|
|
331
|
+
# Sort the outputs in an array and check the number of outputs
|
|
332
|
+
self.outlist = np.array(sorted(self.set_outs))
|
|
333
|
+
self._check_nout(nout)
|
|
334
|
+
|
|
335
|
+
# Store the number of outputs and the time
|
|
336
|
+
self._lennout = len(self.nout)
|
|
337
|
+
self.ntime = np.empty(self._lennout)
|
|
338
|
+
|
|
339
|
+
# Initialize the info dictionary and initialize some relevant variables
|
|
340
|
+
self._d_info = {}
|
|
341
|
+
self._d_info["typefile"] = np.empty(self._lennout, dtype="U20")
|
|
342
|
+
self._d_info["endianess"] = np.empty(self._lennout, dtype="U20")
|
|
343
|
+
self._d_info["binformat"] = np.empty(self._lennout, dtype="U20")
|
|
344
|
+
|
|
345
|
+
if class_name == "LoadPart":
|
|
346
|
+
# Check if the particles file is present
|
|
347
|
+
if "particles" not in self.set_vars:
|
|
348
|
+
raise FileNotFoundError(
|
|
349
|
+
f"file particles.*.{self.format} \
|
|
350
|
+
not found!"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
# Particles are always 'single_file', initialize additional variables
|
|
354
|
+
self._d_info["typefile"][:] = "single_file"
|
|
355
|
+
self._d_info["varslist"] = [[] for _ in range(self._lennout)]
|
|
356
|
+
self._d_info["varskeys"] = [[] for _ in range(self._lennout)]
|
|
357
|
+
|
|
358
|
+
# Check if we are loading a single file (to be fixed)
|
|
359
|
+
if self._lennout != 1:
|
|
360
|
+
# Particles can be read only at a single fixed time
|
|
361
|
+
raise NotImplementedError("multiple loading not implemented yet")
|
|
362
|
+
|
|
363
|
+
elif class_name == "Load":
|
|
364
|
+
# Check if the fluid files are present as multiple files
|
|
365
|
+
if "data" not in self.set_vars or self._multiple is True:
|
|
366
|
+
# If the files are multiple, the typefile is set to 'multiple_files'
|
|
367
|
+
self._d_info["typefile"][:] = "multiple_files"
|
|
368
|
+
self._d_info["varslist"] = np.empty(
|
|
369
|
+
(self._lennout, len(self.set_vars)), dtype="U20"
|
|
370
|
+
)
|
|
371
|
+
self._d_info["varslist"][:] = list(self.set_vars)
|
|
372
|
+
else:
|
|
373
|
+
# If the files are single, the typefile is set to 'single_file'
|
|
374
|
+
self._d_info["typefile"][:] = "single_file"
|
|
375
|
+
self._d_info["varslist"] = [[] for _ in range(self._lennout)]
|
|
376
|
+
|
|
377
|
+
else:
|
|
378
|
+
# If the class name is not recognized, raise an error
|
|
379
|
+
raise NameError("Invalid class name.")
|
|
380
|
+
|
|
381
|
+
# Compute the endpath
|
|
382
|
+
if self.nfile_lp is None:
|
|
383
|
+
# If the number of LP files is not given, the format is standard
|
|
384
|
+
format_string = f".%04d.{self.format}"
|
|
385
|
+
else:
|
|
386
|
+
# If the number of LP files is given, the format is different
|
|
387
|
+
format_string = f".%04d_ch{self.nfile_lp:02d}.{self.format}"
|
|
388
|
+
self._d_info["endpath"] = np.char.mod(format_string, self.nout)
|
|
389
|
+
|
|
390
|
+
# End of the function
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def _init_vardict(self, var: str) -> None:
|
|
394
|
+
"""If not initialized, a new dictionary is created to store the
|
|
395
|
+
variables. The dictionary is stored in the class. The shape of the
|
|
396
|
+
dictionary is computed depending on the number of outputs and the
|
|
397
|
+
shape of the variable.
|
|
398
|
+
|
|
399
|
+
Returns
|
|
400
|
+
-------
|
|
401
|
+
- None
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
- var (not optional): str
|
|
406
|
+
The variable to be loaded.
|
|
407
|
+
|
|
408
|
+
----
|
|
409
|
+
|
|
410
|
+
Examples
|
|
411
|
+
--------
|
|
412
|
+
- Example #1: Initialize the dictionary of a non-initialized variable
|
|
413
|
+
|
|
414
|
+
>>> _init_vardict("rho")
|
|
415
|
+
|
|
416
|
+
"""
|
|
417
|
+
# If the variable is not initialized, create a new dictionary
|
|
418
|
+
if var not in self._d_vars.keys():
|
|
419
|
+
self._d_vars[var] = {}
|
|
420
|
+
"""
|
|
421
|
+
if isinstance(self._shape[var], tuple):
|
|
422
|
+
# If the shape is a tuple, the shape is reversed
|
|
423
|
+
sh_type = self._shape[var][::-1]
|
|
424
|
+
else:
|
|
425
|
+
# If the shape is not a tuple, the shape is converted to a tuple
|
|
426
|
+
sh_type = (self._shape[var],)
|
|
427
|
+
|
|
428
|
+
if self.__class__.__name__ == 'LoadPart':
|
|
429
|
+
varsh = self._dictdim[var]
|
|
430
|
+
# If the particles have multidimensional vars, the shape is changed
|
|
431
|
+
shape = (varsh,) + sh_type if varsh != 1 else sh_type
|
|
432
|
+
else:
|
|
433
|
+
# If multiple fluid files are loaded, the shape is changed
|
|
434
|
+
shape = (self._lennout,) + sh_type if self._lennout != 1 \
|
|
435
|
+
else sh_type
|
|
436
|
+
|
|
437
|
+
# Create a temporary file to store the data
|
|
438
|
+
with tempfile.NamedTemporaryFile() as temp_file:
|
|
439
|
+
# Create the dictionary key and fill the values with nan
|
|
440
|
+
self._d_vars[var] = np.memmap(temp_file, \
|
|
441
|
+
mode='w+', \
|
|
442
|
+
dtype=np.float32, \
|
|
443
|
+
shape = shape)
|
|
444
|
+
self._d_vars[var][:] = np.nan
|
|
445
|
+
"""
|
|
446
|
+
|
|
447
|
+
# End of the function
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _assign_var(self, time: int, var: str, scrh: np.memmap) -> None:
|
|
451
|
+
"""Assigns the memmap object to the dictionary. If the number of
|
|
452
|
+
outputs is 1, the variable is stored directly in the dictionary,
|
|
453
|
+
otherwise the variable is stored in the dictionary at the
|
|
454
|
+
corresponding output.
|
|
455
|
+
|
|
456
|
+
Returns
|
|
457
|
+
-------
|
|
458
|
+
- None
|
|
459
|
+
|
|
460
|
+
Parameters
|
|
461
|
+
----------
|
|
462
|
+
- scrh (not optional): np.memmap
|
|
463
|
+
The memmap object containing the data to be stored.
|
|
464
|
+
- time (not optional): int
|
|
465
|
+
The output file to be loaded
|
|
466
|
+
- var (not optional): str
|
|
467
|
+
The variable to be loaded.
|
|
468
|
+
|
|
469
|
+
----
|
|
470
|
+
|
|
471
|
+
Examples
|
|
472
|
+
--------
|
|
473
|
+
- Example #1: Assign the variable to the dictionary (single output time)
|
|
474
|
+
|
|
475
|
+
>>> _assign_var(3, "rho", scrh)
|
|
476
|
+
|
|
477
|
+
- Example #2: Assign the variable to the dictionary (multiple output times)
|
|
478
|
+
|
|
479
|
+
>>> _assign_var(1, "rho", scrh)
|
|
480
|
+
|
|
481
|
+
"""
|
|
482
|
+
# Assign the memmap object to the dictionary
|
|
483
|
+
if self._lennout != 1:
|
|
484
|
+
# If the number of outputs is not 1, the variable is stored at the
|
|
485
|
+
# corresponding output
|
|
486
|
+
self._d_vars[var][time] = scrh
|
|
487
|
+
else:
|
|
488
|
+
# If the number of outputs is 1, the variable is stored directly
|
|
489
|
+
self._d_vars[var] = scrh
|
|
490
|
+
|
|
491
|
+
# End of the function
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _varsouts(self, elem: str, class_name: str) -> None:
|
|
495
|
+
"""From the matching files finds the variables and the outputs for
|
|
496
|
+
the fluid and particles files (variables are to be intended here as
|
|
497
|
+
the first part of the output filename, they are the effective
|
|
498
|
+
variables only in case of multiple fluid files).
|
|
499
|
+
|
|
500
|
+
Returns
|
|
501
|
+
-------
|
|
502
|
+
- None
|
|
503
|
+
|
|
504
|
+
Parameters
|
|
505
|
+
----------
|
|
506
|
+
- class_name (not optional): str
|
|
507
|
+
The name of the class. Supported classes are 'Load' or 'LoadPart'.
|
|
508
|
+
- elem (not optional): str
|
|
509
|
+
The matching file.
|
|
510
|
+
|
|
511
|
+
----
|
|
512
|
+
|
|
513
|
+
Examples
|
|
514
|
+
--------
|
|
515
|
+
- Example #1: Find the outputs (particles, non LP)
|
|
516
|
+
|
|
517
|
+
>>> _varsouts_p("particles.0000.dbl")
|
|
518
|
+
|
|
519
|
+
- Example #2: Find the outputs (fluid)
|
|
520
|
+
|
|
521
|
+
>>> _varsouts_f("rho.0000.dbl")
|
|
522
|
+
|
|
523
|
+
- Example #3: Find the outputs (LP)
|
|
524
|
+
|
|
525
|
+
>>> _varsouts_lp("particles.0000_ch_00.dbl")
|
|
526
|
+
|
|
527
|
+
"""
|
|
528
|
+
# Splits the matching filename (variable/data and output number)
|
|
529
|
+
raw_str = Path(elem).name.split(".")
|
|
530
|
+
vars = raw_str[0]
|
|
531
|
+
outs = raw_str[1]
|
|
532
|
+
|
|
533
|
+
# Set the conditions if the file is fluid or particles
|
|
534
|
+
isfluid = vars != "particles" and class_name == "Load"
|
|
535
|
+
ispart = vars == "particles" and class_name == "LoadPart"
|
|
536
|
+
|
|
537
|
+
if isfluid or (ispart and "_" not in outs):
|
|
538
|
+
# File is fluid or particles, but not LP
|
|
539
|
+
if self.nfile_lp is not None:
|
|
540
|
+
# If the file is not LP, but nfile_lp is not None, raise a warning
|
|
541
|
+
warn = f"nfile_lp is not None, but the file {elem} is not LP."
|
|
542
|
+
warnings.warn(warn, UserWarning)
|
|
543
|
+
# Control variable set to True
|
|
544
|
+
outc = True
|
|
545
|
+
|
|
546
|
+
elif ispart and "_" in outs:
|
|
547
|
+
# File is LP
|
|
548
|
+
if self.nfile_lp is None:
|
|
549
|
+
# If the file is LP, but nfile_lp is None, raise a warning
|
|
550
|
+
self.nfile_lp = 0
|
|
551
|
+
warn = f"nfile_lp is None, but the file {elem} is LP, set to 0."
|
|
552
|
+
warnings.warn(warn, UserWarning)
|
|
553
|
+
|
|
554
|
+
# Control variable set to the number of LP files
|
|
555
|
+
scrh = outs.split("_")
|
|
556
|
+
outs = int(scrh[0])
|
|
557
|
+
outc = int(scrh[1][2:])
|
|
558
|
+
else:
|
|
559
|
+
# Control variable set to False
|
|
560
|
+
outc = False
|
|
561
|
+
|
|
562
|
+
# Add the variables and the outputs
|
|
563
|
+
if outc is True or outc == self.nfile_lp:
|
|
564
|
+
self.set_vars.add(vars)
|
|
565
|
+
self.set_outs.add(int(outs))
|
|
566
|
+
|
|
567
|
+
# End of the function
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import inifix
|
|
6
|
+
|
|
7
|
+
from pyPLUTO.loadmixin import LoadMixin
|
|
8
|
+
from pyPLUTO.loadstate import LoadState
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FiledefpliniManager(LoadMixin):
|
|
12
|
+
def __init__(self, state: LoadState, **kwargs: Any) -> None:
|
|
13
|
+
self.state = state
|
|
14
|
+
defh = kwargs.get("defh")
|
|
15
|
+
if defh is not False:
|
|
16
|
+
pathdefh = self.pathdir / Path("definitions.h")
|
|
17
|
+
defhfile = "definitions.hpp"
|
|
18
|
+
if not pathdefh.exists():
|
|
19
|
+
pathdefh = self.pathdir / Path("definitions.hpp")
|
|
20
|
+
defhfile = "definitions.h"
|
|
21
|
+
try:
|
|
22
|
+
self.defh = self.read_defh(pathdefh)
|
|
23
|
+
except FileNotFoundError:
|
|
24
|
+
print(f"No {defhfile} is read!") if defh is True else ...
|
|
25
|
+
|
|
26
|
+
# Try to read the file pluto.ini
|
|
27
|
+
plini = kwargs.get("plini")
|
|
28
|
+
if isinstance(plini, str):
|
|
29
|
+
pathplini = self.pathdir / Path(plini)
|
|
30
|
+
elif plini is not False:
|
|
31
|
+
pathplini = self.pathdir / Path("pluto.ini")
|
|
32
|
+
try:
|
|
33
|
+
self.plini = inifix.load(pathplini, sections="require")
|
|
34
|
+
except FileNotFoundError:
|
|
35
|
+
print("No pluto.ini is read!") if plini is True else ...
|
|
36
|
+
|
|
37
|
+
def read_defh(self, filepath: Path) -> dict:
|
|
38
|
+
"""Read a header file and extract definitions.
|
|
39
|
+
|
|
40
|
+
This function reads a header file, extracts lines that start with
|
|
41
|
+
'#define', and converts the values to their appropriate types (boolean,
|
|
42
|
+
integer, float, or string).
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
dict
|
|
47
|
+
A dictionary where keys are the defined names and values are the
|
|
48
|
+
converted values.
|
|
49
|
+
|
|
50
|
+
Parameters
|
|
51
|
+
----------
|
|
52
|
+
filepath : str
|
|
53
|
+
The path to the header file to read.
|
|
54
|
+
|
|
55
|
+
"""
|
|
56
|
+
# Read the file, check if a line starts with '#define',
|
|
57
|
+
# and split the line into key and value.
|
|
58
|
+
# Convert the value using _convert_value function.
|
|
59
|
+
with open(filepath) as file:
|
|
60
|
+
# Return a dictionary comprehension that processes each line
|
|
61
|
+
return {
|
|
62
|
+
key: self.convert_value(value)
|
|
63
|
+
for line in file
|
|
64
|
+
if line.strip().startswith("#define")
|
|
65
|
+
for _, key, value in [
|
|
66
|
+
re.split(r"\s+", line.strip(), maxsplit=2)
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def convert_value(self, value: str) -> bool | int | float | str:
|
|
71
|
+
"""Convert a string value to its appropriate type.
|
|
72
|
+
|
|
73
|
+
This function attempts to convert a string value into a boolean,
|
|
74
|
+
integer, float, or leave it as a string if it cannot be converted.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
bool | int | float | str
|
|
79
|
+
The converted value, which can be a boolean, integer, float, or the
|
|
80
|
+
original string if conversion is not possible.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
value : str
|
|
85
|
+
The string value to convert.
|
|
86
|
+
|
|
87
|
+
"""
|
|
88
|
+
# Convert the value to uppercase to handle case-insensitive comparisons
|
|
89
|
+
value_upper = value.upper()
|
|
90
|
+
|
|
91
|
+
# Check for boolean values first
|
|
92
|
+
if value_upper in {"YES", "TRUE"}:
|
|
93
|
+
return True
|
|
94
|
+
if value_upper in {"NO", "FALSE"}:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# Try to convert to an integer or float, if possible
|
|
98
|
+
try:
|
|
99
|
+
return float(value) if "." in value or "e" in value else int(value)
|
|
100
|
+
except ValueError:
|
|
101
|
+
return value
|