WindGrid.jl is a Julia package for the analysis and exploration of self-consistent stellar wind model grids produced by LOCUS-HYD. It ingests the per-model output directories generated by a LOCUS-HYD cluster run, stores them efficiently in a single HDF5 file, and exposes a clean Julia API for querying, materializing radial profiles, interpolating, and plotting the full grid.
Author: Felipe I. Figueroa-Tapia (Instituto de Física y Astronomía, Universidad de Valparaíso)
References: Figueroa-Tapia et al. (2026, arXiv:2602.16526); Gormaz-Matamala et al. (2019, ApJ 873); Curé (2004, ApJ 614)
- Robust ingestion — a 7-step defensive pipeline classifies every model as
CONVERGED,NOT_CONVERGED,INCOMPLETE,MISSING_DIR, orPARSE_ERROR. Failed models are never silently discarded; their partial information is preserved. - Efficient two-level storage — scalar outputs (~5 MB for 20 000 models) always live in RAM as a
DataFrame; full radial profiles live compressed in HDF5 and are loaded lazily only when needed. - Flexible queries — filter by any combination of
Teff,log g,R*,Z/Z⊙, and convergence status, with support for exact values or closed ranges. - Lazy profile access —
materializereads only the requested models and columns from HDF5, keeping memory proportional to what you actually need. - IDW interpolation — builds an inverse-distance-weighted interpolator over the converged grid to predict Ṁ, v∞, k, α, δ for arbitrary stellar parameters.
- Empirical recipes — 7 fully implemented mass-loss prescriptions: Vink et al. (2001), de Jager et al. (1988), Gormaz-Matamala et al. (2019, 2022), Björklund et al. (2022), Krtička & Kubát (2018), and Figueroa-Tapia et al. (2026, H/HHe/CNO grids).
compare_recipesevaluates all on the full grid and returns a DataFrame of ratios. - Publication-ready plots — single or multiple radial profiles coloured by any scalar field (
plot_profiles,plot_profile,plot_profiles_vary,plot_profiles_where), Hertzsprung-Russell diagram (plot_hrd), and Ṁ vs Teff with recipe comparisons (plot_mdot_grid). Colorbars auto-convert to log10 for quantities spanning orders of magnitude.
- Julia ≥ 1.9 (1.11+ recommended)
- Packages:
DataFrames,HDF5,Interpolations,Plots
using Pkg
Pkg.add(["DataFrames", "HDF5", "Interpolations", "Plots"])WindGrid.jl/
├── Project.toml
├── README.md
├── WindGrid.jl — User Manual.pdf
├── src/
│ ├── WindGrid.jl # Module root (entry point, exports)
│ ├── types.jl # ModelStatus enum, WindModel, WindProfile, WindGridData
│ ├── utils.jl # Internal helpers (directory name parsing, etc.)
│ ├── io.jl # Ingestion from LOCUS-HYD outputs
│ ├── database.jl # HDF5 save/load, query, materialize, grid_summary
│ ├── interpolation.jl # IDW interpolator over converged models
│ ├── recipes.jl # Empirical Ṁ prescriptions (Vink 2001, de Jager 1988, ...)
│ └── plotting.jl # plot_profiles, plot_hrd, plot_mdot_grid, plot_profile,
│ # plot_profiles_vary, plot_profiles_where
└── notebooks/
├── 00_quickstart.ipynb # Full pipeline in one notebook
├── 01_ingestion_pipeline.ipynb # Detailed ingestion and status flags
├── 02_database_queries.ipynb # HDF5 queries and lazy profile access
├── 03_recipe_comparison.ipynb # All 7 mass-loss recipes vs LOCUS-HYD
├── 04_interpolation.ipynb # IDW interpolation and cross-validation
└── 05_plotting.ipynb # All plotting functions
WindGrid.jl is not a registered package. Clone or copy the repository and add its src/ directory to Julia's load path:
push!(LOAD_PATH, "/path/to/WindGrid.jl/src")
using WindGridTo load it automatically in every Julia session, add the line above to ~/.julia/config/startup.jl.
using WindGrid, Plots
# ── Step 1: Ingest a LOCUS-HYD results directory (run once after the cluster job)
ingest_grid("/path/to/locus_hyd_output", "my_grid.h5")
# ── Step 2: Load the grid (scalars in RAM ~5 MB, profiles on disk)
grid = load_windgrid("my_grid.h5")
grid_summary(grid)
# ── Step 3: Filter and compare empirical recipes
df = compare_recipes(grid; recipes=[:vink2001, :bjorklund2022, :gormaz2022])
# ── Step 4: IDW interpolation — predict all wind parameters
itp = build_interpolator(grid; power=2.0)
res = predict(itp, 41_000.0, 3.6, 14.0, 1.0)
@printf("Ṁ = %.2e M☉/yr v∞ = %.0f km/s k=%.3f α=%.3f δ=%.3f\n",
res.mdot, res.v_inf, res.k, res.alpha, res.delta)
# ── Step 5a: Single radial profile
plot_profile(grid, 35_000, 3.5, 18.0, 1.0; y=:v, color=:steelblue)
# ── Step 5b: Multiple profiles — vary one parameter
plot_profiles_vary(grid; vary=:teff, logg=3.5, r_star=18.0, z_scale=1.0, y=:v)
# ── Step 5c: Filter by output wind parameters
plot_profiles_where(grid; mdot=(1e-7, 1e-6), color_by=:teff)
# ── Step 6: HR diagram
plot_hrd(grid; color_by=:mdot, colormap=:plasma)Every model in the grid is classified with one of the following status values:
| Status | Meaning |
|---|---|
CONVERGED |
Fully converged; all output files present and valid |
NOT_CONVERGED |
HYDWIND ran but did not reach convergence within max_iter |
INCOMPLETE |
Directory exists but key output files are missing (mid-run crash) |
MISSING_DIR |
Directory does not exist (removed by clean_on_fail, or never ran) |
PARSE_ERROR |
Files exist but could not be parsed correctly |
Non-converged models are never discarded — their partial information (e.g. number of iterations reached, final error) is stored and queryable.
WindGrid uses a two-level storage strategy to handle large grids (≥ 20 000 models) efficiently:
| Level | Content | Size (20k models) | Loaded |
|---|---|---|---|
scalars/ in HDF5 |
All scalar outputs (Ṁ, v∞, k, α, δ, status, …) | ~5 MB | Always in RAM |
profiles/ in HDF5 |
Radial arrays from for007.dat + force_results.txt |
~600 MB – 1 GB | Lazy, on request |
The scalar layer supports instant queries over the full grid. Radial profiles are loaded only for the subset you request via materialize, keeping RAM usage proportional to the actual analysis.
| Function | Description |
|---|---|
ingest_grid(root_dir, h5path) |
Parse all LOCUS-HYD output directories → HDF5 |
ingest_model(id, dir_path) |
Parse a single model directory |
| Function | Description |
|---|---|
load_windgrid(h5path) |
Load scalar layer into RAM |
grid_summary(grid) |
Print status distribution and parameter coverage |
query(grid; teff, logg, r_star, z_scale, status) |
Filter models by stellar parameters |
materialize(grid, ids; columns) |
Lazy-load radial profiles from HDF5 |
| Function | Description |
|---|---|
build_interpolator(grid) |
Build IDW interpolator over CONVERGED models |
predict(interp, teff, logg, r_star, z_scale) |
Predict Ṁ, v∞, k, α, δ for new parameters |
predict_mdot(interp, ...) |
Convenience wrapper returning only Ṁ |
| Function | Description |
|---|---|
mdot_vink2001(teff, logg, r_star, z_scale) |
Vink, de Koter & Lamers (2001) — verified |
mdot_dejager1988(teff, logg, r_star) |
de Jager, Nieuwenhuijzen & van der Hucht (1988) — verified |
mdot_gormaz2019(teff, logg, r_star) |
Gormaz-Matamala et al. (2019), ApJ 873 |
mdot_gormaz2022(teff, logg, r_star, z_scale) |
Gormaz-Matamala et al. (2022), A&A 665 |
mdot_figueroatapia2026(teff, logg, r_star; grid=:CNO) |
Figueroa-Tapia et al. (2026), A&A 707 — H/HHe/CNO |
mdot_bjorklund2022(teff, logg, r_star, z_scale) |
Björklund et al. (2022), A&A 676 |
mdot_krticka2018(teff, logg, r_star, z_scale) |
Krtička & Kubát (2018), A&A 612 |
compare_recipes(grid; recipes) |
Evaluate recipes on full grid → DataFrame |
| Function | Description |
|---|---|
plot_profile(grid, teff, logg, r_star, z_scale; rotation, y, color) |
Single profile — finds closest model automatically |
plot_profiles_vary(grid; vary, logg, r_star, z_scale, ...) |
Multiple profiles — fix parameters, vary one |
plot_profiles_where(grid; mdot, v_inf, teff, ...) |
Multiple profiles — filter by output wind parameters |
plot_profiles(profiles, scalars; x, y, color_by) |
Low-level: overlay profiles from a Vector{WindProfile} |
plot_hrd(grid; color_by, colormap) |
HRD coloured by any scalar (log10 auto for Ṁ, ρ, L) |
plot_mdot_grid(grid; z_scale, recipe) |
Ṁ vs Teff with optional recipe overlay |
WindGrid reads the following files produced by LOCUS-HYD for each model:
| File | Content |
|---|---|
summary_report.txt |
Scalar outputs: Ṁ, v∞, k, α, δ, convergence status |
final_parameters.dat |
Fallback source for k, α, δ |
for007.dat |
Full hydrodynamic profile (20 columns, HYDWIND format) |
force_results.txt |
Force-multiplier profile (8 columns, LOCUS format) |
From for007.dat (columns per HydWind.jl manual, Section 7.2):
1:r/R* 2:u=-R*/r 3:v[km/s] 6:rho[g/cm³] 9:grad_line[cm/s²]
18:tau_CAK 19:M(t) 20:dv/dr[s⁻¹]
From force_results.txt (columns per LOCUS-HYD manual, Section 7.4):
3:T(r)[K] 4:t_CAK 5:M(t)_mid 6:M(t)_high 7:M(t)_low 8:Ne/W[cm⁻³]
LOCUS-HYD (cluster job)
↓ per-model output directories on disk
ingest_grid(root_dir, "grid.h5") ← run once
↓
load_windgrid("grid.h5") ← scalars in RAM (~5 MB)
↓
query(grid, ...) ← filter by stellar parameters
↓
materialize(grid, idx) ← lazy-load profiles from HDF5
↓
plot_profiles / compare_recipes / build_interpolator
If you use WindGrid.jl in published work, please cite:
@article{figueroa2026,
author = {Figueroa-Tapia, F. and Panei, J. A. and Curé, M. and Araya, I. and
Ekström, S. and Gormaz-Matamala, A. C. and Venero, R. O. J. and Cidale, L. S.},
title = {New self-consistent theoretical descriptions for mass-loss rates of O-type stars},
journal = {arXiv e-prints},
year = {2026},
eprint = {2602.16526}
}
@mastersthesis{figueroa2024,
author = {Figueroa-Tapia, Felipe I.},
title = {Stellar Winds of O-Type Stars: A Self-Consistent Approach
for Mass-Loss Rates \& Wind Parameters},
school = {Universidad de Valparaíso},
year = {2024},
type = {MSc Thesis}
}
@article{gormaz2019,
author = {Gormaz-Matamala, A. C. and Curé, M. and Cidale, L. S. and Venero, R. O. J.},
title = {New Self-consistent Stellar Wind Parameters for O-type Stars},
journal = {ApJ},
year = {2019},
volume = {873},
pages = {131}
}Felipe I. Figueroa-Tapia
Instituto de Física y Astronomía, Universidad de Valparaíso, Chile
https://felipefigueroat.github.io/
felipe.figueroat@postgrado.uv.cl