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,638 @@
1
+ from typing import Any
2
+
3
+ import numpy as np
4
+ from numpy.typing import NDArray
5
+ from scipy.interpolate import RectBivariateSpline, RegularGridInterpolator
6
+ from scipy.ndimage import map_coordinates
7
+
8
+ from ..h_pypluto import check_par
9
+
10
+
11
+ def slices(
12
+ self,
13
+ var: NDArray,
14
+ check: bool = True,
15
+ diag: bool | None = None,
16
+ x1: int | list | None = None,
17
+ x2: int | list | None = None,
18
+ x3: int | list | None = None,
19
+ **kwargs: Any,
20
+ ) -> np.ndarray:
21
+ """Function that slices the variable in the 3 directions. Also, it
22
+ can slice the diagonal of the variable.
23
+
24
+ Returns
25
+ -------
26
+ - newvar: NDArray
27
+ The sliced variable.
28
+
29
+ Parameters
30
+ ----------
31
+ - axis1: int | None, default None
32
+ Axis to be used as the first axis of the 2-D sub-arrays from which the
33
+ diagonals should be taken. Defaults to first axis (0).
34
+ - axis2: int | None, default None
35
+ Axis to be used as the second axis of the 2-D sub-arrays from which the
36
+ diagonals should be taken. Defaults to second axis (1).
37
+ - diag: bool | None, default None
38
+ If not None (or 'min'), slice the main diagonal of the variable.
39
+ If 'min', slice the opposite diagonal.
40
+ - offset: int | None, default None
41
+ Offset of the diagonal from the main diagonal. Can be positive or
42
+ negative. Defaults to main diagonal (0).
43
+ - var: np.ndarray
44
+ The variable to slice.
45
+ - x1: int | list | None, default None
46
+ The slice in the 1st direction.
47
+ - x2: int | list | None, default None
48
+ The slice in the 2nd direction.
49
+ - x3: int | list | None, default None
50
+ The slice in the 3rd direction.
51
+
52
+ ----
53
+
54
+ Examples
55
+ --------
56
+ - Example #1: Slice the variable in the 3 directions
57
+
58
+ >>> slices(var, x1=0, x2=0, x3=0)
59
+
60
+ - Example #2: Slice the variable in the diagonal
61
+
62
+ >>> slices(var, diag=True)
63
+
64
+ - Example #3: Slice the variable in the opposite diagonal
65
+
66
+ >>> slices(var, diag="min")
67
+
68
+ """
69
+ # Check the kwargs parameters
70
+ param = {"axis1", "axis2", "offset"}
71
+ if check is True:
72
+ check_par(param, "slice", **kwargs)
73
+
74
+ # Make a copy to not modify the variable
75
+ newvar = np.copy(var)
76
+
77
+ # Slice the diagonal
78
+ if diag is not None:
79
+ if diag == "min":
80
+ newvar = np.diagonal(np.flipud(newvar), **kwargs)
81
+ else:
82
+ newvar = np.diagonal(newvar, **kwargs)
83
+
84
+ # Slice 3rd direction
85
+ if x3 is not None:
86
+ newvar = newvar[:, :, x3]
87
+
88
+ # Slice 2nd direction
89
+ if x2 is not None:
90
+ newvar = newvar[:, x2]
91
+
92
+ # Slice 1st direction
93
+ if x1 is not None:
94
+ newvar = newvar[x1]
95
+
96
+ # End of the function, return the sliced array
97
+ return newvar
98
+
99
+
100
+ def mirror(
101
+ self, var: NDArray, dirs="l", xax=None, yax=None
102
+ ) -> list[np.ndarray]:
103
+ """Function that mirrors the variable in the specified directions.
104
+ Multiple directions can be specified.
105
+
106
+ Returns
107
+ -------
108
+ - newvar: np.ndarray
109
+ The mirrored variable.
110
+ - xax: np.ndarray
111
+ The mirrored x-axis.
112
+ - yax: np.ndarray
113
+ The mirrored y-axis.
114
+
115
+ Parameters
116
+ ----------
117
+ - dirs: str | list, default 'l'
118
+ The directions to mirror the variable. Can be 'l', 'r', 't', 'b' or a
119
+ list or combination of them.
120
+ - var: np.ndarray
121
+ The variable to mirror.
122
+ - xax: np.ndarray | None, default None
123
+ The x-axis to mirror.
124
+ - yax: np.ndarray | None, default None
125
+ The y-axis to mirror.
126
+
127
+ ----
128
+
129
+ Examples
130
+ --------
131
+ - Example #1: Mirror the variable in the left direction
132
+
133
+ >>> mirror(var, dirs="l")
134
+
135
+ - Example #2: Mirror the variable in the right direction with axis
136
+
137
+ >>> mirror(var, dirs="r", xax=xax)
138
+
139
+ - Example #3: Mirror the variable in the top and left directions
140
+
141
+ >>> mirror(var, dirs=["t", "l"])
142
+
143
+ - Example #4: Mirror the variable in the top and left directions (no list)
144
+
145
+ >>> mirror(var, dirs="tl")
146
+
147
+ - Example #5: Mirror the variable in the left direction three times
148
+
149
+ >>> mirror(var, dirs="lll")
150
+
151
+ """
152
+ spp = [*dirs] if not isinstance(dirs, list) else dirs
153
+ newvar, axx, axy = np.copy(var), np.copy(xax), np.copy(yax)
154
+ dim = np.ndim(var) - 1
155
+ if dim > 1:
156
+ raise ValueError("Mirror function does not works for 3D arrays")
157
+ nax = []
158
+ for dir in spp:
159
+ lvx = len(newvar[:, 0]) if dim == 1 else len(var)
160
+ lvy = len(newvar[0, :]) if dim == 1 else len(var)
161
+ choices = {
162
+ "l": [(lvx, 0), ((lvx, 0), (0, 0))],
163
+ "r": [(0, lvx), ((0, lvx), (0, 0))],
164
+ "t": [(0, lvy), ((0, 0), (0, lvy))],
165
+ "b": [(lvy, 0), ((0, 0), (lvy, 0))],
166
+ }
167
+ newvar = np.pad(newvar, choices[dir][dim], "symmetric")
168
+ if xax is not None and dir in {"l", "r"}:
169
+ axx = np.pad(axx, choices[dir][0], "reflect", reflect_type="odd")
170
+ if yax is not None and dir in {"t", "b"}:
171
+ axy = np.pad(axy, choices[dir][0], "reflect", reflect_type="odd")
172
+ xax is not None and nax.append(axx)
173
+ yax is not None and nax.append(axy)
174
+ if len(nax) > 1:
175
+ return newvar, nax
176
+ elif len(nax) > 0:
177
+ return newvar, nax[0]
178
+ else:
179
+ return newvar
180
+
181
+
182
+ def repeat(
183
+ self,
184
+ var: NDArray,
185
+ dirs: str | list,
186
+ xax: NDArray | None = None,
187
+ yax: NDArray | None = None,
188
+ ) -> np.ndarray:
189
+ """Function that repeats the variable in the specified directions.
190
+ Multiple directions can be specified.
191
+
192
+ Returns
193
+ -------
194
+ - newvar: np.ndarray
195
+ The repeated variable.
196
+
197
+ Parameters
198
+ ----------
199
+ - dirs: str | list
200
+ The directions to repeat the variable. Can be 'l', 'r', 't', 'b' or a
201
+ list or combination of them.
202
+ - var: np.ndarray
203
+ The variable to repeat.
204
+ - xax: np.ndarray | None, default None
205
+ The x-axis to repeat.
206
+ - yax: np.ndarray | None, default None
207
+ The y-axis to repeat.
208
+
209
+ ----
210
+
211
+ Examples
212
+ --------
213
+ - Example #1: Repeat the variable in the left direction
214
+
215
+ >>> repeat(var, dirs="l")
216
+
217
+ - Example #2: Repeat the variable in the right direction with axis
218
+
219
+ >>> repeat(var, dirs="r", xax=xax)
220
+
221
+ - Example #3: Repeat the variable in the top and left directions
222
+
223
+ >>> repeat(var, dirs=["t", "l"])
224
+
225
+ - Example #4: Repeat the variable in the top and left directions (no list)
226
+
227
+ >>> repeat(var, dirs="tl")
228
+
229
+ """
230
+ raise NotImplementedError("Function repeat not implemented yet")
231
+
232
+ spp = [*dirs] if not isinstance(dirs, list) else dirs
233
+ newvar, axx, axy = np.copy(var), np.copy(xax), np.copy(yax)
234
+
235
+ for dir in spp:
236
+ lvx = len(newvar[:, 0])
237
+ lvy = len(newvar[0, :])
238
+ choices = {
239
+ "l": [(lvx, 0), ((lvx, 0), (0, 0))],
240
+ "r": [(0, lvx), ((0, lvx), (0, 0))],
241
+ "t": [(0, lvy), ((0, 0), (0, lvy))],
242
+ "b": [(lvy, 0), ((0, 0), (lvy, 0))],
243
+ }
244
+ newvar = np.pad(newvar, choices[dir][1], "wrap")
245
+ if xax is not None and dir in {"l", "r"}:
246
+ axx = np.pad(axx, choices[dir][0], "wrap")
247
+ if yax is not None and dir in {"t", "b"}:
248
+ axy = np.pad(axy, choices[dir][0], "wrap")
249
+
250
+ if xax is not None and yax is not None:
251
+ return newvar, axx, axy
252
+ elif xax is not None:
253
+ return newvar, axx
254
+ elif yax is not None:
255
+ return newvar, axy
256
+ else:
257
+ return newvar
258
+
259
+
260
+ def cartesian_vector(
261
+ self, var: str | None = None, **kwargs: Any
262
+ ) -> tuple[NDArray, ...]:
263
+ """Function that converts a vector from spherical or polar
264
+ components to cartesian components.
265
+
266
+ Returns
267
+ -------
268
+ - newvar: tuple(np.ndarray)
269
+ The converted vector components.
270
+
271
+ Parameters
272
+ ----------
273
+ - transpose: bool, default False
274
+ If True, the variable is transposed.
275
+ - var: np.ndarray
276
+ The variable to convert.
277
+ - var1: np.ndarray
278
+ The first variable to convert if var is not used.
279
+ - var2: np.ndarray
280
+ The second variable to convert if var is not used.
281
+ - var3: np.ndarray
282
+ The third variable to convert if var is not used.
283
+ - x1: int
284
+ The first index of the variable.
285
+ - x2: int
286
+ The second index of the variable.
287
+
288
+ ----
289
+
290
+ Examples
291
+ --------
292
+ - Example #1: Convert the vector from spherical to cartesian components
293
+
294
+ >>> Bx, By, Bz = cartesian_vector(var="B")
295
+
296
+ - Example #2: Convert the vector from polar to cartesian components
297
+
298
+ >>> Bx, By = cartesian_vector(var1=D.Bx1, var2=D.Bx2)
299
+
300
+ """
301
+ vars = {
302
+ "B": ["Bx1", "Bx2", "Bx3"],
303
+ "E": ["Ex1", "Ex2", "Ex3"],
304
+ "v": ["vx1", "vx2", "vx3"],
305
+ }
306
+
307
+ if var is not None:
308
+ var_0 = [
309
+ self._check_var(v, kwargs.get("transpose", False))
310
+ for v in vars[var]
311
+ ]
312
+ elif "var1" in kwargs and "var2" in kwargs:
313
+ var_0 = [
314
+ self._check_var(kwargs["var1"], kwargs.get("transpose", False)),
315
+ self._check_var(kwargs["var2"], kwargs.get("transpose", False)),
316
+ ]
317
+ else:
318
+ raise ValueError("Either var or var1 and var2 must be specified.")
319
+
320
+ if "var3" in kwargs:
321
+ var_0.append(
322
+ self._check_var(kwargs["var3"], kwargs.get("transpose", False))
323
+ )
324
+
325
+ # x1 = kwargs.get("x1", self.x1)
326
+ x2 = kwargs.get("x2", self.x2)
327
+ x3 = kwargs.get("x3", self.x3)
328
+
329
+ if self.geom == "SPHERICAL":
330
+ varr = var_0[0] * np.sin(x2) + var_0[1] * np.cos(x2)
331
+ varz = var_0[0] * np.cos(x2) - var_0[1] * np.sin(x2)
332
+ if self.dim == 3:
333
+ varx = varr * np.cos(x3) - var_0[2] * np.sin(x3)
334
+ vary = varr * np.sin(x3) + var_0[2] * np.cos(x3)
335
+ if kwargs.get("fullout", False):
336
+ return varx, vary, varz, varr
337
+ else:
338
+ return varx, vary, varz
339
+ else:
340
+ return varr, varz
341
+
342
+ elif self.geom == "POLAR":
343
+ varx = var_0[0] * np.cos(x2) - var_0[1] * np.sin(x2)
344
+ vary = var_0[0] * np.sin(x2) + var_0[1] * np.cos(x2)
345
+ return varx, vary
346
+
347
+
348
+ def reshape_cartesian(self, *args: Any, **kwargs: Any) -> tuple[NDArray, ...]:
349
+ """Function that reshapes a variable from a cylindrical or spherical
350
+ grid into a cartesian grid. Zones not covered by the original domain
351
+ (e.g. the very inner radial regions) are also interpolated. At the
352
+ current stage, the transformation is only in 2D.
353
+
354
+ Returns
355
+ -------
356
+ - newvar: tuple(np.ndarray)
357
+ The converted variable.
358
+
359
+ Parameters
360
+ ----------
361
+ - nx1: int, default len(x1)
362
+ The number of grid points in the first direction.
363
+ - nx2: int, default len(x2)
364
+ The number of grid points in the second direction.
365
+ - transpose: bool, default False
366
+ If True, the variable is transposed.
367
+ - var: np.ndarray
368
+ The variable to convert.
369
+ - x1: int
370
+ The first index of the variable.
371
+ - x2: int
372
+ The second index of the variable.
373
+
374
+ ----
375
+
376
+ Examples
377
+ --------
378
+ - Example #1: Convert the vector from spherical to cartesian components
379
+
380
+ >>> Bx, By, Bz = cartesian_vector(var="B")
381
+
382
+ - Example #2: Convert the vector from polar to cartesian components
383
+
384
+ >>> Bx, By = cartesian_vector(var1=D.Bx1, var2=D.Bx2)
385
+
386
+ """
387
+ # Get the variable, if it is a string, get the variable from the dataset.
388
+ # The .T is used to transpose the variable to the correct shape.
389
+ vars = []
390
+ newv = []
391
+ for i in args:
392
+ vars.append(self._check_var(i, kwargs.get("transpose", False)))
393
+
394
+ # Get the grid information
395
+ x1 = kwargs.pop("x1", self.x1)
396
+ x2 = kwargs.pop("x2", self.x2)
397
+
398
+ # Get the grid limits
399
+ xx = x1[:, np.newaxis] * np.cos(x2)
400
+ yy = x1[:, np.newaxis] * np.sin(x2)
401
+
402
+ xmin, xmax = xx.min(), xx.max()
403
+ ymin, ymax = yy.min(), yy.max()
404
+
405
+ del xx, yy
406
+
407
+ # Get the number of grid points of the new grid
408
+ nx1 = int(kwargs.get("nx1", len(x1)))
409
+ nx2 = int(kwargs.get("nx2", nx1 * (ymax - ymin) / (xmax - xmin)))
410
+ # nx2 = int(kwargs.get('nx2', nx1*(ymax-ymin)//(xmax-xmin)))
411
+
412
+ # Get the cartesian grid
413
+
414
+ if self.geom == "SPHERICAL":
415
+ xc0 = np.linspace(xmin, xmax, nx2)
416
+ yc0 = np.linspace(ymin, ymax, nx1)
417
+ xc, yc = np.meshgrid(xc0, yc0, indexing="xy")
418
+ else:
419
+ xc0 = np.linspace(xmin, xmax, nx1)
420
+ yc0 = np.linspace(ymin, ymax, nx2)
421
+ xc, yc = np.meshgrid(xc0, yc0, indexing="ij")
422
+
423
+ # Create the new grid
424
+ x1, x2, vars = self.reshape_uniform(x1, x2, *vars, **kwargs)
425
+
426
+ # Convert grid
427
+ ww, nn = _convert2cartgrid(xc, yc, x1, x2)
428
+
429
+ xcong = self._congrid(xc, (nx1, nx2), method="linear")
430
+ ycong = self._congrid(yc, (nx1, nx2), method="linear")
431
+
432
+ for i, var in enumerate(vars):
433
+ newv.append(np.sum([ww[j] * var.flat[nn[j]] for j in range(4)], axis=0))
434
+ newv[i] = self._congrid(newv[i], (nx1, nx2), method="linear")
435
+
436
+ if self.geom == "SPHERICAL":
437
+ return ycong[:, 0], xcong[0], *newv
438
+ else:
439
+ return xcong[:, 0], ycong[0], *newv
440
+
441
+
442
+ def reshape_uniform(self, x1, x2, *args, **kwargs):
443
+ """Reshapes a non-uniform (cartesian) 2D grid into a uniform grid.
444
+
445
+ Returns
446
+ -------
447
+ tuple: A tuple containing the reshaped x1, x2, varx, and vary.
448
+
449
+ Parameters
450
+ ----------
451
+ - nx1: int, default len(x1)
452
+ The number of grid points in the first direction.
453
+ - nx2: int, default len(x2)
454
+ The number of grid points in the second direction.
455
+ - transpose: bool, default False
456
+ If True, the variable is transposed.
457
+ - var: np.ndarray
458
+ The variable to convert.
459
+ - x1: int
460
+ The first index of the variable.
461
+ - x2: int
462
+ The second index of the variable.
463
+
464
+ ----
465
+
466
+ Examples
467
+ --------
468
+ - Example #1: Reshape the grid into a uniform grid
469
+
470
+ >>> x1new, x2new, varx = reshape_uniform(x1, x2, var)
471
+
472
+ """
473
+ uniform_x = all(np.diff(x1) == np.diff(x1)[0])
474
+ uniform_y = all(np.diff(x2) == np.diff(x2)[0])
475
+
476
+ nx1new = kwargs.get("nx1", len(x1))
477
+ nx2new = kwargs.get("nx2", len(x2))
478
+
479
+ uniform_x = False if nx1new != len(x1) else uniform_x
480
+ uniform_y = False if nx2new != len(x2) else uniform_y
481
+
482
+ newvars = []
483
+
484
+ if not uniform_x or not uniform_y:
485
+ x1new = np.linspace(x1.min(), x1.max(), nx1new) if not uniform_x else x1
486
+ x2new = np.linspace(x2.min(), x2.max(), nx2new) if not uniform_y else x2
487
+
488
+ for i in args:
489
+ interp = RectBivariateSpline(x2, x1, i.T)
490
+ newvars.append(interp(x2new, x1new))
491
+
492
+ else:
493
+ x1new = x1
494
+ x2new = x2
495
+ newvars = [arg for arg in args]
496
+
497
+ return x1new, x2new, newvars
498
+
499
+
500
+ def _convert2cartgrid(R, Z, new_r, new_t):
501
+ """Function that converts a grid from spherical to cartesian
502
+ coordinates.
503
+
504
+ Returns
505
+ -------
506
+ - newvar: tuple(np.ndarray)
507
+ The new grid.
508
+
509
+ Parameters
510
+ ----------
511
+ - R: np.ndarray
512
+ The radial grid.
513
+ - Z: np.ndarray
514
+ The vertical grid.
515
+ - new_r: np.ndarray
516
+ The new radial grid.
517
+ - new_t: np.ndarray
518
+ The new vertical grid.
519
+
520
+ ----
521
+
522
+ Examples
523
+ --------
524
+ - Example #1: Convert the grid from spherical to cartesian coordinates
525
+
526
+ >>> new_r, new_t, newvar = _convert2cartgrid(R, Z, new_r, new_t)
527
+
528
+ """
529
+ # Convert Cartesian coordinates (R, Z) to polar (Rs, Th)
530
+ Rs = np.sqrt(R**2 + Z**2)
531
+
532
+ Th = np.arctan2(Z, R)
533
+ Th = np.where(Th < 0, Th + 2 * np.pi, Th) # Ensure Th is in [0, 2pi]
534
+
535
+ # Clip Rs and Th to the range of the new grid
536
+ Rs_clipped = np.clip(Rs, new_r[0], new_r[-1])
537
+ Th_clipped = np.clip(Th, new_t[0], new_t[-1])
538
+
539
+ # Normalize Rs and Th to the new grid indices
540
+ ra = (len(new_r) - 1) * (Rs_clipped - new_r[0]) / (new_r[-1] - new_r[0])
541
+ tha = (len(new_t) - 1) * (Th_clipped - new_t[0]) / (new_t[-1] - new_t[0])
542
+
543
+ # Get the integer and fractional parts of the grid indices
544
+ rn, dra = np.divmod(ra, 1)
545
+ thn, dtha = np.divmod(tha, 1)
546
+ rn, thn = rn.astype(int), thn.astype(int)
547
+
548
+ # Ensure indices are within bounds
549
+ rn = np.clip(rn, 0, len(new_r) - 2)
550
+ thn = np.clip(thn, 0, len(new_t) - 2)
551
+
552
+ # Bilinear interpolation
553
+ lrx = len(new_r)
554
+ NN1 = rn + thn * lrx
555
+ NN2 = (rn + 1) + thn * lrx
556
+ NN3 = rn + (thn + 1) * lrx
557
+ NN4 = (rn + 1) + (thn + 1) * lrx
558
+
559
+ w1 = (1 - dra) * (1 - dtha)
560
+ w2 = dra * (1 - dtha)
561
+ w3 = (1 - dra) * dtha
562
+ w4 = dra * dtha
563
+
564
+ return [w1, w2, w3, w4], [NN1, NN2, NN3, NN4]
565
+
566
+
567
+ def _congrid(self, a, newdims, method="linear", center=False, minusone=False):
568
+ """Arbitrary resampling of source array to new dimension sizes.
569
+
570
+ Returns
571
+ -------
572
+ - The resampled array.
573
+
574
+ Parameters
575
+ ----------
576
+ - a: np.ndarray
577
+ The array to be resampled.
578
+ - newdims: tuple
579
+ The new dimension sizes.
580
+ - method: str, default 'linear'
581
+ The interpolation method to be used.
582
+ - center: bool, default False
583
+ If True, centers the resampled array at the new dimensions.
584
+ - minusone: bool, default False
585
+ If True, the new dimensions should be larger by 1 in each dimension.
586
+
587
+ ----
588
+
589
+ Examples
590
+ --------
591
+ - Example #1: Resample the grid
592
+
593
+ >>> newvar = _congrid(newvar, (10, 10))
594
+
595
+ """
596
+ # Based on IDL's congrid routine
597
+ # Ensure input is a floating-point array for interpolation
598
+ a = a.astype(float, copy=False)
599
+
600
+ olddims = np.array(a.shape)
601
+ newdims = np.asarray(newdims, dtype=int)
602
+
603
+ if olddims.size != newdims.size:
604
+ raise ValueError(
605
+ "Dimension mismatch: newdims must have the same number \
606
+ of dimensions as the input array."
607
+ )
608
+
609
+ m1 = int(minusone)
610
+ ofs = 0.5 if center else 0.0
611
+
612
+ # Generate the original grid
613
+ old_grid = [np.arange(n) for n in olddims]
614
+
615
+ # Generate the new grid, scaled to match the new dimensions
616
+ new_grid = np.meshgrid(
617
+ *[
618
+ np.linspace(ofs, olddims[i] - 1 - ofs, num=newdims[i])
619
+ for i in range(len(olddims))
620
+ ],
621
+ indexing="ij",
622
+ )
623
+
624
+ # Stack the coordinates for RegularGridInterpolator
625
+ new_coords = np.stack(new_grid, axis=-1)
626
+
627
+ if method == "spline":
628
+ # Use spline interpolation with map_coordinates
629
+ scale = (olddims - m1) / (newdims - m1)
630
+ coords = np.array(new_grid) * scale[:, None, None]
631
+ return map_coordinates(a, coords, order=3, mode="nearest")
632
+
633
+ else:
634
+ # Use RegularGridInterpolator for 'linear' and 'nearest' methods
635
+ interpolator = RegularGridInterpolator(
636
+ old_grid, a, method=method, bounds_error=False, fill_value=None
637
+ )
638
+ return interpolator(new_coords)
@@ -0,0 +1,27 @@
1
+ """Docstring for pyPLUTO.annotator."""
2
+
3
+ from typing import TypedDict
4
+
5
+ from matplotlib.figure import Figure
6
+
7
+
8
+ class AllKwargs(TypedDict, total=False):
9
+ """Docstring for AllKwargs."""
10
+
11
+ close: bool
12
+ fig: Figure | None
13
+ figsize: list[float]
14
+ fontsize: int
15
+ fontweight: str
16
+ kwargscheck: bool
17
+ LaTeX: bool | str
18
+ numcolors: int
19
+ nwin: int
20
+ oldcolor: bool
21
+ replace: bool
22
+ style: str
23
+ suptitle: str | None
24
+ suptitlesize: int | str
25
+ tight: bool
26
+ withblack: bool
27
+ withwhite: bool