-
Notifications
You must be signed in to change notification settings - Fork 22
Updates for PyPi upload and improve app memory usage #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,4 @@ | ||||||||
| # This workflows will upload a Python Package using Twine when a release is created. | ||||||||
|
|
||||||||
| # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries | ||||||||
| # This workflows will upload a Python Package to PyPi | ||||||||
|
|
||||||||
| name: Upload Python Package | ||||||||
|
|
||||||||
|
|
@@ -13,22 +11,25 @@ jobs: | |||||||
|
|
||||||||
| runs-on: ubuntu-latest | ||||||||
|
|
||||||||
| permissions: | ||||||||
|
||||||||
| permissions: | |
| permissions: | |
| contents: read # required for actions/checkout to read repository contents |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,68 @@ | ||
| Install via | ||
| # PyGEDM Web App | ||
|
|
||
| A [Plotly Dash](https://dash.plotly.com/) web interface for [PyGEDM](https://github.com/FRBs/pygedm), providing interactive access to Galactic electron density models: **NE2001**, **NE2025**, and **YMW16**. | ||
|
|
||
| ## Features | ||
|
|
||
| - Convert between dispersion measure (DM) and distance for any sky position | ||
| - Interactive all-sky DM maps with distance slider | ||
| - Supports Galactic (gl, gb) and celestial (RA, DEC) coordinates | ||
| - Compare all three models side-by-side | ||
|
|
||
| ## Running with Docker Compose (recommended) | ||
|
|
||
| ```bash | ||
| docker compose up --build | ||
| ``` | ||
|
|
||
| This will build the image and start the app at [localhost:8050](http://localhost:8050). | ||
|
|
||
| To run in the background: | ||
|
|
||
| ```bash | ||
| docker compose up -d --build | ||
| ``` | ||
|
|
||
| To stop: | ||
|
|
||
| ```bash | ||
| docker compose down | ||
| ``` | ||
|
|
||
| ### Data file | ||
|
|
||
| The app requires `data/gedm_dist_maps.hkl`. If it is present in `app/data/` at build time it will be copied in; otherwise the Dockerfile downloads it automatically from [Zenodo](https://zenodo.org/records/18779007). | ||
|
|
||
| The `docker-compose.yml` mounts `./data` into the container at runtime (read-only), so you can replace the data file without rebuilding the image — just restart the container: | ||
|
|
||
| ```bash | ||
| docker compose restart | ||
| ``` | ||
|
|
||
| ## Running with Docker (manual) | ||
|
|
||
| ```bash | ||
| docker build --tag pygedm_app . | ||
| docker run -p 8050:8050 pygedm_app | ||
| docker run -p 8050:8050 -v "$(pwd)/data:/app/data:ro" pygedm_app | ||
| ``` | ||
|
|
||
| This will start a web app running on [localhost:8050](http://localhost:8050), which you can access via your browser. | ||
| ## Logs | ||
|
|
||
| All startup and request activity is written to stdout, visible via: | ||
|
|
||
| ```bash | ||
| docker compose logs -f | ||
| ``` | ||
|
|
||
| Key log messages to look for: | ||
|
|
||
| | Message | Meaning | | ||
| |---|---| | ||
| | `Loading skymap data from …` | Data file read starting | | ||
| | `Skymap data loaded in X.XXs (XX.X MB …)` | Successful load with timing | | ||
| | `FATAL – could not initialise skymap data` | File missing or corrupt — check `data/` | | ||
| | `HDF5 data group keys: …` | Lists actual keys found in the file | | ||
|
|
||
| ## Screenshots | ||
|
|
||
|  | ||
|  |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,7 +1,20 @@ | ||||||
| import logging | ||||||
| import sys | ||||||
| import time | ||||||
|
|
||||||
| # ── Logging setup (before any other imports so all startup is captured) ────── | ||||||
| logging.basicConfig( | ||||||
| level=logging.INFO, | ||||||
| format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", | ||||||
| stream=sys.stdout, | ||||||
| ) | ||||||
| logger = logging.getLogger("pygedm-app") | ||||||
| logger.info("Starting pygedm web application") | ||||||
|
|
||||||
| import dash | ||||||
| import dash_bootstrap_components as dbc | ||||||
| from dash import dcc, html, Input, Output, State, ctx, no_update | ||||||
| import hickle as hkl | ||||||
| import h5py | ||||||
| import numpy as np | ||||||
| import plotly.express as px | ||||||
| import plotly.graph_objects as pgo | ||||||
|
|
@@ -11,40 +24,82 @@ | |||||
|
|
||||||
| import pygedm | ||||||
|
|
||||||
| # Load skymap data | ||||||
| dskymap = hkl.load('data/gedm_dist_maps.hkl') | ||||||
| skymap_dist = dskymap["dist"] | ||||||
|
|
||||||
| skymap_data_ne = xr.DataArray( | ||||||
| dskymap["ne2001"][:, ::2, ::2], | ||||||
| dims=("distance_kpc", "gb", "gl"), | ||||||
| coords={ | ||||||
| "distance_kpc": skymap_dist, | ||||||
| "gl": dskymap["gl"][::2], | ||||||
| "gb": dskymap["gb"][::2], | ||||||
| }, | ||||||
| attrs={"units": "DM pc/cm3"}, | ||||||
| ) | ||||||
| skymap_data_ne25 = xr.DataArray( | ||||||
| dskymap["ne2025"][:, ::2, ::2], | ||||||
| dims=("distance_kpc", "gb", "gl"), | ||||||
| coords={ | ||||||
| "distance_kpc": skymap_dist, | ||||||
| "gl": dskymap["gl"][::2], | ||||||
| "gb": dskymap["gb"][::2], | ||||||
| }, | ||||||
| attrs={"units": "DM pc/cm3"}, | ||||||
| ) | ||||||
| skymap_data_ymw = xr.DataArray( | ||||||
| dskymap["ymw16"][:, ::2, ::2], | ||||||
| dims=("distance_kpc", "gb", "gl"), | ||||||
| coords={ | ||||||
| "distance_kpc": skymap_dist, | ||||||
| "gl": dskymap["gl"][::2], | ||||||
| "gb": dskymap["gb"][::2], | ||||||
| }, | ||||||
| attrs={"units": "DM pc/cm3"}, | ||||||
| ) | ||||||
| logger.info("All imports completed") | ||||||
|
|
||||||
| # ── Data loading ───────────────────────────────────────────────────────────── | ||||||
| DATA_PATH = "data/gedm_dist_maps.hkl" | ||||||
|
|
||||||
|
|
||||||
| def load_skymap_data(path): | ||||||
| """Load skymap data from HKL file using h5py directly. | ||||||
| """ | ||||||
| logger.info("Loading skymap data from %s", path) | ||||||
| t0 = time.time() | ||||||
|
|
||||||
| try: | ||||||
| with h5py.File(path, "r") as h: | ||||||
| if "data" not in h: | ||||||
| logger.error("HDF5 file missing 'data' group. Top-level keys: %s", list(h.keys())) | ||||||
| raise KeyError("Expected 'data' group in HDF5 file") | ||||||
|
|
||||||
| grp = h["data"] | ||||||
| logger.info("HDF5 data group keys: %s", list(grp.keys())) | ||||||
|
|
||||||
| # hickle wraps dict string keys in quotes: '"keyname"' | ||||||
| dist = grp['"dist"'][()] | ||||||
| gl = grp['"gl"'][()][::2] | ||||||
| gb = grp['"gb"'][()][::2] | ||||||
|
|
||||||
| # Read and downsample in one step – never holds full-res in memory | ||||||
| ne2001 = grp['"ne2001"'][:, ::2, ::2] | ||||||
| ne2025 = grp['"ne2025"'][:, ::2, ::2] | ||||||
| ymw16 = grp['"ymw16"'][:, ::2, ::2] | ||||||
|
|
||||||
| logger.info( | ||||||
| "Loaded arrays – ne2001: %s %s, ne2025: %s %s, ymw16: %s %s", | ||||||
| ne2001.shape, ne2001.dtype, | ||||||
| ne2025.shape, ne2025.dtype, | ||||||
| ymw16.shape, ymw16.dtype, | ||||||
| ) | ||||||
|
|
||||||
| except FileNotFoundError: | ||||||
| logger.critical("Data file not found: %s", path) | ||||||
| raise | ||||||
| except KeyError as exc: | ||||||
| logger.critical("Missing expected key in HDF5 file: %s", exc) | ||||||
| raise | ||||||
| except Exception: | ||||||
| logger.critical("Failed to load skymap data", exc_info=True) | ||||||
| raise | ||||||
|
|
||||||
| nbytes = sum(a.nbytes for a in (ne2001, ne2025, ymw16, dist, gl, gb)) | ||||||
| elapsed = time.time() - t0 | ||||||
| logger.info("Skymap data loaded in %.2fs (%.1f MB in arrays)", elapsed, nbytes / 1024**2) | ||||||
|
|
||||||
| return dist, gl, gb, ne2001, ne2025, ymw16 | ||||||
|
|
||||||
|
|
||||||
| def _build_xarray(data, dist, gl, gb): | ||||||
| """Wrap a numpy array as an xarray DataArray.""" | ||||||
| return xr.DataArray( | ||||||
| data, | ||||||
| dims=("distance_kpc", "gb", "gl"), | ||||||
| coords={"distance_kpc": dist, "gl": gl, "gb": gb}, | ||||||
| attrs={"units": "DM pc/cm3"}, | ||||||
| ) | ||||||
|
|
||||||
|
|
||||||
| try: | ||||||
| _dist, _gl, _gb, _ne2001, _ne2025, _ymw16 = load_skymap_data(DATA_PATH) | ||||||
| skymap_dist = _dist | ||||||
| skymap_data_ne = _build_xarray(_ne2001, _dist, _gl, _gb) | ||||||
| skymap_data_ne25 = _build_xarray(_ne2025, _dist, _gl, _gb) | ||||||
| skymap_data_ymw = _build_xarray(_ymw16, _dist, _gl, _gb) | ||||||
| del _ne2001, _ne2025, _ymw16, _dist, _gl, _gb # free raw arrays | ||||||
|
||||||
| del _ne2001, _ne2025, _ymw16, _dist, _gl, _gb # free raw arrays | |
| del _ne2001, _ne2025, _ymw16, _dist, _gl, _gb # drop extra references; data are held by xarray objects |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,26 @@ | ||||||||||||||||||||||
| services: | ||||||||||||||||||||||
| pygedm: | ||||||||||||||||||||||
| build: | ||||||||||||||||||||||
| context: . | ||||||||||||||||||||||
| dockerfile: Dockerfile | ||||||||||||||||||||||
| image: pygedm-app | ||||||||||||||||||||||
| container_name: pygedm | ||||||||||||||||||||||
| ports: | ||||||||||||||||||||||
| - "8050:8050" | ||||||||||||||||||||||
| # Mount the data directory so the HKL file can be updated without | ||||||||||||||||||||||
| # rebuilding the image. Remove this volume if you prefer the file | ||||||||||||||||||||||
| # to be baked in at build time. | ||||||||||||||||||||||
| volumes: | ||||||||||||||||||||||
| - ./data:/app/data:ro | ||||||||||||||||||||||
|
Comment on lines
+10
to
+14
|
||||||||||||||||||||||
| # Mount the data directory so the HKL file can be updated without | |
| # rebuilding the image. Remove this volume if you prefer the file | |
| # to be baked in at build time. | |
| volumes: | |
| - ./data:/app/data:ro | |
| # Mount the HKL data file so it can be updated without rebuilding | |
| # the image. Remove this volume if you prefer the file to be baked | |
| # in at build time. | |
| volumes: | |
| - ./data/gedm_dist_maps.hkl:/app/data/gedm_dist_maps.hkl:ro |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,5 @@ gunicorn | |
| Pillow | ||
| requests | ||
| h5py | ||
| hickle | ||
| numba | ||
| git+https://github.com/telegraphic/mwprop | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The header comment has a couple of typos/inconsistencies: “workflows” → “workflow”, and “PyPi” should be spelled “PyPI”. Since this file documents the publishing workflow, it’s worth keeping the naming consistent with the official registry.