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,435 @@
1
+ """A custom variable evaluator for PyPLUTO."""
2
+
3
+ import os
4
+ import re
5
+ import tempfile
6
+
7
+ import numexpr as ne
8
+ import numpy as np
9
+ from PySide6.QtCore import Qt
10
+ from PySide6.QtWidgets import (
11
+ QComboBox,
12
+ QDialog,
13
+ QDialogButtonBox,
14
+ QFormLayout,
15
+ QLabel,
16
+ QPlainTextEdit,
17
+ QVBoxLayout,
18
+ )
19
+
20
+ SENTINEL = "Custom var..."
21
+
22
+ # --- tiny normalizer ---------------------------------------------------------
23
+ # Allow both D.something and bare names; allow np./numpy. prefixes.
24
+ _NORM_PATTERNS = [
25
+ (re.compile(r"\bD\."), ""), # D.Bx1 -> Bx1
26
+ (re.compile(r"\bnp\."), ""), # np.sqrt -> sqrt
27
+ (re.compile(r"\bnumpy\."), ""), # numpy.sqrt -> sqrt
28
+ ]
29
+
30
+
31
+ def _normalize_expr(expr: str) -> str:
32
+ s = expr.strip()
33
+ for pat, repl in _NORM_PATTERNS:
34
+ s = pat.sub(repl, s)
35
+ return s
36
+
37
+
38
+ def _frozen_var_names(D):
39
+ """Names that must never be overridden by custom vars."""
40
+ names = set(map(str, getattr(D, "_load_vars", []))) # loaded-from-file vars
41
+ names.update({"x1", "x2", "x3"}) # base grid
42
+ g = (getattr(D, "geom", "") or "").upper()
43
+ if g == "CARTESIAN":
44
+ names.update({"x", "y", "z"})
45
+ elif g == "POLAR":
46
+ names.update({"R", "phi", "z", "x", "y", "xr", "yr"})
47
+ elif g == "SPHERICAL":
48
+ names.update({"r", "theta", "phi", "R", "z", "rt", "zt"})
49
+ return names
50
+
51
+
52
+ def _apply_grid_shaping(local, D):
53
+ """
54
+ Reshape only x1/x2/x3 if they are 1-D so they broadcast with D.nshp.
55
+
56
+ PyPLUTO order: 1D -> (nx1,), 2D -> (nx1, nx2), 3D -> (nx1, nx2, nx3)
57
+ x1 aligns to axis 0, x2 to axis 1, x3 to axis 2.
58
+ """
59
+ nshp = D.nshp if isinstance(D.nshp, tuple) else tuple(D.nshp)
60
+ for axis, name in enumerate(("x1", "x2", "x3")[: D.dim]):
61
+ v = local.get(name)
62
+ if (
63
+ isinstance(v, np.ndarray)
64
+ and v.ndim == 1
65
+ and v.shape[0] == nshp[axis]
66
+ ):
67
+ try:
68
+ # make a singleton shape on other axes, then broadcast to D.nshp
69
+ pattern = [1] * D.dim
70
+ pattern[axis] = v.shape[0] # axis-aligned length
71
+ v_view = v.reshape(tuple(pattern))
72
+ local[name] = np.broadcast_to(
73
+ v_view, nshp
74
+ ) # FINAL SHAPE == D.nshp
75
+ except Exception:
76
+ pass # stay quiet per your policy
77
+
78
+ IS_3D = 3
79
+ if D.geom == "CARTESIAN":
80
+ local["x"] = local["x1"]
81
+ local["y"] = local["x2"]
82
+ local["z"] = local["x3"]
83
+ elif D.geom == "POLAR":
84
+ local["R"] = local["x1"]
85
+ local["phi"] = local["x2"]
86
+ local["z"] = local["x3"]
87
+ local["x"] = D.x1c.T[:, :, None] if D.dim == IS_3D else D.x1c.T
88
+ local["y"] = D.x2c.T[:, :, None] if D.dim == IS_3D else D.x2c.T
89
+ elif D.geom == "SPHERICAL":
90
+ local["r"] = local["x1"]
91
+ local["theta"] = local["x2"]
92
+ local["phi"] = local["x3"]
93
+ local["R"] = D.x1p.T[:, :, None] if D.dim == IS_3D else D.x1p.T
94
+ local["z"] = D.x2p.T[:, :, None] if D.dim == IS_3D else D.x2p.T
95
+ if D.dim == 3:
96
+ local["rt"] = D.x1t.T[:, None, :]
97
+ local["zt"] = D.x3t.T[:, None, :]
98
+
99
+
100
+ # --- evaluator ---------------------------------------------------------------
101
+ def evaluate_custom_var(D, name: str, expr: str, *, assign: bool = True):
102
+ """
103
+ numexpr-only, memmap output for arrays, scalar stays scalar.
104
+
105
+ Accepts D.foo or foo; np.func/numpy.func or func directly.
106
+ """
107
+ expr = _normalize_expr(expr)
108
+
109
+ # Do not allow overriding loaded or grid variables
110
+ if name in _frozen_var_names(D):
111
+ raise ValueError(f"protected name: {name}")
112
+
113
+ # Build locals from D (public, non-callable) + constants
114
+ local = {"pi": float(np.pi), "e": float(np.e)}
115
+ for k, v in D.__dict__.items():
116
+ if not k.startswith("_") and not callable(v):
117
+ local[k] = v
118
+
119
+ _apply_grid_shaping(local, D) # <<< add this
120
+
121
+ # Parse/compile (syntax check)
122
+ try:
123
+ compiled = ne.NumExpr(expr)
124
+ except Exception as e:
125
+ raise ValueError(f"compile error: {e}")
126
+
127
+ # Validate cheaply on only the used names (1-element views)
128
+ names = compiled.input_names
129
+ tiny = {}
130
+ for n in names:
131
+ v = local.get(n)
132
+ if v is None:
133
+ raise ValueError(f"unknown name: {n}")
134
+ tiny[n] = v.reshape(-1)[:1] if isinstance(v, np.ndarray) else v
135
+ try:
136
+ tiny_res = ne.evaluate(expr, local_dict=tiny, global_dict={})
137
+ except Exception as e:
138
+ raise ValueError(f"validation error: {e}")
139
+
140
+ # Infer shape from arrays actually referenced (broadcasting)
141
+ arrs = [local[n] for n in names if isinstance(local[n], np.ndarray)]
142
+ if not arrs:
143
+ # Scalar result
144
+ try:
145
+ res = ne.evaluate(expr, local_dict=local, global_dict={})
146
+ except Exception as e:
147
+ raise ValueError(f"evaluate error: {e}")
148
+ if isinstance(res, np.ndarray) and res.shape == ():
149
+ res = res.item()
150
+ else:
151
+ out_shape = np.broadcast(
152
+ *[np.empty(a.shape, dtype=[]) for a in arrs]
153
+ ).shape
154
+ out_dtype = getattr(tiny_res, "dtype", arrs[0].dtype)
155
+ # Tempfile-backed memmap keeps result on disk
156
+ tmp = tempfile.NamedTemporaryFile(
157
+ prefix=f"{name}_", suffix=".dat", delete=False
158
+ )
159
+ tmp_path = tmp.name
160
+ tmp.close()
161
+ try:
162
+ out = np.memmap(
163
+ tmp_path, mode="w+", dtype=out_dtype, shape=out_shape
164
+ )
165
+ ne.evaluate(expr, local_dict=local, global_dict={}, out=out)
166
+ res = out
167
+ except Exception as e:
168
+ try:
169
+ if "out" in locals():
170
+ del out
171
+ if os.path.exists(tmp_path):
172
+ os.remove(tmp_path)
173
+ except Exception:
174
+ pass
175
+ raise ValueError(f"evaluate error: {e}")
176
+
177
+ if assign:
178
+ setattr(D, name, res)
179
+
180
+ return res
181
+
182
+
183
+ # --- Dialog: now a single textbox with one-or-more "name = expr" lines -------
184
+ _LINE_RE = re.compile(r"^\s*(!?[A-Za-z_]\w*)\s*=\s*(.+?)\s*$")
185
+
186
+
187
+ class CustomVarDialog(QDialog):
188
+ def __init__(self, D, parent=None):
189
+ super().__init__(parent)
190
+ self.D = D
191
+ self.setWindowTitle("Add Custom Variables")
192
+ self.setMinimumWidth(460)
193
+
194
+ self.exprs = QPlainTextEdit()
195
+ self.exprs.setPlaceholderText("Define variables:\nA = ...\nB = ...")
196
+ self.exprs.setMinimumHeight(160)
197
+
198
+ self.err = QLabel("")
199
+ self.err.setStyleSheet("color:#b00020;")
200
+ self.err.setWordWrap(True)
201
+
202
+ form = QFormLayout()
203
+ form.addRow("Variables:", self.exprs)
204
+
205
+ btns = QDialogButtonBox(
206
+ QDialogButtonBox.StandardButton.Ok
207
+ | QDialogButtonBox.StandardButton.Cancel
208
+ )
209
+ btns.accepted.connect(self._accept)
210
+ btns.rejected.connect(self.reject)
211
+
212
+ lay = QVBoxLayout(self)
213
+ lay.addLayout(form)
214
+ lay.addWidget(self.err)
215
+ lay.addWidget(btns)
216
+
217
+ self._pairs: list[tuple[str, str]] = []
218
+ self.exprs.textChanged.connect(lambda: self.err.setText(""))
219
+
220
+ def _parse_lines(self, text: str):
221
+ """Return a list of tuples.
222
+
223
+ (display_name, expr_display, hidden, clean_name, expr_clean)
224
+ - display_name: what the user typed for the name (may start with '!')
225
+ - expr_display: right-hand side exactly as typed (keeps '# comment')
226
+ - hidden: True if name starts with '!'
227
+ - clean_name: display_name without leading '!' (actual Python attribute)
228
+ - expr_clean: expr_display with trailing comment stripped (before evaluation)
229
+
230
+ """
231
+ pairs = []
232
+ for raw in text.splitlines():
233
+ if not raw.strip():
234
+ continue
235
+ m = _LINE_RE.match(raw)
236
+ if not m:
237
+ raise ValueError(f"invalid line: {raw!r} (use NAME = EXPR)")
238
+ display_name, expr_display = m.group(1), m.group(2).strip()
239
+ hidden = display_name.startswith("!")
240
+ clean_name = display_name[1:] if hidden else display_name
241
+ expr_clean = expr_display.split("#", 1)[
242
+ 0
243
+ ].strip() # ignore comments when evaluating
244
+ if not expr_clean:
245
+ raise ValueError(
246
+ f"empty expression after comment stripping in line: {raw!r}"
247
+ )
248
+ pairs.append(
249
+ (display_name, expr_display, hidden, clean_name, expr_clean)
250
+ )
251
+ if not pairs:
252
+ raise ValueError("Please enter at least one 'NAME = EXPR' line.")
253
+ return pairs
254
+
255
+ def _accept(self):
256
+ text = self.exprs.toPlainText()
257
+ try:
258
+ pairs = self._parse_lines(text)
259
+ # validate sequentially using clean_name / expr_clean
260
+ seq = [(p[3], p[4]) for p in pairs] # (clean_name, expr_clean)
261
+ _validate_lines_sequential(self.D, seq)
262
+ except Exception as ex:
263
+ self.err.setText(f"Invalid definitions: {ex}")
264
+ return
265
+ self._pairs = pairs
266
+ self.accept()
267
+
268
+ @property
269
+ def values(self):
270
+ # Backwards-compatible name (used by _on_activated)
271
+ return self._pairs
272
+
273
+
274
+ def setup_var_selector(combo: QComboBox, D):
275
+ """Set up a QComboBox to handle custom variable creation and re-apply."""
276
+ if combo.property("_cv_connected"):
277
+ # still re-apply on each load
278
+ _reapply_custom_vars(combo, D)
279
+ return
280
+ combo.setProperty("_cv_connected", True)
281
+ combo.setProperty("_last", 0 if combo.count() else -1)
282
+ if combo.property("_cv_defs") is None:
283
+ combo.setProperty("_cv_defs", []) # list[(name, expr)]
284
+ combo.activated[int].connect(lambda i: _on_activated(combo, i, D))
285
+ _reapply_custom_vars(combo, D)
286
+
287
+
288
+ def _build_locals(D):
289
+ local = {"pi": float(np.pi), "e": float(np.e)}
290
+ for k, v in D.__dict__.items():
291
+ if not k.startswith("_") and not callable(v):
292
+ local[k] = v
293
+ return local
294
+
295
+
296
+ def _validate_lines_sequential(D, pairs):
297
+ """Cheap, sequential validation using 1-element array views."""
298
+ base = _build_locals(D)
299
+ _apply_grid_shaping(base, D) # <<< add this
300
+ # tiny env: scalars unchanged, arrays -> 1 element
301
+ tiny = {
302
+ k: (v.reshape(-1)[:1] if isinstance(v, np.ndarray) else v)
303
+ for k, v in base.items()
304
+ }
305
+ tiny["pi"], tiny["e"] = float(np.pi), float(np.e)
306
+ for name, expr in pairs:
307
+ s = _normalize_expr(expr)
308
+ compiled = ne.NumExpr(s)
309
+ names = compiled.input_names
310
+ # ensure all symbols exist
311
+ env = {}
312
+ for n in names:
313
+ if n not in tiny:
314
+ raise ValueError(f"unknown name: {n}")
315
+ env[n] = tiny[n]
316
+ if name in _frozen_var_names(D):
317
+ raise ValueError(
318
+ f"'{name}' is protected and cannot be redefined"
319
+ )
320
+ try:
321
+ res = ne.evaluate(s, local_dict=env, global_dict={})
322
+ except Exception as e:
323
+ raise ValueError(f"error in '{name} = {expr}': {e}")
324
+ # store the tiny result for subsequent lines
325
+ tiny[name] = res
326
+
327
+
328
+ def _on_activated(combo: QComboBox, idx: int, D):
329
+ # Always use the live D from the main window if available
330
+ D = getattr(combo.window(), "D", D)
331
+
332
+ if combo.itemText(idx) != SENTINEL:
333
+ combo.setProperty("_last", idx)
334
+ return
335
+ last = combo.property("_last")
336
+ if last is not None and last >= 0:
337
+ combo.setCurrentIndex(last)
338
+ dlg = CustomVarDialog(D, combo.window())
339
+ if dlg.exec() != QDialog.DialogCode.Accepted:
340
+ return
341
+
342
+ # tuples: (display_name, expr_display, hidden, clean_name, expr_clean)
343
+ pairs = dlg.values or []
344
+ stored = list(combo.property("_cv_defs") or [])
345
+
346
+ for display_name, expr_display, hidden, clean_name, expr_clean in pairs:
347
+ # evaluate & assign on D using clean values
348
+ try:
349
+ evaluate_custom_var(D, clean_name, expr_clean, assign=True)
350
+ except Exception:
351
+ continue # silent skip per requirement
352
+
353
+ # add to combo only if not hidden
354
+ if not hidden:
355
+ # duplicate? select existing
356
+ for i in range(combo.count()):
357
+ if combo.itemText(i).lower() == clean_name.lower():
358
+ combo.setCurrentIndex(i)
359
+ combo.setProperty("_last", i)
360
+ break
361
+ else:
362
+ # insert before sentinel
363
+ sent = next(
364
+ (
365
+ i
366
+ for i in range(combo.count())
367
+ if combo.itemText(i) == SENTINEL
368
+ ),
369
+ -1,
370
+ )
371
+ pos = sent if sent != -1 else combo.count()
372
+ combo.insertItem(pos, clean_name)
373
+ combo.setItemData(
374
+ pos, expr_clean, role=Qt.ItemDataRole.UserRole
375
+ )
376
+ combo.setCurrentIndex(pos)
377
+ combo.setProperty("_last", pos)
378
+
379
+ # store triple so we can reapply (expr_clean) and display comments (expr_display)
380
+ stored.append((display_name, expr_clean, expr_display))
381
+
382
+ combo.setProperty("_cv_defs", stored)
383
+
384
+ # Refresh info panel if present (shows the display expr with comments)
385
+ top = combo.window()
386
+ if hasattr(top, "info_label") and stored:
387
+ lines = []
388
+ for tup in stored:
389
+ if len(tup) == 3:
390
+ disp_name, _clean, disp_expr = tup
391
+ lines.append(f"{disp_name} = {disp_expr}")
392
+ else:
393
+ # backward-compat if older pairs (name, expr)
394
+ n, e = tup
395
+ lines.append(f"{n} = {e}")
396
+ old_text = top.info_label.toPlainText()
397
+ base = old_text.split("\n\nCustom variables:")[0]
398
+ top.info_label.setPlainText(
399
+ f"{base}\n\nCustom variables:\n" + "\n".join(lines)
400
+ )
401
+
402
+
403
+ def _reapply_custom_vars(combo: QComboBox, D):
404
+ """Recreate previously defined session custom vars on the new D, silently skipping failures."""
405
+ defs = list(combo.property("_cv_defs") or [])
406
+ if not defs:
407
+ return
408
+ for item in defs:
409
+ # Support both (name, expr) legacy pairs and (display_name, expr_clean, expr_display) triples
410
+ if len(item) == 3:
411
+ display_name, expr_clean, _expr_display = item
412
+ else:
413
+ display_name, expr_clean = item
414
+ hidden = display_name.startswith("!")
415
+ clean_name = display_name[1:] if hidden else display_name
416
+
417
+ try:
418
+ evaluate_custom_var(D, clean_name, expr_clean, assign=True)
419
+ except Exception:
420
+ continue # silent skip
421
+
422
+ if not hidden and not any(
423
+ combo.itemText(i) == clean_name for i in range(combo.count())
424
+ ):
425
+ sent = next(
426
+ (
427
+ i
428
+ for i in range(combo.count())
429
+ if combo.itemText(i) == SENTINEL
430
+ ),
431
+ -1,
432
+ )
433
+ pos = sent if sent != -1 else combo.count()
434
+ combo.insertItem(pos, clean_name)
435
+ combo.setItemData(pos, expr_clean, role=Qt.ItemDataRole.UserRole)
pyPLUTO/gui/globals.py ADDED
@@ -0,0 +1,108 @@
1
+ """Contains global variables for the GUI."""
2
+
3
+ import matplotlib.scale as mscale
4
+ from matplotlib import colormaps as cmaps
5
+
6
+ scale = list(mscale.get_scale_names())
7
+ scales = [scale[i] for i in [3, 4, 6, 0]]
8
+ vscales = ["linear", "log", "symlog", "2slope", "power", "asinh"]
9
+
10
+ cmaps_list = list(cmaps)
11
+ cmaps_avail0 = [cmaps_list[2], *cmaps_list[0:2], *cmaps_list[3:83]]
12
+ cmaps_avail = [cmap for cmap in cmaps_avail0 if cmap in cmaps_list]
13
+
14
+ cmaps_divided = {
15
+ "All": cmaps_avail,
16
+ "Uniform": ["plasma", "viridis", "inferno", "magma", "cividis"],
17
+ "Sequential": [
18
+ "Greys",
19
+ "Purples",
20
+ "Blues",
21
+ "Greens",
22
+ "Oranges",
23
+ "Reds",
24
+ "YlOrBr",
25
+ "YlOrRd",
26
+ "OrRd",
27
+ "PuRd",
28
+ "RdPu",
29
+ "BuPu",
30
+ "GnBu",
31
+ "PuBu",
32
+ "YlGnBu",
33
+ "PuBuGn",
34
+ "BuGn",
35
+ "YlGn",
36
+ ],
37
+ "Sequential (2)": [
38
+ "binary",
39
+ "gist_yarg",
40
+ "gist_gray",
41
+ "gray",
42
+ "bone",
43
+ "pink",
44
+ "spring",
45
+ "summer",
46
+ "autumn",
47
+ "winter",
48
+ "cool",
49
+ "Wistia",
50
+ "hot",
51
+ "afmhot",
52
+ "gist_heat",
53
+ "copper",
54
+ ],
55
+ "Diverging": [
56
+ "PiYG",
57
+ "PRGn",
58
+ "BrBG",
59
+ "PuOr",
60
+ "RdGy",
61
+ "RdBu",
62
+ "RdYlBu",
63
+ "RdYlGn",
64
+ "Spectral",
65
+ "coolwarm",
66
+ "bwr",
67
+ "seismic",
68
+ "berlin",
69
+ "managua",
70
+ "vanimo",
71
+ ],
72
+ "Cyclic": ["twilight", "twilight_shifted", "hsv"],
73
+ "Qualitative": [
74
+ "Pastel1",
75
+ "Pastel2",
76
+ "Paired",
77
+ "Accent",
78
+ "Dark2",
79
+ "Set1",
80
+ "Set2",
81
+ "Set3",
82
+ "tab10",
83
+ "tab20",
84
+ "tab20b",
85
+ "tab20c",
86
+ ],
87
+ "Miscellaneous": [
88
+ "flag",
89
+ "prism",
90
+ "ocean",
91
+ "gist_earth",
92
+ "terrain",
93
+ "gist_stern",
94
+ "gnuplot",
95
+ "gnuplot2",
96
+ "CMRmap",
97
+ "cubehelix",
98
+ "brg",
99
+ "gist_rainbow",
100
+ "rainbow",
101
+ "jet",
102
+ "turbo",
103
+ "nipy_spectral",
104
+ "gist_ncar",
105
+ ],
106
+ }
107
+
108
+ format_avail = ["None", "dbl", "flt", "vtk", "dbl.h5", "flt.h5", "hdf5", "tab"]
pyPLUTO/gui/main.py ADDED
@@ -0,0 +1,17 @@
1
+ import sys
2
+
3
+ from PySide6.QtWidgets import QApplication
4
+
5
+ from .main_window import PyPLUTOApp
6
+
7
+
8
+ def main():
9
+ app = QApplication(sys.argv)
10
+ window = PyPLUTOApp(code="PLUTO")
11
+ window.resize(1150, 720)
12
+ window.show()
13
+ sys.exit(app.exec())
14
+
15
+
16
+ if __name__ == "__main__":
17
+ main()