# GURU — `guru-hugoniot` Manual (EN, phase‑aware)

**Purpose.** Solve the Rankine–Hugoniot condition along **isotherms** and/or **isochors** using thermo data from `guru-thermo` and an initial state from `guru-iso-fit` (recommended) or `guru-thermo`. If a `phase` column is present (e.g., from `guru-thermo-phase`), each grouped curve is **split by phase** and solved separately with a clear preference for **non‑extrapolated** solutions.

---

## Hugoniot equation

The energy form of the Rankine–Hugoniot relation:

$$
(E - E_0) + \frac{1}{2} \bigl(V - V_0\bigr) \bigl(P + P_0\bigr) = 0
$$

with units expected by GURU tools:

* $E, E_0$ — **eV/atom** (internally converted to J/atom),
* $P, P_0$ — **kbar** (converted to Pa),
* $V, V_0$ — **cm³/g** (converted to m³/atom using molar mass $M_a$).

All conversions use SciPy/CODATA constants.

---

## Synopsis

```bash
guru-hugoniot --Ma MOLAR_MASS \
  [--initial init.tsv | (--grid-eos EOS.tsv [--grid-init-T T0 --grid-init-rho R0])] \
  [-i data1.tsv ...] [--rho0 R0] \
  [--curves both|iso|rho] [--group-dT 20] [--group-drho 0.01] \
  [--interp pchip|linear] \
  [--grid-step-T 50 --grid-step-rho 0.05 \
   --grid-T-min Tmin --grid-T-max Tmax \
   --grid-rho-min Rmin --grid-rho-max Rmax \
   --extrap-frac 0.10 --combine concat|avg] \
  [-o hugoniot_full.tsv \
   --out-compact hugoniot_compact.tsv [--compact-with-velocity] [--compact-extra-units]] \
  [--plot [--plot-prefix PREFIX]] [--progress|--no-progress] \
  [--u0 U0_KMS] [--P0 P0_KBAR] [--E0 E0_EV]
```

**Required**

* `--initial` — reference state TSV accepted formats:

  * from **`guru-iso-fit` isotherm**: header `T  P  E  V  rho ...`,
  * from **`guru-iso-fit` isochor** : header `rho  P  E  T ...`,
  * from **`guru-thermo --out`**   : uses `*_mean` fields (`density_g_cm3` if present),
  * **compact `T P E`**            : allowed; then pass `--rho0`.
* `--Ma` — molar mass (g/mol) to convert $V$ to m³/atom.

**Thermo inputs (sample‑based mode)**

* `-i` — one or more TSVs (compact `T P E` or rich `--out`). If a **`phase`** column exists, phase‑aware logic is enabled.

**Grid EOS mode (spline‑based)**

* `--grid-eos` — rectangular EOS grid TSV with columns `T`, `rho`, `P`, `E` (or `E_atom`).
* Optional override of the initial point from the grid:
  * `--grid-init-T`, `--grid-init-rho` — set T0, ρ0 manually; P0, E0 are taken by interpolation from the grid; V0=1/ρ0.
* Scan control:
  * `--grid-step-T` (K), `--grid-step-rho` (g/cm³) — sampling along isotherms/isochors.
  * `--grid-T-min/max`, `--grid-rho-min/max` — explicit scan bounds; by default bounds are taken from the grid, expanded by `± --extrap-frac * span`.
  * `--extrap-frac` — allow fractional extrapolation beyond the grid bounds (default 10%).
* Combining families when `--curves both`:
  * `--combine concat` — list results from both scans;
  * `--combine avg` — pair nearest‑by‑pressure isotherm/isochor points and output their averages.

**Progress and plots**

* `--progress` / `--no-progress` — toggle progress display (stderr). In grid mode, shows “hugoniot iso/rho”.
* `--plot` — save PNG plots (see below). `--plot-prefix` sets output prefix (default from `--out`).
* Double‑shock: `--u0` — upstream material velocity in km/s. If set (non‑zero), the reported particle speed is
  adjusted as $U^* = u_0 - U$ (material already has velocity before the shock).
* Initial overrides: `--P0` (kbar) and/or `--E0` (eV/atom) override values from the initial TSV or grid.

---

## Grouping & phase handling

### Grouping curves

* **Isotherms**: bin by $T$ using `--group-dT` (default 20 K). Sort by **decreasing density** (ascending $V=1/\rho$).
* **Isochors**: bin by $\rho$ using `--group-drho` (default 0.01 g/cm³). Sort by **increasing $T$**.
* Each curve requires **≥ 2 points** to build interpolants.

### Phase splitting & preference

If a `phase` column is present:

1. Split the curve by distinct integer `phase` values.
2. **Attempt in‑range (non‑extrapolated) solutions** per phase. If any succeed, **report the one with the largest data support** (most points) and **skip** other phases for this curve.
3. If no in‑range solution exists, **allow extrapolation**; among extrapolated candidates, pick the one with **smallest normalized distance** beyond the data span.

Without a `phase` column, the entire curve is treated as a single set.

---

## Interpolation & root search

For each candidate (curve × phase):

* Build two interpolants:

  * **Isotherm**: $E(V)$ and $P(V)$ vs $V=1/\rho$,
  * **Isochor** : $E(T)$ and $P(T)$ vs $T$.
* Sample‑based curves: `pchip` (shape‑preserving; default) or `linear`.
* Find a sign change of the Hugoniot residual between adjacent samples and locate the root by **bisection**.
* If none, try a **single symmetric extrapolated bracket** (one span beyond data); mark as extrapolated.

### Interpolation in grid mode (rectangular EOS)

