sage-viewer 0.3.0__tar.gz

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 (56) hide show
  1. sage_viewer-0.3.0/LICENSE +21 -0
  2. sage_viewer-0.3.0/PKG-INFO +288 -0
  3. sage_viewer-0.3.0/README.md +224 -0
  4. sage_viewer-0.3.0/pyproject.toml +80 -0
  5. sage_viewer-0.3.0/sage_viewer/__init__.py +3 -0
  6. sage_viewer-0.3.0/sage_viewer/__main__.py +3 -0
  7. sage_viewer-0.3.0/sage_viewer/_version.py +1 -0
  8. sage_viewer-0.3.0/sage_viewer/app.py +1300 -0
  9. sage_viewer-0.3.0/sage_viewer/cli.py +147 -0
  10. sage_viewer-0.3.0/sage_viewer/config.py +48 -0
  11. sage_viewer-0.3.0/sage_viewer/io/__init__.py +13 -0
  12. sage_viewer-0.3.0/sage_viewer/io/galaxy_reader.py +380 -0
  13. sage_viewer-0.3.0/sage_viewer/io/halo_reader.py +233 -0
  14. sage_viewer-0.3.0/sage_viewer/io/par_reader.py +81 -0
  15. sage_viewer-0.3.0/sage_viewer/io/sage_header.py +77 -0
  16. sage_viewer-0.3.0/sage_viewer/io/snapshot_table.py +65 -0
  17. sage_viewer-0.3.0/sage_viewer/parallel/__init__.py +3 -0
  18. sage_viewer-0.3.0/sage_viewer/parallel/loader.py +146 -0
  19. sage_viewer-0.3.0/sage_viewer/scene/__init__.py +6 -0
  20. sage_viewer-0.3.0/sage_viewer/scene/box_profile.py +172 -0
  21. sage_viewer-0.3.0/sage_viewer/scene/camera.py +492 -0
  22. sage_viewer-0.3.0/sage_viewer/scene/fof_layer.py +136 -0
  23. sage_viewer-0.3.0/sage_viewer/scene/galaxy_layer.py +524 -0
  24. sage_viewer-0.3.0/sage_viewer/scene/halo_layer.py +213 -0
  25. sage_viewer-0.3.0/sage_viewer/scene/model.py +181 -0
  26. sage_viewer-0.3.0/sage_viewer/scene/scene.py +634 -0
  27. sage_viewer-0.3.0/sage_viewer/ui/__init__.py +9 -0
  28. sage_viewer-0.3.0/sage_viewer/ui/info_panel.py +164 -0
  29. sage_viewer-0.3.0/sage_viewer/ui/navigation_panel.py +4403 -0
  30. sage_viewer-0.3.0/sage_viewer/ui/toolbar.py +728 -0
  31. sage_viewer-0.3.0/sage_viewer/utils/__init__.py +15 -0
  32. sage_viewer-0.3.0/sage_viewer/utils/catalogue.py +278 -0
  33. sage_viewer-0.3.0/sage_viewer/utils/colormap.py +66 -0
  34. sage_viewer-0.3.0/sage_viewer/utils/command_parser.py +466 -0
  35. sage_viewer-0.3.0/sage_viewer/utils/discover.py +50 -0
  36. sage_viewer-0.3.0/sage_viewer/utils/galaxy_info.py +193 -0
  37. sage_viewer-0.3.0/sage_viewer/utils/group_info.py +92 -0
  38. sage_viewer-0.3.0/sage_viewer/utils/kdtree.py +43 -0
  39. sage_viewer-0.3.0/sage_viewer/utils/sizing.py +87 -0
  40. sage_viewer-0.3.0/sage_viewer/wizard/__init__.py +0 -0
  41. sage_viewer-0.3.0/sage_viewer/wizard/controller.py +848 -0
  42. sage_viewer-0.3.0/sage_viewer/wizard/launch.py +78 -0
  43. sage_viewer-0.3.0/sage_viewer/wizard/ui.py +248 -0
  44. sage_viewer-0.3.0/sage_viewer.egg-info/PKG-INFO +288 -0
  45. sage_viewer-0.3.0/sage_viewer.egg-info/SOURCES.txt +54 -0
  46. sage_viewer-0.3.0/sage_viewer.egg-info/dependency_links.txt +1 -0
  47. sage_viewer-0.3.0/sage_viewer.egg-info/entry_points.txt +2 -0
  48. sage_viewer-0.3.0/sage_viewer.egg-info/requires.txt +20 -0
  49. sage_viewer-0.3.0/sage_viewer.egg-info/top_level.txt +1 -0
  50. sage_viewer-0.3.0/setup.cfg +4 -0
  51. sage_viewer-0.3.0/tests/test_colormap.py +36 -0
  52. sage_viewer-0.3.0/tests/test_galaxy_reader.py +31 -0
  53. sage_viewer-0.3.0/tests/test_halo_reader.py +68 -0
  54. sage_viewer-0.3.0/tests/test_par_reader.py +21 -0
  55. sage_viewer-0.3.0/tests/test_sizing.py +39 -0
  56. sage_viewer-0.3.0/tests/test_snapshot_table.py +28 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michael Bradley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,288 @@
