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