To capture sharp features (e.g., phase transitions) without spurious oscillations, grid mode uses a separable, shape‑preserving scheme (“PCHIP2D”):

- For each ρ column, build a 1D PCHIP over T.
- At a requested T, evaluate all columns to get P(T,ρ_j) (and E likewise), then PCHIP over ρ.
- Extrapolation uses PCHIP’s endpoint slopes and is limited by `--extrap-frac` scan bounds.

### Root bracketing and solver in grid mode

- The Hugoniot residual is computed in consistent SI units per atom (same physics as sample‑based mode).
- Bracketing is attempted on the original EOS grid nodes (`Tvec`, `Rvec`), then refined with `brentq` (faster than plain bisection).
- Fallback coarse scans (limited) are used only if node bracketing fails.
- Practical guards: scan starts at `--grid-init-T` if provided; if the base grid is non‑negative in T or ρ, lower bounds are clamped to ≥ 0, and ρ=0 is avoided in reci­procal computations.

---

## Output

Columns (TSV):

```
curve_type  label P[kbar] T[K]  rho[g/cm^3] is_extrapolated phase rho/rho0  D[km/s] U[km/s]
```

* `curve_type` — `isotherm` or `isochor`.
* `label` — e.g., `T=300.0K` or `rho=8.9000g/cm^3`.
* `P[kbar]`, `T[K]`, `rho[g/cm^3]` — Hugoniot point.
* `is_extrapolated` — `0` (in‑range) or `1` (extrapolated).
* `phase` — integer phase label if provided in inputs.
* `rho/rho0` — compression ratio; \( r = \rho / \rho_0 \).
* `D[km/s]` — shock speed;
  \[ D = 10^{-3}\,\sqrt{ \dfrac{ (P - P_0)\,[\mathrm{Pa}]\; \rho\,[\mathrm{kg/m^3}] }{ \rho_0\,[\mathrm{kg/m^3}]\; (\rho - \rho_0)\,[\mathrm{kg/m^3}] } } \],
  equivalently from momentum and mass conservation.
* `U[km/s]` — particle speed;
  \[ U = 10^{-3}\,\sqrt{ \dfrac{ (P - P_0)\,[\mathrm{Pa}]\; (\rho - \rho_0)\,[\mathrm{kg/m^3}] }{ \rho\,[\mathrm{kg/m^3}]\; \rho_0\,[\mathrm{kg/m^3}] } } \] 
  (equivalently $U = D\,(1 - \rho_0/\rho)$).
  With `--u0`, the output shows $U^* = u_0 - U$.

### Compact output (optional)

Provide `--out-compact compact.tsv` to write a reduced table:

```
P[kbar]  T[K]  rho[g/cm^3]
```

Options:

- `--compact-with-velocity` — add `D[km/s]`, `U[km/s]`.
- `--compact-extra-units` — add `P[GPa]` and `T[kK]`.

---

## Examples

```bash
# Both families, PCHIP interpolation
guru-hugoniot --initial init_T300.tsv --Ma 27.0 \
  -i isotherms/T300/*.tsv --curves both --interp pchip -o hugo_T300.tsv
```

```bash
# Phase‑aware: thermo with `phase` column
guru-hugoniot --initial ep0.tsv --Ma 63.5 \
  -i thermo_with_phase_T300.tsv thermo_with_phase_T500.tsv \
  --curves both -o hugo_phase.tsv
```

```bash
# Compact thermo (no density in initial), provide rho0 explicitly
guru-hugoniot --initial init_compact.tsv --rho0 8.90 --Ma 26.98 \
  -i compact_*.tsv -o hugo.tsv
```

```bash
# Grid EOS: spline‑based Hugoniot along isotherms with manual initial point from grid
guru-hugoniot --Ma 20.18 \
  --grid-eos EOS_grid.tsv --grid-init-T 19000 --grid-init-rho 0.96 \
  --curves iso --grid-step-T 100 --extrap-frac 0.1 \
  -o hug_grid_iso.tsv --out-compact hug_grid_iso_compact.tsv \
  --compact-with-velocity --compact-extra-units --plot --plot-prefix hug_grid
```

```bash
# Grid EOS: both families and averaging
guru-hugoniot --Ma 20.18 --grid-eos EOS_grid.tsv \
  --curves both --combine avg --grid-step-T 100 --grid-step-rho 0.05 \
  -o hug_grid_avg.tsv
```

---

## Troubleshooting

* **“Initial state lacks density; supply --rho0.”** Provide `--rho0` (g/cm³) when the initial TSV lacks density.
* **“No thermo rows in input files.”** Verify the headers; prefer `guru-thermo --out` or `--dump-log` outputs.
* **Only extrapolated solutions.** Extend the sampled range or refine `--group-dT`/`--group-drho` to avoid mixing distinct curves.
* **Unexpected phase mixing.** Generate `phase` with `guru-thermo-phase` and ensure each curve’s subset is phase‑clean.
* **Warnings about division by zero/overflow.** In grid mode, densities at or very close to 0 are automatically avoided; consider tightening `--grid-rho-min` if needed.

---

## Best practices

* Generate the initial state with **`guru-iso-fit`** along the same family (isotherm/isochor) you intend to use.
* Keep grouping tolerances small enough to avoid mixing, but large enough to ensure ≥2 points per curve.
* Prefer **PCHIP** over linear interpolation for better shape preservation and monotonicity.
* In grid mode, supplying `--grid-init-T` and `--grid-init-rho` ensures the reference state is taken consistently from the EOS and the T scan starts at your intended temperature.
