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
pyPLUTO/amr.py ADDED
@@ -0,0 +1,745 @@
1
+ import h5py
2
+ import numpy as np
3
+
4
+
5
+ def _inspect_hdf5(self, i: int, exout: int) -> None:
6
+ """Routine to inspect the hdf5 file from chombo.
7
+
8
+ Returns
9
+ -------
10
+ - None
11
+
12
+ Parameters
13
+ ----------
14
+ - exout (not optional): int
15
+ The index of the output to be loaded.
16
+ - i (not optional): int
17
+ The index of the file to be loaded.
18
+
19
+ Notes
20
+ -----
21
+ - This routines will be optimize in the future, alongside a novel
22
+ implementation of the AMR in the PLUTO code.
23
+
24
+ ----
25
+
26
+ Examples
27
+ --------
28
+ - Example #1: Inspect the hdf5 file
29
+
30
+ >>> _inspect_hdf5(0, 0)
31
+
32
+ """
33
+ try:
34
+ self._read_gridfile()
35
+ except FileNotFoundError:
36
+ pass
37
+
38
+ self.x1range = None
39
+ self.x2range = None
40
+ self.x3range = None
41
+
42
+ h5file = h5py.File(self._filepath, "r")
43
+ self.multiple = False
44
+ self.d_info["varslist"] = []
45
+ self.ntime = h5file.attrs.get("time")
46
+ for i in range(h5file.attrs.get("num_components")):
47
+ self.d_info["varslist"].append(h5file.attrs.get("component_" + str(i)))
48
+
49
+ NewData = self._DataScanHDF5(h5file, self.d_info["varslist"], self.level)
50
+
51
+ for key in NewData.keys():
52
+ if key == "grid":
53
+ for key2 in NewData[key].keys():
54
+ setattr(self, key2, NewData[key][key2])
55
+ continue
56
+ setattr(self, key, NewData[key])
57
+
58
+ h5file.close()
59
+
60
+
61
+ def _DataScanHDF5(self, fp, myvars, ilev) -> dict:
62
+ """Scans the Chombo HDF5 data files for AMR in PLUTO.
63
+
64
+ Returns
65
+ -------
66
+ - OutDict: dictionary
67
+ The dictionary consisting of variable names as keys and its values.
68
+
69
+ Parameters
70
+ ----------
71
+ - fp: pointer
72
+ The data file pointer.
73
+ - ilev: float
74
+ The AMR level.
75
+ - myvars: str
76
+ The names of the variables to be read.
77
+
78
+ Notes
79
+ -----
80
+ - Due to the particularity of AMR, the grid arrays loaded in ReadGridFile
81
+ are overwritten here.
82
+ - This routines will be optimize in the future, alongside a novel
83
+ implementation of the AMR in the PLUTO code.
84
+
85
+ ----
86
+
87
+ Examples
88
+ --------
89
+ - Example #1:
90
+
91
+ >>> _DataScanHDF5(fp, myvars, ilev)
92
+
93
+ """
94
+ # Read the grid information
95
+ dim = fp["Chombo_global"].attrs.get("SpaceDim")
96
+ nlev = fp.attrs.get("num_levels")
97
+ il = min(nlev - 1, ilev)
98
+ lev = []
99
+ for i in range(nlev):
100
+ lev.append("level_" + str(i))
101
+ freb = np.zeros(nlev, dtype="int")
102
+ for i in range(il + 1)[::-1]:
103
+ fl = fp[lev[i]]
104
+ if i == il:
105
+ pdom = fl.attrs.get("prob_domain")
106
+ dx = fl.attrs.get("dx")
107
+ dt = fl.attrs.get("dt")
108
+ ystr = 1.0
109
+ zstr = 1.0
110
+ logr = 0
111
+ try:
112
+ # self.geom = fl.attrs.get("geometry")
113
+ logr = fl.attrs.get("logr")
114
+ if dim >= 2:
115
+ ystr = fl.attrs.get("g_x2stretch")
116
+ if dim == 3:
117
+ zstr = fl.attrs.get("g_x3stretch")
118
+ except DeprecationWarning:
119
+ print("Old HDF5 file, not reading stretch and logr factors")
120
+ freb[i] = 1
121
+ x1b = fl.attrs.get("domBeg1")
122
+ if dim == 1:
123
+ x2b = 0
124
+ else:
125
+ x2b = fl.attrs.get("domBeg2")
126
+ if dim == 1 or dim == 2:
127
+ x3b = 0
128
+ else:
129
+ x3b = fl.attrs.get("domBeg3")
130
+ jbeg = 0
131
+ jend = 0
132
+ ny = 1
133
+ kbeg = 0
134
+ kend = 0
135
+ nz = 1
136
+ if dim == 1:
137
+ ibeg = pdom[0]
138
+ iend = pdom[1]
139
+ nx = iend - ibeg + 1
140
+ elif dim == 2:
141
+ ibeg = pdom[0]
142
+ iend = pdom[2]
143
+ nx = iend - ibeg + 1
144
+ jbeg = pdom[1]
145
+ jend = pdom[3]
146
+ ny = jend - jbeg + 1
147
+ elif dim == 3:
148
+ ibeg = pdom[0]
149
+ iend = pdom[3]
150
+ nx = iend - ibeg + 1
151
+ jbeg = pdom[1]
152
+ jend = pdom[4]
153
+ ny = jend - jbeg + 1
154
+ kbeg = pdom[2]
155
+ kend = pdom[5]
156
+ nz = kend - kbeg + 1
157
+ else:
158
+ rat = fl.attrs.get("ref_ratio")
159
+ freb[i] = rat * freb[i + 1]
160
+
161
+ dx0 = dx * freb[0]
162
+
163
+ ## Allow to load only a portion of the domain
164
+ if self.x1range is not None:
165
+ if logr == 0:
166
+ self.x1range = self.x1range - x1b
167
+ else:
168
+ self.x1range = [
169
+ np.log(self.x1range[0] / x1b),
170
+ np.log(self.x1range[1] / x1b),
171
+ ]
172
+ ibeg0 = min(self.x1range) / dx0
173
+ iend0 = max(self.x1range) / dx0
174
+ ibeg = max([ibeg, int(ibeg0 * freb[0])])
175
+ iend = min([iend, int(iend0 * freb[0] - 1)])
176
+ nx = iend - ibeg + 1
177
+ if self.x2range is not None:
178
+ self.x2range = (self.x2range - x2b) / ystr
179
+ jbeg0 = min(self.x2range) / dx0
180
+ jend0 = max(self.x2range) / dx0
181
+ jbeg = max([jbeg, int(jbeg0 * freb[0])])
182
+ jend = min([jend, int(jend0 * freb[0] - 1)])
183
+ ny = jend - jbeg + 1
184
+ if self.x3range is not None:
185
+ self.x3range = (self.x3range - x3b) / zstr
186
+ kbeg0 = min(self.x3range) / dx0
187
+ kend0 = max(self.x3range) / dx0
188
+ kbeg = max([kbeg, int(kbeg0 * freb[0])])
189
+ kend = min([kend, int(kend0 * freb[0] - 1)])
190
+ nz = kend - kbeg + 1
191
+
192
+ ## Create uniform grids at the required level
193
+ if logr == 0:
194
+ x1 = x1b + (ibeg + np.array(range(nx)) + 0.5) * dx
195
+ else:
196
+ x1 = (
197
+ x1b
198
+ * (
199
+ np.exp((ibeg + np.array(range(nx)) + 1) * dx)
200
+ + np.exp((ibeg + np.array(range(nx))) * dx)
201
+ )
202
+ * 0.5
203
+ )
204
+ x2 = x2b + (jbeg + np.array(range(ny)) + 0.5) * dx * ystr
205
+ x3 = x3b + (kbeg + np.array(range(nz)) + 0.5) * dx * zstr
206
+ if logr == 0:
207
+ dx1 = np.ones(nx) * dx
208
+ else:
209
+ dx1 = x1b * (
210
+ np.exp((ibeg + np.array(range(nx)) + 1) * dx)
211
+ - np.exp((ibeg + np.array(range(nx))) * dx)
212
+ )
213
+ dx2 = np.ones(ny) * dx * ystr
214
+ dx3 = np.ones(nz) * dx * zstr
215
+
216
+ # Create the xr arrays containing the edges positions
217
+ # Useful for pcolormesh which should use those
218
+ x1r = np.zeros(len(x1) + 1)
219
+ x1r[1:] = x1 + dx1 / 2.0
220
+ x1r[0] = x1r[1] - dx1[0]
221
+ x2r = np.zeros(len(x2) + 1)
222
+ x2r[1:] = x2 + dx2 / 2.0
223
+ x2r[0] = x2r[1] - dx2[0]
224
+ x3r = np.zeros(len(x3) + 1)
225
+ x3r[1:] = x3 + dx3 / 2.0
226
+ x3r[0] = x3r[1] - dx3[0]
227
+ NewGridDict = dict(
228
+ [
229
+ ("n1", nx),
230
+ ("n2", ny),
231
+ ("n3", nz),
232
+ ("x1", x1),
233
+ ("x2", x2),
234
+ ("x3", x3),
235
+ ("x1r", x1r),
236
+ ("x2r", x2r),
237
+ ("x3r", x3r),
238
+ ("dx1", dx1),
239
+ ("dx2", dx2),
240
+ ("dx3", dx3),
241
+ ("Dt", dt),
242
+ ]
243
+ )
244
+
245
+ # Variables table
246
+ nvar = len(myvars)
247
+ vars = np.zeros((nx, ny, nz, nvar))
248
+ LevelDic = {
249
+ "nbox": 0,
250
+ "ibeg": ibeg,
251
+ "iend": iend,
252
+ "jbeg": jbeg,
253
+ "jend": jend,
254
+ "kbeg": kbeg,
255
+ "kend": kend,
256
+ }
257
+ AMRLevel = []
258
+ AMRBoxes = np.zeros((nx, ny, nz))
259
+ for i in range(il + 1):
260
+ AMRLevel.append(LevelDic.copy())
261
+ fl = fp[lev[i]]
262
+ data = fl["data:datatype=0"]
263
+ boxes = fl["boxes"]
264
+ nbox = len(boxes["lo_i"])
265
+ AMRLevel[i]["nbox"] = nbox
266
+ ncount = 0
267
+ AMRLevel[i]["box"] = []
268
+ for j in range(nbox): # loop on all boxes of a given level
269
+ AMRLevel[i]["box"].append(
270
+ {
271
+ "x0": 0.0,
272
+ "x1": 0.0,
273
+ "ib": 0,
274
+ "ie": 0,
275
+ "y0": 0.0,
276
+ "y1": 0.0,
277
+ "jb": 0,
278
+ "je": 0,
279
+ "z0": 0.0,
280
+ "z1": 0.0,
281
+ "kb": 0,
282
+ "ke": 0,
283
+ }
284
+ )
285
+ # Box indexes
286
+ ib = boxes[j]["lo_i"]
287
+ ie = boxes[j]["hi_i"]
288
+ nbx = ie - ib + 1
289
+ jb = 0
290
+ je = 0
291
+ nby = 1
292
+ kb = 0
293
+ ke = 0
294
+ nbz = 1
295
+ if dim > 1:
296
+ jb = boxes[j]["lo_j"]
297
+ je = boxes[j]["hi_j"]
298
+ nby = je - jb + 1
299
+ if dim > 2:
300
+ kb = boxes[j]["lo_k"]
301
+ ke = boxes[j]["hi_k"]
302
+ nbz = ke - kb + 1
303
+ szb = nbx * nby * nbz * nvar
304
+ # Rescale to current level
305
+ kb = kb * freb[i]
306
+ ke = (ke + 1) * freb[i] - 1
307
+ jb = jb * freb[i]
308
+ je = (je + 1) * freb[i] - 1
309
+ ib = ib * freb[i]
310
+ ie = (ie + 1) * freb[i] - 1
311
+
312
+ # Skip boxes lying outside ranges
313
+ if (
314
+ (ib > iend)
315
+ or (ie < ibeg)
316
+ or (jb > jend)
317
+ or (je < jbeg)
318
+ or (kb > kend)
319
+ or (ke < kbeg)
320
+ ):
321
+ ncount = ncount + szb
322
+ else:
323
+ ### Read data
324
+ q = data[ncount : ncount + szb].reshape((nvar, nbz, nby, nbx)).T
325
+
326
+ ### Find boxes intersections with current domain ranges
327
+ ib0 = max([ibeg, ib])
328
+ ie0 = min([iend, ie])
329
+ jb0 = max([jbeg, jb])
330
+ je0 = min([jend, je])
331
+ kb0 = max([kbeg, kb])
332
+ ke0 = min([kend, ke])
333
+
334
+ ### Store box corners in the AMRLevel structure
335
+ if logr == 0:
336
+ AMRLevel[i]["box"][j]["x0"] = x1b + dx * (ib0)
337
+ AMRLevel[i]["box"][j]["x1"] = x1b + dx * (ie0 + 1)
338
+ else:
339
+ AMRLevel[i]["box"][j]["x0"] = x1b * np.exp(dx * (ib0))
340
+ AMRLevel[i]["box"][j]["x1"] = x1b * np.exp(dx * (ie0 + 1))
341
+ AMRLevel[i]["box"][j]["y0"] = x2b + dx * (jb0) * ystr
342
+ AMRLevel[i]["box"][j]["y1"] = x2b + dx * (je0 + 1) * ystr
343
+ AMRLevel[i]["box"][j]["z0"] = x3b + dx * (kb0) * zstr
344
+ AMRLevel[i]["box"][j]["z1"] = x3b + dx * (ke0 + 1) * zstr
345
+ AMRLevel[i]["box"][j]["ib"] = ib0
346
+ AMRLevel[i]["box"][j]["ie"] = ie0
347
+ AMRLevel[i]["box"][j]["jb"] = jb0
348
+ AMRLevel[i]["box"][j]["je"] = je0
349
+ AMRLevel[i]["box"][j]["kb"] = kb0
350
+ AMRLevel[i]["box"][j]["ke"] = ke0
351
+ AMRBoxes[
352
+ ib0 - ibeg : ie0 - ibeg + 1,
353
+ jb0 - jbeg : je0 - jbeg + 1,
354
+ kb0 - kbeg : ke0 - kbeg + 1,
355
+ ] = il
356
+
357
+ ### Extract the box intersection from data stored in q
358
+ cib0 = (ib0 - ib) // freb[i]
359
+ cie0 = (ie0 - ib) // freb[i]
360
+ cjb0 = (jb0 - jb) // freb[i]
361
+ cje0 = (je0 - jb) // freb[i]
362
+ ckb0 = (kb0 - kb) // freb[i]
363
+ cke0 = (ke0 - kb) // freb[i]
364
+ q1 = np.zeros(
365
+ (cie0 - cib0 + 1, cje0 - cjb0 + 1, cke0 - ckb0 + 1, nvar)
366
+ )
367
+ q1 = q[cib0 : cie0 + 1, cjb0 : cje0 + 1, ckb0 : cke0 + 1, :]
368
+
369
+ # Remap the extracted portion
370
+ if dim == 1:
371
+ new_shape = (ie0 - ib0 + 1, 1)
372
+ elif dim == 2:
373
+ new_shape = (ie0 - ib0 + 1, je0 - jb0 + 1)
374
+ else:
375
+ new_shape = (ie0 - ib0 + 1, je0 - jb0 + 1, ke0 - kb0 + 1)
376
+
377
+ stmp = list(new_shape)
378
+ while stmp.count(1) > 0:
379
+ stmp.remove(1)
380
+ new_shape = tuple(stmp)
381
+
382
+ for iv in range(nvar):
383
+ vars[
384
+ ib0 - ibeg : ie0 - ibeg + 1,
385
+ jb0 - jbeg : je0 - jbeg + 1,
386
+ kb0 - kbeg : ke0 - kbeg + 1,
387
+ iv,
388
+ ] = self._congrid(
389
+ q1[:, :, :, iv].squeeze(),
390
+ new_shape,
391
+ method="linear",
392
+ minusone=True,
393
+ ).reshape((ie0 - ib0 + 1, je0 - jb0 + 1, ke0 - kb0 + 1))
394
+ ncount = ncount + szb
395
+
396
+ h5vardict = {}
397
+ for iv in range(nvar):
398
+ myvars[iv] = myvars[iv].decode()
399
+ h5vardict[myvars[iv]] = vars[:, :, :, iv].squeeze().T
400
+ self.load_vars = myvars
401
+ AMRdict = dict([("AMRBoxes", AMRBoxes), ("AMRLevel", AMRLevel)])
402
+ OutDict = dict(NewGridDict)
403
+ OutDict.update(AMRdict)
404
+ OutDict.update(h5vardict)
405
+
406
+ return OutDict
407
+
408
+
409
+ def oplotbox(
410
+ self,
411
+ AMRLevel,
412
+ lrange=[0, 0],
413
+ cval=None,
414
+ islice=-1,
415
+ jslice=-1,
416
+ kslice=-1,
417
+ geom="CARTESIAN",
418
+ ax=None,
419
+ **kwargs,
420
+ ) -> None:
421
+ """This method overplots the AMR boxes up to the specified level.
422
+
423
+ Returns
424
+ -------
425
+ - None
426
+
427
+ Parameters
428
+ ----------
429
+ - AMRLevel: AMR object
430
+ AMR object loaded during the reading and stored in the pload object.
431
+ - ax: axis object
432
+ The axis object where to plot the AMR boxes.
433
+ - cval: str | None
434
+ List of colors for the levels to be overplotted.
435
+ - geom: str, default 'CARTESIAN'
436
+ The specified geometry. At the moment 'CARTESIAN' and 'POLAR' are the
437
+ handled geometries.
438
+ - islice: int
439
+ The index of the 2D slice along x-axis direction.
440
+ - jslice: int
441
+ The index of the 2D slice along y-axis direction.
442
+ - kslice: int, default min(x3)
443
+ The index of the 2D slice along z-axis direction.
444
+ - kwargs: Any
445
+ The kwargs of the plot method.
446
+ - lrange: [level_min, level_max]
447
+ The range to be overplotted.
448
+
449
+ ----
450
+
451
+ Examples
452
+ --------
453
+ - Example #1: Overplot the AMR boxes up to the specified level
454
+
455
+ >>> oplotbox(AMRLevel, lrange=[0, 2])
456
+
457
+ """
458
+ nlev = len(AMRLevel)
459
+ lrange[1] = min(lrange[1], nlev - 1)
460
+ npl = lrange[1] - lrange[0] + 1
461
+ lpls = [lrange[0] + v for v in range(npl)]
462
+ cols = cval[0:nlev] if cval is not None else self.color[0:nlev]
463
+ # Get the offset and the type of slice
464
+ Slice = 0
465
+ inds = "k"
466
+ xx = "x"
467
+ yy = "y"
468
+ if islice >= 0:
469
+ Slice = islice + AMRLevel[0]["ibeg"]
470
+ inds = "i"
471
+ xx = "y"
472
+ yy = "z"
473
+ if jslice >= 0:
474
+ Slice = jslice + AMRLevel[0]["jbeg"]
475
+ inds = "j"
476
+ xx = "x"
477
+ yy = "z"
478
+ if kslice >= 0:
479
+ Slice = kslice + AMRLevel[0]["kbeg"]
480
+ inds = "k"
481
+ xx = "x"
482
+ yy = "y"
483
+
484
+ # Overplot the boxes
485
+ for il in lpls:
486
+ level = AMRLevel[il]
487
+ for ib in range(level["nbox"]):
488
+ box = level["box"][ib]
489
+ if (Slice - box[inds + "b"]) * (box[inds + "e"] - Slice) >= 0:
490
+ if geom == "CARTESIAN":
491
+ x0 = box[xx + "0"]
492
+ x1 = box[xx + "1"]
493
+ y0 = box[yy + "0"]
494
+ y1 = box[yy + "1"]
495
+ self.plot(
496
+ [x0, x1, x1, x0, x0],
497
+ [y0, y0, y1, y1, y0],
498
+ color=cols[il],
499
+ ax=ax,
500
+ **kwargs,
501
+ )
502
+ elif (geom == "POLAR") or (geom == "SPHERICAL"):
503
+ dn = np.pi / 50.0
504
+ x0 = box[xx + "0"]
505
+ x1 = box[xx + "1"]
506
+ y0 = box[yy + "0"]
507
+ y1 = box[yy + "1"]
508
+ if y0 == y1:
509
+ y1 = 2 * np.pi + y0 - 1.0e-3
510
+ if kslice >= 0 and geom == "SPHERICAL":
511
+ y0 = np.pi / 2 - y0
512
+ y1 = np.pi / 2 - y1
513
+ xb = np.concatenate(
514
+ [
515
+ [x0 * np.cos(y0), x1 * np.cos(y0)],
516
+ x1
517
+ * np.cos(
518
+ np.linspace(y0, y1, num=int(abs(y0 - y1) / dn))
519
+ ),
520
+ [x1 * np.cos(y1), x0 * np.cos(y1)],
521
+ x0
522
+ * np.cos(
523
+ np.linspace(y1, y0, num=int(abs(y0 - y1) / dn))
524
+ ),
525
+ ]
526
+ )
527
+ yb = np.concatenate(
528
+ [
529
+ [x0 * np.sin(y0), x1 * np.sin(y0)],
530
+ x1
531
+ * np.sin(
532
+ np.linspace(y0, y1, num=int(abs(y0 - y1) / dn))
533
+ ),
534
+ [x1 * np.sin(y1), x0 * np.sin(y1)],
535
+ x0
536
+ * np.sin(
537
+ np.linspace(y1, y0, num=int(abs(y0 - y1) / dn))
538
+ ),
539
+ ]
540
+ )
541
+ self.plot(xb, yb, c=cols[il], ax=ax, **kwargs)
542
+
543
+
544
+ def _read_gridfile(self) -> None:
545
+ """The file grid.out is read and all the grid information are stored
546
+ in the Load class. Such information are the dimensions, the
547
+ geometry, the center and edges of each cell, the grid shape and size
548
+ and, in case of non cartesian coordinates, the transformed cartesian
549
+ coordinates (only 2D for now).bThe full non-cartesian 3D
550
+ transformations have not been implemented yet.
551
+
552
+ Returns
553
+ -------
554
+ - None
555
+
556
+ Parameters
557
+ ----------
558
+ - None
559
+
560
+ ----
561
+
562
+ Examples
563
+ --------
564
+ - Example #1: read the grid file
565
+
566
+ >>> _read_gridfile()
567
+
568
+ """
569
+ # Initialize relevant lists
570
+ nmax, xL, xR = [], [], []
571
+
572
+ # Open and read the gridfile
573
+ with open(self._pathgrid) as gfp:
574
+ for i in gfp.readlines():
575
+ self._split_gridfile(i, xL, xR, nmax)
576
+
577
+ # Compute nx1, nx2, nx3
578
+ self.nx1, self.nx2, self.nx3 = nmax
579
+ nx1p2 = self.nx1 + self.nx2
580
+ nx1p3 = self.nx1 + self.nx2 + self.nx3
581
+
582
+ # Define grid shapes based on dimensions
583
+ nx1s, nx2s, nx3s = self.nx1 + 1, self.nx2 + 1, self.nx3 + 1
584
+ GRID_SHAPES = {
585
+ 1: lambda nx1, _, __: (nx1, nx1s, None, None),
586
+ 2: lambda nx1, nx2, _: ((nx2, nx1), (nx2, nx1s), (nx2s, nx1), None),
587
+ 3: lambda nx1, nx2, nx3: (
588
+ (nx3, nx2, nx1),
589
+ (nx3, nx2, nx1s),
590
+ (nx3, nx2s, nx1),
591
+ (nx3s, nx2, nx1),
592
+ ),
593
+ }
594
+
595
+ # Determine grid shape based on dimension
596
+ (self.nshp, self._nshp_st1, self._nshp_st2, self._nshp_st3) = GRID_SHAPES[
597
+ self.dim
598
+ ](self.nx1, self.nx2, self.nx3)
599
+
600
+ # Compute the centered and staggered grid values
601
+ self.x1r = np.array(xL[0 : self.nx1] + [xR[self.nx1 - 1]])
602
+ self.x1 = 0.5 * (self.x1r[:-1] + self.x1r[1:])
603
+ self.dx1 = self.x1r[1:] - self.x1r[:-1]
604
+
605
+ self.x2r = np.array(xL[self.nx1 : nx1p2] + [xR[nx1p2 - 1]])
606
+ self.x2 = 0.5 * (self.x2r[:-1] + self.x2r[1:])
607
+ self.dx2 = self.x2r[1:] - self.x2r[:-1]
608
+
609
+ self.x3r = np.array(xL[nx1p2:nx1p3] + [xR[nx1p3 - 1]])
610
+ self.x3 = 0.5 * (self.x3r[:-1] + self.x3r[1:])
611
+ self.dx3 = self.x3r[1:] - self.x3r[:-1]
612
+
613
+ # Compute the cartesian grid coordinates (non-cartesian geometry)
614
+
615
+ if self.geom == "POLAR" or self.geom == "CYLINDRICAL":
616
+ x1_2D, x2_2D = np.meshgrid(self.x1, self.x2, indexing="ij")
617
+ x1r_2D, x2r_2D = np.meshgrid(self.x1r, self.x2r, indexing="ij")
618
+
619
+ self.x1c = (np.cos(x2_2D) * x1_2D).T
620
+ self.x2c = (np.sin(x2_2D) * x1_2D).T
621
+ self.x1rc = (np.cos(x2r_2D) * x1r_2D).T
622
+ self.x2rc = (np.sin(x2r_2D) * x1r_2D).T
623
+
624
+ self.gridlist3 = ["x1c", "x2c", "x1rc", "x2rc"]
625
+ del x1_2D, x2_2D, x1r_2D, x2r_2D
626
+ elif self.geom == "SPHERICAL":
627
+ x1_2D, x2_2D = np.meshgrid(self.x1, self.x2, indexing="ij")
628
+ x1r_2D, x2r_2D = np.meshgrid(self.x1r, self.x2r, indexing="ij")
629
+
630
+ self.x1p = (np.sin(x2_2D) * x1_2D).T
631
+ self.x2p = (np.cos(x2_2D) * x1_2D).T
632
+ self.x1rp = (np.sin(x2r_2D) * x1r_2D).T
633
+ self.x2rp = (np.cos(x2r_2D) * x1r_2D).T
634
+
635
+ x1_2D, x3_2D = np.meshgrid(self.x1, self.x3, indexing="ij")
636
+ x1r_2D, x3r_2D = np.meshgrid(self.x1r, self.x3r, indexing="ij")
637
+
638
+ self.x1t = (np.cos(x3_2D) * x1_2D).T
639
+ self.x3t = (np.sin(x3_2D) * x1_2D).T
640
+ self.x1rt = (np.cos(x3r_2D) * x1r_2D).T
641
+ self.x3rt = (np.sin(x3r_2D) * x1r_2D).T
642
+
643
+ self.gridlist3 = [
644
+ "x1p",
645
+ "x2p",
646
+ "x1rp",
647
+ "x2rp",
648
+ "x1t",
649
+ "x3t",
650
+ "x1rt",
651
+ "x3rt",
652
+ ]
653
+
654
+ del x1_2D, x2_2D, x1r_2D, x2r_2D, x3_2D, x3r_2D
655
+
656
+ if self.dim == 3 and self._full3d is True:
657
+ x1_3D, x2_3D, x3_3D = np.meshgrid(
658
+ self.x1, self.x2, self.x3, indexing="ij"
659
+ )
660
+ x1r_3D, x2r_3D, x3r_3D = np.meshgrid(
661
+ self.x1r, self.x2r, self.x3r, indexing="ij"
662
+ )
663
+
664
+ self.x1c = (np.sin(x2_3D) * np.cos(x3_3D) * x1_3D).T
665
+ self.x2c = (np.sin(x2_3D) * np.sin(x3_3D) * x1_3D).T
666
+ self.x3c = (np.cos(x2_3D) * x1_3D).T
667
+ self.x1rc = (np.sin(x2r_3D) * np.cos(x3r_3D) * x1r_3D).T
668
+ self.x2rc = (np.sin(x2r_3D) * np.sin(x3r_3D) * x1r_3D).T
669
+ self.x3rc = (np.cos(x2r_3D) * x1r_3D).T
670
+
671
+ self.gridlist3.extend(["x1c", "x2c", "x3c", "x1rc", "x2rc", "x3rc"])
672
+
673
+ del x1_3D, x2_3D, x3_3D, x1r_3D, x2r_3D, x3r_3D
674
+ else:
675
+ pass
676
+ # self.x1c = np.zeros((self.nx1,self.nx2,self.nx3))
677
+ # print(np.shape(self.x1c))
678
+ # self.pippo = np.meshgrid(self.x2, self.x3, indexing='xy')
679
+ # print(np.shape(self.pippo))
680
+
681
+ # Compute the gridsize both centered and staggered
682
+ self.gridsize = self.nx1 * self.nx2 * self.nx3
683
+ self.gridsize_st1 = nx1s * self.nx2 * self.nx3
684
+ self.gridsize_st2 = self.nx1 * nx2s * self.nx3
685
+ self.gridsize_st3 = self.nx1 * self.nx2 * nx3s
686
+
687
+ self.info = False
688
+
689
+
690
+ def _split_gridfile(
691
+ self, i: str, xL: list[float], xR: list[float], nmax: list[int]
692
+ ) -> None:
693
+ """Splits the gridfile, storing the information in the variables
694
+ passed by the function. Dimensions and geometry are stored in the
695
+ class.
696
+
697
+ Return
698
+ ------
699
+
700
+ - None
701
+
702
+ Parameters
703
+ ----------
704
+ - i (not optional): str
705
+ The line of the gridfile.
706
+ - nmax (not optional): list[int]
707
+ The number of the cells in the grid.
708
+ - xL (not optional): list[float]
709
+ The list of the left cell boundaries values.
710
+ - xR (not optional): list[float]
711
+ The list of the right cell boundaries values.
712
+
713
+ ----
714
+
715
+ Examples
716
+ --------
717
+ - Example #1: Split the gridfile
718
+
719
+ >>> _split_gridfile(i, xL, xR, nmax)
720
+
721
+ """
722
+ # If the splitted line has only one string, try to convert it
723
+ # to an integer (number of cells in a dimension).
724
+ if len(i.split()) == 1:
725
+ try:
726
+ nmax.append(int(i.split(maxsplit=1)[0]))
727
+ except ValueError:
728
+ pass
729
+
730
+ # Check if the splitted line has three strings
731
+ if len(i.split()) == 3:
732
+ # Try to convert the first string to an int (cell number in a dimension)
733
+ # and the other two to floats (left and right cell boundaries)
734
+ try:
735
+ int(i.split(maxsplit=1)[0])
736
+ xL.append(float(i.split()[1]))
737
+ xR.append(float(i.split()[2]))
738
+
739
+ # Check if the keyword is geometry or dimensions and
740
+ # store the information in the class
741
+ except ValueError:
742
+ if i.split()[1] == "GEOMETRY:":
743
+ self.geom = i.split()[2]
744
+ if i.split()[1] == "DIMENSIONS:":
745
+ self.dim = int(i.split()[2])