1
+ Metadata-Version: 2.4
2
+ Name: sage-viewer
3
+ Version: 0.3.0
4
+ Summary: Interactive 3D viewer for SAGE semi-analytic galaxy formation outputs
5
+ Author-email: Michael Bradley <mbrads85@live.com.au>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Michael Bradley
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/MBradley1985/SAGE-Viewer
29
+ Project-URL: Documentation, https://sage-viewer.readthedocs.io/en/latest/
30
+ Project-URL: Repository, https://github.com/MBradley1985/SAGE-Viewer
31
+ Project-URL: Issues, https://github.com/MBradley1985/SAGE-Viewer/issues
32
+ Keywords: astronomy,galaxy-formation,dark-matter,visualization,SAGE
33
+ Classifier: Development Status :: 3 - Alpha
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Programming Language :: Python :: 3.10
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
41
+ Classifier: Topic :: Scientific/Engineering :: Visualization
42
+ Requires-Python: >=3.10
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: pyvista>=0.44
46
+ Requires-Dist: trame>=3.6
47
+ Requires-Dist: trame-vtk>=2.8
48
+ Requires-Dist: trame-vuetify>=2.7
49
+ Requires-Dist: h5py>=3.9
50
+ Requires-Dist: numpy>=1.24
51
+ Requires-Dist: joblib>=1.3
52
+ Provides-Extra: docs
53
+ Requires-Dist: mkdocs-material>=9.5; extra == "docs"
54
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
55
+ Requires-Dist: mkdocs-macros-plugin>=1.0; extra == "docs"
56
+ Provides-Extra: dev
57
+ Requires-Dist: pytest>=7.4; extra == "dev"
58
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
59
+ Requires-Dist: ruff>=0.3; extra == "dev"
60
+ Requires-Dist: black>=24.0; extra == "dev"
61
+ Requires-Dist: pre-commit>=3.6; extra == "dev"
62
+ Requires-Dist: sage-viewer[docs]; extra == "dev"
63
+ Dynamic: license-file
64
+
65
+ <p align="center">
66
+ <img src="docs/images/SAGElogo.jpg" alt="SAGE-Viewer logo" width="220"/>
67
+ </p>
68
+
69
+ # SAGE-Viewer
70
+
71
+ An interactive 3D visualization package for [SAGE26](https://github.com/MBradley1985/SAGE26) semi-analytic galaxy formation outputs.
72
+
73
+ Renders dark matter haloes and SAGE galaxies together in a browser-based interactive viewer powered by [PyVista](https://pyvista.org) and [Trame](https://kitware.github.io/trame/).
74
+
75
+ ![SAGE-Viewer](docs/images/hero.png)
76
+
77
+ ## Features
78
+
79
+ ### Rendering
80
+ - World-space gaussian splat rendering of haloes and galaxies — splats scale with camera distance and stay physically meaningful at any zoom
81
+ - **Structure** render mode: each galaxy drawn as a layered composition — cold-gas envelope (blue, sized by ColdGas) + outer envelope (green for CGM-regime sized by CGMgas, red for Hot-regime sized by HotGas)
82
+ - 27 selectable matplotlib colormaps, identical lists for halo and galaxy layers
83
+ - Live colormap, colour-by mode, opacity and visibility controls per layer
84
+ - Colour-by dropdowns are model-aware — only modes whose underlying field is present in the loaded model appear in the list; they update automatically on model switch
85
+ - Full still-quality rendering at all times — no resolution drop during camera drag or playback
86
+
87
+ ![Structure mode](docs/images/structure_mode.png)
88
+
89
+ ### Playback & camera
90
+ - Play / Pause / Stop / Reverse / Repeat transport at 0.1× – 5× speeds
91
+ - Continuous camera rotation (CW / CCW at 15° / 30° / 60° per second)
92
+ - Reset / Centre / Focus buttons
93
+ - Fly to halo, galaxy, coordinates, or sub-box (with focus mode that masks everything outside)
94
+ - **Draw Sphere** (Coords tab): place a live two-handle sphere in the viewport — drag the centre ball to translate, drag the edge ball to resize; **Lock Sphere** commits it as the active focus region
95
+ - **Draw Box** (Box tab): place a live resizable box widget — drag any face or corner handle; **Lock Box** commits it; **Clear** on both tabs cancels the widget without navigating
96
+ - Switching models always lands at z=0 of the new model; slider and snap chip update immediately
97
+ - Camera bookmarks (save, restore, delete)
98
+
99
+ ### Selection & inspection
100
+ - **Galaxy Info** panel (Target tab) — GalaxyID, type, halo Mvir, stellar mass, sSFR, cold gas, B/T, BH mass, H2 mass, gas regime, FFB regime, environment classification, mass-weighted stellar age
101
+ - **Group Info** panel (Environment tab) — FOF-aggregate stats: classification, member breakdown (centrals vs satellites), host Mvir, total stellar / cold gas / SFR, mean B/T, spatial extent, target role, BCG stellar mass
102
+ - **Highlight Galaxy** / **Highlight Members** buttons add regime-coloured splat overlays — CGM-regime members in dodgerblue, Hot-regime in tomato; the selected galaxy is marked with a white border ring
103
+ - **Double-click any point** in the viewport (any tab) to populate the Target tab's halo + galaxy IDs and draw a red marker on the selection. Only currently visible galaxies (passing all filters and focus) are selectable. If Focus is active, the camera carries to the new selection at the last-used radius.
104
+ - **Enter to run** in every input field — Halo idx, Galaxy idx, Coords X/Y/Z, Box bounds, Console command, script path, screenshot/movie label all submit on Enter, equivalent to clicking the paired Go / Zoom / Run / Take Screenshot button
105
+
106
+ ![Filters in action](docs/images/filters.gif)
107
+
108
+ ### Filtering
109
+ - Halo filters: Mvir (log10), Rvir (Mpc/h), Vvir (km/s)
110
+ - Galaxy filters: stellar mass, sSFR, B/T, age, BH mass, ICS mass, type (centrals / satellites), FFB regime, CGM / Hot regime, environment class (Field / Isolated / Group / Cluster, via checkboxes in the Environment tab)
111
+ - Filters are **active-only** — a slider sitting at its full-range endpoints has no effect; move it inward to filter. Every galaxy with detectable mass is visible at startup.
112
+ - Filters auto-disable when the loaded model doesn't contain the underlying field
113
+ - Reset Filters button restores defaults
114
+ - **FoF links are filter-aware** — satellite→central gold lines are only drawn for halos that pass the active filter mask, focus sphere/box, and the halos-visible toggle; they stay correct during playback and recording
115
+ - **Playback respects all scene state** — the pre-render frame cache is keyed on filter values, focus region, layer visibility/opacity/color-mode, and FoF state; changing any of these and pressing Play again always produces fresh frames
116
+
117
+ ### Side-by-side multi-box comparison
118
+ - Load two or more SAGE models side-by-side in a single viewport with `+SBS` in the Models section of the hamburger menu
119
+ - Each box is fully independent: its own snapshot, filters, colormaps, opacity, and visibility settings
120
+ - A box strip at the bottom of the viewport shows all loaded boxes; click any box label to make it active — the entire right panel (Structure, Filters, Target, Console, …) then controls that box
121
+ - Active box label is **green**; idle boxes are **white**
122
+ - Play, step, and the snapshot slider advance only the active box's snapshot
123
+ - Rotation is disabled in multi-box mode (all boxes share one camera; independent rotation is not supported)
124
+ - Halo Mvir colour mode is always locked to Viridis; the colormap selector is greyed out when Mvir is selected
125
+ - CLR button in the box strip resets that box to its defaults without affecting others
126
+
127
+ ### Multi-model (overlays)
128
+ - Auto-scans `<sage_root>/output/` for SAGE model subfolders
129
+ - Switch the primary model from the hamburger menu (any box size)
130
+ - Overlay a second compatible model on top (same box size + snap count)
131
+ - Loading spinner during model swaps; warning snackbar for incompatible overlays
132
+
133
+ ### Output
134
+ - Screenshots in PNG / JPG / TIFF
135
+ - Movie recording in GIF / MOV (H.264, via ffmpeg) / PNG sequence
136
+ - Configurable FPS (1 – 60) and resolution (Native / 2× / 4× supersampled)
137
+ - Optional user-typed label per capture; everything goes into a single session folder per app launch
138
+
139
+ ### Launch Mode wizard
140
+ - Guided setup flow for configuring and launching SAGE26, accessible from the top-left Launch Mode menu or the Explore Mode hamburger
141
+ - Step chips in the header track progress (cyan = current step, green = done, white = pending)
142
+ - **Rescan** button re-runs the environment scan from scratch at any point
143
+ - **Create config file** option generates a new `.par` from the built-in millennium.par template; choose a custom filename before writing
144
+ - Par file editor opens side-by-side with the terminal when a `.par` file needs editing — both panels visible simultaneously
145
+ - Wizard always resets cleanly when reopened from Explore Mode
146
+
147
+ <!-- Console screenshot: drop docs/images/console.png here once captured -->
148
+
149
+ ### Embedded console (Console tab)
150
+ - **Terminal mode** — a live xterm.js terminal backed by a real PTY (`$SHELL -l`); full ANSI colour, cursor control, and interactive programs (`vim`, `top`, `htop`, `less`) all work
151
+ - **SAGE command mode** — natural-language SAGE commands (`show only clusters`, `go to halo 42`, `snap 30`, `screenshot`, …); switch via the **SAGE Cmds** button, `terminal` returns to the shell
152
+ - **Multiple sessions** with a `+` button — each console has its own PTY process and command history
153
+ - **Pop-out** floats a movable / resizable console card over the viewport so you can keep typing while watching the render
154
+
155
+ ### Self-contained metadata
156
+ - Cosmology (h, Ω_m, Ω_Λ), box size, and snapshot redshifts are read directly from `model_0.hdf5`'s `Header/Simulation`
157
+ - The `.par` file is now only needed for tree-file paths
158
+
159
+ ## Supported simulations
160
+
161
+ | Simulation | Box size | Snapshots | Tree format |
162
+ |---|---|---|---|
163
+ | miniMillennium | 62.5 Mpc/h | 64 | lhalo_binary |
164
+ | microUchuu | 96 Mpc/h | 50 | lhalo_binary |
165
+
166
+ Both supported automatically — point at the `.par` file and SAGE-Viewer figures out the rest from the HDF5.
167
+
168
+ ## Quick start
169
+
170
+ ```bash
171
+ git clone https://github.com/MBradley1985/SAGE-Viewer
172
+ cd SAGE-Viewer
173
+ pip install -e .
174
+ sage-viewer --par /path/to/millennium.par
175
+ ```
176
+
177
+ > **Note:** PyPI publishing is coming with v1.0. Until then, install from source as above.
178
+
179
+ Open the printed URL in any browser. To launch on a remote cluster and view locally, use SSH port-forwarding:
180
+
181
+ ```bash
182
+ # On the cluster
183
+ sage-viewer --par millennium.par --port 8080
184
+
185
+ # In a local terminal
186
+ ssh -L 8080:localhost:8080 user@cluster
187
+ # Then open http://localhost:8080 in your browser
188
+ ```
189
+
190
+ ## Command-line options
191
+
192
+ ```text
193
+ --par FILE Path to a SAGE .par file (required)
194
+ --par-dir DIR Directory to scan for additional .par files
195
+ (defaults to the parent of --par; used for the
196
+ multi-model dropdown)
197
+ --snap N Initial snapshot number (default: last = z=0)
198
+ --port N Trame server port (default: 8080)
199
+ --n-jobs N Worker threads for parallel halo file reads
200
+ --max-halos N Downsample ceiling per snapshot
201
+ --max-galaxies N Downsample ceiling per snapshot
202
+ --min-halo-mass MSUN Minimum halo mass to load
203
+ --min-stellar-mass MSUN Minimum stellar mass to load
204
+ ```
205
+
206
+ ## Multi-model workflow
207
+
208
+ If your SAGE root looks like:
209
+
210
+ ```
211
+ SAGE26/
212
+ ├── input/
213
+ │ ├── millennium.par
214
+ │ ├── millennium_vanilla.par
215
+ │ └── microuchuu.par
216
+ └── output/
217
+ ├── millennium/model_0.hdf5
218
+ ├── millennium_vanilla/model_0.hdf5
219
+ └── microuchuu/model_0.hdf5
220
+ ```
221
+
222
+ then `sage-viewer --par input/millennium.par` discovers all three models automatically. Click the hamburger icon (top-left) → Models section to switch, or click "+ overlay" next to a compatible model to render both at once.
223
+
224
+ ## Installation
225
+
226
+ ```bash
227
+ # From source (PyPI release coming with v1.0)
228
+ git clone https://github.com/MBradley1985/SAGE-Viewer
229
+ cd SAGE-Viewer
230
+ pip install -e ".[dev]"
231
+ ```
232
+
233
+ Requires Python ≥ 3.10. Movie recording in MOV format requires `ffmpeg` in your `PATH`.
234
+
235
+ ### HPC / supercomputer
236
+
237
+ A helper script is included for module-system clusters (Slurm, PBS, etc.):
238
+
239
+ ```bash
240
+ # Load a Python module first (name varies by cluster)
241
+ module load python/3.12.0
242
+
243
+ # Create a venv and install SAGE-Viewer in one step
244
+ ./install_hpc.sh
245
+
246
+ # Optional: place the venv on scratch for faster I/O
247
+ ./install_hpc.sh /scratch/$USER/sage-viewer-env
248
+ ```
249
+
250
+ The install is editable (`pip install -e .`) so a `git pull` updates the code immediately with no reinstall. `ffmpeg` is checked separately — load it via your module system if you need MOV recording.
251
+
252
+ Then in every session:
253
+
254
+ ```bash
255
+ source .venv/bin/activate
256
+ sage-viewer --par /path/to/millennium.par --port 8080
257
+ # SSH-tunnel the port to your local browser
258
+ ```
259
+
260
+ ## Documentation
261
+
262
+ Full documentation at [sage-viewer.readthedocs.io](https://sage-viewer.readthedocs.io/en/latest/).
263
+
264
+ ## Tabs at a glance
265
+
266
+ When multiple boxes are loaded a **box strip** appears at the bottom of the viewport. Click any box to make it active (green label). All tab controls then target that box.
267
+
268
+ | Tab | Purpose |
269
+ |---|---|
270
+ | Structure | Layer visibility, opacity, colour-by mode, colormap (with inline colorbar) |
271
+ | Filters | Range sliders for halo and galaxy properties |
272
+ | Record | Screenshots and movie recording |
273
+ | Target | Halo / galaxy navigation, focus zoom, Galaxy Info, Highlight Galaxy |
274
+ | Environment| Halo selector, environment-class checkboxes, Group Info, Highlight Members |
275
+ | Coords | Fly to arbitrary (x, y, z) — "Use Current Position" populates from camera; **Draw Sphere** places an interactive two-handle sphere (drag centre ball to translate, drag edge ball to resize); **Lock Sphere** commits it as the focus region |
276
+ | Box | Zoom to axis-aligned sub-box — "Use Current View" populates from camera; **Draw Box** places a resizable interactive box; **Lock Box** commits it as the focus region |
277
+ | Console | Live xterm.js shell terminal (PTY-backed) + SAGE natural-language command mode. Multiple sessions, pop-out window |
278
+ | Library | Browse stored screenshots / movies; double-click a row to open as a movable, resizable floating card over the viewport (multiple items open simultaneously); per-row delete button removes the file from disk immediately |
279
+
280
+ ![Draw Sphere](docs/images/draw_sphere.gif)
281
+
282
+ <!-- Library pop-out screenshot: drop docs/images/library_popup.png here once captured -->
283
+
284
+ The **Focus button** (top of the right panel) is tab-aware: it focuses on whatever's active in the current tab (target galaxy, environment halo, coords point, or box region).
285
+
286
+ ## License
287
+
288
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,224 @@
1
+ <p align="center">
2
+ <img src="docs/images/SAGElogo.jpg" alt="SAGE-Viewer logo" width="220"/>
3
+ </p>
4
+
5
+ # SAGE-Viewer
6
+
7
+ An interactive 3D visualization package for [SAGE26](https://github.com/MBradley1985/SAGE26) semi-analytic galaxy formation outputs.
8
+
9
+ Renders dark matter haloes and SAGE galaxies together in a browser-based interactive viewer powered by [PyVista](https://pyvista.org) and [Trame](https://kitware.github.io/trame/).
10
+
11
+ ![SAGE-Viewer](docs/images/hero.png)
12
+
13
+ ## Features
14
+
15
+ ### Rendering
16
+ - World-space gaussian splat rendering of haloes and galaxies — splats scale with camera distance and stay physically meaningful at any zoom
17
+ - **Structure** render mode: each galaxy drawn as a layered composition — cold-gas envelope (blue, sized by ColdGas) + outer envelope (green for CGM-regime sized by CGMgas, red for Hot-regime sized by HotGas)
18
+ - 27 selectable matplotlib colormaps, identical lists for halo and galaxy layers
19
+ - Live colormap, colour-by mode, opacity and visibility controls per layer
20
+ - Colour-by dropdowns are model-aware — only modes whose underlying field is present in the loaded model appear in the list; they update automatically on model switch
21
+ - Full still-quality rendering at all times — no resolution drop during camera drag or playback
22
+
23
+ ![Structure mode](docs/images/structure_mode.png)
24
+
25
+ ### Playback & camera
26
+ - Play / Pause / Stop / Reverse / Repeat transport at 0.1× – 5× speeds
27
+ - Continuous camera rotation (CW / CCW at 15° / 30° / 60° per second)
28
+ - Reset / Centre / Focus buttons
29
+ - Fly to halo, galaxy, coordinates, or sub-box (with focus mode that masks everything outside)
30
+ - **Draw Sphere** (Coords tab): place a live two-handle sphere in the viewport — drag the centre ball to translate, drag the edge ball to resize; **Lock Sphere** commits it as the active focus region
31
+ - **Draw Box** (Box tab): place a live resizable box widget — drag any face or corner handle; **Lock Box** commits it; **Clear** on both tabs cancels the widget without navigating
32
+ - Switching models always lands at z=0 of the new model; slider and snap chip update immediately
33
+ - Camera bookmarks (save, restore, delete)
34
+
35
+ ### Selection & inspection
36
+ - **Galaxy Info** panel (Target tab) — GalaxyID, type, halo Mvir, stellar mass, sSFR, cold gas, B/T, BH mass, H2 mass, gas regime, FFB regime, environment classification, mass-weighted stellar age
37
+ - **Group Info** panel (Environment tab) — FOF-aggregate stats: classification, member breakdown (centrals vs satellites), host Mvir, total stellar / cold gas / SFR, mean B/T, spatial extent, target role, BCG stellar mass
38
+ - **Highlight Galaxy** / **Highlight Members** buttons add regime-coloured splat overlays — CGM-regime members in dodgerblue, Hot-regime in tomato; the selected galaxy is marked with a white border ring
39
+ - **Double-click any point** in the viewport (any tab) to populate the Target tab's halo + galaxy IDs and draw a red marker on the selection. Only currently visible galaxies (passing all filters and focus) are selectable. If Focus is active, the camera carries to the new selection at the last-used radius.
40
+ - **Enter to run** in every input field — Halo idx, Galaxy idx, Coords X/Y/Z, Box bounds, Console command, script path, screenshot/movie label all submit on Enter, equivalent to clicking the paired Go / Zoom / Run / Take Screenshot button
41
+
42
+ ![Filters in action](docs/images/filters.gif)
43
+
44
+ ### Filtering
45
+ - Halo filters: Mvir (log10), Rvir (Mpc/h), Vvir (km/s)
46
+ - Galaxy filters: stellar mass, sSFR, B/T, age, BH mass, ICS mass, type (centrals / satellites), FFB regime, CGM / Hot regime, environment class (Field / Isolated / Group / Cluster, via checkboxes in the Environment tab)
47
+ - Filters are **active-only** — a slider sitting at its full-range endpoints has no effect; move it inward to filter. Every galaxy with detectable mass is visible at startup.
48
+ - Filters auto-disable when the loaded model doesn't contain the underlying field
49
+ - Reset Filters button restores defaults
50
+ - **FoF links are filter-aware** — satellite→central gold lines are only drawn for halos that pass the active filter mask, focus sphere/box, and the halos-visible toggle; they stay correct during playback and recording
51
+ - **Playback respects all scene state** — the pre-render frame cache is keyed on filter values, focus region, layer visibility/opacity/color-mode, and FoF state; changing any of these and pressing Play again always produces fresh frames
52
+
53
+ ### Side-by-side multi-box comparison
54
+ - Load two or more SAGE models side-by-side in a single viewport with `+SBS` in the Models section of the hamburger menu
55
+ - Each box is fully independent: its own snapshot, filters, colormaps, opacity, and visibility settings
56
+ - A box strip at the bottom of the viewport shows all loaded boxes; click any box label to make it active — the entire right panel (Structure, Filters, Target, Console, …) then controls that box
57
+ - Active box label is **green**; idle boxes are **white**
58
+ - Play, step, and the snapshot slider advance only the active box's snapshot
59
+ - Rotation is disabled in multi-box mode (all boxes share one camera; independent rotation is not supported)
60
+ - Halo Mvir colour mode is always locked to Viridis; the colormap selector is greyed out when Mvir is selected
61
+ - CLR button in the box strip resets that box to its defaults without affecting others
62
+
63
+ ### Multi-model (overlays)
64
+ - Auto-scans `<sage_root>/output/` for SAGE model subfolders
65
+ - Switch the primary model from the hamburger menu (any box size)
66
+ - Overlay a second compatible model on top (same box size + snap count)
67
+ - Loading spinner during model swaps; warning snackbar for incompatible overlays
68
+
69
+ ### Output
70
+ - Screenshots in PNG / JPG / TIFF
71
+ - Movie recording in GIF / MOV (H.264, via ffmpeg) / PNG sequence
72
+ - Configurable FPS (1 – 60) and resolution (Native / 2× / 4× supersampled)
73
+ - Optional user-typed label per capture; everything goes into a single session folder per app launch
74
+
75
+ ### Launch Mode wizard
76
+ - Guided setup flow for configuring and launching SAGE26, accessible from the top-left Launch Mode menu or the Explore Mode hamburger
77
+ - Step chips in the header track progress (cyan = current step, green = done, white = pending)
78
+ - **Rescan** button re-runs the environment scan from scratch at any point
79
+ - **Create config file** option generates a new `.par` from the built-in millennium.par template; choose a custom filename before writing
80
+ - Par file editor opens side-by-side with the terminal when a `.par` file needs editing — both panels visible simultaneously
81
+ - Wizard always resets cleanly when reopened from Explore Mode
82
+
83
+ <!-- Console screenshot: drop docs/images/console.png here once captured -->
84
+
85
+ ### Embedded console (Console tab)
86
+ - **Terminal mode** — a live xterm.js terminal backed by a real PTY (`$SHELL -l`); full ANSI colour, cursor control, and interactive programs (`vim`, `top`, `htop`, `less`) all work
87
+ - **SAGE command mode** — natural-language SAGE commands (`show only clusters`, `go to halo 42`, `snap 30`, `screenshot`, …); switch via the **SAGE Cmds** button, `terminal` returns to the shell
88
+ - **Multiple sessions** with a `+` button — each console has its own PTY process and command history
89
+ - **Pop-out** floats a movable / resizable console card over the viewport so you can keep typing while watching the render
90
+
91
+ ### Self-contained metadata
92
+ - Cosmology (h, Ω_m, Ω_Λ), box size, and snapshot redshifts are read directly from `model_0.hdf5`'s `Header/Simulation`
93
+ - The `.par` file is now only needed for tree-file paths
94
+
95
+ ## Supported simulations
96
+
97
+ | Simulation | Box size | Snapshots | Tree format |
98
+ |---|---|---|---|
99
+ | miniMillennium | 62.5 Mpc/h | 64 | lhalo_binary |
100
+ | microUchuu | 96 Mpc/h | 50 | lhalo_binary |
101
+
102
+ Both supported automatically — point at the `.par` file and SAGE-Viewer figures out the rest from the HDF5.
103
+
104
+ ## Quick start
105
+
106
+ ```bash
107
+ git clone https://github.com/MBradley1985/SAGE-Viewer
108
+ cd SAGE-Viewer
109
+ pip install -e .
110
+ sage-viewer --par /path/to/millennium.par
111
+ ```
112
+
113
+ > **Note:** PyPI publishing is coming with v1.0. Until then, install from source as above.
114
+
115
+ Open the printed URL in any browser. To launch on a remote cluster and view locally, use SSH port-forwarding:
116
+
117
+ ```bash
118
+ # On the cluster
119
+ sage-viewer --par millennium.par --port 8080
120
+
121
+ # In a local terminal
122
+ ssh -L 8080:localhost:8080 user@cluster
123
+ # Then open http://localhost:8080 in your browser
124
+ ```
125
+
126
+ ## Command-line options
127
+
128
+ ```text
129
+ --par FILE Path to a SAGE .par file (required)
130
+ --par-dir DIR Directory to scan for additional .par files
131
+ (defaults to the parent of --par; used for the
132
+ multi-model dropdown)
133
+ --snap N Initial snapshot number (default: last = z=0)
134
+ --port N Trame server port (default: 8080)
135
+ --n-jobs N Worker threads for parallel halo file reads
136
+ --max-halos N Downsample ceiling per snapshot
137
+ --max-galaxies N Downsample ceiling per snapshot
138
+ --min-halo-mass MSUN Minimum halo mass to load
139
+ --min-stellar-mass MSUN Minimum stellar mass to load
140
+ ```
141
+
142
+ ## Multi-model workflow
143
+
144
+ If your SAGE root looks like:
145
+
146
+ ```
147
+ SAGE26/
148
+ ├── input/
149
+ │ ├── millennium.par
150
+ │ ├── millennium_vanilla.par
151
+ │ └── microuchuu.par
152
+ └── output/
153
+ ├── millennium/model_0.hdf5
154
+ ├── millennium_vanilla/model_0.hdf5
155
+ └── microuchuu/model_0.hdf5
156
+ ```
157
+
158
+ then `sage-viewer --par input/millennium.par` discovers all three models automatically. Click the hamburger icon (top-left) → Models section to switch, or click "+ overlay" next to a compatible model to render both at once.
159
+
160
+ ## Installation
161
+
162
+ ```bash
163
+ # From source (PyPI release coming with v1.0)
164
+ git clone https://github.com/MBradley1985/SAGE-Viewer
165
+ cd SAGE-Viewer
166
+ pip install -e ".[dev]"
167
+ ```
168
+
169
+ Requires Python ≥ 3.10. Movie recording in MOV format requires `ffmpeg` in your `PATH`.
170
+
171
+ ### HPC / supercomputer
172
+
173
+ A helper script is included for module-system clusters (Slurm, PBS, etc.):
174
+
175
+ ```bash
176
+ # Load a Python module first (name varies by cluster)
177
+ module load python/3.12.0
178
+
179
+ # Create a venv and install SAGE-Viewer in one step
180
+ ./install_hpc.sh
181
+
182
+ # Optional: place the venv on scratch for faster I/O
183
+ ./install_hpc.sh /scratch/$USER/sage-viewer-env
184
+ ```
185
+
186
+ The install is editable (`pip install -e .`) so a `git pull` updates the code immediately with no reinstall. `ffmpeg` is checked separately — load it via your module system if you need MOV recording.
187
+
188
+ Then in every session:
189
+
190
+ ```bash
191
+ source .venv/bin/activate
192
+ sage-viewer --par /path/to/millennium.par --port 8080
193
+ # SSH-tunnel the port to your local browser
194
+ ```
195
+
196
+ ## Documentation
197
+
198
+ Full documentation at [sage-viewer.readthedocs.io](https://sage-viewer.readthedocs.io/en/latest/).
199
+
200
+ ## Tabs at a glance
201
+
202
+ When multiple boxes are loaded a **box strip** appears at the bottom of the viewport. Click any box to make it active (green label). All tab controls then target that box.
203
+
204
+ | Tab | Purpose |
205
+ |---|---|
206
+ | Structure | Layer visibility, opacity, colour-by mode, colormap (with inline colorbar) |
207
+ | Filters | Range sliders for halo and galaxy properties |
208
+ | Record | Screenshots and movie recording |
209
+ | Target | Halo / galaxy navigation, focus zoom, Galaxy Info, Highlight Galaxy |
210
+ | Environment| Halo selector, environment-class checkboxes, Group Info, Highlight Members |
211
+ | Coords | Fly to arbitrary (x, y, z) — "Use Current Position" populates from camera; **Draw Sphere** places an interactive two-handle sphere (drag centre ball to translate, drag edge ball to resize); **Lock Sphere** commits it as the focus region |
212
+ | Box | Zoom to axis-aligned sub-box — "Use Current View" populates from camera; **Draw Box** places a resizable interactive box; **Lock Box** commits it as the focus region |
213
+ | Console | Live xterm.js shell terminal (PTY-backed) + SAGE natural-language command mode. Multiple sessions, pop-out window |
214
+ | Library | Browse stored screenshots / movies; double-click a row to open as a movable, resizable floating card over the viewport (multiple items open simultaneously); per-row delete button removes the file from disk immediately |
215
+
216
+ ![Draw Sphere](docs/images/draw_sphere.gif)
217
+
218
+ <!-- Library pop-out screenshot: drop docs/images/library_popup.png here once captured -->
219
+
220
+ The **Focus button** (top of the right panel) is tab-aware: it focuses on whatever's active in the current tab (target galaxy, environment halo, coords point, or box region).
221
+
222
+ ## License
223
+
224
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,80 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sage-viewer"
7
+ dynamic = ["version"]
8
+ description = "Interactive 3D viewer for SAGE semi-analytic galaxy formation outputs"
9
+ readme = "README.md"
10
+ license = { file = "LICENSE" }
11
+ authors = [{ name = "Michael Bradley", email = "mbrads85@live.com.au" }]
12
+ requires-python = ">=3.10"
13
+ keywords = ["astronomy", "galaxy-formation", "dark-matter", "visualization", "SAGE"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Science/Research",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Scientific/Engineering :: Astronomy",
23
+ "Topic :: Scientific/Engineering :: Visualization",
24
+ ]
25
+ dependencies = [
26
+ "pyvista>=0.44",
27
+ "trame>=3.6",
28
+ "trame-vtk>=2.8",
29
+ "trame-vuetify>=2.7",
30
+ "h5py>=3.9",
31
+ "numpy>=1.24",
32
+ "joblib>=1.3",
33
+ ]
34
+
35
+ [project.optional-dependencies]
36
+ docs = [
37
+ "mkdocs-material>=9.5",
38
+ "mkdocstrings[python]>=0.24",
39
+ "mkdocs-macros-plugin>=1.0",
40
+ ]
41
+ dev = [
42
+ "pytest>=7.4",
43
+ "pytest-cov>=4.1",
44
+ "ruff>=0.3",
45
+ "black>=24.0",
46
+ "pre-commit>=3.6",
47
+ "sage-viewer[docs]",
48
+ ]
49
+
50
+ [project.scripts]
51
+ sage-viewer = "sage_viewer.cli:main"
52
+
53
+ [project.urls]
54
+ Homepage = "https://github.com/MBradley1985/SAGE-Viewer"
55
+ Documentation = "https://sage-viewer.readthedocs.io/en/latest/"
56
+ Repository = "https://github.com/MBradley1985/SAGE-Viewer"
57
+ Issues = "https://github.com/MBradley1985/SAGE-Viewer/issues"
58
+
59
+ [tool.setuptools.dynamic]
60
+ version = { attr = "sage_viewer._version.__version__" }
61
+
62
+ [tool.setuptools.packages.find]
63
+ where = ["."]
64
+ include = ["sage_viewer*"]
65
+
66
+ [tool.ruff]
67
+ line-length = 79
68
+ target-version = "py310"
69
+
70
+ [tool.ruff.lint]
71
+ select = ["E", "F", "W", "B", "C4", "UP"]
72
+ ignore = ["E203", "E266", "E501", "W503", "F403", "F401"]
73
+
74
+ [tool.black]
75
+ line-length = 79
76
+ target-version = ["py310", "py311", "py312"]
77
+
78
+ [tool.pytest.ini_options]
79
+ testpaths = ["tests"]
80
+ addopts = "-v --tb=short"
@@ -0,0 +1,3 @@
1
+ from sage_viewer._version import __version__
2
+
3
+ __all__ = ["__version__"]
@@ -0,0 +1,3 @@
1
+ from sage_viewer.cli import main
2
+
3
+ main()
@@ -0,0 +1 @@
1
+ __version__ = "0.3.0"