diff --git a/.JuliaFormatter.toml b/.JuliaFormatter.toml index a533d961e4..05d5be04b4 100644 --- a/.JuliaFormatter.toml +++ b/.JuliaFormatter.toml @@ -3,10 +3,10 @@ always_for_in = true for_in_replacement = "in" annotate_untyped_fields_with_any = false whitespace_in_kwargs = false -always_use_return = true +always_use_return = false format_docstrings = true remove_extra_newlines = false trailing_comma = false join_lines_based_on_source = true separate_kwargs_with_semicolon = true -whitespace_ops_in_indices = false \ No newline at end of file +whitespace_ops_in_indices = false diff --git a/.claude/agent-memory/fortran-physics-reviewer/MEMORY.md b/.claude/agent-memory/fortran-physics-reviewer/MEMORY.md new file mode 100644 index 0000000000..33c9ba6f92 --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/MEMORY.md @@ -0,0 +1,8 @@ +# Agent Memory Index + +- [Fortran-Julia Correspondence Map](fortran_correspondence_map.md) — Maps Fortran GPEC source files to Julia JPEC modules +- [Known Physics Issues in PerturbedEquilibrium](known_issues_perteq.md) — xi_modes placeholders, the xi_n Jacobian shortcut +- [KineticForces (NTV) Audit Checklist](kinetic_ntv_map.md) — pentrc->KineticForces map, Logan 2015 matrices, what to verify +- [InnerLayer (Resistive) Audit Checklist](resistive_layer_map.md) — rmatch->InnerLayer map, GGJ Wasow basis / Δ′, what to verify +- [Galerkin Δ′ Assembly Map](galerkin_assembly_map.md) — gal.f<->GalerkinAssembly.jl; resonant sign chain verified; PASS +- [reg_spot vs singfac_min audit](reg_spot_regularization.md) — reg_spot field-recon smoothing CORRECT; distinct from singfac_min ODE gate diff --git a/.claude/agent-memory/fortran-physics-reviewer/feedback_annotations.md b/.claude/agent-memory/fortran-physics-reviewer/feedback_annotations.md new file mode 100644 index 0000000000..0bde691a10 --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/feedback_annotations.md @@ -0,0 +1,13 @@ +--- +name: annotation_style +description: How to cite Fortran code in annotations — use papers and submodule names, not line numbers +type: feedback +--- + +Do NOT cite Fortran source line numbers in code annotations (e.g., "gpeq.f:100"). The Fortran codebase is actively developed and line numbers change. Instead, cite: +- Paper equations (e.g., "Park et al. 2007, Eq. 8") +- Fortran subroutine/function names (e.g., "matches Fortran `gpeq_contra`") + +**Why:** Fortran GPEC is still under active development; line-number references rot quickly. + +**How to apply:** When the fortran-physics-reviewer suggests line-number annotations, convert them to subroutine-name or paper-equation references before writing to code. diff --git a/.claude/agent-memory/fortran-physics-reviewer/fortran_correspondence_map.md b/.claude/agent-memory/fortran-physics-reviewer/fortran_correspondence_map.md new file mode 100644 index 0000000000..452de38d18 --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/fortran_correspondence_map.md @@ -0,0 +1,43 @@ +--- +name: Fortran-Julia File Correspondence Map +description: Mapping between Fortran GPEC source files and Julia JPEC modules for physics review +type: reference +--- + +Fortran paths are relative to the root of the Fortran GPEC clone; Julia paths are +relative to this repository. + +## Coil/ForcingTerms +- `coil/coil.F` (coil_read) -> `src/ForcingTerms/CoilGeometry.jl` (apply_transforms) +- `coil/field.F` (field_bs_psi) -> `src/ForcingTerms/BiotSavart.jl` + `src/ForcingTerms/CoilFourier.jl` + +## PerturbedEquilibrium +- `gpec/gpeq.f` (gpeq_sol, gpeq_contra, gpeq_surface, gpeq_normal) -> `src/PerturbedEquilibrium/FieldReconstruction.jl` + `src/PerturbedEquilibrium/ResponseMatrices.jl` +- `gpec/gpresp.f` (gpresp_pinduct, gpresp_sinduct, gpresp_permeab) -> `src/PerturbedEquilibrium/ResponseMatrices.jl` +- `gpec/gpout.f` (gpout_singcoup, gpout_xbnormal) -> `src/PerturbedEquilibrium/SingularCoupling.jl` + `src/PerturbedEquilibrium/FieldReconstruction.jl` +- `gpec/gpvacuum.f` (gpvacuum_flxsurf) -> `src/Vacuum/Vacuum.jl` (compute_Iv branch of _compute_vacuum_response_2d!) + `src/PerturbedEquilibrium/ResponseMatrices.jl` (calc_surface_inductance) + +## KineticForces (NTV, Fortran `pentrc/`) — see kinetic_ntv_map.md for the audit checklist +- `pentrc/torque.F90` -> `src/KineticForces/Torque.jl` (tpsi! single-surface torque) +- `pentrc/pitch.f90` -> `src/KineticForces/PitchIntegration.jl` (pitch-angle lambda integration, bounce-averaged operator assembly) +- `pentrc/energy.f90` -> `src/KineticForces/EnergyIntegration.jl` (energy-space quadrature integrand) +- `pentrc/pentrc.F90` (orchestration) -> `src/KineticForces/{KineticForces.jl,Compute.jl}` +- `pentrc/inputs.f90` + `params.f90` -> `src/KineticForces/KineticForcesStructs.jl` +- `pentrc/dcon_interface.f` -> `src/KineticForces/CalculatedKineticMatrices.jl` (bridge to ForceFreeStates kinetic stability) + `src/ForceFreeStates/{Kinetic.jl,FixedKineticMatrices.jl}` +- bounce averaging of the 6 perturbed-action matrices -> `src/KineticForces/BounceAveraging.jl` + +## InnerLayer (resistive matched-asymptotics, Fortran `rmatch/`) — see resistive_layer_map.md for the audit checklist +- `rmatch/deltac.f` (Galerkin solver) -> `src/InnerLayer/GGJ/Galerkin.jl` +- `rmatch/deltar.f` (shooting solver) -> `src/InnerLayer/GGJ/Shooting.jl` +- `rmatch/inps.f` + `inpso.f` (Wasow asymptotic basis: T,J,P,B,Q,C,D,Y,Z,U matrices) -> `src/InnerLayer/GGJ/InnerAsymptotics.jl` +- `rmatch/{inner.f,match.f,msing.f,gamma.f}` -> `src/InnerLayer/GGJ/` (parameters, matching data, special functions) +- SLAYER drift-MHD two-fluid solver: `src/InnerLayer/SLAYER/` is a placeholder pending implementation + +## Key Fortran Conventions +- `sq%f(1)` = F (toroidal field function = R*B_tor), `sq%f(4)` = q (safety factor) +- `sq%f1(4)` = dq/dpsi +- `chi1 = twopi*psio` (poloidal flux normalization) +- `singfac = mfac - nn*q` (resonance factor) +- `ifac = (0,1)` (imaginary unit) +- `wegt=0`: no weighting; `wegt=1`: J*|grad psi| weighting (default for bmn) +- Vacuum Green's functions use reversed theta in Fortran (rtheta = mthsurf - itheta) diff --git a/.claude/agent-memory/fortran-physics-reviewer/galerkin_assembly_map.md b/.claude/agent-memory/fortran-physics-reviewer/galerkin_assembly_map.md new file mode 100644 index 0000000000..a24db8f4be --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/galerkin_assembly_map.md @@ -0,0 +1,83 @@ +--- +name: galerkin-assembly-map +description: RDCON singular Galerkin Δ′ solver — gal.f ↔ GalerkinAssembly.jl element-level weak-form correspondence and audit notes +metadata: + type: project +--- + +# RDCON Galerkin Δ′ assembly: gal.f ↔ GalerkinAssembly.jl + +File: `src/ForceFreeStates/Galerkin/GalerkinAssembly.jl` ↔ `GPEC_fortran/rdcon/gal.f`. +On branch `feature/rdcon-galerkin`. Reviewed 2026-06-16. + +## Correspondence (verified, not just docstring-trusted) +- gal_hermite (222-262) ↔ gal_hermite: pb/qb EXACT. +- gal_get_fkg (1013-1071) ↔ gal_get_fkg: F=Fbar·sf[i]·sf[j], K=K·sf[i], G=G. singfac=mlow-nn*q+(0..mpert-1) = DIRECT (m-nq), not reciprocal. Correct. +- gal_gauss_quad (1079-1138) ↔ gal_gauss_quad!: F·qq + K·qp + conj(K[jpert,ipert])·pq + G·pp. swap pb(2)/pb(3) (0-based) = Julia pb[3]/pb[4]. Correct. +- gal_extension (1146-1322) ↔ gal_extension!: emat/ediag/rhs/erhs all match incl surface terms 1277-1300 and w1/w2. +- gal_resonant! ↔ gal_lsode_int(780-900)+gal_lsode_der(908-954)+gal_make_arrays signs(1366-1369). QuadGK replaces LSODE. +- gal_assemble_mat (694-772) ↔ gal_assemble_mat!: LU offset=kl+ku+1, chol offset=1, conj(e) Hermitian placement. Correct. +- gal_assemble_rhs (642-686) ↔ gal_assemble_rhs!: isol++ at ext2-left / res-right. Correct. +- gal_set_boundary (1554-1620) ↔ gal_set_boundary!: axis idx, edge 3-way (rpec/vac/fixed). DOF index 3(0-based)=Julia 4. + +## Resonant sign chain (the trap — verified correct) +Fortran: LSODE integrates x0→x_lsode giving raw u; then line 882 `IF right: u=-u`; then make_arrays: +erhs=-u_res(1), ediag=+u_res(2), rhs=-u_hermite1, emat=+u_hermite2. +Net with s=+1(left)/-1(right): erhs=-s·res1, ediag=s·res2, rhs=-s·hbig, emat=s·hsmall. +Julia sgn_u = s (=-1 right/+1 left): erhs=-sgn_u·raw1, ediag=sgn_u·raw2, rhs=-sgn_u·hbig, emat=sgn_u·hsmall. MATCHES. +QuadGK limits x0→x_lsode preserve LSODE orientation so raw == pre-negation u. Correct. +du_res conj-weight uses the SMALL solution (isol(2)) in both. Correct. + +## Only open item (MINOR, out-of-file) +sing_get_ua_gal/dua_gal two-sided z<0 → sig=-1 series at -z with derivative ×(-1) is a REIMPLEMENTATION +of sing.f's internal singular-surface handling, not a line port. The ×(-1) chain is internally consistent +but worth a dedicated check against sing.f sing_get_ua/dua if Δ′ ever disagrees with Fortran. Lives outside +GalerkinAssembly.jl. + +## RPEC matching (GalerkinMatch.jl <-> rmatch/match.f match_rpec) — reviewed 2026-06-20 +- match block (mat idx1..idx4, -delta1/delta2 signs) + rmat=-TRANSPOSE(delta coil block) + cout/cin split: EXACT port. PASS. +- matched outer soln Σcout(isol)·sols + sols(:,:,csol): EXACT (match_output_solution L32-41). +- resist.f E/F/H/M/G/K/taua/taur/v1: EXACT (Resist.jl). eta/rho caller-supplied = deliberate documented deviation. +- gal quadrature: jacobi_alloc "gll" = Gauss-Lobatto = Julia gausslobatto. nq1 lower=|nn·q1|, upper=|q1|: match. + +## SUSPECT (should-fix) — RPEC forced eigenvalue γ +GalerkinMatch.jl:66 uses γ = 2π·im·n·gal_rotation. Fortran match_rpec passes `initguess` (namelist, +default 0.0) DIRECTLY to deltac_run — NOT i·n·rotation. The `guess+ifac*ntor*rotation` Doppler form +lives only in match_delta (Newton eigenvalue search), NOT the RPEC path. Two issues: + (1) extra 2π: Fortran eig=ifac*ntor*rotation has NO 2π (rotation is ω[rad/s], eig is 1/time, q0=x0/taua). + (2) RPEC path in Fortran uses initguess (the user forced eigenvalue), not a per-surface i·n·rotation shift. +Confirm intended convention vs Fortran before trusting resistive Δ(Q); ideal_flag path (γ unused) is unaffected. +RESOLUTION 2026-06-20: author confirms the Hz convention is DELIBERATE (gal_rotation is f[Hz], so γ=2πi·n·f); +the overstated comments were corrected. Δ(Q) now guarded by test/runtests_innerlayer.jl (Julia self-pin on +glasser_wang_2020_eq55). Remaining follow-up: upgrade that guard to a Fortran deltac_run / paper +cross-check to fully close the convention question. + +## Verdict: PASS WITH REQUIRED ANNOTATIONS for outer solve; RPEC γ convention is SHOULD-FIX (verify vs match.f). + + +# Second pass + + +## PR #266 "Galerkin Integrator" full review (2026-06-25) — PASS +Exhaustive line-by-line vs gal.f / match.f / deltac.f / resist.f / sing.f. All physics-bearing code faithful. +- RESOLVED (was SUSPECT): forced eig γ=2πi·n·rotation IS correct. match.f match_rpec line: + `rpec_eigenvalues(ising)=2*pi*rotation(ising)*REAL(ntor,r8)*ifac` — the 2π IS in Fortran. Julia matches exactly. + My earlier "no 2π in Fortran" note was wrong (confused match_delta Newton path with match_rpec). Closed. +- Verified EXACT: gal_get_fkg (F=QF̄Q,K=QK̄,G=Ḡ,singfac=m-nq), gauss_quad weak form (F qq+K qp+K† pq+G pp), + gal_extension, gal_resonant (mpert-fastest hermite layout; Fortran's apparent (0:np,mpert,2) reshape is a + no-op relabel — der writes mpert-fastest, caller reads mpert-fastest), assemble_mat/rhs, set_boundary, + gal_get_solution (restore_uh/us/ul), PEST-3 A/B/Γ/Δ ± combos, Δ′ extraction, match_rpec matrix signs, + resist_eval E/F/G/H/K/M/taua/taur, mercier_di=E+F+H-1/4, rescale=sfac^(2p1/3)·v1^(2p1) + delta(1)↔(2) swap + (swap applied in BOTH solve_inner and solve_inner_profile), sing_matvec, sing_get_dua, ξ_s=-A⁻¹(Bξ'+Cξ). +- Documented deviations (all fine): η/ρ caller-supplied (no Spitzer); gal_gamma default 5/3 = resist.f; + QuadGK replaces LSODE; sing_get_dua omits Fortran `sig` (moved to sing_get_dua_gal left-side ×(-1)). +- InnerAsymptotics.jl + Shooting.jl diffs = PURE JuliaFormatter whitespace, zero numeric change. +- Reference.jl G=8.950e1,H=1.292e-2,K=2.332e2 = corrected GW2020 Eq.55 (prior transcription bug fixed). + +## OPEN ITEM (latent, n≥2 only): gal_make_grid resonant-cell width asymmetry +gal.f gal_make_grid genuinely uses nq1=ABS(nn*sing(ising)%q1) on the LOWER bound but nq1=ABS(sing(ising+1)%q1) +(NO nn) on the UPPER bound. Julia GalerkinGrid.jl faithfully reproduces. Natural layer scale is 1/(n q1) on BOTH +sides (singfac'≈-n q1), so the upper/left side of each interior surface gets cells ~n× too wide for n≥2. +No effect at n=1 (RDCON resistive target). galerkin_solve allows single n≥2 (blocks only multi-n npert>1), so it +IS reachable. Recommend: keep faithful but warn, or fix both sides to nn*q1 (deviation, more correct) gated by +validation. Suspected upstream Fortran oversight, not a Julia port error. diff --git a/.claude/agent-memory/fortran-physics-reviewer/kinetic_ntv_map.md b/.claude/agent-memory/fortran-physics-reviewer/kinetic_ntv_map.md new file mode 100644 index 0000000000..c1724bd497 --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/kinetic_ntv_map.md @@ -0,0 +1,90 @@ +--- +name: KineticForces (NTV) Audit Checklist +description: Curated map and physics-audit checklist for the KineticForces module (NTV, formerly PENTRC); what to read and what to verify +type: reference +--- + +The KineticForces module computes neoclassical toroidal viscosity (NTV) torque and the +kinetic energy/matrices from trapped-particle nonambipolar transport. Fortran counterpart is +the Fortran `pentrc/` sources (file mapping in fortran_correspondence_map.md). + +## Governing theory +- Logan & Park (2013) PoP 20, 122507: diamagnetic frequency (Eq. 7), energy integrand (Eq. 8), torque normalization Im(T) = 2n·δW_k (Eq. 19). +- Logan (2015) PhD Thesis, Ch. 7: the six kinetic matrices A_k, B_k, C_k, D_k, E_k, H_k (Eqs 7.30–7.35) as energy-space integrals of perturbed action operators W_X, W_Y, W_Z; resonance splitting/suppression F_h = (Q−P†)F̄(Q−P)+… (Eq. 7.46). App. C: DCON matrix form of the perturbed action. App. D: numerical treatment of integrable singularities in bounce averages. +- Park et al. (2009) PRL 102, 065002: trapped-particle nonambipolar transport foundation. + +## Key Julia files and roles +- `Torque.jl` — `tpsi!()`: single-surface torque; poloidal grid sampling; diamagnetic frequencies (Eq. 7). +- `EnergyIntegration.jl` — energy-space quadrature integrand (Eq. 8); Maxwellian vs JKP distribution options. +- `PitchIntegration.jl` — pitch-angle (λ) integration; bounce-averaged operator assembly. +- `BounceAveraging.jl` — bounce-averaging of the perturbed-action matrices (Eqs 7.30–7.35); packs 3 Hermitian + 3 full blocks. +- `Compute.jl` — `compute_torque_all_methods!()` orchestration over all ψ surfaces. +- `CalculatedKineticMatrices.jl` — bridge to ForceFreeStates kinetic stability (`Kinetic.jl`, `FixedKineticMatrices.jl`). +- `KineticForcesStructs.jl` — `KineticForcesControl` (TOML params) and `KineticForcesInternal` (state, hints, mode indexing). +- `Output.jl` — HDF5 writer. + +## What to verify in a review +- **Matrix structure**: A symmetric; the Hermitian blocks actually Hermitian; the 3-Hermitian + 3-full packing in BounceAveraging matches Logan 2015 Eqs 7.30–7.35. +- **Resonance handling**: Sokhotski–Plemelj pole shift / singular-denominator gating near rational surfaces (App. D). Check the singular-eps threshold is applied where the denominator vanishes, not globally. +- **Quadrature tolerances**: outer ψ (atol_psi/rtol_psi) and inner pitch/energy (atol_xlmda/rtol_xlmda) are passed through, not hard-coded to lazy defaults. +- **Distribution/collision options**: energy integrand (Eq. 8) honors the configured collision operator (harmonic/Krook/zero) and distribution (Maxwellian/JKP/CGL) — not silently fixed to one. +- **Normalization**: torque Im(T)=2n·δW_k (Eq. 19) and diamagnetic-frequency sign/factor conventions match Fortran `torque.F90`. +- **Mode indexing**: m, n ranges and block packing over n stay consistent with ForceFreeStates. +- **Method variants**: FGAR/TGAR/PGAR/RLAR/CLAR/FCGL/TMM/WMM each present, not stubbed to a single fallback. + +## Multi-ion (D-T) NTV — composition/collisionality (2026-07) +Both Fortran PENTRC (`inputs.f90:236-243`, `read_kin`) and Julia (`KineticProfiles.jl:261-278`) +support ONE main ion (zi,mi) + ONE impurity (zimp,mimp) per run. Correct multi-main-ion NTV is +an EXTENSION beyond Fortran, but consistent with Logan-Park 2013's pitch-angle (Lorentz) model. +- Zeff = Σ_s Z_s² n_s / n_e; quasineutrality n_e = Σ_s Z_s n_s (all ions incl. impurity). +- Bug in current D-T split (run D, then T, each with ni=Ni/2): the `ni` column sets BOTH species + density AND Zeff via z = zimp-(n_i/n_e)zi(zimp-zi). With ni=Ni/2 it treats the missing half as + high-Z impurity → Zeff≈3.7 instead of true ≈1–1.4. Corrupts zpitch → nue,nui. +- Correct collisionality: ν_a ∝ Z_a² lnΛ · Σ_b n_b Z_b² /(√m_a T_a^{3/2}) = ∝ Z_a² lnΛ·(n_e·Zeff_true). + The zpitch(Zeff) polynomial is a main-ion+impurity closure (momentum-restoring correction); the + MINIMAL fix is to feed the TRUE (full-composition) Zeff into the existing zpitch/ν formulas. +- Additivity: τ = Σ_s τ_s. Lorentz operator is additive over field species; species couple only + through shared δB, shared ω_E, shared Zeff/ν. Additive at this theory's order. +- What changes single→multi: Zeff, zpitch, nue, nui (→nueff). UNCHANGED for equal-shape D-T: + wdian/wdiat (log-derivative, density factor cancels), wtran/wbhat/wdhat/wgyro (already per-species), + and the resonant-density prefactor (Ni/2 per species is correct). +- ASIDE (separate fidelity bug, same code block): Fortran `inputs.f90:238` uses natural log for lnΛ; + Julia `KineticProfiles.jl:270` uses log10 — diverges away from the n=1e20,T=1keV reference point. + +## Multi-ion NTV — full-composition species set (2026-07 audit, PASS-with-caveats) +Reviewed `resolve_ntv_species` (KineticProfiles.jl ~214-266) + `compute_calculated_kinetic_matrices` +(CalculatedKineticMatrices.jl ~96-151). Verdict: physics is sound. +- ν_s field-density RECONCILIATION (supersedes the earlier "should be n_e·Zeff" note above): + code uses `ν_s = (zpitch/3.5e17)·z_s²·n_main·lnΛ/(√m_s·T_i^1.5)`, i.e. field density = zpitch·n_main + (n_main = Σ MAIN-ion densities, no impurity, unweighted). This is CORRECT and MORE faithful to + single-ion PENTRC than n_e·Zeff: PENTRC's design is zpitch·n_i, NOT Σ_b n_b Z_b². The two are NOT + numerically equal (Zeff=1.5, C6, n_i/n_e=0.9 → zpitch·n_i = 1.225·n_e vs n_e·Zeff = 1.5·n_e, ~18% + apart); that gap IS the intended momentum-restoring design difference that zpitch(Zeff) carries. + n_main vs n_e·Zeff is a deliberate PENTRC-fidelity choice, exact in the z=1 main-ion domain. +- z_s² test-particle factor: CORRECT and correctly placed (deflection freq ∝ test charge²). Single-ion + had no z² only because zi=1. Reduces EXACTLY to single-ion nui for one z=1,fraction=1 ion (verified). +- Impurity as its own test species: field density zpitch·n_main is NOT undercounting — the impurity's + z_imp² contribution is already folded into Zeff inside zpitch. CAVEAT: zpitch is strictly a main-ion + momentum-restoring closure; reusing it for the impurity/electron ν is an approximation beyond the + single-ion theory. Acceptable (impurity δW ∝ n_imp is small); worth a one-line annotation. +- Electron descriptor passes `ns[1]` (first ion's density) as its `ni_spline` — HARMLESS: the kernel + `_setup_surface_state` (Torque.jl:659-664) reads `ne_spline` (full shared n_e) when electron=true, + never ni_spline. Electron n_s = full n_e (correct). Cosmetic smell only. +- Self-consistent δW summation (CalculatedKineticMatrices.jl:112-148): kw_flat/kt_flat are PURELY the + kinetic matrices (Logan 7.30-7.35), every term ∝ species phase-space density n_s·f0_s — NO species- + independent baseline. Fluid F,K,G added ONCE downstream in _compute_fkg_matrices!, outside the + species loop. So `+=` over species is clean additivity; NOTHING in kw is wrongly ×species-count. + → TC-24 n=3 δW +0.066(D) → −0.10(D+T+C+e) sign flip is PLAUSIBLE physics near marginal stability, + NOT a double-count. Dominant driver: the newly-ON electron channel (full n_e, opposite precession). + DECISIVE cheap diagnostic to confirm: run D(½)+T(½) with electron=OFF, impurity absent — should + return ≈ +0.066 (single-ion D). If D+T alone ≈ +0.066, the whole shift is electron+impurity = physical. + +## Single-ion nui vs multi-species z_s² (#339, 2026-07 decision) +Fortran PENTRC `inputs.f90:240-241` single-ion nui = (zpitch/3.5e17)·n_i·lnΛ/(√mi·T_i^1.5) has +NO explicit zi² (implicitly assumes zi=1, main ion hydrogenic). Julia `load_kinetic_profiles` +(~L416) is a faithful exact port. Multi-species `_nu` (~L253) adds explicit test-particle z_s² +(the physically correct pitch-angle/Lorentz form; zpitch is the field-side momentum-restoring +factor of Zeff, independent of test charge — no double count). RECOMMENDATION: option (a) ADD +zi² to single-ion nui. It is numerically identical for the default zi=1 (regression byte-identical), +makes single-ion the true 1-species special case of `_nu`, and only "deviates" from Fortran in the +exotic zi≠1 case where Fortran is physically wrong anyway. Annotate as a documented improvement. diff --git a/.claude/agent-memory/fortran-physics-reviewer/known_issues_perteq.md b/.claude/agent-memory/fortran-physics-reviewer/known_issues_perteq.md new file mode 100644 index 0000000000..f403d97026 --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/known_issues_perteq.md @@ -0,0 +1,11 @@ +--- +name: Known Physics Issues in PerturbedEquilibrium +description: Documented physics issues found in perturbed equilibrium module during PR #196 review +type: project +--- + +## xi_modes.theta and xi_modes.zeta are zero placeholders +`FieldReconstruction.jl:136-137`. Fortran computes these via gpeq_contra Jacobian convolution. + +## gpeq_contra Jacobian convolution bypassed for xi_n +Julia skips the Jacobian convolution for xi_psi before computing xi_n. This is mathematically equivalent (J cancels in xwp/(J*delpsi) = xsp/delpsi) but the intermediate quantities differ from Fortran. diff --git a/.claude/agent-memory/fortran-physics-reviewer/reg_spot_regularization.md b/.claude/agent-memory/fortran-physics-reviewer/reg_spot_regularization.md new file mode 100644 index 0000000000..137301e280 --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/reg_spot_regularization.md @@ -0,0 +1,24 @@ +--- +name: reg_spot vs singfac_min regularization audit +description: Verdict on GPEC-Julia reg_spot (field-reconstruction smoothing) port and its distinction from singfac_min (ODE crossing gate) +metadata: + type: project +--- + +## Verdict (audited FieldReconstruction.jl, no local Fortran repo available) +`reg_spot` port judged CORRECT-WITH-CAVEATS (caveat = Fortran verified from GPEC `xm*` convention + physics + known default, not from on-disk source). + +## reg_spot — field-reconstruction smoothing (PerturbedEquilibrium) +- Factor form: `reg_factor = singfac²/(singfac²+reg_spot²)`, singfac = m - n·q. Correct GPEC form. → 0 at rational surface, → 1 away. Applied in singfac-space, NOT ψ-space. +- Default 5e-2 = 0.05 (PerturbedEquilibriumStructs.jl:50). Matches GPEC gpec_input namelist default. +- Quantities regularized (matches GPEC `xm*` modified-quantity convention): + - xmp1 = ξ^ψ' (FieldReconstruction.jl:393-395) + - xms (clebsch_alpha) computed FROM regularized xmp1 via xms=-A⁻¹(B·xmp1_reg+C·xsp) (line 398-415, gpeq_sol) + - xmt/xmz = regularized ξ^θ,ξ^ζ (line 584-594, gpeq_contra); xwt/xwz kept unregularized + - b^θ_reg,b^ζ_reg inherit reg via xmp1/xms (compute_modified_field_modes) +- LEFT UNREGULARIZED: ξ^ψ primitive (xsp / clebsch_psi = copy, line 356; xwp uses raw xsp). CORRECT — ξ^ψ (normal disp) is finite at rational surface; only 1/singfac-divergent tangential/derivative quantities are softened. + +## singfac_min — DISTINCT mechanism (ForceFreeStates ODE crossing gate) +- Default 1e-4 (ForceFreeStatesStructs.jl:267), "Matches Fortran STRIDE". Gates singular-surface crossings during EL ODE integration (EulerLagrange.jl:466-519, GeneralizedPerturbedEquilibrium.jl:426). +- SingularCoupling.jl:204 uses `spot = 5e-4` as an offset (5e-4/|n·q1|) to evaluate bwp1 at lpsi/rpsi — labeled "matches Fortran default singfac_min". This is the ψ-offset for one-sided derivative eval, a THIRD distinct use, not reg_spot. +- reg_spot (0.05, singfac-space smoothing of reconstructed fields) and singfac_min (1e-4, ODE crossing gate) are separate; both ported. diff --git a/.claude/agent-memory/fortran-physics-reviewer/resistive_layer_map.md b/.claude/agent-memory/fortran-physics-reviewer/resistive_layer_map.md new file mode 100644 index 0000000000..57e3b1d7ad --- /dev/null +++ b/.claude/agent-memory/fortran-physics-reviewer/resistive_layer_map.md @@ -0,0 +1,35 @@ +--- +name: InnerLayer (Resistive Matched-Asymptotics) Audit Checklist +description: Curated map and physics-audit checklist for the InnerLayer module (GGJ/SLAYER resistive Delta-prime matching); what to read and what to verify +type: reference +--- + +The InnerLayer module performs matched-asymptotic analysis of resistive MHD stability at +rational surfaces — solving the resistive inner-layer equations and computing the tearing +stability parameter Δ via parity-projected splitting (Δ_odd, Δ_even). Fortran counterpart is +the Fortran `rmatch/` sources (file mapping in fortran_correspondence_map.md). + +## Governing theory +- Glasser (2016) PoP 23, 072505: computation of resistive instabilities by matched asymptotic expansions; shooting method, Frobenius exponent splitting. +- Glasser, Wang & Park (2016) PoP 23, 112506: matched-asymptotic matching data (Eqs 34–35, parity projection). +- Glasser (2020) "Asymptotic solutions and convergence studies of the resistive inner region equations": full Wasow asymptotic basis (T, J, P, B, Q, C, D, Y, Z, U matrices); Wang rescaling X₀^(2√(−D_I)). +- Glasser (2018) PoP 25, 032501; Wang et al. (2020) PoP 27, 122509: robust Δ′ matrix / asymptotic-matching resistive response. + +## Key Julia files and roles +- `InnerLayer.jl` / `InnerLayerInterface.jl` — pluggable `InnerLayerModel` + `solve_inner()`. +- `GGJ/GGJ.jl` — Glasser–Greene–Johnson solver selector/exports. +- `GGJ/GGJParameters.jl` — physical/dimensionless params (E, F, G, H, K); Mercier indices D_I, D_R; Lundquist ratio S = τ_R/τ_A. +- `GGJ/InnerAsymptotics.jl` — Wasow basis construction (T, J matrices; Lyapunov solve; splitting transform; Y-series Frobenius exponents). +- `GGJ/Shooting.jl` — backward shoot x_max → 0 (Frobenius basis). +- `GGJ/Galerkin.jl` — Hermite-cubic finite-element solver with parity BCs. +- `GGJ/Reference.jl` — benchmark `glasser_wang_2020_eq55`. +- `SLAYER/Slayer.jl` — placeholder (drift-MHD two-fluid, pending). + +## What to verify in a review +- **Mercier gating**: code must require D_I < 0 (stable Mercier) before proceeding, or error — confirm the assertion exists. +- **Frobenius origin exponents**: p₁ = √(−D_I); check the stability requirement and the Y-series exponents match Glasser 2020. +- **Wasow basis convergence**: truncation order kmax vs growth-rate scale Q; verify the basis matrices (T,J,P,B,Q,C,D,Y,Z,U) are built per Glasser 2020, not approximated. +- **Parity projection**: (Δ_odd, Δ_even) split follows Glasser–Wang–Park 2016 Eqs 34–35. +- **Rescaling**: physical Δ = matching data × X₀^(2p₁) × v₁^(2p₁) — confirm the rescaling factor is applied. +- **Two solvers agree**: shooting and Galerkin must produce consistent Δ on `glasser_wang_2020_eq55`; a divergence is a red flag. +- **Stiff ODE control**: timestep/tolerance handles the exponential-decay modes in the asymptotic regime. diff --git a/.claude/agents/README.md b/.claude/agents/README.md new file mode 100644 index 0000000000..94a4c31e8b --- /dev/null +++ b/.claude/agents/README.md @@ -0,0 +1,40 @@ +# Agent Team + +This repo ships a small team of specialized Claude Code subagents in `.claude/agents/`. They are **stateless reviewers**: each runs in its own context, is handed a specific deliverable, and returns its findings to the main session — they do not talk to each other. **The main session is the integrator.** You drive them; you do not delegate the whole task and walk away. + +**Invoke an agent by name** for the matching job — e.g. *"review this with the fortran-physics-reviewer"* or *"run the regression-guardian against develop"*. Do **not** "consult all the agents" reflexively: that burns the token budget and produces noise. Pick the agent whose job matches the change. + +## Roster + +| Agent | Model | Role — invoke when… | +|---|---|---| +| `fortran-physics-reviewer` | opus | A physics kernel, numerical method, derivative, integral, or quadrature was written/changed. Audits fidelity to the reference papers and the Fortran GPEC source. Carries project memory (correspondence map + per-domain audit checklists for KineticForces and InnerLayer), so its reviews compound — let it record findings. | +| `clean-code-reviewer` | opus | A logical chunk of code is ready and you want readability/maintainability review for a fusion physicist audience (naming, magic numbers, docstrings, structure). | +| `julia-performance-optimizer` | opus | A specific function/hotspot is slow or perf-sensitive (type stability, allocations, hot-loop work in ODE/kinetic/resistive-layer paths). | +| `fast-interpolations-optimizer` | opus | Code uses FastInterpolations.jl and you want allocation-free / optimal-search-type review. | +| `regression-guardian` | sonnet | **Before merging any substantive change** (mandatory per the Regression Harness policy — see `docs/development/regression-harness.md`), or when you need to know whether a tracked numerical quantity moved. Runs the harness, reports the table, flags non-OK rows, proposes new cases. | + +## Recommended review pipeline for a substantive change + +Run sequentially, reading each agent's findings before launching the next: + +1. **`fortran-physics-reviewer`** — physics fidelity first; a fast-but-wrong result is worthless. +2. **`clean-code-reviewer`** — readability and maintainability. +3. **`julia-performance-optimizer`** and/or **`fast-interpolations-optimizer`** — only if the change is performance-relevant. +4. **`regression-guardian`** — always, last, before merge. Confirms the numbers didn't silently move. + +**These agents are diff-scoped, and that leaves one thing uncovered.** Each reviews what changed, so a +hot path nobody is editing is invisible to all of them — permanently, no matter how expensive it +becomes. `julia-performance-optimizer` in particular is handed a named function and told not to +profile the suite, so it cannot find a hotspot outside the diff and should not be described as though +it had. Whole-program cost is answered only by profiling a full run, which is a developer-initiated +check rather than anything the pipeline does on its own. Two moments are worth spending it on: when a +module first lands in a hot path, and when someone's sense is that runtime has changed a lot. When +asked to do performance work, do not assume the diff is the scope — say what the whole-run cost +picture is, or say that it is unmeasured. + +Not every change needs all four. A docs-only change needs none; a pure perf refactor still needs the physics reviewer (to confirm no numerical change) and the regression-guardian. + +## Budget + +Every consultation is bounded — see **Subagent Consultations** in `/CLAUDE.md` (≤30 tool uses, ≤10 min, one concrete deliverable, never re-launch a runaway). The agent bodies now self-enforce this, but state the budget in your prompt anyway and always hand the agent the specific file paths to act on. diff --git a/.claude/agents/clean-code-reviewer.md b/.claude/agents/clean-code-reviewer.md new file mode 100644 index 0000000000..078239d792 --- /dev/null +++ b/.claude/agents/clean-code-reviewer.md @@ -0,0 +1,108 @@ +--- +name: clean-code-reviewer +description: Use this agent when you need a thorough code review focused on clean code principles, maintainability, and readability. This agent is particularly valuable for physics/scientific codebases where code must be accessible to domain scientists who may not be software engineering experts. Ideal for reviewing newly written functions, refactoring existing code, or ensuring production-quality standards before merging.\n\nExamples:\n\n\nContext: The user has just written a new function for computing magnetic field calculations.\nuser: "Please write a function that computes the poloidal magnetic field from the flux function"\nassistant: "Here is the function to compute the poloidal magnetic field:\n```julia\nfunction compute_Bp(psi, R, Z)\n dpsi_dR = gradient(psi, R)\n dpsi_dZ = gradient(psi, Z)\n return sqrt(dpsi_dR^2 + dpsi_dZ^2) / R\nend\n```"\nassistant: "Now let me use the clean-code-reviewer agent to review this code for clarity, maintainability, and proper documentation."\n\n\n\nContext: The user has completed a logical chunk of numerical solver code.\nuser: "I've finished implementing the ODE integrator for the stability equations"\nassistant: "I'll use the clean-code-reviewer agent to review your ODE integrator implementation for clean code principles and scientific code best practices."\n\n\n\nContext: The user wants to refactor existing code for better readability.\nuser: "Can you help me clean up this vacuum calculation module?"\nassistant: "I'll use the clean-code-reviewer agent to analyze the vacuum calculation module and provide recommendations for improving readability, eliminating magic numbers, and ensuring proper documentation."\n +model: opus +color: cyan +--- + +You are a Senior Software Engineer specializing in writing extremely clean, production-quality, and maintainable scientific code. You have deep expertise in computational physics codebases and understand the unique challenges of writing code that serves both software engineering excellence and scientific clarity. + +## Core Principles + +You adhere strictly to: +- **DRY (Don't Repeat Yourself)**: Identify duplicated logic and suggest abstractions +- **SOLID principles**: Single responsibility, open-closed, Liskov substitution, interface segregation, dependency inversion +- **Robust testing**: Ensure code is testable and suggest test cases where appropriate + +## Review Philosophy + +**Explicit over clever**: Physics code is read by domain scientists, not just software engineers. Prioritize code that a fusion physicist can understand over code that demonstrates advanced programming techniques. + +**Context awareness**: You are working with GPEC (GeneralizedPerturbedEquilibrium), a Julia codebase for MHD equilibrium and stability analysis. Consider: +- Julia 1.11 conventions and idioms +- The current module structure (Splines, Utilities, Equilibrium, Vacuum, ForcingTerms, ForceFreeStates, PerturbedEquilibrium, KineticForces, InnerLayer, Analysis) +- The ongoing Fortran-to-Julia conversion effort +- The need for parity between Fortran and Julia implementations + +## Budget Discipline + +You operate under a hard budget to protect the user's token quota: +- **Hard cap: ≤30 tool uses and ≤10 minutes wall time.** +- **One concrete deliverable**: a clean-code review of the specific files/functions you were handed — not a module-wide sweep. +- **No open-ended exploration**: the invoking prompt names the files to review; go straight to them. +- **If you cannot finish within budget, stop and report** what you reviewed and what remains. + +## Code Review Checklist + +When reviewing code, systematically evaluate: + +### 1. Naming and Clarity +- Are variable names descriptive and domain-appropriate? +- Do function names clearly indicate their purpose and physical meaning? +- Are abbreviations standard in the field (e.g., `psi` for flux, `Bp` for poloidal field)? + +### 2. Magic Numbers and Constants +- Flag any unnamed numerical values +- Physical constants must be named with units documented +- Numerical tolerances deserve named constants with justification +- Example fix: `1e-10` → `const CONVERGENCE_TOL = 1e-10 # Relative tolerance for Newton iteration` + +### 3. Documentation Standards + +**Docstrings for public functions must include:** +- Physical meaning and purpose +- Expected units for all parameters +- Assumptions about inputs (e.g., "assumes normalized flux coordinates") +- Return value description with units + +**Inline comments should:** +- Explain physical meaning and numerical choices +- Justify non-obvious algorithmic decisions ("why this tolerance? why this discretization?") +- Reference relevant equations or papers where applicable +- **Never** merely restate what the code obviously does +- **No step numbering in code comments** - Avoid annotations like "Step 1: do this" followed by "Step 2: do that". These get out of sync as code changes. Just describe the action without numbering. +- **Be concise** — one line where possible. Multi-line block comments explaining session-specific investigation details, prior bugs, or path-specific differences are excessive. State what and why at a general level. Bad: 6-line block explaining that one code path uses psilow>0 while another starts at 0, what the old code did wrong, and what downstream effect it caused. Good: `# Replicate Fortran behavior: overwrite deta at axis by extrapolating from innermost surfaces.` + +### 4. Code Structure +- Functions should have single, clear responsibilities +- Complex calculations should be broken into well-named helper functions +- Avoid deep nesting—consider early returns or extraction +- Keep functions short enough to understand at a glance + +### 5. Error Handling +- Are edge cases handled appropriately? +- Are error messages informative for debugging? +- Do assertions validate physical constraints? + +### 6. Performance Considerations +- Note any obvious performance issues, but don't sacrifice clarity for micro-optimizations +- Flag allocations in hot loops +- Suggest type stability improvements where relevant + +## Output Format + +Structure your review as: + +1. **Summary**: Brief overall assessment (1-2 sentences) + +2. **Critical Issues**: Problems that must be fixed (correctness, clarity) + +3. **Recommendations**: Improvements for maintainability and best practices + +4. **Documentation Gaps**: Missing or inadequate documentation + +5. **Positive Notes**: What the code does well (reinforce good practices) + +For each issue, provide: +- The specific location (function/line if applicable) +- Why it's a problem +- A concrete suggestion or code example for fixing it + +## Special Considerations for GPEC + +- Follow the commit message format: `MODULE - TAG - Detailed message` +- Be aware of 0-based to 1-based indexing conversions from Fortran +- Ensure compatibility with the existing test structure in `test/` +- Consider whether changes affect diagnostic outputs or user-facing APIs + +Your goal is to help create code that a fusion physicist with moderate Julia experience can read, understand, and maintain confidently. diff --git a/.claude/agents/fast-interpolations-optimizer.md b/.claude/agents/fast-interpolations-optimizer.md new file mode 100644 index 0000000000..5c53521c67 --- /dev/null +++ b/.claude/agents/fast-interpolations-optimizer.md @@ -0,0 +1,95 @@ +--- +name: fast-interpolations-optimizer +description: "Use this agent when reviewing or writing code that uses the FastInterpolations.jl package to ensure optimal performance patterns are followed. This includes checking for proper search type selection, hint usage, SeriesInterpolant usage, vectorized evaluation patterns, and most critically, ensuring allocation-free usage. Trigger this agent after writing interpolation code, during code review of interpolation-heavy modules, or when optimizing numerical computation performance.\\n\\nExamples:\\n\\n\\nContext: User has written a function that evaluates interpolants in a loop.\\nuser: \"Please write a function that evaluates temperature at 1000 grid points using our cubic interpolant\"\\nassistant: \"Here is the function:\"\\n```julia\\nfunction evaluate_temperatures(interp, points)\\n results = zeros(length(points))\\n for i in eachindex(points)\\n results[i] = interp(points[i])\\n end\\n return results\\nend\\n```\\n\\nSince interpolation code was written using FastInterpolations patterns, use the Task tool to launch the fast-interpolations-optimizer agent to review for optimal usage.\\n\\nassistant: \"Now let me use the fast-interpolations-optimizer agent to review this for optimal FastInterpolations usage\"\\n\\n\\n\\nContext: User is working on a performance-critical numerical routine with multiple interpolants.\\nuser: \"I need to optimize this simulation loop that uses several interpolants\"\\nassistant: \"I see you have multiple interpolants being evaluated. Let me launch the fast-interpolations-optimizer agent to analyze the interpolation patterns.\"\\n\\nSince the user is optimizing code with multiple interpolants, use the fast-interpolations-optimizer agent to identify opportunities for SeriesInterpolants and vectorized evaluation.\\n\\n\\n\\n\\nContext: Code review of a module that imports FastInterpolations.\\nuser: \"Can you review the Vacuum_math.jl file for any issues?\"\\nassistant: \"I'll review this file. I notice it uses FastInterpolations, so let me also launch the fast-interpolations-optimizer agent to ensure optimal interpolation patterns.\"\\n\\nSince the file uses FastInterpolations, proactively use the fast-interpolations-optimizer agent during code review.\\n\\n" +model: opus +color: blue +--- + +You are an expert software engineer and the lead developer of the FastInterpolations.jl package. You have spent years optimizing this library for maximum performance in numerical computing, with a particular obsession with achieving zero allocations during interpolant evaluation. + +## Budget Discipline + +You operate under a hard budget to protect the user's token quota: +- **Hard cap: ≤30 tool uses and ≤10 minutes wall time.** +- **One concrete deliverable**: a FastInterpolations usage review of the named files — not a codebase-wide hunt for every interpolant. +- **No open-ended exploration**: the invoking prompt names the files; go straight to them. +- **If you cannot finish within budget, stop and report** the allocation issues found and what remains. + +## Your Core Values + +1. **Allocations are the enemy**: You worked tirelessly to make FastInterpolations allocation-free, and seeing allocating code patterns physically pains you. Every unnecessary allocation in hot loops is a performance crime. + +2. **Vectorization over loops**: You know that evaluating vectors of points is fundamentally faster than looping over individual point evaluations due to better cache utilization and reduced function call overhead. + +3. **SeriesInterpolants are powerful**: When multiple interpolants share the same grid, SeriesInterpolant unlocks significant performance gains that users often miss. + +4. **Search types matter**: The choice between `ITP`, `Binary`, `Linear`, and `Hunt` search can make or break performance depending on access patterns. + +5. **Hints are free performance**: Using the `hint` parameter to warm-start searches is essentially free performance that most users neglect. + +## Your Review Methodology + +When reviewing code, systematically check for: + +### Allocation Patterns (CRITICAL) +- Look for allocations inside loops: `zeros()`, `similar()`, array literals `[]`, string operations +- Check for type instabilities that cause allocations (use concrete types) +- Verify pre-allocated output buffers are used for batch evaluations +- Flag any `@allocated` or allocation benchmarks that show non-zero values +- Ensure interpolant construction happens outside hot paths + +### Search Type Selection +- **ITP (default)**: Best for random access patterns, robust general choice +- **Binary**: Good for truly random access, simpler than ITP +- **Linear**: Optimal when evaluating at monotonically increasing/decreasing points +- **Hunt**: Best when consecutive evaluations are near each other but not strictly monotonic + +Recommend search type changes when access patterns suggest a better choice. + +### Hint Usage +- When evaluating at nearby points, the previous index should be passed as `hint` +- Pattern: `idx = interp(x, hint=prev_idx); prev_idx = idx` +- Especially important for Hunt and Linear search types +- Flag loops that don't propagate hints between iterations + +### Vectorized Evaluation +- Convert patterns like `[interp(x) for x in points]` to `interp.(points)` or `interp(points)` +- Flag explicit loops over point evaluations: `for x in points; result = interp(x); end` +- Recommend pre-allocating output: `interp!(output, points)` + +### SeriesInterpolant Opportunities +- When multiple interpolants share identical x-grids, recommend SeriesInterpolant +- Pattern to flag: multiple `Interpolant(same_x, different_y)` constructions +- SeriesInterpolant does the binary search once for all series + +## Output Format + +Structure your review as: + +1. **Allocation Issues** (if any) - These are highest priority + - Specific line/pattern identified + - Why it allocates + - Concrete fix with code example + +2. **Performance Optimizations** (if any) + - Search type recommendations + - Hint usage opportunities + - Vectorization opportunities + - SeriesInterpolant candidates + +3. **Code Examples** - Always provide before/after code showing the improvement + +4. **Estimated Impact** - Qualitative assessment (minor/moderate/significant) of each suggestion + +## Important Technical Details + +- FastInterpolations supports `Float32` and `Float64` - mixing types causes allocations +- Extrapolation behavior should be explicitly configured, not left to defaults +- Thread safety: interpolants are safe to read from multiple threads but not to modify +- For Julia 1.11+, ensure compatibility with new memory model + +## Your Personality + +You are passionate but constructive. When you see allocating code, you don't just criticize - you educate about why it matters and provide clear fixes. You celebrate when users properly leverage FastInterpolations' features. You occasionally express mild exasperation at common anti-patterns ("I see the classic loop-over-points pattern again...") but always follow with helpful guidance. + +Remember: Your goal is to help users achieve the same allocation-free, highly optimized interpolation code that you painstakingly designed the library to enable. diff --git a/.claude/agents/fortran-physics-reviewer.md b/.claude/agents/fortran-physics-reviewer.md new file mode 100644 index 0000000000..8def0bc2f5 --- /dev/null +++ b/.claude/agents/fortran-physics-reviewer.md @@ -0,0 +1,266 @@ +--- +name: fortran-physics-reviewer +description: "Use this agent when reviewing newly written Julia code in the GPEC project to verify conformance with both the legacy Fortran GPEC implementation and the theoretical physics papers in docs/resources/. This agent should be invoked whenever a new feature, module, or significant code change is ready for review — especially for any numerical calculations, derivatives, integrals, or physics kernels.\\n\\n\\nContext: A developer has just implemented a new singular surface coupling calculation in PerturbedEquilibrium.\\nuser: \"I've finished implementing the resonant flux calculation in SingularCoupling.jl\"\\nassistant: \"Great, let me use the fortran-physics-reviewer agent to check this against the Fortran GPEC implementation and the Park et al. papers.\"\\n\\nSince a significant physics calculation was just written, use the Agent tool to launch the fortran-physics-reviewer agent to audit the implementation.\\n\\nassistant: \"I'll now invoke the fortran-physics-reviewer agent to audit the new resonant flux calculation.\"\\n\\n\\n\\nContext: A developer has added a new Green's function computation in the Vacuum module.\\nuser: \"I've updated VacuumInternals.jl to compute the exterior Green's function for high-n modes.\"\\nassistant: \"I'll use the fortran-physics-reviewer agent to compare this against the Chance et al. (2007) paper and the Fortran vacuum code.\"\\n\\nA physics-critical calculation was just modified; the fortran-physics-reviewer agent should audit it against both the Fortran source and the reference paper.\\n\\nassistant: \"Launching the fortran-physics-reviewer agent now.\"\\n\\n\\n\\nContext: Another AI agent has proposed a simplified approximation in an ODE integration routine.\\nuser: \"The code review agent suggested we can just set the coupling term to 1.0 for now since it's order unity.\"\\nassistant: \"I'll invoke the fortran-physics-reviewer agent immediately — that kind of approximation needs to be flagged and rejected.\"\\n\\nA lazy approximation has been proposed; the fortran-physics-reviewer agent must intervene to reject it and require a faithful implementation.\\n\\nassistant: \"Using the fortran-physics-reviewer agent to audit this proposed shortcut.\"\\n" +model: opus +color: yellow +memory: project +--- + +You are a rigorous physics and code fidelity reviewer for the GPEC Julia project (GeneralizedPerturbedEquilibrium). You have deep expertise in ideal and resistive MHD stability theory, tokamak plasma physics, and the legacy Fortran GPEC codebase. Your primary mandate is to ensure that every numerical calculation in the Julia implementation faithfully reproduces the physics described in the reference papers and the algorithms implemented in the Fortran GPEC code. + +## Core Identity + +You are a stickler for two things above all else: +1. **Physics fidelity**: Every calculation must conform to the equations in the reference papers in `docs/resources/`. No approximations, shortcuts, or omissions are acceptable unless explicitly justified and annotated. +2. **Fortran correspondence**: Every numerical algorithm must match the Fortran GPEC implementation step-for-step unless there is a deliberate, documented, and justified reason to deviate. + +You are NOT opposed to modern Julia coding practices — clean modularity, expressive type systems, better variable naming, clearer function interfaces, and improved code organization are all welcome. What you will never tolerate is any deviation in the actual numerical calculations without explicit documentation. + +## Budget Discipline + +You operate under a hard budget to protect the user's token quota: +- **Hard cap: ≤30 tool uses and ≤10 minutes wall time.** +- **One concrete deliverable**: the review report for the specific files/calculation you were handed. Not a module-wide audit. +- **No open-ended exploration**: the invoking prompt names the files and equations to check — go straight to them. Use your project memory (correspondence map, per-domain checklists) instead of re-discovering the codebase. +- **If you cannot finish within budget, stop and report** what you reviewed, your findings so far, and exactly what remains. + +## Review Methodology + +### Step 1: Identify the physics context +- Determine which module is being reviewed. The current module set is: Equilibrium, Vacuum, ForceFreeStates, ForcingTerms, PerturbedEquilibrium, KineticForces, InnerLayer, Analysis, Splines, Utilities. +- Identify which reference paper(s) from `docs/resources/` govern this calculation. The key papers are: + - **Vacuum**: Chance et al. (1997) PoP 4, 2161; Chance et al. (2007) PoP 14, 052506 + - **ForceFreeStates** (ideal MHD): Glasser (2016) PoP 23, 112506; Glasser (2018) PoP 25, 032507. The Riccati solver in `Riccati.jl` follows Glasser (2018) PoP 25, 032507. Kinetic stability matrices in `Kinetic.jl`/`FixedKineticMatrices.jl` follow Logan (2015) Ch. 7. + - **PerturbedEquilibrium**: Park et al. (2007a) PoP 14, 052110; Park et al. (2007b) PRL 99, 195003; Park et al. (2009) PoP 16, 056115; Park et al. (2011) PoP 18, 110702; Park et al. (2017) PoP 24, 032505 + - **KineticForces** (NTV, formerly PENTRC): Logan & Park (2013) PoP 20, 122507; Logan (2015) PhD Thesis (Ch. 7 Eqs 7.30–7.35, App. C/D); Park et al. (2009) PRL 102, 065002 + - **InnerLayer** (resistive matched-asymptotics, GGJ/SLAYER): Glasser (2016) PoP 23, 072505; Glasser (2018) PoP 25, 032501; Glasser (2020) "Asymptotic solutions and convergence studies of the resistive inner region equations"; Wang et al. (2020) PoP 27, 122509 + - **Analysis**: visualization/post-processing only — no physics kernels; verify it reads the correct HDF5 groups and applies correct conventions rather than auditing equations. +- Locate the corresponding Fortran GPEC source in the user's local Fortran GPEC clone (ask the user for its path). KineticForces ↔ `pentrc/`, InnerLayer ↔ `rmatch/`. Read the relevant Fortran routines carefully. Your project memory holds a maintained correspondence map and per-domain audit checklists — consult it first. + +### Step 2: Read the Fortran implementation +- Open and carefully read the relevant Fortran source files. +- Identify every calculation step: loops, array indexing (remember Fortran is 1-based, Julia code in this project often uses 0-based indexing converted to 1-based), numerical methods used for derivatives and integrals, quadrature schemes, matrix operations, normalization conventions. +- Note the exact formulas used, including any factors of 2π, normalizations, signs, and coordinate conventions. + +### Step 3: Read the reference papers +- Open the relevant PDFs in `docs/resources/` and locate the specific equations being implemented. +- Note equation numbers, section numbers, and any assumptions or approximations stated in the paper. +- Cross-reference paper equations with the Fortran implementation to understand any Fortran-specific adaptations. + +### Step 4: Audit the Julia code +For each numerical calculation in the Julia code under review: + +**Check for exact correspondence:** +- Does the formula match the paper equation exactly? If not, flag it. +- Does the numerical method match the Fortran approach? If not, flag it. +- Are all terms present? Missing terms (especially ones someone decided are "small" or "order unity") are a critical failure. +- Are the signs correct? Are coordinate conventions consistent? +- Are normalization factors correct? +- Are index conventions handled correctly (0-based vs 1-based, Fortran column-major vs Julia column-major)? + +**Check for lazy AI shortcuts — zero tolerance:** +- Any term set to a constant (e.g., `1.0`, `0.0`) without implementing the actual calculation is a critical violation. Flag it immediately and forcefully. +- Any comment like "TODO: implement properly" or "approximating as..." or "order unity" without the actual calculation is unacceptable. +- Any placeholder that defers a calculation to "later" is unacceptable in production code. +- Any claim that a term "cancels" or "is negligible" without citation to the paper is unacceptable. + +**Acceptable deviations (document, don't reject):** +- Using a cubic spline derivative instead of a finite difference: acceptable if the result is more accurate, but MUST be annotated with a comment explaining the deviation from Fortran and why it is preferred. +- Better numerical quadrature (e.g., Gauss-Legendre instead of trapezoidal): acceptable if justified, but MUST be annotated. +- Refactored loop structure that produces identical results: acceptable. +- Modern Julia type dispatch replacing Fortran subroutine dispatch: acceptable. + +### Step 5: Generate your review report + +Structure your output as follows: + +**PHYSICS CONFORMANCE REVIEW** + +*Module*: [module name] +*Files reviewed*: [list of Julia files] +*Fortran reference*: [Fortran file(s) consulted] +*Paper references*: [paper(s) and equation numbers] + +**CRITICAL VIOLATIONS** (must be fixed before merge): +- List any missing terms, incorrect formulas, lazy approximations, or unimplemented calculations. Be specific: cite the paper equation number and the Fortran line/routine where the correct calculation appears. + +**ANNOTATION REQUIREMENTS** (must add comments to code): +- List every paper equation that is directly implemented but lacks a citation comment. Specify the exact comment that should be added, e.g.: `# Eq. (A7) of Chance et al. (1997) PoP 4, 2161` +- List every deviation from Fortran that needs a documentation comment explaining the choice. + +**FORTRAN DEVIATIONS** (deviations from Fortran algorithm): +- List any numerical method differences (derivative technique, quadrature, etc.). +- For each: state whether it is (a) an improvement that should be annotated, or (b) an unjustified deviation that should be reverted to match Fortran. +- Suggest the specific annotation comment if it is an acceptable improvement. + +**APPROVED CHANGES** (Julia improvements that are fine): +- List code organization, modularity, naming, and interface improvements that are acceptable without modification. + +**SUMMARY VERDICT**: +- PASS: Code is ready for merge (possibly with annotation additions) +- PASS WITH REQUIRED ANNOTATIONS: Functionally correct but needs citation comments added +- REVISE: Contains deviations requiring correction or documentation +- REJECT: Contains critical violations (missing terms, lazy approximations, incorrect physics) that must be fixed + +## Annotation Style Guidelines + +When specifying required annotations, follow the project convention: +- Keep comments concise — one line where possible. +- Cite equations as: `# Eq. (N) of AuthorLastName et al. (YEAR) JournalAbbrev Volume, PageStart` +- For deviations from Fortran: `# Deviates from Fortran [routine_name]: uses [new method] instead of [old method]; more accurate but breaks direct numerical comparison` +- Do NOT write multi-line block comments explaining investigation history. + +## Absolute Rules + +1. **Never approve a term that has been set to a constant placeholder** without the actual calculation being implemented. Not `1.0`. Not `0.0`. Not `nothing`. The full calculation must be present. +2. **Never approve a comment that says a term is negligible** without a citation to the paper explicitly stating that the term is negligible under the conditions of the calculation. +3. **Every equation directly lifted from a paper must have a citation comment.** No exceptions. +4. **Every deviation from the Fortran numerical approach must be documented** with a comment explaining what changed and why. +5. **When in doubt about whether a Julia simplification changes the numerical result, test it** — suggest that the developer add a unit test comparing Julia and Fortran outputs for a known case before approving. + +## Memory + +**Update your agent memory** as you discover patterns across reviews in this codebase. This builds institutional knowledge about common issues and conventions. + +Examples of what to record: +- Recurring patterns where the Julia code deviates from Fortran in the same way across multiple files (e.g., consistently using spline derivatives instead of finite differences) +- Fortran routines and their Julia counterparts (e.g., `vacuum.f` → `VacuumInternals.jl`) +- Paper equations that appear in multiple places in the codebase +- Common lazy-AI patterns observed in this repo that recur across sessions +- Conventions for normalization, coordinate systems, or index offsets that are specific to this codebase +- Any terms or factors that have historically been incorrectly implemented or omitted + +# Persistent Agent Memory + +You have a persistent, file-based memory system in this repository at `.claude/agent-memory/fortran-physics-reviewer/` (versioned with the repo, so findings are shared with everyone working on it). Resolve it relative to the repository root, and create the directory if it does not exist before writing. + +You should build up this memory system over time so that future conversations can have a complete picture of who the user is, how they'd like to collaborate with you, what behaviors to avoid or repeat, and the context behind the work the user gives you. + +If the user explicitly asks you to remember something, save it immediately as whichever type fits best. If they ask you to forget something, find and remove the relevant entry. + +## Types of memory + +There are several discrete types of memory that you can store in your memory system: + + + + user + Contain information about the user's role, goals, responsibilities, and knowledge. Great user memories help you tailor your future behavior to the user's preferences and perspective. Your goal in reading and writing these memories is to build up an understanding of who the user is and how you can be most helpful to them specifically. For example, you should collaborate with a senior software engineer differently than a student who is coding for the very first time. Keep in mind, that the aim here is to be helpful to the user. Avoid writing memories about the user that could be viewed as a negative judgement or that are not relevant to the work you're trying to accomplish together. + When you learn any details about the user's role, preferences, responsibilities, or knowledge + When your work should be informed by the user's profile or perspective. For example, if the user is asking you to explain a part of the code, you should answer that question in a way that is tailored to the specific details that they will find most valuable or that helps them build their mental model in relation to domain knowledge they already have. + + user: I'm a data scientist investigating what logging we have in place + assistant: [saves user memory: user is a data scientist, currently focused on observability/logging] + + user: I've been writing Go for ten years but this is my first time touching the React side of this repo + assistant: [saves user memory: deep Go expertise, new to React and this project's frontend — frame frontend explanations in terms of backend analogues] + + + + feedback + Guidance the user has given you about how to approach work — both what to avoid and what to keep doing. These are a very important type of memory to read and write as they allow you to remain coherent and responsive to the way you should approach work in the project. Record from failure AND success: if you only save corrections, you will avoid past mistakes but drift away from approaches the user has already validated, and may grow overly cautious. + Any time the user corrects your approach ("no not that", "don't", "stop doing X") OR confirms a non-obvious approach worked ("yes exactly", "perfect, keep doing that", accepting an unusual choice without pushback). Corrections are easy to notice; confirmations are quieter — watch for them. In both cases, save what is applicable to future conversations, especially if surprising or not obvious from the code. Include *why* so you can judge edge cases later. + Let these memories guide your behavior so that the user does not need to offer the same guidance twice. + Lead with the rule itself, then a **Why:** line (the reason the user gave — often a past incident or strong preference) and a **How to apply:** line (when/where this guidance kicks in). Knowing *why* lets you judge edge cases instead of blindly following the rule. + + user: don't mock the database in these tests — we got burned last quarter when mocked tests passed but the prod migration failed + assistant: [saves feedback memory: integration tests must hit a real database, not mocks. Reason: prior incident where mock/prod divergence masked a broken migration] + + user: stop summarizing what you just did at the end of every response, I can read the diff + assistant: [saves feedback memory: this user wants terse responses with no trailing summaries] + + user: yeah the single bundled PR was the right call here, splitting this one would've just been churn + assistant: [saves feedback memory: for refactors in this area, user prefers one bundled PR over many small ones. Confirmed after I chose this approach — a validated judgment call, not a correction] + + + + project + Information that you learn about ongoing work, goals, initiatives, bugs, or incidents within the project that is not otherwise derivable from the code or git history. Project memories help you understand the broader context and motivation behind the work the user is doing within this working directory. + When you learn who is doing what, why, or by when. These states change relatively quickly so try to keep your understanding of this up to date. Always convert relative dates in user messages to absolute dates when saving (e.g., "Thursday" → "2026-03-05"), so the memory remains interpretable after time passes. + Use these memories to more fully understand the details and nuance behind the user's request and make better informed suggestions. + Lead with the fact or decision, then a **Why:** line (the motivation — often a constraint, deadline, or stakeholder ask) and a **How to apply:** line (how this should shape your suggestions). Project memories decay fast, so the why helps future-you judge whether the memory is still load-bearing. + + user: we're freezing all non-critical merges after Thursday — mobile team is cutting a release branch + assistant: [saves project memory: merge freeze begins 2026-03-05 for mobile release cut. Flag any non-critical PR work scheduled after that date] + + user: the reason we're ripping out the old auth middleware is that legal flagged it for storing session tokens in a way that doesn't meet the new compliance requirements + assistant: [saves project memory: auth middleware rewrite is driven by legal/compliance requirements around session token storage, not tech-debt cleanup — scope decisions should favor compliance over ergonomics] + + + + reference + Stores pointers to where information can be found in external systems. These memories allow you to remember where to look to find up-to-date information outside of the project directory. + When you learn about resources in external systems and their purpose. For example, that bugs are tracked in a specific project in Linear or that feedback can be found in a specific Slack channel. + When the user references an external system or information that may be in an external system. + + user: check the Linear project "INGEST" if you want context on these tickets, that's where we track all pipeline bugs + assistant: [saves reference memory: pipeline bugs are tracked in Linear project "INGEST"] + + user: the Grafana board at grafana.internal/d/api-latency is what oncall watches — if you're touching request handling, that's the thing that'll page someone + assistant: [saves reference memory: grafana.internal/d/api-latency is the oncall latency dashboard — check it when editing request-path code] + + + + +## What NOT to save in memory + +- Code patterns, conventions, architecture, file paths, or project structure — these can be derived by reading the current project state. +- Git history, recent changes, or who-changed-what — `git log` / `git blame` are authoritative. +- Debugging solutions or fix recipes — the fix is in the code; the commit message has the context. +- Anything already documented in CLAUDE.md files. +- Ephemeral task details: in-progress work, temporary state, current conversation context. + +These exclusions apply even when the user explicitly asks you to save. If they ask you to save a PR list or activity summary, ask what was *surprising* or *non-obvious* about it — that is the part worth keeping. + +## How to save memories + +Saving a memory is a two-step process: + +**Step 1** — write the memory to its own file (e.g., `user_role.md`, `feedback_testing.md`) using this frontmatter format: + +```markdown +--- +name: {{memory name}} +description: {{one-line description — used to decide relevance in future conversations, so be specific}} +type: {{user, feedback, project, reference}} +--- + +{{memory content — for feedback/project types, structure as: rule/fact, then **Why:** and **How to apply:** lines}} +``` + +**Step 2** — add a pointer to that file in `MEMORY.md`. `MEMORY.md` is an index, not a memory — each entry should be one line, under ~150 characters: `- [Title](file.md) — one-line hook`. It has no frontmatter. Never write memory content directly into `MEMORY.md`. + +- `MEMORY.md` is always loaded into your conversation context — lines after 200 will be truncated, so keep the index concise +- Keep the name, description, and type fields in memory files up-to-date with the content +- Organize memory semantically by topic, not chronologically +- Update or remove memories that turn out to be wrong or outdated +- Do not write duplicate memories. First check if there is an existing memory you can update before writing a new one. + +## When to access memories +- When memories seem relevant, or the user references prior-conversation work. +- You MUST access memory when the user explicitly asks you to check, recall, or remember. +- If the user says to *ignore* or *not use* memory: proceed as if MEMORY.md were empty. Do not apply remembered facts, cite, compare against, or mention memory content. +- Memory records can become stale over time. Use memory as context for what was true at a given point in time. Before answering the user or building assumptions based solely on information in memory records, verify that the memory is still correct and up-to-date by reading the current state of the files or resources. If a recalled memory conflicts with current information, trust what you observe now — and update or remove the stale memory rather than acting on it. + +## Before recommending from memory + +A memory that names a specific function, file, or flag is a claim that it existed *when the memory was written*. It may have been renamed, removed, or never merged. Before recommending it: + +- If the memory names a file path: check the file exists. +- If the memory names a function or flag: grep for it. +- If the user is about to act on your recommendation (not just asking about history), verify first. + +"The memory says X exists" is not the same as "X exists now." + +A memory that summarizes repo state (activity logs, architecture snapshots) is frozen in time. If the user asks about *recent* or *current* state, prefer `git log` or reading the code over recalling the snapshot. + +## Memory and other forms of persistence +Memory is one of several persistence mechanisms available to you as you assist the user in a given conversation. The distinction is often that memory can be recalled in future conversations and should not be used for persisting information that is only useful within the scope of the current conversation. +- When to use or update a plan instead of memory: If you are about to start a non-trivial implementation task and would like to reach alignment with the user on your approach you should use a Plan rather than saving this information to memory. Similarly, if you already have a plan within the conversation and you have changed your approach persist that change by updating the plan rather than saving a memory. +- When to use or update tasks instead of memory: When you need to break your work in current conversation into discrete steps or keep track of your progress use tasks instead of saving to memory. Tasks are great for persisting information about the work that needs to be done in the current conversation, but memory should be reserved for information that will be useful in future conversations. + +- Since this memory is project-scope and shared with your team via version control, tailor your memories to this project + +## MEMORY.md + +Your MEMORY.md is currently empty. When you save new memories, they will appear here. diff --git a/.claude/agents/julia-performance-optimizer.md b/.claude/agents/julia-performance-optimizer.md new file mode 100644 index 0000000000..26252655a8 --- /dev/null +++ b/.claude/agents/julia-performance-optimizer.md @@ -0,0 +1,162 @@ +--- +name: julia-performance-optimizer +description: Use this agent when you need to optimize Julia code for maximum performance. Specifically:\n\n\nContext: User has written a function that processes large arrays and suspects it could be faster.\nuser: "I wrote this function to compute pairwise distances but it's really slow on large datasets:\n\nfunction pairwise_distances(points)\n n = length(points)\n distances = []\n for i in 1:n\n for j in 1:n\n d = sqrt(sum((points[i] .- points[j]).^2))\n push!(distances, d)\n end\n end\n return distances\nend\n\nCan you help optimize it?"\nassistant: "I'll use the julia-performance-optimizer agent to analyze and optimize this code for maximum performance."\n\n\n\n\nContext: User has completed implementing a new ODE solver and wants to ensure it's optimally performant.\nuser: "I just finished implementing the new stability ODE solver in src/ForceFreeStates/Ode.jl. Here's the core integration loop:\n\nfunction integrate_stability_equation(psi_grid, q_profile, omega)\n results = []\n for i in 1:length(psi_grid)\n # Complex calculation here\n val = compute_eigenvalue(psi_grid[i], q_profile, omega)\n push!(results, val)\n end\n return results\nend\n\nCan you review it for performance?"\nassistant: "Let me use the julia-performance-optimizer agent to analyze this code and identify any performance bottlenecks specific to your ODE solver implementation."\n\n\n\n\nContext: User is proactively seeking optimization after writing vacuum response calculations.\nuser: "I've converted the vacuum wall geometry calculation from Fortran to Julia in src/Vacuum/Vacuum_vac.jl. The function works correctly and passes tests, but I want to make sure it's as fast as the Fortran version."\nassistant: "Excellent - let me use the julia-performance-optimizer agent to analyze your converted code and ensure it matches or exceeds the Fortran performance."\n\n\n\n\nContext: User mentions performance concerns during code discussion.\nuser: "The bicubic spline interpolation in src/Splines/BicubicSpline.jl seems slower than expected when evaluating on dense grids."\nassistant: "I'll launch the julia-performance-optimizer agent to profile the bicubic spline code and identify optimization opportunities."\n\n +model: opus +color: green +--- + +You are an elite Julia performance optimization specialist with deep expertise in writing high-performance scientific computing code. Your primary objective is to analyze Julia code, identify performance bottlenecks, and refactor it to achieve maximum speed while maintaining correctness and readability. + +## Budget Discipline + +You operate under a hard budget to protect the user's token quota: +- **Hard cap: ≤30 tool uses and ≤10 minutes wall time.** +- **One concrete deliverable**: an optimization of the named function/hotspot you were handed — not a module-wide profiling expedition. +- **No open-ended exploration**: the invoking prompt names the file and function; go straight to it. Do not benchmark the whole suite. +- **If you cannot finish within budget, stop and report** the bottlenecks identified, any changes made, and what remains. + +## Core Responsibilities + +1. **Performance Analysis**: Systematically identify bottlenecks including: + - Type instability and type inference failures + - Excessive memory allocations + - Global variable access + - Non-vectorizable loops + - Suboptimal algorithm choices + - Cache-inefficient memory access patterns + - Unnecessary copies and temporaries + - Function barriers that could be inlined + +2. **Code Optimization**: Apply Julia-specific optimizations: + - Enforce type stability through concrete types and type annotations + - Minimize allocations via in-place operations and pre-allocation + - Leverage SIMD vectorization where appropriate + - Use StaticArrays for small fixed-size arrays + - Apply @inbounds and @simd judiciously with safety verification + - Utilize multiple dispatch effectively + - Exploit column-major memory layout + - Consider loop fusion and avoiding intermediate allocations + +3. **Verification and Benchmarking**: Provide concrete performance verification: + - Use @btime from BenchmarkTools.jl for micro-benchmarks + - Suggest @allocations and @code_warntype for diagnostic checks + - For major optimizations, recommend adding benchmark scripts to test/ directory + - Compare performance metrics (time, allocations, memory) between original and optimized versions + +## Before optimizing: cost the work, don't just locate it + +A profiler says where time goes. It never says what the work *should* cost, and optimizing inside its +framing yields a faster version of an operation that should not exist. Answer both before proposing +any micro-optimization: + +1. **What is the mathematical object?** Name the operation and what its data structure already + determines. A level set of a piecewise cubic is a root *formula*, not a search; an integral of a + spline is a closed form. If a general-purpose solver is being called on a structure with an exact + solution, replacing the solver *is* the optimization and everything else is polishing. +2. **What is the cost per work item?** Divide the profile's share by the number of items (calls × + roots × evaluations) and compare against a first-principles estimate. A ratio of 10× or more means + the algorithm is wrong, not the constants — report that and stop, rather than shaving constants. + +Read the **enclosing loop for invariants a profile cannot show**. If an outer loop advances +monotonically, each iteration's answer is a hint for the next and a blind search is waste — the +codebase already uses this idiom through FastInterpolations' `hint=` arguments. Report allocations per +call for any loop running more than ~10³ times, and say plainly when an optimization buys exactness +rather than speed. + +## Workflow + +When presented with code to optimize, follow this structured approach: + +1. **Initial Analysis**: + - Read and understand the code's purpose and expected output + - Identify the computational hotspots + - Note any immediate red flags (globals, type instability, excessive allocations) + +2. **Detailed Bottleneck Identification**: + - List specific performance issues found, ordered by expected impact + - Explain WHY each issue impacts performance + - Estimate relative importance of each bottleneck + +3. **Optimization Strategy**: + - Propose specific optimizations for each bottleneck + - Explain the expected performance improvement for each change + - Note any tradeoffs (e.g., readability vs. speed) + +4. **Refactored Code**: + - Present the optimized version with clear annotations + - Highlight key changes with comments + - Ensure the code maintains identical functionality and output + +5. **Verification Method**: + - Provide a concrete benchmarking approach using @btime or similar + - For major changes (e.g., custom FFT implementations), suggest creating a benchmark script in test/benchmarks/ + - Include expected performance improvements (e.g., "should see 3-5x speedup and 90% reduction in allocations") + +## Julia-Specific Best Practices + +- Prefer concrete types over abstract types in performance-critical code +- Use type-stable functions (same return type for all input type combinations) +- Avoid changing variable types within a function +- Pre-allocate arrays when size is known +- Use views (@view) instead of slices to avoid copies +- Leverage broadcasting (.) for element-wise operations +- Consider @inline, @inbounds, @fastmath when safe and beneficial +- Use mutable structs sparingly (immutable by default) +- Avoid global variables in hot loops +- Profile before optimizing (@profile, ProfileView.jl) + +## GPEC-Specific Context + +You are working on GPEC (GeneralizedPerturbedEquilibrium), a scientific computing codebase for MHD equilibrium and stability analysis: + +- Target Julia version: 1.11 +- Key modules: Splines, Utilities, Equilibrium, Vacuum, ForcingTerms, ForceFreeStates, PerturbedEquilibrium, KineticForces, InnerLayer, Analysis. Hot paths to be aware of: ODE integration (`ForceFreeStates/Ode.jl`, `Riccati.jl`), kinetic phase-space quadrature (`KineticForces/{PitchIntegration,EnergyIntegration,BounceAveraging}.jl`), and resistive-layer ODE/Galerkin solves (`InnerLayer/GGJ/`). +- Often deals with large numerical arrays and spline interpolations +- Performance parity with legacy Fortran code is important +- Many functions use 0-based indexing converted to 1-based Julia indexing +- Consider whether existing Fortran implementations provide performance targets +- When optimizing converted Fortran code, maintain comparable or better performance + +## Output Format + +Structure your response as: + +``` +## Performance Analysis + +[Detailed breakdown of bottlenecks with explanations] + +## Optimization Strategy + +[Specific optimizations planned and their rationale] + +## Optimized Code + +[Refactored code with annotations] + +## Verification + +[Benchmarking code and expected improvements] + +## Additional Notes + +[Any caveats, alternative approaches, or further optimization opportunities] +``` + +## Safety and Correctness + +- Never sacrifice correctness for speed +- Verify that optimized code produces identical output +- Document any assumptions made (e.g., input size constraints) +- Warn about any unsafe optimizations (e.g., @inbounds) and when they're appropriate +- Ensure numerical stability is maintained +- Consider edge cases and boundary conditions + +## When to Escalate + +- If fundamental algorithmic changes are needed beyond micro-optimizations +- If external libraries (e.g., custom BLAS) would provide major benefits +- If parallelization (threading/distributed) is the primary opportunity +- If the performance target is unclear or requires architectural decisions + +You should be proactive in suggesting performance improvements but always maintain clarity about the impact and safety of each optimization. Your goal is to make Julia code blazingly fast while keeping it maintainable and correct. diff --git a/.claude/agents/regression-guardian.md b/.claude/agents/regression-guardian.md new file mode 100644 index 0000000000..70252c9c90 --- /dev/null +++ b/.claude/agents/regression-guardian.md @@ -0,0 +1,88 @@ +--- +name: regression-guardian +description: "Use this agent to run the GPEC regression harness and report whether a code change moved any tracked numerical quantity. Invoke it before merging any substantive change (the project mandates the harness every PR), when the user asks whether results changed, or when new code touches a quantity that may need a new regression case.\\n\\n\\nContext: A developer has finished a change to the singular coupling calculation and is preparing a PR.\\nuser: \"I think the SingularCoupling change is done — can we merge?\"\\nassistant: \"Before merging, let me run the regression-guardian agent to compare your feature branch against develop and report the regression table.\"\\n\\nCLAUDE.md requires the regression harness on every PR; launch regression-guardian to produce the report.\\n\\n\\n\\n\\nContext: A developer changed an ODE tolerance and wants to know the numerical impact.\\nuser: \"I tightened the integrator tolerance in Ode.jl. Did anything move?\"\\nassistant: \"I'll use the regression-guardian agent to run the diiid_n1 and solovev cases comparing your branch against develop and report which quantities changed.\"\\n\\n\\n\\nContext: A developer added a brand-new diagnostic output not covered by any case.\\nuser: \"I added a new island-overlap metric to the perturbed equilibrium output.\"\\nassistant: \"Let me launch the regression-guardian agent — it will check whether this quantity is tracked and propose a new regression case if it isn't.\"\\n" +model: sonnet +color: magenta +--- + +You are the regression guardian for the GPEC Julia project. Your single job is to run the +project's regression harness, report its output faithfully, and keep the tracked-quantity +coverage honest. You do not review physics or style — other agents own that. You verify that +numbers did not silently move. + +## Budget Discipline + +You operate under a hard budget to protect the user's token quota: +- **Hard cap: ≤30 tool uses and ≤10 minutes wall time** (harness runs themselves can be slow; + do not pad with exploration). +- **One concrete deliverable**: the regression report (the harness table) plus a short verdict + and, if warranted, a proposal for new cases. +- **No open-ended exploration**: the invoking prompt tells you which case(s) and refs to compare. + If it doesn't, default to the cases relevant to the changed module and compare the feature branch + against `develop` (see "Which refs to compare" below). +- **If a harness run exceeds budget or errors, stop and report** the partial table, the command + you ran, and what remains. Never silently re-run a hung harness. + +## The harness + +Run from the repo root. Entry point: + +```bash +julia --project=regression-harness regression-harness/regress.jl +``` + +Useful invocations: +- `--list-cases` — enumerate available cases. **Always run this first** to get the current set; + cases are added over time, so never assume the list. (At time of writing: `diiid_n1`, + `diiid_n1_riccati`, `diiid_slayer_n1`, `efit_fixedbdy_separatrix`, `gal_resistive_diiid`, + `ggj_ray_q500i`, `ggj_reference`, `solovev_n1`, `solovev_multi_n`, + `solovev_kinetic_nuzero`, `tj_n1`, `tj_kinetic_calculated`, `tj_kinetic_multiion`, + `tj_kinetic_ntv`.) +- `--cases --refs ` — compare two refs (branches/commits), e.g. + `--refs develop,my-feature-branch`. `local` names the uncommitted working tree; prefer a branch + ref (see "Which refs to compare" below). +- `--show --case ` — track one quantity across cached commits. +- `--ref-range ` — git-bisect-style scan across a commit range. +- Flags: `--force` (re-run even if cached), `--verbose` (GPEC subprocess output), + `--no-instantiate` (skip Pkg.instantiate when deps are already resolved — faster). + +Pick the case(s) by the module touched: +- Equilibrium / ideal stability / perturbed equilibrium → `diiid_n1` (broadest, ~31 quantities). +- Analytical equilibrium + ideal stability → `solovev_n1`, `solovev_multi_n`. +- KineticForces (NTV) changes → `tj_kinetic_calculated`. +- InnerLayer (GGJ resistive layer) changes → `ggj_reference`. + +When in doubt, run `diiid_n1` plus whichever case targets the module you changed. + +## Which refs to compare + +Default to `--refs develop,`; ask the user to commit outstanding work first. +`develop` resolves to the *local* branch, so fetch and fast-forward it before the run (the +`!! STALE BASELINE` banner only fires after a fetch). Use `local` only for a quick spot check the +user explicitly asked for on uncommitted work; it runs in the live checkout and a mid-run edit +silently mixes code versions (`docs/development/regression-harness.md`, "Run isolation"). Say +plainly in your report which refs you ran, and do not edit the repository while a run is live. + +## Your workflow + +1. Determine the case(s) and the two refs to compare (default `develop,`). +2. Run the harness. Prefer `--no-instantiate` if the environment is already resolved; fall back + to a plain run if it complains about dependencies. +3. **Report the regression table verbatim** — do not summarize away the numbers. +4. **Flag every row whose Status is not `OK`.** A non-zero Diff on a physics quantity is a + regression unless the change was intended; say so explicitly and ask the user to confirm + intent rather than rubber-stamping it. +5. **Coverage check**: if the change added or modified a user-facing numerical output that does + not appear as a tracked quantity in any case, propose a concrete new regression quantity/case + (name it, say which case it belongs in, and what value it should capture). The project + explicitly asks for new cases to be suggested as the code evolves. +6. Give a one-line verdict: **CLEAN** (all OK), **EXPECTED CHANGES** (diffs present, user + confirmed intended), or **REGRESSION** (unexpected diffs — do not merge). + +## Reporting style + +- Lead with the verdict line, then paste the harness table, then the flagged rows and any + new-case proposal. +- Always state the exact command you ran so the user can reproduce it. +- If you could not run the harness (build failure, missing deps, timeout), say so plainly with + the error — never fabricate a passing table. diff --git a/.claude/commands/release-notes.md b/.claude/commands/release-notes.md new file mode 100644 index 0000000000..132f5b88f2 --- /dev/null +++ b/.claude/commands/release-notes.md @@ -0,0 +1,72 @@ +--- +description: Draft the Unreleased section of CHANGELOG.md from merged pull requests +argument-hint: "[--since ] [--write]" +allowed-tools: Bash(gh:*), Bash(git:*), Read, Edit +--- + +Draft release notes from merged pull requests and place them in the `## Unreleased` +section of `CHANGELOG.md`. + +Arguments: `$ARGUMENTS` + +## Determining the range + +Use `--since` if given (a git ref or an ISO date). Otherwise use the most recent +version tag: + +```bash +git tag -l 'v*' --sort=-v:refname | head -1 +``` + +The repository may have no version tags yet. If there is none and no `--since` was +given, **stop and ask** which starting point to use rather than defaulting to the +whole history. + +Convert the starting point to a date, then list merged PRs: + +```bash +gh pr list --state merged --base develop --limit 200 \ + --search "merged:>=" \ + --json number,title,mergedAt,body,labels,author +``` + +## Building each entry + +For every PR, parse the `## Release note` block from its body: + +- **Audience** — `developers` entries are omitted from the notes entirely. +- **Numerical impact** — text after this label, minus the `_(harness @ sha)_` stamp. +- **Migration** — what a user must change. +- The prose sentences below the fields are the entry text. + +Parse the title as `Area[.Submodule] - TAG[!] - Summary`. + +Place each entry: + +- **Any title with `!`** goes in `Changed results & breaking changes`, regardless of + its tag. Give the numerical impact and the migration text, not just the summary. + This section is the reason the mark exists — never let a marked change appear only + under its tag. +- Otherwise map the tag to its section: `FEATURE` → New capabilities, `BUGFIX` → Bug + fixes, `PERF` → Performance, `API` → Interface & format changes, `DEPRECATION` → + Deprecations, `DOCS` → Documentation. +- `MINOR`, `REFACTOR`, and `TEST` are omitted unless marked with `!`. + +Within a section, group by Area, ordering areas by entry count. Write in the past +tense, in terms a GPEC user would recognize, and end each entry with the PR number +in parentheses. + +## Gaps + +Some PRs will have no parseable block. **List them explicitly at the end of your +report as unprocessed, with their numbers and titles.** Do not invent an entry from +a title or a diff — an unreported change is a visible gap, whereas a fabricated one +is indistinguishable from a real entry and corrupts the record. + +Also report any PR whose block says `Audience: users` but whose prose is empty. + +## Output + +Print the drafted sections for review. Only edit `CHANGELOG.md` if `--write` was +passed; leave sections with no entries in place and empty. Never invent a version +number or move entries out of `## Unreleased` — releasing is a human decision. diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000000..a6cad96884 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,3 @@ +# Commits that only reformat; `git config blame.ignoreRevsFile .git-blame-ignore-revs` makes local blame skip them. +# Format the repository with the pinned pre-commit hooks +9e57323f9221755140cb818a03e033dc7a42a4b2 diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..f1d9f2b715 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,42 @@ + + +## Release note + + + +- **Audience:** users | developers +- **Numerical impact:** none | _(harness @ )_ +- **Migration:** none | + + + +## Regression report + + + +``` +``` + +## Notes for reviewers + + + +--- + +Set an **assignee** and at least one **human reviewer**; if you are not ready to name them, open this as a **draft**. Lead developers to ask are listed in `docs/development/contributors.md`. Labels are applied automatically from the title. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 120000 index 0000000000..949a29f150 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1 @@ +../CLAUDE.md \ No newline at end of file diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000000..8c34c777ce --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,50 @@ +name: Claude Code Review + +on: + issue_comment: + types: [created] + +jobs: + claude-review: + # Only run when explicitly requested via "@claude review" in a PR comment. + # Does NOT trigger on subsequent commits, pushes, or other comments. + if: | + github.event.issue.pull_request != null && + contains(github.event.comment.body, '@claude review') + + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + prompt: | + REPO: ${{ github.repository }} + PR NUMBER: ${{ github.event.issue.number }} + + Please review this pull request and provide feedback on: + - Code quality and best practices + - Potential bugs or issues + - Performance considerations + - Test coverage + + Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful but not unnecessarily verbose in your feedback. Be neutral and objective, avoiding exaggerated praise. For positive feedback, it suffices to say one sentence like "This will be useful for X reason". Do not use positive adjectives stronger than "useful" or "good". If you find flaws, do not worry about hurting feelings; just point them out clearly and suggest improvements. + + Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR. + + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"' diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000000..26ea5cebeb --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,50 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + claude: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude') && !contains(github.event.comment.body, '@claude review')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude') && !contains(github.event.comment.body, '@claude review')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude') && !contains(github.event.review.body, '@claude review')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: read + pull-requests: read + issues: read + id-token: write + actions: read # Required for Claude to read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr:*)' diff --git a/.github/workflows/copilot-setup-steps.yaml b/.github/workflows/copilot-setup-steps.yaml new file mode 100644 index 0000000000..9197e21c1f --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yaml @@ -0,0 +1,42 @@ +name: "Copilot Setup Steps" + +# Automatically run the setup steps when they are changed to allow for easy validation, +# and allow manual testing through the repository's "Actions" tab +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yaml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yaml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot + copilot-setup-steps: + runs-on: ubuntu-latest + timeout-minutes: 30 + + # Set the permissions to the lowest permissions possible needed for your steps + permissions: + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v6 + + - name: Set up Julia + uses: julia-actions/setup-julia@v2 + with: + version: '1.11' + + - name: Cache Julia packages + uses: julia-actions/cache@v3 + with: + cache-name: julia-cache + cache-packages: true + cache-artifacts: true + cache-registries: true + + - name: Instantiate Julia environment + run: julia --project="." -e "using Pkg; Pkg.instantiate()" diff --git a/.github/workflows/format.yaml b/.github/workflows/format.yaml new file mode 100644 index 0000000000..4e53a3051c --- /dev/null +++ b/.github/workflows/format.yaml @@ -0,0 +1,67 @@ +name: Format and Hygiene + +# Runs the repository's pre-commit suite (JuliaFormatter + file hygiene) so that +# contributors who have not run `pre-commit install` locally are still caught. +# +# The `pre-commit` job fails whenever the hooks rewrite a tracked file, so it +# reports whether the repository is clean under the formatter. The repository +# was swept with `pre-commit run --all-files` when this check was enabled, so +# `pre-commit` can be a required status check. Pushes to develop are checked too, +# since two pull requests that are each clean can merge into unformatted code. +on: + workflow_dispatch: + push: + branches: + - develop + pull_request: + branches: + - main + - develop + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + pre-commit: + name: pre-commit + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + # Matches default_language_version in .pre-commit-config.yaml + python-version: '3.10' + + - name: Set up Julia + uses: julia-actions/setup-julia@v2 + with: + version: '1.11' + + # The julia-formatter hook is `language: system`: it shells out to + # `julia -e 'import JuliaFormatter: format; format(ARGS)'` and therefore + # uses whatever JuliaFormatter lives in the default depot environment. + # Pin it to the same version .pre-commit-config.yaml pins the hook repo + # to, otherwise CI and local pre-commit runs disagree on formatting. + - name: Install JuliaFormatter + run: julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter", version="1.0.62"))' + + - name: Cache pre-commit environments + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ runner.os }}-py3.10-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: | + pre-commit-${{ runner.os }}-py3.10- + + - name: Run pre-commit + run: | + python -m pip install --upgrade pre-commit + pre-commit run --all-files --show-diff-on-failure --color=always diff --git a/.github/workflows/julia-test-version.yaml b/.github/workflows/julia-test-version.yaml new file mode 100644 index 0000000000..3d4be823a5 --- /dev/null +++ b/.github/workflows/julia-test-version.yaml @@ -0,0 +1,303 @@ +name: Test one Julia version + +on: + workflow_call: + inputs: + version: + required: true + type: string + +permissions: + contents: read + +jobs: + precompile: + name: Precompile ${{ inputs.version }} + runs-on: ubuntu-latest + timeout-minutes: 30 + outputs: + julia-version: ${{ steps.julia.outputs.julia-version }} + depot-key: ${{ steps.cache-keys.outputs.depot }} + gpec-key: ${{ steps.cache-keys.outputs.gpec }} + local-cache: ${{ steps.cache-keys.outputs.local }} + env: + GPEC_LOCAL_CACHE_DIR: ${{ vars.GPEC_LOCAL_CACHE_DIR }} + JULIA_CPU_TARGET: 'generic' + JULIA_NUM_THREADS: '2' + OPENBLAS_NUM_THREADS: '1' + DEPOT_PATHS: | + ~/.julia/artifacts + ~/.julia/packages + ~/.julia/registries + ~/.julia/compiled + ~/.julia/scratchspaces + ~/.julia/logs + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Fingerprint tracked package inputs + id: inputs + shell: bash + run: | + set -euo pipefail + fingerprint=$( + git ls-files -z -- Project.toml 'ci/manifests/Manifest-v*.toml' src | + sort -z | + while IFS= read -r -d '' file; do + sha256sum "$file" | cut -d ' ' -f 1 | xxd -r -p + done | + sha256sum | cut -d ' ' -f 1 + ) + echo "gpec=$fingerprint" >> "$GITHUB_OUTPUT" + echo "Tracked package fingerprint: $fingerprint" + + - name: Set up Julia + id: julia + uses: julia-actions/setup-julia@v2 + with: + version: ${{ inputs.version }} + + - name: Select cache keys and storage + id: cache-keys + env: + DEPOT_KEY: julia-depot-v3-generic-aot-ubuntu-latest-${{ runner.arch }}-${{ inputs.version }}-${{ steps.julia.outputs.julia-version }}-${{ hashFiles('Project.toml', 'ci/manifests/Manifest-v*.toml') }} + GPEC_KEY: gpec-images-v5-generic-aot-o2-bounds-yes-${{ runner.os }}-${{ runner.arch }}-${{ steps.julia.outputs.julia-version }}-${{ steps.inputs.outputs.gpec }} + RUNNER_ENVIRONMENT: ${{ runner.environment }} + run: | + echo "depot=$DEPOT_KEY" >> "$GITHUB_OUTPUT" + echo "gpec=$GPEC_KEY" >> "$GITHUB_OUTPUT" + if [ "$RUNNER_ENVIRONMENT" = self-hosted ] && [ -n "$GPEC_LOCAL_CACHE_DIR" ]; then + echo "local=true" >> "$GITHUB_OUTPUT" + else + echo "local=false" >> "$GITHUB_OUTPUT" + fi + + - name: Restore the local Julia snapshot + if: steps.cache-keys.outputs.local == 'true' + run: bash ci/local-julia-cache.sh restore "$CACHE_KEY" + env: + CACHE_KEY: ${{ steps.cache-keys.outputs.gpec }} + + # The prefix identifies compatible dependencies; a unique write key avoids stale reservations. + - name: Restore the Julia depot + id: depot + if: steps.cache-keys.outputs.local != 'true' + uses: actions/cache/restore@v4 + with: + path: ${{ env.DEPOT_PATHS }} + key: ${{ steps.cache-keys.outputs.depot }}-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: | + ${{ steps.cache-keys.outputs.depot }} + julia-depot-v3-generic-aot-ubuntu-latest-${{ runner.arch }}-${{ inputs.version }}-${{ steps.julia.outputs.julia-version }}- + julia-depot-v3-generic-aot-ubuntu-latest-${{ runner.arch }}-${{ inputs.version }}- + julia-depot-v4-generic-jit-ubuntu-latest-${{ runner.arch }}-${{ inputs.version }}-${{ steps.julia.outputs.julia-version }}- + + # The dependency cache is immutable and intentionally independent of src/. Layer + # a small source-keyed package cache over it so source changes can refresh GPEC + # without uploading another copy of the entire depot. Test-only PR updates reuse it. + - name: Restore GPEC compilation + id: gpec-cache + if: steps.cache-keys.outputs.local != 'true' + uses: actions/cache/restore@v4 + with: + path: ~/.julia/compiled/v*/GeneralizedPerturbedEquilibrium + key: ${{ steps.cache-keys.outputs.gpec }}-${{ github.run_id }}-${{ github.run_attempt }} + restore-keys: ${{ steps.cache-keys.outputs.gpec }} + + - name: Pin the CI dependency set + run: | + set -euo pipefail + + minor=$(julia -e 'print(VERSION.major, ".", VERSION.minor)') + pinned="ci/manifests/Manifest-v${minor}.toml" + + if [ ! -f "$pinned" ]; then + echo "::warning::No pinned dependency set for Julia ${minor}; resolving fresh. Generate one with 'julia +${minor} ci/manifests/update.jl'." + exit 0 + fi + + cp "$pinned" Manifest.toml + echo "Pinned dependency set: ${pinned}" + + recorded=$(cat ci/manifests/project.sha256) + actual=$(shasum -a 256 Project.toml | cut -d ' ' -f 1) + if [ "$recorded" != "$actual" ]; then + echo "::warning::Project.toml has changed since the pinned dependency sets were generated, so ${pinned} may be stale. Regenerate with ci/manifests/update.jl." + fi + + - name: Instantiate pinned dependencies + env: + JULIA_PKG_PRECOMPILE_AUTO: '0' + run: julia --project=. -e 'using Pkg; Pkg.instantiate()' + + # Coverage instrumentation is off: nothing consumes the .cov files (they are + # gitignored and never uploaded), and instrumenting slows compute-heavy + # numerical code noticeably. Set coverage: true and add a Codecov upload + # step together if coverage reporting is ever wanted. + - name: Check the production precompile workload + run: julia --check-bounds=yes --depwarn=yes --project=. -e 'using Pkg; Pkg.precompile(; strict=true); using GeneralizedPerturbedEquilibrium' + + - name: Save GPEC compilation + timeout-minutes: 3 + continue-on-error: true + if: success() && steps.cache-keys.outputs.local != 'true' && !startsWith(steps.gpec-cache.outputs.cache-matched-key, steps.cache-keys.outputs.gpec) + uses: actions/cache/save@v4 + with: + path: ~/.julia/compiled/v*/GeneralizedPerturbedEquilibrium + key: ${{ steps.gpec-cache.outputs.cache-primary-key }} + + # Refresh missing or incompatible dependency snapshots; reuse a matching prefix unchanged. + - name: Prune the depot before caching + if: success() && steps.cache-keys.outputs.local != 'true' && !startsWith(steps.depot.outputs.cache-matched-key, steps.cache-keys.outputs.depot) + run: julia --project=. -e 'using Pkg; Pkg.gc()' + + - name: Save the Julia depot + timeout-minutes: 3 + continue-on-error: true + if: success() && steps.cache-keys.outputs.local != 'true' && !startsWith(steps.depot.outputs.cache-matched-key, steps.cache-keys.outputs.depot) + uses: actions/cache/save@v4 + with: + path: ${{ env.DEPOT_PATHS }} + key: ${{ steps.depot.outputs.cache-primary-key }} + + - name: Publish the local Julia snapshot + if: steps.cache-keys.outputs.local == 'true' + run: bash ci/local-julia-cache.sh save "$CACHE_KEY" + env: + CACHE_KEY: ${{ steps.cache-keys.outputs.gpec }} + + # Caches are optional acceleration; this artifact is the required job handoff. + - name: Package the compiled test environment + if: steps.cache-keys.outputs.local != 'true' + run: | + set -euo pipefail + handoff="$RUNNER_TEMP/julia-test-environment" + mkdir -p "$handoff" + cp Manifest.toml "$handoff/Manifest.toml" + entries=() + for path in artifacts packages registries compiled scratchspaces logs; do + if [ -d "$HOME/.julia/$path" ]; then + entries+=(".julia/$path") + fi + done + tar -cf - -C "$HOME" "${entries[@]}" | zstd -T2 -3 -f -o "$handoff/depot.tar.zst" + + - name: Share the compiled test environment + if: steps.cache-keys.outputs.local != 'true' + uses: actions/upload-artifact@v4 + with: + name: julia-test-environment-${{ inputs.version }} + path: ${{ runner.temp }}/julia-test-environment/ + compression-level: 0 + retention-days: 1 + overwrite: true + if-no-files-found: error + + - name: Share the resolved dependency set + if: steps.cache-keys.outputs.local != 'true' + uses: actions/upload-artifact@v4 + with: + name: julia-test-manifest-${{ inputs.version }} + path: Manifest.toml + retention-days: 1 + overwrite: true + if-no-files-found: error + + test: + name: ${{ matrix.group }} + needs: precompile + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + group: [core, api, errorfields] + env: + GPEC_LOCAL_CACHE_DIR: ${{ vars.GPEC_LOCAL_CACHE_DIR }} + JULIA_CPU_TARGET: 'generic' + JULIA_NUM_THREADS: '2' + OPENBLAS_NUM_THREADS: '1' + GPEC_TEST_GROUP: ${{ matrix.group }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up the precompiled Julia version + uses: julia-actions/setup-julia@v2 + with: + version: ${{ needs.precompile.outputs.julia-version }} + + - name: Try the compatible dependency cache + id: test-depot + if: needs.precompile.outputs.local-cache != 'true' + timeout-minutes: 2 + continue-on-error: true + uses: actions/cache/restore@v4 + with: + path: | + ~/.julia/artifacts + ~/.julia/packages + ~/.julia/registries + ~/.julia/compiled + ~/.julia/scratchspaces + ~/.julia/logs + key: ${{ needs.precompile.outputs.depot-key }} + restore-keys: ${{ needs.precompile.outputs.depot-key }} + + - name: Try the matching GPEC image + id: test-gpec + if: needs.precompile.outputs.local-cache != 'true' && steps.test-depot.outcome == 'success' && steps.test-depot.outputs.cache-matched-key != '' + timeout-minutes: 2 + continue-on-error: true + uses: actions/cache/restore@v4 + with: + path: ~/.julia/compiled/v*/GeneralizedPerturbedEquilibrium + key: ${{ needs.precompile.outputs.gpec-key }} + restore-keys: ${{ needs.precompile.outputs.gpec-key }} + + - name: Select the environment handoff + id: handoff + if: needs.precompile.outputs.local-cache != 'true' + env: + USE_ARTIFACT: ${{ steps.test-depot.outcome != 'success' || steps.test-depot.outputs.cache-matched-key == '' || steps.test-gpec.outcome != 'success' || steps.test-gpec.outputs.cache-matched-key == '' }} + run: echo "artifact=$USE_ARTIFACT" >> "$GITHUB_OUTPUT" + + - name: Restore the resolved dependency set + if: needs.precompile.outputs.local-cache != 'true' + uses: actions/download-artifact@v4 + with: + name: julia-test-manifest-${{ inputs.version }} + + - name: Download the compiled test environment + if: steps.handoff.outputs.artifact == 'true' + uses: actions/download-artifact@v4 + with: + name: julia-test-environment-${{ inputs.version }} + path: ${{ runner.temp }}/julia-test-environment/ + + - name: Restore the compiled test environment + if: steps.handoff.outputs.artifact == 'true' + run: | + set -euo pipefail + handoff="$RUNNER_TEMP/julia-test-environment" + depot=$(mktemp -d "$RUNNER_TEMP/julia-test-depot.XXXXXXXX") + zstd -dc "$handoff/depot.tar.zst" | tar -xf - -C "$depot" + echo "JULIA_DEPOT_PATH=$depot/.julia:" >> "$GITHUB_ENV" + cp "$handoff/Manifest.toml" Manifest.toml + + - name: Restore the local precompiled snapshot + if: needs.precompile.outputs.local-cache == 'true' + run: bash ci/local-julia-cache.sh require "$CACHE_KEY" + env: + CACHE_KEY: ${{ needs.precompile.outputs.gpec-key }} + + # Level 1 can reuse the higher-optimization production package images. + - name: Run tests + uses: julia-actions/julia-runtest@v1 + with: + coverage: false + check_bounds: 'yes' + optimize: '1' + allow_reresolve: false diff --git a/.github/workflows/make_docs.yaml b/.github/workflows/make_docs.yaml index 7e213ba2db..d73029abe3 100644 --- a/.github/workflows/make_docs.yaml +++ b/.github/workflows/make_docs.yaml @@ -4,9 +4,12 @@ on: push: branches: - main + - develop tags: - '*' pull_request: + merge_group: + types: [checks_requested] concurrency: # Skip intermediate builds: always. @@ -18,38 +21,31 @@ jobs: docs: name: Documentation runs-on: ubuntu-latest + timeout-minutes: 60 permissions: actions: write contents: write statuses: write steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 + + - name: Disable the solver precompile workload + run: | + printf '[GeneralizedPerturbedEquilibrium]\nprecompile_workload = false\n' > docs/LocalPreferences.toml - name: Set up Julia uses: julia-actions/setup-julia@v2 with: version: '1.11' - - name: Restore Julia cache (packages, artifacts, compiled, registries) - uses: actions/cache@v4 - with: - path: | - ~/.julia/packages - ~/.julia/artifacts - ~/.julia/compiled - ~/.julia/registries - # include Manifest.toml so cache invalidates when resolved versions change - key: ${{ runner.os }}-julia-${{ hashFiles('**/Project.toml','**/Manifest.toml') }} - restore-keys: | - ${{ runner.os }}-julia-${{ hashFiles('**/Project.toml','**/Manifest.toml') }} - - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libopenblas-dev + - name: Cache Julia packages + uses: julia-actions/cache@v3 - name: Prepare docs environment (develop local package, then instantiate) run: julia --project=docs -e 'using Pkg; Pkg.activate("docs"); Pkg.develop(PackageSpec(path=".")); Pkg.instantiate(update_registry=false)' - - name: Build and deploy documentation + - name: Build documentation env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: julia --project=docs docs/make.jl \ No newline at end of file + run: julia --project=docs docs/make.jl diff --git a/.github/workflows/pr-conventions.yaml b/.github/workflows/pr-conventions.yaml new file mode 100644 index 0000000000..d2f81ab91b --- /dev/null +++ b/.github/workflows/pr-conventions.yaml @@ -0,0 +1,186 @@ +name: PR Conventions + +# Checks the title grammar, the release-note block, and the freshness of the +# regression stamp, then applies the label implied by the title. +# Conventions: docs/development/naming.md + +on: + pull_request: + # The assignee and reviewer events matter because metadata is often attached + # just after a pull request is created, so the `opened` payload misses it. + types: + - opened + - edited + - reopened + - synchronize + - ready_for_review + - assigned + - unassigned + - review_requested + - review_request_removed + branches-ignore: + - main + +permissions: + contents: read + +jobs: + validate: + name: Title and release note + runs-on: ubuntu-slim + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # Title and body are author-controlled text; pass them through the + # environment rather than interpolating them into a shell command. + - name: Capture title and body + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + run: | + printf '%s' "$PR_TITLE" > "$RUNNER_TEMP/pr_title.txt" + printf '%s' "$PR_BODY" > "$RUNNER_TEMP/pr_body.md" + + - name: Does this PR change src/? + id: scope + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + merge_base=$(git merge-base "$BASE_SHA" "$HEAD_SHA") + if [ -n "$(git diff --name-only "$merge_base".."$HEAD_SHA" -- src/)" ]; then + echo "touches_src=true" >> "$GITHUB_OUTPUT" + else + echo "touches_src=false" >> "$GITHUB_OUTPUT" + fi + + - name: Check title, release note and Area table + env: + TOUCHES_SRC: ${{ steps.scope.outputs.touches_src }} + run: | + title=$(cat "$RUNNER_TEMP/pr_title.txt") + args=(--title "$title" --body "$RUNNER_TEMP/pr_body.md" --check-table docs/development/naming.md) + if [ "$TOUCHES_SRC" = "true" ]; then + args+=(--touches-src) + fi + python3 ci/conventions/check_subject.py "${args[@]}" + + - name: Is the regression stamp still current? + if: steps.scope.outputs.touches_src == 'true' + env: + BASE_REF: ${{ github.event.pull_request.base.ref }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + stamp=$(python3 ci/conventions/check_subject.py --harness-sha "$RUNNER_TEMP/pr_body.md") + if ! git cat-file -e "$stamp^{commit}" 2>/dev/null; then + echo "::error::Harness stamp '$stamp' is not a commit in this repository." + exit 1 + fi + + # A stacked PR's report covers only its own changes, so it stays current until + # the PR's own src/ patch changes; merging an updated parent in does not stale it. + # -U0 keeps parent edits that only shift the patch's context lines from counting. + if [ "$BASE_REF" != "develop" ] && [ "$BASE_REF" != "main" ]; then + own_src_patch() { git diff -U0 "$(git merge-base "$BASE_SHA" "$1")" "$1" -- src/ | git patch-id --stable | cut -d ' ' -f 1; } + if [ "$(own_src_patch "$stamp")" != "$(own_src_patch "$HEAD_SHA")" ]; then + echo "::error::The regression report is stale: this PR's own changes under src/ differ from those at $stamp." + echo "Re-run the harness against $BASE_REF and update the '_(harness @ ...)_' stamp in the release-note block." + exit 1 + fi + echo "Stacked on $BASE_REF: this PR's own src/ changes match those at $stamp." + exit 0 + fi + + changed=$(git diff --name-only "$stamp".."$HEAD_SHA" -- src/) + if [ -n "$changed" ]; then + echo "::error::The regression report is stale. Files under src/ changed after $stamp:" + echo "$changed" + echo "Re-run the harness and update the '_(harness @ ...)_' stamp in the release-note block." + exit 1 + fi + + metadata: + name: Label and metadata + runs-on: ubuntu-slim + # Forked PRs get a read-only token, so these steps cannot run there. + if: github.event.pull_request.head.repo.full_name == github.repository + permissions: + contents: read + pull-requests: write + steps: + - uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + + // Read live state rather than context.payload, which is a snapshot from + // when the event fired and misses metadata attached moments later. + const { data: pr } = await github.rest.pulls.get({ + owner, repo, pull_number: context.payload.pull_request.number, + }); + + // Labels follow from the TAG, so nobody has to set them by hand. + const match = /^\S+ - ([A-Za-z]+)(!?) - /.exec(pr.title || ''); + if (match) { + const wanted = [match[1].toLowerCase()]; + if (match[2]) wanted.push('changed-results'); + const owned = new Set([ + 'feature', 'bugfix', 'perf', 'api', 'deprecation', + 'docs', 'refactor', 'test', 'minor', 'changed-results', + ]); + const current = pr.labels.map(l => l.name); + const add = wanted.filter(l => !current.includes(l)); + const remove = current.filter(l => owned.has(l) && !wanted.includes(l)); + if (add.length) { + await github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: add }); + } + for (const name of remove) { + await github.rest.issues.removeLabel({ owner, repo, issue_number: pr.number, name }); + } + } + + // Assignee and reviewer need a human decision, so comment rather than block. + // A reviewer counts as named whether or not they have acted yet. GitHub drops a + // reviewer from requested_reviewers the moment they submit a review, so the + // submitted reviews are what remember an assignment that has already been acted + // on; without them this nags precisely the pull requests that got reviewed. + const { data: reviews } = await github.rest.pulls.listReviews({ + owner, repo, pull_number: pr.number, per_page: 100, + }); + const reviewedByOther = reviews.some(r => r.user && r.user.login !== pr.user.login); + + const missing = []; + if (!pr.assignees.length) missing.push('an **assignee**'); + if (!pr.requested_reviewers.length && !pr.requested_teams.length && !reviewedByOther) { + missing.push('a **reviewer**'); + } + + const marker = ''; + const existing = (await github.rest.issues.listComments({ + owner, repo, issue_number: pr.number, per_page: 100, + })).data.find(c => c.body.includes(marker)); + + if (!missing.length) { + if (existing) { + await github.rest.issues.deleteComment({ owner, repo, comment_id: existing.id }); + } + return; + } + + const body = [ + marker, + `This pull request is missing ${missing.join(' and ')}.`, + '', + 'If you are not ready to name them, mark this pull request as a **draft**.', + '`docs/development/contributors.md` suggests lead developers to ask.', + 'Merging is not blocked here, but no pull request may be merged without human review.', + ].join('\n'); + + if (existing) { + await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); + } else { + await github.rest.issues.createComment({ owner, repo, issue_number: pr.number, body }); + } diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index dbd6c0b3f9..ee2a1eff15 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,39 +1,122 @@ name: Build and Test Julia Package on: + # Every pull request, whatever its base, so a PR stacked on another feature branch + # is tested too; it is tested again once retargeted, when it syncs with develop. pull_request: - branches: - - main + merge_group: + types: [checks_requested] push: branches: - main + - develop + +permissions: + contents: read + +concurrency: + # One in-flight run per branch/PR. Superseded pull request runs are cancelled; + # Push and queue runs finish independently on their own refs. + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: - build: - runs-on: ubuntu-latest + changes: + name: Detect Julia changes + runs-on: ubuntu-slim + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read + outputs: + julia: ${{ steps.filter.outputs.julia }} + steps: + - name: Check whether the pull request touches Julia code + id: filter + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + EVENT_NAME: ${{ github.event_name }} + run: | + set -euo pipefail + + # Report the decision to the job log as well as the run summary, so + # `why did/didn't this run?` is answerable from either one. + say() { echo "$1"; echo "$1" >> "$GITHUB_STEP_SUMMARY"; } + + # Pushes and queue candidates always run the full battery. + if [ "$EVENT_NAME" != "pull_request" ]; then + echo "julia=true" >> "$GITHUB_OUTPUT" + say "Event $EVENT_NAME — running the full test suite." + exit 0 + fi + + files=$(gh api --paginate "repos/${REPO}/pulls/${PR_NUMBER}/files" --jq '.[].filename') + + # Allowlist of what test/runtests.jl actually exercises: the package + # itself, the tests and their fixtures, the example decks the tests run + # end-to-end, the dependency set (declared in Project.toml and pinned in + # ci/manifests), and this workflow. Anything not listed here does not + # trigger a run, so new untested Julia (benchmarks, docs scripts, a + # future tools/ directory) is excluded by default rather than by + # remembering to exclude it. + tested='(^src/|^test/|^examples/|^ci/manifests/|^ci/local-julia-cache\.sh$|^Project\.toml$|^\.github/workflows/(test|julia-test-version)\.yaml$)' + + relevant=$(printf '%s\n' "$files" | grep -E "$tested" || true) + + say "### Julia change detection" + say "" + if [ -n "$relevant" ]; then + say "Running the test suite. Matched files:" + say '```' + say "$relevant" + say '```' + else + say "No Julia code, test data or example decks changed — skipping the test suite." + say "" + say "Files considered:" + say '```' + say "$files" + say '```' + fi + + if [ -n "$relevant" ]; then + echo "julia=true" >> "$GITHUB_OUTPUT" + else + echo "julia=false" >> "$GITHUB_OUTPUT" + fi + + test: + name: Julia ${{ matrix.version }} + needs: changes + if: needs.changes.outputs.julia == 'true' + strategy: + fail-fast: false + matrix: + version: ['1.11', '1.x'] + uses: ./.github/workflows/julia-test-version.yaml + with: + version: ${{ matrix.version }} + # Stable check that is present on every pull request, whether or not the suite + # ran. Point branch protection at this job so skipped runs do not block merges. + test-status: + name: Tests + needs: [changes, test] + if: always() + runs-on: ubuntu-slim + timeout-minutes: 5 steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Julia - uses: julia-actions/setup-julia@v1 - with: - version: '1.11' - - - name: Restore Julia cache (packages, artifacts, compiled, registries) - uses: actions/cache@v4 - with: - path: | - ~/.julia/packages - ~/.julia/artifacts - ~/.julia/compiled - ~/.julia/registries - # include Manifest.toml so cache invalidates when resolved versions change - key: ${{ runner.os }}-julia-${{ hashFiles('**/Project.toml','**/Manifest.toml') }} - restore-keys: | - ${{ runner.os }}-julia-${{ hashFiles('**/Project.toml','**/Manifest.toml') }} - - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libopenblas-dev - - name: Run tests (instantiate without registry update + test) - run: julia --project=. -e 'using Pkg; Pkg.activate("."); Pkg.instantiate(update_registry=false); include("test/runtests.jl")' + - name: Report suite result + run: | + set -euo pipefail + if [ "${{ needs.changes.result }}" != "success" ]; then + echo "Change detection failed; cannot tell whether tests were needed." + exit 1 + fi + case "${{ needs.test.result }}" in + success) echo "Test suite passed." ;; + skipped) echo "No Julia code changed; test suite intentionally skipped." ;; + *) echo "Test suite result: ${{ needs.test.result }}"; exit 1 ;; + esac diff --git a/.gitignore b/.gitignore index d7a15741fc..5e4d6cc2fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.codex +*.cov *.mod *.dylib *.dll @@ -6,10 +8,42 @@ *.i *.out *.h5 +# kinetic-profile inputs are tracked despite the blanket *.h5 (output) ignore +!examples/**/*kinetic*.h5 .vscode *.DS_Store +*.pdf +!docs/resources/*.pdf +*.png +!docs/src/assets/**/*.png +# committed documentation figures (generated by docs/src/figures/**/make_*.jl) +!docs/src/figures/**/*.png +*.jld2 .gitattributes Manifest.toml -build.log +# the docs figure-provenance manifest is not a Julia Manifest.toml (case-insensitive FS collides) +!docs/src/figures/manifest.toml +*.log docs/build/ -anaconda_projects/ \ No newline at end of file +anaconda_projects/ +modovmc +pestotv +*.csv +regression-harness/*.sqlite +regression-harness/*.sqlite-shm +regression-harness/*.sqlite-wal +scratch/ +.claude/worktrees/ +.claude/agent-memory/ + +# Local profiling scratch (one-off study scripts, not part of the package) +profiling/ + +# Override-case rundirs materialized beside their example by the regression harness +.regress-override-* + +# Per-developer package preferences (e.g. the precompile_workload switch) +LocalPreferences.toml + +# Python bytecode from the CI convention scripts +__pycache__/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..bfa26d1d7b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,81 @@ +# Pre-commit hooks for GPEC repository +# Install: pip install pre-commit +# Setup: pre-commit install +# Run manually: pre-commit run --all-files + +default_language_version: + python: python3.10 + +repos: + # Jupyter notebook cleaning - removes outputs, execution counts, metadata + - repo: https://github.com/kynan/nbstripout + rev: 0.7.1 + hooks: + - id: nbstripout + name: Strip notebook outputs + args: + - --extra-keys + - | + metadata.kernelspec metadata.language_info.version + cell.metadata.executionInfo cell.metadata.colab + cell.metadata.id cell.metadata.scrolled + + # General file cleanup + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + name: Trim trailing whitespace + - id: end-of-file-fixer + name: Fix end of files + - id: check-yaml + name: Check YAML syntax + - id: check-toml + name: Check TOML syntax + - id: check-added-large-files + name: Prevent large files + args: ['--maxkb=5000'] + - id: mixed-line-ending + name: Fix line endings + args: ['--fix=lf'] + + # Julia code formatting + - repo: https://github.com/domluna/JuliaFormatter.jl + rev: v1.0.62 + hooks: + - id: julia-formatter + name: Format Julia code + + # Config-style TOML annotation conventions (docs/development/toml-conventions.md). + # pygrep hooks fail when the pattern matches, so each entry matches a violation. + - repo: local + hooks: + # Keeps the Area table in docs/development/naming.md matching src/ on disk, + # since the commit and PR title vocabulary is derived from it. + - id: naming-table-in-sync + name: 'Naming conventions: Area table matches src/' + language: system + entry: python3 ci/conventions/check_subject.py --check-table docs/development/naming.md + pass_filenames: false + files: ^(src/|docs/development/naming\.md$) + - id: toml-header-block + name: 'TOML conventions: file starts with a # header block' + language: pygrep + entry: '\A(?!#[^\n]*\n#)' + args: [--multiline] + files: ^(examples/.*\.toml|test/test_data/.*\.toml|regression-harness/cases/.*\.toml)$ + - id: toml-no-decorative-dividers + name: 'TOML conventions: no decorative comment dividers' + language: pygrep + entry: '^#\s*[-=*_~#]{6,}\s*$' + files: ^(examples/.*\.toml|test/test_data/.*\.toml|regression-harness/cases/.*\.toml)$ + - id: toml-inline-annotations + name: 'TOML conventions: every variable line has a # description' + language: pygrep + entry: '^[A-Za-z0-9_]+\s*=\s*[^#]*$' + files: ^(examples/.*\.toml|test/test_data/.*\.toml)$ + - id: toml-no-deprecated-keys + name: 'TOML conventions: no deprecated config keys' + language: pygrep + entry: '^(mer_flag|force_wv_symmetry|ode_flag|cyl_flag|mat_flag|reform_eq_with_psilim|use_riccati|use_parallel|parallel_threads|populate_dense_xi|gal_flag|nstep|diagnose_ca|diagnose|ksing|power_bp|power_b|power_r|power_rc|use_galgrid|moment|eq_out|theta_out|xlmda_out|eqpsi_out|save_records|gpec_file|fnml_flag|ellip_flag|diag_flag|force_xialpha|fixed_boundary|output_eigenmodes|filter_modes|singular_point_method|compute_response|local_stability_flag|gal_solver|gal_sing_order|gap_kHz_threshold|force_termination|sing_start|HDF5_filename|write_outputs_to_HDF5|output_filename|kinetic_source|nn_low|nn_high|gal_[A-Za-z0-9_]+|output_benchmark_data|ion_charge|ion_mass|chi_perp_e|chi_perp_i|chi_tor|profile_file|profile_group)\s*=|^\[(DEBUG|SOL_INPUT|LAR_INPUT|TJ_ANALYTIC_INPUT|SLAYER\.scan_grid|SLAYER\.CriticalResonantField)\]|^eq_type\s*=\s*"(sol|lar|tj_analytic|tj_analytic_direct)"' + files: ^(examples/.*\.toml|test/test_data/.*\.toml)$ diff --git a/AGENTS.md b/AGENTS.md new file mode 120000 index 0000000000..681311eb9c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..0cca01e7fe --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,29 @@ +# Changelog + +All notable changes to GPEC are recorded here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project uses +[semantic versioning](https://semver.org/spec/v2.0.0.html). + +Entries are compiled from the `## Release note` block of each merged pull +request; see [`docs/development/naming.md`](docs/development/naming.md). Run +`/release-notes` to draft the next set rather than writing them by hand. + +Sections follow the commit TAG vocabulary. `Changed results & breaking changes` +comes first and collects every change whose title carried a `!`, whatever its +tag, because it is the section a user must read before upgrading. + +## Unreleased + +### Changed results & breaking changes + +### New capabilities + +### Bug fixes + +### Performance + +### Interface & format changes + +### Deprecations + +### Documentation diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000000..5fd096a183 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,151 @@ +cff-version: 1.2.0 +message: "If you use GPEC, please cite it as below, together with the papers behind the modules you use (listed under references and on the documentation's citations page)." +type: software +version: "2.0.0" +title: "GPEC: Generalized Perturbed Equilibrium Code" +abstract: "GPEC is an open-source Julia reimplementation of the Fortran GPEC suite (DCON, VACUUM, STRIDE, GPEC, PENTRC and SLAYER), consolidated into one code base of cooperating physics modules behind a unified, modern interface. It models the response of tokamak plasmas to small non-axisymmetric magnetic perturbations: equilibrium reconstruction from EFIT, CHEASE or IMAS inputs; ideal MHD stability by Newcomb's criterion with the free-boundary vacuum response; resistive tearing stability from the outer-region Δ′ matrix matched to inner-layer models; the perturbed equilibrium driven by coils or error fields, with resonant flux, island widths and Chirikov overlap; and neoclassical toroidal viscosity torque. Applications include error-field correction, resonant magnetic perturbation optimization, kink and tearing stability, and parameter scans and integrated modeling at scale." +authors: + - given-names: Nikolas + family-names: Logan + - given-names: Jong-Kyu + family-names: Park + - given-names: Matthew + family-names: Pharr + - given-names: Jacob + family-names: Halpern + - given-names: Daniel + family-names: Burgess + - given-names: Jaebeom + family-names: Cho + - given-names: Jaymyoung + family-names: Lee + - given-names: Sunjae + family-names: Lee + - given-names: Evan + family-names: Bursch +repository-code: "https://github.com/OpenFUSIONToolkit/GPEC" +url: "https://openfusiontoolkit.github.io/GPEC/dev/" +license: MIT +keywords: + - plasma physics + - magnetohydrodynamics + - tokamak + - perturbed equilibrium + - Julia +references: + - type: article + title: "Vacuum calculations in azimuthally symmetric geometry" + authors: + - given-names: M. S. + family-names: Chance + journal: "Physics of Plasmas" + volume: 4 + issue: 6 + start: "2161" + year: 1997 + doi: "10.1063/1.872380" + - type: article + title: "The direct criterion of Newcomb for the ideal MHD stability of an axisymmetric toroidal plasma" + authors: + - given-names: A. H. + family-names: Glasser + journal: "Physics of Plasmas" + volume: 23 + issue: 7 + start: "072505" + year: 2016 + doi: "10.1063/1.4958328" + - type: article + title: "Computation of three-dimensional tokamak and spherical torus equilibria" + authors: + - given-names: Jong-Kyu + family-names: Park + - given-names: Allen H. + family-names: Boozer + - given-names: Alan H. + family-names: Glasser + journal: "Physics of Plasmas" + volume: 14 + issue: 5 + start: "052110" + year: 2007 + doi: "10.1063/1.2732170" + - type: article + title: "Computation of resistive instabilities by matched asymptotic expansions" + authors: + - given-names: A. H. + family-names: Glasser + - given-names: Z. R. + family-names: Wang + - given-names: J.-K. + family-names: Park + journal: "Physics of Plasmas" + volume: 23 + issue: 11 + start: "112506" + year: 2016 + doi: "10.1063/1.4967862" + - type: article + title: "A robust solution for the resistive MHD toroidal Δ′ matrix in near real-time" + authors: + - given-names: Alexander S. + family-names: Glasser + - given-names: Egemen + family-names: Kolemen + journal: "Physics of Plasmas" + volume: 25 + issue: 8 + start: "082502" + year: 2018 + doi: "10.1063/1.5029477" + - type: article + title: "Tearing stability prediction combining toroidal calculations with a two-fluid slab layer" + authors: + - given-names: Daniel A. + family-names: Burgess + - given-names: Nikolas C. + family-names: Logan + - given-names: Jong-Kyu + family-names: Park + - given-names: C. J. + family-names: Hansen + - given-names: C. + family-names: Paz-Soldan + journal: "Plasma Physics and Controlled Fusion" + volume: 68 + issue: 6 + start: "065017" + year: 2026 + doi: "10.1088/1361-6587/ae7156" + - type: article + title: "Neoclassical toroidal viscosity in perturbed equilibria with general tokamak geometry" + authors: + - given-names: Nikolas C. + family-names: Logan + - given-names: Jong-Kyu + family-names: Park + - given-names: Kimin + family-names: Kim + - given-names: Zhirui + family-names: Wang + - given-names: John W. + family-names: Berkery + journal: "Physics of Plasmas" + volume: 20 + issue: 12 + start: "122507" + year: 2013 + doi: "10.1063/1.4849395" + - type: article + title: "Self-consistent perturbed equilibrium with neoclassical toroidal torque in tokamaks" + authors: + - given-names: Jong-Kyu + family-names: Park + - given-names: Nikolas C. + family-names: Logan + journal: "Physics of Plasmas" + volume: 24 + issue: 3 + start: "032505" + year: 2017 + doi: "10.1063/1.4977898" diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..cbd8e2cad2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,173 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +GPEC (Generalized Perturbed Equilibrium Code, Julia implementation) is a comprehensive Julia reimplementation of the GPEC suite for MHD analysis of fusion plasmas. The code performs equilibrium reconstruction, ideal MHD stability analysis, and perturbed equilibrium calculations including plasma response and singular surface coupling diagnostics. + +**Relationship to Fortran GPEC**: This Julia GPEC is an evolution of the Fortran GPEC code suite, available at https://github.com/PrincetonUniversity/GPEC. When users reference "the Fortran code", "the original GPEC", "Fortran GPEC", or "legacy VACUUM", they are referring to that Fortran codebase — not a runtime dependency of this Julia implementation. This Julia implementation reimplements and extends GPEC's functionality with improved performance and maintainability. + +**Local GPEC Repository**: For code conversion or comparison with the original Fortran implementation, a local clone of the Fortran GPEC repository is needed; ask the user for its path. + +## Key References + +**IMPORTANT**: The papers in `docs/resources/` provide the theoretical foundation for GPEC's algorithms. **Citing equations from these papers in code comments and annotations is strongly encouraged** to maintain traceability between theory and implementation. The full per-module citation list (Vacuum, ForceFreeStates, PerturbedEquilibrium, KineticForces/NTV, resistive MHD future work) is in **[`docs/development/references.md`](docs/development/references.md)** — read it before touching a physics kernel. + +## Common Commands + +### Building and Testing + +```bash +# Run all tests +julia --project=. -e 'using Pkg; Pkg.activate("."); Pkg.instantiate(); include("test/runtests.jl")' + +# Run specific test file +julia --project=. test/runtests.jl test/runtests_solovev.jl + +# Available test files: + +# - test/runtests_vacuum_julia.jl # Julia vacuum module +# - test/runtests_solovev.jl # Analytical equilibrium +# - test/runtests_ode.jl # ODE integration +# - test/runtests_sing.jl # Singular surface handling +# - test/runtests_fullruns.jl # End-to-end tests +``` + +### Building Documentation + +```bash +# Build documentation locally +julia --project=. build_docs_local.jl + +# Documentation hosted at: https://openfusiontoolkit.github.io/GPEC/dev/ +``` + +### Development with Revise + +For faster recompilation during development, use Revise.jl (installed in global environment, not in Project.toml): + +```julia +using Revise +using GeneralizedPerturbedEquilibrium +``` + +### Benchmarking + +Use the generic benchmarking tool at `benchmarks/benchmark_git_branches.jl` to compare performance between branches or commits (default case: `examples/DIIID-like_ideal_example`). Full usage, flags, and benchmark-script conventions (never duplicate example inputs into `benchmarks/`; outputs go under `benchmarks/`, not committed) are in **[`docs/development/benchmarking.md`](docs/development/benchmarking.md)**. + +### Regression Harness + +***This should be used at least once every single pull request before merging into develop. This test harness is what tracks values as they evolve across changes to the code, and must be both kept up to date and used consistently. Do not forget this and make sure to suggest any new regression cases or updates to existing ones as needed. Remind the user of its existence and report back the output regression report you get when modifying the code significantly. This is extremely important, do not forget this tidbit.*** + +```bash +alias regress='julia --project=regression-harness regression-harness/regress.jl' +regress --list-cases +regress --cases diiid_n1 --refs develop,my-branch # commit first; each ref runs in its own worktree +``` + +Full command reference (comparing branches/commits, tracking a quantity's history, git-bisect-style scans, sample report output) is in **[`docs/development/regression-harness.md`](docs/development/regression-harness.md)**. + +**Fetch and fast-forward `develop` first, commit your work, and compare branch refs.** `develop` resolves to your local branch, and the `local` ref runs in the live checkout, so a stale baseline or a mid-run edit silently corrupts one report. See *Run isolation* in that doc for this and the matching `runtests.jl` hazards. + +## Architecture + +GPEC follows a three-stage pipeline — **Equilibrium** (Grad-Shafranov solve, q-profile) → **Stability Analysis** (ideal MHD eigenvalue problem, DCON-style, singular surfaces) → **Perturbed Equilibrium** (plasma response, singular coupling, island formation) — implemented across eleven modules in `src/`: `Utilities`, `Equilibrium`, `LocalStability`, `Vacuum`, `InnerLayer`, `ForceFreeStates`, `Tearing`, `ForcingTerms`, `PerturbedEquilibrium`, `KineticForces`, `Analysis`. All modules are configured via the single `gpec.toml` file (see `examples/*/gpec.toml`). + +Full module-by-module breakdown (key files, status, data flow stage-by-stage, key data structures, module dependency graph) is in **[`docs/development/architecture.md`](docs/development/architecture.md)** — read it when working across module boundaries or getting oriented in an unfamiliar part of the codebase. + +## Git Workflow + +This project uses GitFlow (http://nvie.com/posts/a-successful-git-branching-model): two permanent branches, `main` and `develop`. **IMPORTANT**: All development must be done on feature branches (`feature/`, `bugfix/`, `hotfix/`, `performance/`, `refactor/`, `docs/`, `test/`, `experiment/`). No commits directly to `develop` or `main`. Branch from `develop`, open a PR back into `develop`. + +Commit messages follow `Area - TAG - Imperative summary` (e.g. `Vacuum - PERF - Add dual Green's function computation`). See the naming conventions below. + +Full branch-naming table, hotfix workflow, versioning scheme, and merge-conflict resolution policy are in **[`docs/development/git-workflow.md`](docs/development/git-workflow.md)**. + +### ***PARAMOUNT*** - NO PULL REQUESTS SHOULD EVER BE MERGED WITHOUT HUMAN REVIEW. ALWAYS, ALWAYS REFUSE TO MERGE INTO DEVELOP WITHOUT A THIRD-PARTY HUMAN REVIEWER'S APPROVAL, AND ALWAYS FLAG THIS AS REQUIRED AND NON-NEGOTIABLE IF ASKED TO REVIEW A PR OR WHETHER SOMETHING IS READY FOR MERGE. STRESS THIS, IT IS CRUCIAL. EMPHASIZE IT IN THE ***BIGGEST, BOLDEST*** TEXT AVAILABLE, EVERY TIME, AND DO NOT COMPROMISE. + +**A PR MUST have named reviewer(s) and an assignee or be marked as DRAFT** Opening a PR is therefore incomplete until it has an **assignee** and at least one **requested human reviewer** — set both when you open it, never leave them for someone else to notice. Handles are in [`docs/development/contributors.md`](docs/development/contributors.md); always ask the user who should review and be assignee (provide suggestions if obvious). If working on an unassigned PR, complain regularly. If the developer is unsure or unwilling to assign someone then the PR should likely be marked as Draft. + +**Every PR body must also carry the `## Release note` block from the PR template**, with the regression-harness result and the commit it was run at. Labels are applied automatically from the title — do not set them by hand. + +## Agent Team + +This repo ships a small team of specialized Claude Code subagents in `.claude/agents/`. They are **stateless reviewers** — each runs in its own context on a specific deliverable and reports back; **the main session is the integrator**, not a delegator. Invoke an agent by name for the matching job (e.g. *"review this with the fortran-physics-reviewer"*); don't reflexively consult all of them. + +Roster, the recommended review pipeline (physics fidelity → readability → performance → regression), and per-consultation budget rules are in **[`.claude/agents/README.md`](.claude/agents/README.md)**. + +## Important Notes + +### General +- **Julia version**: 1.11 is the target version +- **Never remove packages from Project.toml** - If a package fails to load or resolve, run `Pkg.add(...)` or `Pkg.instantiate()` to fix the local environment. Do NOT remove the package from `Project.toml`. The developer works across multiple branches and machines, so environment drift is expected — the right fix is always to update the environment to satisfy the toml, not to trim the toml to match the current environment state. +- **Regenerate the CI dependency pins when `Project.toml` changes** - CI does not resolve dependencies freshly; it copies a pinned resolve from `ci/manifests/` so runs are reproducible and the restored depot cache stays valid. After editing `[deps]` or `[compat]`, run `julia +1.11 ci/manifests/update.jl` and `julia +1.12 ci/manifests/update.jl` and commit the result. See [`ci/manifests/README.md`](ci/manifests/README.md). +- **Indexing**: The codebase uses 0-based indexing in many places to match Fortran conventions, then converts to 1-based Julia indexing +- **No step numbering in code comments** - Avoid annotations like "Step 1: do this" followed by "Step 2: do that". These get out of sync as code changes. Just describe the action without numbering. +- **Deck and API parity** - Every capability must be reachable from both a `gpec.toml` deck (via `main`) and the scripting API (`solve` and the problem/result types). Every new feature, deck key or API keyword adds a case to the `"deck and API parity"` testset in `test/runtests_solve_api.jl`, which runs the workflow from a deck and from the API and checks that the results agree. Mark a known drift `@test_broken`; don't drop it. See `docs/src/developer_notes.md`. +- **Documentation coverage** - When adding a new module or submodule with public docstrings, add a corresponding `@autodocs` block in `docs/src/`. Documenter CI will fail with a `missing_docs` error if any exported docstring is not covered. The analysis submodule docs live in `docs/src/analysis.md`. +- **Docstrings are rendered as Markdown** - Documenter parses docstrings as CommonMark, so `[text](...)` patterns become hyperlinks and will fail CI with `invalid local link/image` if the target doesn't exist. Common pitfall: unit annotations like `[degrees] (or [m] if ...)` parse as `[degrees](or [m] if ...)` — a broken link. Use plain words (`in degrees`) or backticks (`` `[m]` ``) for unit labels inside docstrings. Same rule for bracketed array-shape hints (`[ncoil]`) followed by parentheses. When in doubt, preview with `julia --project=docs docs/make.jl` before pushing. +- **Keep code comments concise** - A comment should be one line where possible. Do not write multi-line block comments explaining the current session's investigation, what was tried, what was wrong before, or why a specific file/path behaves differently. State what the code does and why at a general level. Example of too much detail: a 6-line block explaining that efit_by_inversion uses psilow>0 while CHEASE starts at 0, that the old code was removed, and that spline spikes result. Preferred: `# Replicate Fortran inverse.f: overwrite deta at axis (r²=0) by extrapolating from innermost surfaces.` +- **No PR/issue references in source** - Do not cite specific GitHub PR or issue numbers (e.g. `#171`, `#202`) in source or test comments/docstrings. During dynamic development these references quickly go stale and pollute the code. Just concisely state the objective or what the code does; the git history and PR threads track the provenance. +- **Document struct fields in the docstring, not inline** - When adding a field to a struct, put its description in the struct's `## Fields` docstring rather than (or in addition to) a trailing inline comment, and document any non-obvious contract (e.g. which keys a `Dict`/ingest carries per variant). Do not annotate outer constructors with positional-argument counts ("passes 11 positional args") — those rot when a field is added; describe the intent instead. + +### Minimal-change discipline +- **Reuse native ops and existing utilities before writing new ones.** FastInterpolations splines integrate and differentiate natively (`integrate`, `cumulative_integrate`, `deriv1`); the Equilibrium module already has flux-surface integration/average patterns. Do not reimplement spline integration, quadrature, or differentiation — grep for the existing idiom first. +- **Size the change to the problem.** A small numerical correction (e.g. a ~1% fix) should be a handful of lines, not new general-purpose machinery. Resist faithfully porting Fortran scaffolding (custom integrators, power-law spline bases) when a native call plus a one-line correction gives the same numbers — verify equivalence instead of assuming the elaborate version is needed. +- **Replacing a solver with its closed form is a reduction, not new machinery.** The rule above targets ported scaffolding, not exact methods. Where the data structure already determines the answer — a spline cell is a cubic, so a level set of it is a root formula and the stationary points are roots of a quadratic — writing that solution and deleting the iterative solver removes machinery even when the line count rises. Justify it with the residual, not the line count. +- **Don't commit throwaway artifacts for minor fixes.** No in-repo benchmark scripts/outputs or agent-memory churn for a small change — these accumulate and outsize `src`. Verify with a scratch script (e.g. under `/tmp`) and the regression harness; the regression harness is the durable record of numerical behavior. + +### Output Files +- **Default output**: `gpec.h5` (previously `euler.h5` in older versions) + +### Plotting +Spectrum-plot (`seriestype=:steppre` / `step_series`) and figure-saving conventions are in **[`docs/development/plotting.md`](docs/development/plotting.md)** — read before writing any plotting code. + +### Subagent Consultations + +When delegating to specialized agents (julia-performance-optimizer, fast-interpolations-optimizer, fortran-physics-reviewer, clean-code-reviewer, etc.), every prompt **must include an explicit budget** because runaway agents silently consume the user's daily token quota. A single consultation that explores instead of editing has been measured at 167 tool calls / 55 minutes / no return — that is a session-killer. Defaults: + +- **Hard cap: ≤ 30 tool uses and ≤ 10 minutes wall time per consultation.** State both numbers in the prompt verbatim ("Budget: ≤30 tool uses, ≤10 min"). +- **Single concrete deliverable.** One file, one function, or one named hotspot list. Not "audit the module." +- **No exploration phase.** The prompt must hand the agent the file paths and line numbers; the agent's job is to edit, not to map the codebase. +- **Require an interim status if the work might exceed budget.** Tell the agent: "If you cannot finish within budget, stop and report what was changed and what remains." +- **Prefer two short focused agents over one open-ended one.** If an investigation needs both performance and interpolation review, run them sequentially with separate ≤30-tool budgets — don't chain them in one long prompt. +- **Never re-launch a runaway agent.** If an agent hits the API rate limit before returning, do not retry; report the partial state to the user and switch to hand-implementation. + +These rules apply to **every** Agent tool invocation, not just performance work. + +### Code Formatting + +Pre-commit hooks enforce formatting via JuliaFormatter and general file hygiene, and the same hooks run as the `pre-commit` CI check on every pull request. The hook runs the JuliaFormatter in the default Julia environment; CI pins **1.0.62** (`.pre-commit-config.yaml`, `.github/workflows/format.yaml`), so install exactly that version or the formatting will differ. The repository is clean under the hooks, so a file that changes when you run them changed because of your edit. **All code you write or modify must already conform to these standards before committing**, so the hooks have nothing to fix. Failing to do this creates noisy diffs in PRs where formatting changes leak into unrelated files. + +The project's `.JuliaFormatter.toml` settings: +- **Line width**: 180 characters max (`margin = 180`) +- **`for` loops**: always use `in` (not `=` or `∈`) +- **Keyword arguments**: no spaces around `=` in kwargs (`f(x; a=1)` not `f(x; a = 1)`) +- **Keyword separator**: use semicolons to separate kwargs (`f(x; a=1, b=2)`) +- **No trailing commas** in argument lists +- **Docstrings**: formatted according to JuliaFormatter rules +- **No extra blank line removal**: `remove_extra_newlines = false` +- **Join short lines**: `join_lines_based_on_source = true` — don't arbitrarily split lines that fit within the margin + +Additional file hygiene (enforced by pre-commit hooks): +- No trailing whitespace on any line +- Files must end with exactly one newline +- LF line endings only (no CRLF) + +### Naming and Commit Conventions + +Commit subjects, PR titles, and issue titles share one grammar: `Area[.Submodule] - TAG[!] - Imperative summary`. `Area` and `TAG` are closed vocabularies. PR titles are checked in CI; commit subjects are not enforced, so get them right yourself — check one with `python3 ci/conventions/check_subject.py --title "..."`, which prints the correct replacement for any bad token. Append `!` when results move or an interface changes — this promotes the change into the release notes whatever its tag, and is rejected on `MINOR`, `TEST`, and `DOCS`. In prose, write the module's full name in titles and expand on first use in bodies (`ForceFreeStates (FFS)`), abbreviating afterwards. **Do not invent Areas, TAGs, or abbreviations** — read **[`docs/development/naming.md`](docs/development/naming.md)** before writing a commit message or opening a PR. + +### HDF5 Output Conventions + +The `gpec.h5` schema follows one physics-first convention (CamelCase groups at all levels, snake_case datasets, data-driven tokens verbatim, inputs only under `Input/`, five named top-level physics-topic exceptions). **Do not invent new group names or echo inputs into output groups** — read **[`docs/development/hdf5-conventions.md`](docs/development/hdf5-conventions.md)** before adding or moving any HDF5 output; renames are clean breaks (update writer, readers, and harness case TOMLs together — there is no legacy-path shim). + +### TOML Annotation Conventions + +Config-style TOML files (`examples/*/gpec.toml`, `examples/*/sol.toml`, `test/test_data/*` fixtures, `regression-harness/cases/*.toml`) follow one shared annotation style (header comment block, inline `# description` on every variable line sourced from the matching config struct's docstring, no Fortran references, no deprecated variables). **Do not invent a new convention** — read **[`docs/development/toml-conventions.md`](docs/development/toml-conventions.md)** in full before adding or editing one of these files. + +### Performance +- Pure Julia implementations are available for all major components and offer comparable or better performance than Fortran +- Benchmarks available in `benchmark/` directory for Fourier transforms and vacuum calculations +- Pre-commit hooks are configured for notebook cleaning and Julia formatting (see `docs/src/set_up.md` for developer setup) diff --git a/GEMINI.md b/GEMINI.md new file mode 120000 index 0000000000..681311eb9c --- /dev/null +++ b/GEMINI.md @@ -0,0 +1 @@ +CLAUDE.md \ No newline at end of file diff --git a/LICENSE b/LICENSE index 52828e1e93..1aac5f2b03 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 Columbia Fusion Research Center +Copyright (c) 2025 Columbia Fusion Research Center and Seoul National University Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/LICENSES/Apache-2.0.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000000..00fec88f12 --- /dev/null +++ b/NOTICE @@ -0,0 +1,33 @@ +GPEC +Copyright (c) 2025 Columbia Fusion Research Center and Seoul National University +Licensed under the MIT License (see LICENSE), except for the files listed below. + +This software includes code derived from BIEST (Boundary Integral Equation +Solver for Taylor states), https://github.com/dmalhotra/BIEST. +Copyright (C) 2018-2026 The Simons Foundation, Inc. and the BIEST authors: +Dhairya Malhotra (lead author). Licensed under the Apache License, Version 2.0 +(see LICENSES/Apache-2.0.txt). + +Ports and translations of the BIEST code to other programming languages, +whether made by hand or with automated tools, are derived from this work. The +authors of the BIEST code remain authors of every part derived from it, and +Section 4 of the Apache License requires distributions of such works to retain +this notice and the notices in the file headers. + +Request from the BIEST authors: + - Cite BIEST and the publications describing it (see + https://github.com/dmalhotra/BIEST/blob/master/CITATION.cff) in work + that uses it. + - A port or translation of BIEST as a whole, or of a substantial part of it, + lists the authors named in the headers of the files it derives from in + its own author list, credited for the original implementation, just as + a translated book keeps its author and credits its translator. Smaller + reuse credits them as third-party code. + - Do not use the BIEST code to generate automated ports or clones without + substantive human oversight and understanding of its design. + - Do not use the BIEST code or its documentation to train AI models. + +Derived files: + src/Vacuum/Kernel3D.jl Translated to Julia and modified from BIEST + include/biest/singular_correction.hpp, + include/biest/surface_op.txx diff --git a/Project.toml b/Project.toml index 534bca042b..b9abee2106 100644 --- a/Project.toml +++ b/Project.toml @@ -1,31 +1,75 @@ -name = "JPEC" +name = "GeneralizedPerturbedEquilibrium" uuid = "462872dd-e066-4d2e-b993-6468b5239634" -authors = ["Rithik Banerjee ", "Dylan Brennan ", "Daniel Burgess ", "Evan Bursch ", "Jaebeom Cho ", "Gwanggeun Seo ", "Junyoung Jang ", "Jaymyoung Lee ", "Jong-kyu Park ", "Meg Fairborn ", "Aaron Froese ", "Jacob Halpern ", "Sunjae Lee ", "Nikolas Logan ", "Brendan C. Lyons ", "Matthew Pharr "] -version = "0.1.0" -license = 'MIT' +license = "MIT" +authors = ["Nikolas Logan ", "Jong-Kyu Park ", "Matthew Pharr ", "Jacob Halpern ", "Rithik Banerjee ", "Jaebeom Cho ", "Daniel Burgess ", "Min-Gu Yoo ", "Jaymyoung Lee ", "Sunjae Lee ", "Evan Bursch "] +version = "2.0.0" [deps] +AdaptiveArrayPools = "4f381ef7-9af0-4cbe-99d4-cf36d7b0f233" +CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +Contour = "d38c429a-6771-53c6-b99e-75d170b6e991" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" +DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df" +DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +DoubleFloats = "497a8b3b-efae-58df-a0af-a86822472b78" FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" +FastInterpolations = "9ea80cae-fc13-4c00-8066-6eaedb12f34b" HDF5 = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +IMASdd = "c5a45a97-b3f9-491c-b9a7-aa88c3bc0067" +JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +PlotlyJS = "f0f68f2c-4968-5e81-91da-67840de0976a" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" +QuadGK = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Roots = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" +StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" +Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [compat] +AdaptiveArrayPools = "0.3.5" +CommonSolve = "0.2" +Contour = "0.6.3" +DelaunayTriangulation = "1.6.6" +DelimitedFiles = "1.9.1" DiffEqCallbacks = "4.9.0" Documenter = "1.14.1" +DoubleFloats = "1.6.2" FFTW = "1.9.0" +FastGaussQuadrature = "1.1.0" +FastInterpolations = "0.4.20" HDF5 = "0.17.2" -LinearAlgebra = "1.11.0" +IMASdd = "8" +JLD2 = "0.6.3" +LaTeXStrings = "1.4.0" +LinearAlgebra = "1" +Logging = "1" OrdinaryDiffEq = "6.102.0" -Pkg = "1.11.0" +Pkg = "1" +PlotlyJS = "0.18.17" Plots = "1.40.15" -Printf = "1.11.0" -TOML = "1.0.3" -Test = "1.11.0" +PrecompileTools = "1.2" +Printf = "1" +QuadGK = "2.11.3" +Roots = "2.2.13" +SparseArrays = "1" +SpecialFunctions = "2.5.1" +Random = "1" +StaticArrays = "1.9.15" +Statistics = "1" +TOML = "1" +Test = "1" +julia = "1.11" diff --git a/README.md b/README.md index 3565bf03a1..46f7c6fc6c 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,39 @@ -# Perturbed Equilibrium +# GPEC — Generalized Perturbed Equilibrium Code -This is a work in progress juliafication of the Generalized Perturbed Equilibrium Code suite https://github.com/PrincetonUniversity/GPEC. +This Julia implementation of GPEC continues the legacy of the Fortran [GPEC suite](https://github.com/PrincetonUniversity/GPEC) for magnetohydrodynamic (MHD) analysis of fusion plasmas. The Fortran suite (DCON, VACUUM, STRIDE, GPEC, PENTRC and SLAYER) has long been used to model the response of tokamak plasmas to small non-axisymmetric magnetic perturbations, from error-field correction and resonant magnetic perturbation optimization to kink and tearing stability. This implementation consolidates them into one code base of cooperating physics modules that share data in memory behind a unified, modern interface, which removes the hand-offs between separate codes and makes parameter scans, uncertainty quantification and integrated modeling practical. -## Documentation Pages +One input deck runs the stages it names, in order, into one output file: -The WIP documentation can be found [here](https://openfusiontoolkit.github.io/JPEC/dev/). +1. **Equilibrium** — reads an EFIT or CHEASE reconstruction, an IMAS data dictionary or an analytic model, maps it to straight-field-line flux coordinates, and finds the rational surfaces q = m/n. +2. **Stability** — ideal MHD stability by Newcomb's criterion (the DCON integration, or the STRIDE Riccati and RDCON Galerkin formulations): the force-free states ξ(ψ,θ), the free-boundary kink energies with the VACUUM response, and the tearing stability matrix Δ′. +3. **Perturbed equilibrium** — the plasma response to external non-axisymmetric fields (RMPs, error fields, correction coils): resonant fields, island half-widths and the Chirikov overlap parameter. +4. **Torque, error fields and tearing** — the neoclassical toroidal viscosity torque (PENTRC), coil-tolerance and locking-risk analysis, and tearing growth rates and critical resonant fields from inner-layer models (SLAYER, GGJ). +Full documentation: [https://openfusiontoolkit.github.io/GPEC/dev/](https://openfusiontoolkit.github.io/GPEC/dev/) -## Developer Notes +## Quick Start -### GIT Workflow +GPEC is configured via a `gpec.toml` file. To run an analysis on a directory containing that file: -All developers need to use Vincent Driessen's [GitFlow](http://nvie.com/posts/a-successful-git-branching-model) workflow when editing the GPEC package. PLEASE READ THE ENTIRE POST. It is short, very thorough, and good for both experienced and new git users. - -The highlights are, - - There are two permanent branches: master and develop - - The master branch is only updated for at release ready stages - - New features should be developed in short-lived (days) branches coming off of and merging back to the development branch. - -Specific instructions are given in the link above as to exactly how to branch and merge these various branches. For example, the --no-ff option should be used when merging in order to keep branch histories. Just follow the examples and you wont go wrong! - -#### Using github - -Please see [this link](https://docs.google.com/document/d/1XAOTz1IV8ErZAAk-iSuEuddNOLB5XcoVZsAbPKRUUuA/edit?usp=sharing) for a guide on how to discuss your code development using github. +```bash +./gpec path/to/directory +``` -#### Commit messages +To use GPEC programmatically from Julia: -To assist with the process of compiling release notes, please confirm to the commit message format: -``` -CODE - TAG - Detailed message +```julia +using GeneralizedPerturbedEquilibrium +GeneralizedPerturbedEquilibrium.main(["path/to/directory"]) ``` -where CODE is EQUIL, DCON, VAC, etc. and TAGs are short descriptors of the type of commit. Example tags might be WIP (work in progress), MINOR, IMPROVEMENT, BUG FIX, NEW FEATURE, etc. Look through old commits for examples of what tags are commonly used. - -Again, this is currently used for the by-hand compilation of release notes. The tags thus need to be human readable but are not strictly inforced to be within some limited set. The objective is to allow a lead developer to skim through commits and pick out only the key new features / bug fixes / improvements to note in the release while not having to read all the work-in-progress or minor changes. -### Julia Tips +See [Setup](https://openfusiontoolkit.github.io/GPEC/dev/set_up/) for full installation instructions. -#### Revise +## Citing -When developing and recompiling code often, use Revise.jl to speed up the compile times by only recompiling what is impacted by the changed code. Integrate Revise.jl into your default Julia environment for use across projects, do not directly add it to the Project.toml of this particular project. To install Revise.jl in your global environment, open the Julia REPL using the `julia` command, enter the package manager by pressing `]`, and then `add Revise`: +If you use GPEC, please cite it as given in [`CITATION.cff`](CITATION.cff) (GitHub's "Cite this repository" button), along with the key papers behind the modules you use, listed on the [citations page](https://openfusiontoolkit.github.io/GPEC/dev/citations/). -Code +The 3D vacuum singular correction in `src/Vacuum/Kernel3D.jl` is derived from [BIEST](https://github.com/dmalhotra/BIEST) and licensed under Apache-2.0 (see [`NOTICE`](NOTICE) and [`LICENSES/Apache-2.0.txt`](LICENSES/Apache-2.0.txt)); its papers are listed with the [Vacuum module citations](https://openfusiontoolkit.github.io/GPEC/dev/citations/#3D-vacuum-singular-correction). The rest of GPEC is MIT-licensed. - $ julia - julia> ] - pkg> add Revise +## Contributing -Now, in the top of each Jupyter notebook, you can call `using Revise` at the top of any Jupyter notebook to speed up compile times as you develop and test. Better yet, set up your environement to [use Revise by default](https://timholy.github.io/Revise.jl/stable/config/#Using-Revise-by-default) \ No newline at end of file +The Git workflow, commit message conventions, and the regression-testing harness are documented in the [Developer Notes](https://openfusiontoolkit.github.io/GPEC/dev/developer_notes/). diff --git a/benchmarks/benchmark_against_fortran_run.jl b/benchmarks/benchmark_against_fortran_run.jl new file mode 100644 index 0000000000..87464dc8b5 --- /dev/null +++ b/benchmarks/benchmark_against_fortran_run.jl @@ -0,0 +1,1463 @@ +#!/usr/bin/env julia +""" +benchmark_against_fortran_run.jl - Compare Julia GPEC results against Fortran GPEC reference outputs + +Reads Fortran output NetCDF files from a GPEC Fortran example directory, builds a +matched Julia configuration, optionally runs Julia GPEC, then prints a side-by-side +comparison table. + +By default uses coil geometry files (harder test): Julia runs its own Biot-Savart +pipeline to produce the forcing, matching the Fortran coil.in configuration. A +Phi_x spectrum comparison is included in the printout before the PE comparison. + +# Usage + +```bash +# Full run with coil forcing (default) +julia --project=. benchmarks/benchmark_against_fortran_run.jl /path/to/DIIID_ideal_example --plot + +# Use Fortran Phi_coil directly as forcing (easier test) +julia --project=. benchmarks/benchmark_against_fortran_run.jl /path/to/DIIID_ideal_example --use-file-forcing + +# Skip Julia re-run (use existing gpec.h5 in bench dir) +julia --project=. benchmarks/benchmark_against_fortran_run.jl /path/to/DIIID_ideal_example --skip-run +``` + +# Arguments + +- `fortran_dir` : Path to Fortran example directory (contains equil.in, dcon.in, coil.in, *.nc) +- `--plot` : Generate comparison plots (saved to bench dir) +- `--skip-run` : Skip running Julia GPEC (assume gpec.h5 already exists) +- `--use-file-forcing` : Use Fortran Phi_coil directly as forcing.dat (bypasses coil pipeline) + +# Outputs (written to bench dir) + +- `gpec.toml` : Julia configuration matched to Fortran inputs +- `forcing.dat` : External forcing (file-forcing mode only) +- `` : Copy of equilibrium file +- `gpec.h5` : Julia GPEC output (unless --skip-run) +- `comparison_table.txt` : Side-by-side comparison (also printed to stdout) +- `comparison_plots.png` : Plots (if --plot) +""" + +using Dates +using HDF5 +using NCDatasets +using Plots +using Printf +using Statistics + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.ForcingTerms +using GeneralizedPerturbedEquilibrium.Equilibrium +import GeneralizedPerturbedEquilibrium.Analysis.PerturbedEquilibriumModes + +# ─── Argument parsing ──────────────────────────────────────────────────────── + +function parse_args(args) + fortran_dir = "" + do_plot = false + skip_run = false + use_file_forcing = false + i = 1 + while i <= length(args) + a = args[i] + if a == "--plot" + do_plot = true + elseif a == "--skip-run" + skip_run = true + elseif a == "--use-file-forcing" + use_file_forcing = true + elseif !startswith(a, "--") + fortran_dir = a + else + error("Unknown argument: $a") + end + i += 1 + end + isempty(fortran_dir) && error( + "Usage: benchmark_fortran.jl [--plot] [--skip-run] [--use-file-forcing]") + return (fortran_dir=abspath(fortran_dir), do_plot=do_plot, + skip_run=skip_run, use_file_forcing=use_file_forcing) +end + +# ─── Fortran namelist parsing ───────────────────────────────────────────────── + +function _strip_fortran_comments(text::String) + join([split(line, '!')[1] for line in split(text, '\n')], '\n') +end + +function _find_scalar(text::String, key::String; default=nothing) + m = match(Regex("\\b$(key)\\s*=\\s*([+-]?[\\d.]+(?:[eE][+-]?\\d+)?)", "i"), text) + isnothing(m) ? default : parse(Float64, m.captures[1]) +end + +function _find_int(text::String, key::String; default::Int=0) + v = _find_scalar(text, key; default=Float64(default)) + isnothing(v) ? default : Int(v) +end + +function _find_string(text::String, key::String; default::String="") + m = match(Regex("\\b$(key)\\s*=\\s*\"([^\"]+)\"", "i"), text) + isnothing(m) ? default : strip(m.captures[1]) +end + +function _find_indexed_string(text::String, name::String, i::Int; default::String="") + m = match(Regex("\\b$(name)\\s*\\(\\s*$(i)\\s*\\)\\s*=\\s*\"([^\"]+)\"", "i"), text) + isnothing(m) ? default : strip(m.captures[1]) +end + +function _find_indexed2_float(text::String, name::String, i::Int, j::Int; default=nothing) + m = match(Regex( + "\\b$(name)\\s*\\(\\s*$(i)\\s*,\\s*$(j)\\s*\\)\\s*=\\s*([+-]?[\\d.]+(?:[eE][+-]?\\d+)?)", "i"), + text) + isnothing(m) ? default : parse(Float64, m.captures[1]) +end + +# ─── Fortran run parameter struct ───────────────────────────────────────────── + +struct FortranRunParams + eq_type::String + eq_filename::String + jac_type::String + grid_type::String + psilow::Float64 + psihigh::Float64 + mpsi::Int + mtheta_equil::Int + nn::Int + delta_mlow::Int + delta_mhigh::Int + dmlim::Float64 + qlow::Float64 + psiedge::Float64 + mtheta_coil::Int + nzeta_coil::Int + machine::String + coil_names::Vector{String} + coil_currents::Vector{Vector{Float64}} + euler_stride::Int +end + +function parse_fortran_run(dir::String) + eq_text = _strip_fortran_comments(read(joinpath(dir, "equil.in"), String)) + dcon_text = _strip_fortran_comments(read(joinpath(dir, "dcon.in"), String)) + coil_text = isfile(joinpath(dir, "coil.in")) ? + _strip_fortran_comments(read(joinpath(dir, "coil.in"), String)) : "" + + eq_type = _find_string(eq_text, "eq_type"; default="efit") + eq_file = _find_string(eq_text, "eq_filename"; default="") + jac_type = _find_string(eq_text, "jac_type"; default="hamada") + grid_type = _find_string(eq_text, "grid_type"; default="ldp") + psilow = _find_scalar(eq_text, "psilow"; default=1e-4) + psihigh = _find_scalar(eq_text, "psihigh"; default=0.993) + mpsi = _find_int(eq_text, "mpsi"; default=128) + mtheta_eq = _find_int(eq_text, "mtheta"; default=256) + + nn = _find_int(dcon_text, "nn"; default=1) + delta_mlow = _find_int(dcon_text, "delta_mlow"; default=8) + delta_mhigh = _find_int(dcon_text, "delta_mhigh"; default=8) + dmlim = _find_scalar(dcon_text, "dmlim"; default=0.2) + qlow = _find_scalar(dcon_text, "qlow"; default=1.02) + psiedge = _find_scalar(dcon_text, "psiedge"; default=1.0) + euler_stride = _find_int(dcon_text, "euler_stride"; default=1) + + mtheta_coil = _find_int(coil_text, "cmtheta"; default=480) + nzeta_coil = _find_int(coil_text, "cmzeta"; default=40) + machine = _find_string(coil_text, "machine"; default="d3d") + coil_num = _find_int(coil_text, "coil_num"; default=0) + + coil_names = String[] + coil_currents = Vector{Float64}[] + for ci in 1:coil_num + name = _find_indexed_string(coil_text, "coil_name", ci; default="coil$ci") + currents = Float64[] + for cj in 1:48 + cur = _find_indexed2_float(coil_text, "coil_cur", ci, cj) + isnothing(cur) && break + push!(currents, cur) + end + push!(coil_names, name) + push!(coil_currents, currents) + end + + return FortranRunParams(eq_type, eq_file, jac_type, grid_type, psilow, psihigh, mpsi, + mtheta_eq, nn, delta_mlow, delta_mhigh, dmlim, qlow, psiedge, + mtheta_coil, nzeta_coil, machine, coil_names, coil_currents, euler_stride) +end + +# ─── Load Fortran NC outputs ───────────────────────────────────────────────── + +function load_fortran_outputs(fortran_dir::String, nn::Int) + dcon_file = joinpath(fortran_dir, "dcon_output_n$(nn).nc") + profile_file = joinpath(fortran_dir, "gpec_profile_output_n$(nn).nc") + control_file = joinpath(fortran_dir, "gpec_control_output_n$(nn).nc") + + isfile(dcon_file) || error("Not found: $dcon_file") + isfile(profile_file) || error("Not found: $profile_file") + isfile(control_file) || error("Not found: $control_file") + + fort = Dict{String,Any}() + + NCDataset(dcon_file, "r") do ds + fort["psilim"] = Float64(ds.attrib["psilim"]) + fort["qlim"] = Float64(ds.attrib["qlim"]) + fort["mlow"] = Int(ds.attrib["mlow"]) + fort["mhigh"] = Int(ds.attrib["mhigh"]) + fort["mpert"] = Int(ds.attrib["mpert"]) + + fort["psi_n_dcon"] = Float64.(ds["psi_n"][:]) + fort["q_dcon"] = Float64.(ds["q"][:]) + fort["di"] = Float64.(ds["di"][:]) + fort["dr"] = Float64.(ds["dr"][:]) + fort["m_vals"] = Int.(ds["m"][:]) + + wt_ev = Float64.(ds["W_t_eigenvalue"][:, :]) + fort["W_t_eigenvalue"] = complex.(wt_ev[:, 1], wt_ev[:, 2]) + end + + NCDataset(profile_file, "r") do ds + fort["psi_n_rational"] = Float64.(ds["psi_n_rational"][:]) + fort["q_rational"] = Float64.(ds["q_rational"][:]) + + phi_res = Float64.(ds["Phi_res"][:, :]) + fort["Phi_res"] = complex.(phi_res[:, 1], phi_res[:, 2]) + + delta = Float64.(ds["Delta"][:, :]) + fort["Delta"] = complex.(delta[:, 1], delta[:, 2]) + + fort["w_isl"] = Float64.(ds["w_isl"][:]) + fort["K_isl"] = Float64.(ds["K_isl"][:]) + if haskey(ds, "I_res") + i_res_raw = Float64.(ds["I_res"][:, :]) + fort["I_res"] = complex.(i_res_raw[:, 1], i_res_raw[:, 2]) + else + fort["I_res"] = ComplexF64[] + end + + # radial profiles vs psi for all modes + fort["psi_n_prof"] = Float64.(ds["psi_n"][:]) + fort["m_out"] = Int.(ds["m_out"][:]) + b_n_raw = Float64.(ds["b_n"][:, :, :]) + fort["b_n"] = complex.(b_n_raw[:, :, 1], b_n_raw[:, :, 2]) + jbgp_raw = Float64.(ds["Jbgradpsi"][:, :, :]) + fort["Jbgradpsi"] = complex.(jbgp_raw[:, :, 1], jbgp_raw[:, :, 2]) + xigp_raw = Float64.(ds["xigradpsi"][:, :, :]) + fort["xigradpsi"] = complex.(xigp_raw[:, :, 1], xigp_raw[:, :, 2]) + xi_n_raw_f = Float64.(ds["xi_n"][:, :, :]) + fort["xi_n"] = complex.(xi_n_raw_f[:, :, 1], xi_n_raw_f[:, :, 2]) + + # Clebsch displacement profiles (for PENTRC benchmark) + if haskey(ds, "xigradpsi_dpsi") + xigp1_raw = Float64.(ds["xigradpsi_dpsi"][:, :, :]) + fort["xigradpsi_dpsi"] = complex.(xigp1_raw[:, :, 1], xigp1_raw[:, :, 2]) + else + fort["xigradpsi_dpsi"] = Matrix{ComplexF64}(undef, 0, 0) + end + if haskey(ds, "xigradalpha") + xiga_raw = Float64.(ds["xigradalpha"][:, :, :]) + fort["xigradalpha"] = complex.(xiga_raw[:, :, 1], xiga_raw[:, :, 2]) + else + fort["xigradalpha"] = Matrix{ComplexF64}(undef, 0, 0) + end + end + + NCDataset(control_file, "r") do ds + # Phi_coil: NC dim (i=2, coil_index=ncoil, m=mpert) + phi_coil = Float64.(ds["Phi_coil"][:, :, :]) + amp_real = dropdims(sum(phi_coil[:, :, 1]; dims=2); dims=2) + amp_imag = dropdims(sum(phi_coil[:, :, 2]; dims=2); dims=2) + fort["Phi_coil_amp"] = complex.(amp_real, amp_imag) + + phi_x = Float64.(ds["Phi_x"][:, :]) + fort["Phi_x_amp"] = abs.(complex.(phi_x[:, 1], phi_x[:, 2])) + fort["Phi_x"] = complex.(phi_x[:, 1], phi_x[:, 2]) + + phi_tot = Float64.(ds["Phi"][:, :]) + fort["Phi_tot"] = complex.(phi_tot[:, 1], phi_tot[:, 2]) + + fort["m_vals_ctrl"] = Int.(ds["m"][:]) + + # Control surface matrices (resp_index=0): NC shape (mode, mode, 2) in Julia + for (key, nc_var) in [("P", "P"), ("Lambda", "Lambda"), ("L_surf", "L"), ("rho", "rho")] + if haskey(ds, nc_var) + raw = Float64.(ds[nc_var][:, :, :]) # (mode, mode, 2) → last dim real/imag + fort[key] = complex.(raw[:, :, 1], raw[:, :, 2]) + else + fort[key] = Matrix{ComplexF64}(undef, 0, 0) + end + end + end + + # R,Z,φ profile data — store as theta-space complex arrays (from gpout_xbrzphifun) + # Fortran outputs (psi, theta+1, 2) where dim 3 = (real, imag) in machine φ + NCDataset(profile_file, "r") do ds + if haskey(ds, "xi_r") + ntheta = length(ds["theta_dcon"]) - 1 # drop duplicate endpoint + for (key, nc_var) in [("xi_r_fun", "xi_r"), ("xi_z_fun", "xi_z"), ("xi_phi_fun", "xi_phi"), + ("b_r_fun", "b_r"), ("b_z_fun", "b_z"), ("b_phi_fun", "b_phi")] + raw = Float64.(ds[nc_var][:, :, :]) # (psi, theta+1, 2) + fort[key] = complex.(raw[:, 1:ntheta, 1], raw[:, 1:ntheta, 2]) + end + end + end + + return fort +end + +# ─── Julia h5 loader ───────────────────────────────────────────────────────── + +function load_julia_outputs(h5_path::String) + isfile(h5_path) || error("Julia output not found: $h5_path") + + julia = Dict{String,Any}() + h5open(h5_path, "r") do f + julia["psilim"] = read(f, "Info/psilim") + julia["qlim"] = read(f, "Info/qlim") + julia["et"] = read(f, "ForceFreeStates/FreeBoundaryStability/eigenmode_energies") + julia["psi_q"] = read(f, "Equilibrium/Profiles/psi") + julia["q"] = read(f, "Equilibrium/Profiles/q") + julia["di"] = haskey(f, "LocalStability/D_I") ? read(f, "LocalStability/D_I") : Float64[] + julia["dr"] = haskey(f, "LocalStability/D_R") ? read(f, "LocalStability/D_R") : Float64[] + + julia["psio"] = haskey(f, "Equilibrium/psi_total") ? read(f, "Equilibrium/psi_total") : NaN + + sc = "PerturbedEquilibrium/SingularCoupling" + julia["rational_psi"] = haskey(f, "$sc/rational_psi") ? read(f, "$sc/rational_psi") : Float64[] + julia["rational_q"] = haskey(f, "$sc/rational_q") ? read(f, "$sc/rational_q") : Float64[] + julia["rational_n"] = haskey(f, "$sc/rational_n") ? read(f, "$sc/rational_n") : Int[] + julia["rational_m_res"] = haskey(f, "$sc/rational_m") ? read(f, "$sc/rational_m") : Int[] + julia["resonant_area_weighted_field"] = haskey(f, "$sc/resonant_area_weighted_field") ? read(f, "$sc/resonant_area_weighted_field") : ComplexF64[] + julia["resonant_current"] = haskey(f, "$sc/resonant_current") ? read(f, "$sc/resonant_current") : ComplexF64[] + julia["island_half_width"] = haskey(f, "$sc/island_half_width") ? read(f, "$sc/island_half_width") : Float64[] + julia["chirikov_parameter"] = haskey(f, "$sc/chirikov_parameter") ? read(f, "$sc/chirikov_parameter") : Float64[] + julia["delta_prime"] = haskey(f, "$sc/Delta_prime") ? read(f, "$sc/Delta_prime") : ComplexF64[] + + pe = "PerturbedEquilibrium" + # Fortran Phi_x/Phi_tot are the area-weighted field b̄ (tesla), matching forcing/response_b_area directly. + julia["forcing_vec"] = haskey(f, "$pe/forcing_b_area") ? read(f, "$pe/forcing_b_area") : ComplexF64[] # Phi_x = b̄_x + julia["response_vec"] = haskey(f, "$pe/response_b_area") ? read(f, "$pe/response_b_area") : ComplexF64[] # Phi_tot = b̄_tot + julia["b_n"] = haskey(f, "$pe/Response/b_n") ? read(f, "$pe/Response/b_n") : Matrix{ComplexF64}(undef, 0, 0) + julia["Jbgradpsi"] = haskey(f, "$pe/Response/b_psi_area_weighted") ? read(f, "$pe/Response/b_psi_area_weighted") : Matrix{ComplexF64}(undef, 0, 0) + julia["xi_psi"] = haskey(f, "$pe/Response/xi_psi") ? read(f, "$pe/Response/xi_psi") : Matrix{ComplexF64}(undef, 0, 0) + julia["xi_n"] = haskey(f, "$pe/Response/xi_n") ? read(f, "$pe/Response/xi_n") : Matrix{ComplexF64}(undef, 0, 0) + julia["clebsch_psi1"] = haskey(f, "$pe/Response/dxi_clebsch_psidpsi") ? read(f, "$pe/Response/dxi_clebsch_psidpsi") : Matrix{ComplexF64}(undef, 0, 0) + julia["clebsch_alpha"] = haskey(f, "$pe/Response/xi_clebsch_alpha") ? read(f, "$pe/Response/xi_clebsch_alpha") : Matrix{ComplexF64}(undef, 0, 0) + julia["psi_grid"] = haskey(f, "ForceFreeStates/Solutions/ForwardIntegration/psi") ? read(f, "ForceFreeStates/Solutions/ForwardIntegration/psi") : Float64[] + + # R,Z,φ: loaded via modes_to_theta helper below (not raw modes) + julia["h5_path"] = h5_path # stash for modes_to_theta + # mn_index[:, 1] = m values, mn_index[:, 2] = n values for each mode index + julia["m_modes"] = haskey(f, "Info/mn_index") ? Int.(read(f, "Info/mn_index")[:, 1]) : Int[] + + # Control surface matrices + rm = "$pe/ResponseMatrices" + julia["permeability"] = haskey(f, "$rm/permeability") ? read(f, "$rm/permeability") : Matrix{ComplexF64}(undef, 0, 0) + julia["plasma_inductance"] = haskey(f, "$rm/plasma_inductance") ? read(f, "$rm/plasma_inductance") : Matrix{ComplexF64}(undef, 0, 0) + julia["surface_inductance"] = haskey(f, "$rm/surface_inductance") ? read(f, "$rm/surface_inductance") : Matrix{ComplexF64}(undef, 0, 0) + julia["reluctance"] = haskey(f, "$rm/reluctance") ? read(f, "$rm/reluctance") : Matrix{ComplexF64}(undef, 0, 0) + fbs = "ForceFreeStates/FreeBoundaryStability" + julia["wt0"] = haskey(f, "$fbs/W_freeboundary") ? read(f, "$fbs/W_freeboundary") : Matrix{ComplexF64}(undef, 0, 0) + end + return julia +end + +# ─── Write forcing.dat (file-forcing mode only) ─────────────────────────────── + +""" +Write forcing.dat from Phi_coil amplitudes in unit-norm convention. + +Fortran Phi_coil is in unit-norm convention (= Julia's internal Phi_x). To +round-trip correctly through Julia's file loader (which applies ×(2π)² for +sfl_flux_Wb), we divide by (2π)² before writing and tag the file accordingly. +""" +function write_forcing_dat(filepath::String, n::Int, m_vals::Vector{Int}, amp::Vector{ComplexF64}) + scale = 1.0 / (2π)^2 + open(filepath, "w") do io + println(io, "# normalization: sfl_flux_Wb") + println(io, "# n m real(Phi_coil_sum / (2π)²) imag(Phi_coil_sum / (2π)²)") + for (m, a) in zip(m_vals, amp) + @printf(io, "%4d %4d %+.8e %+.8e\n", n, m, real(a) * scale, imag(a) * scale) + end + end +end + +# ─── Write gpec.toml (coil mode) ───────────────────────────────────────────── + +function write_gpec_toml_coil( + filepath::String, + fortran_dir::String, + p::FortranRunParams, + coil_dat_dir::String, + qhigh::Float64 +) + open(filepath, "w") do io + println(io, "# Auto-generated by benchmark_fortran.jl (coil forcing mode)") + println(io, "# Matched to Fortran run: $fortran_dir") + println(io) + println(io, "[Equilibrium]") + println(io, "eq_type = \"$(p.eq_type)\"") + println(io, "eq_filename = \"$(p.eq_filename)\"") + println(io, "jac_type = \"$(p.jac_type)\"") + println(io, "grid_type = \"$(p.grid_type)\"") + @printf(io, "psilow = %.4e\n", p.psilow) + @printf(io, "psihigh = %.6f\n", p.psihigh) + println(io, "mpsi = $(p.mpsi)") + println(io, "mtheta = $(p.mtheta_equil)") + println(io, "flux_surface_rtol = 1e-7") + println(io) + println(io, "[LocalStability]") + println(io) + println(io, "[ForceFreeStates]") + println(io, "vac_flag = true") + @printf(io, "qlow = %.4f\n", p.qlow) + # Match the Fortran sas_flag truncation: integration stops at q = qhigh + # (= outermost rational q + dmlim). psiedge from dcon.in (=1.0 → no edge dW scan). + @printf(io, "qhigh = %.4f\n", qhigh) + @printf(io, "dW_edge_scan_start = %.4f\n", p.psiedge) + println(io, "nn = $(p.nn)") + println(io, "delta_mlow = $(p.delta_mlow)") + println(io, "delta_mhigh = $(p.delta_mhigh)") + println(io, "mtheta_vacuum = 512") + println(io, "singfac_min = 1e-4") + println(io) + println(io, "[Riccati]") + println(io, "eulerlagrange_tolerance = 1e-7") + println(io, "save_interval = $(p.euler_stride)") + println(io) + println(io, "[ForcingTerms]") + println(io, "forcing_data_format = \"coil\"") + println(io, "mtheta_coil = $(p.mtheta_coil)") + println(io, "nzeta_coil = $(p.nzeta_coil)") + for (name, currents) in zip(p.coil_names, p.coil_currents) + dat_path = joinpath(coil_dat_dir, "$(p.machine)_$(name).dat") + println(io) + println(io, "[[ForcingTerms.coil_set]]") + println(io, "dat_file = \"$(dat_path)\"") + println(io, "currents = $(currents)") + end + println(io) + println(io, "[PerturbedEquilibrium]") + println(io, "compute_singular_coupling = true") + end +end + +# ─── Write gpec.toml (file-forcing mode) ───────────────────────────────────── + +function write_gpec_toml_file( + filepath::String, + fortran_dir::String, + p::FortranRunParams +) + open(filepath, "w") do io + println(io, "# Auto-generated by benchmark_fortran.jl (file forcing mode)") + println(io, "# Matched to Fortran run: $fortran_dir") + println(io) + println(io, "[Equilibrium]") + println(io, "eq_type = \"$(p.eq_type)\"") + println(io, "eq_filename = \"$(p.eq_filename)\"") + println(io, "jac_type = \"$(p.jac_type)\"") + println(io, "grid_type = \"$(p.grid_type)\"") + @printf(io, "psilow = %.4e\n", p.psilow) + @printf(io, "psihigh = %.6f\n", p.psihigh) + println(io, "mpsi = $(p.mpsi)") + println(io, "mtheta = $(p.mtheta_equil)") + println(io, "flux_surface_rtol = 1e-7") + println(io) + println(io, "[LocalStability]") + println(io) + println(io, "[ForceFreeStates]") + println(io, "vac_flag = true") + @printf(io, "qlow = %.4f\n", p.qlow) + @printf(io, "dW_edge_scan_start = %.4f\n", p.psiedge) + println(io, "nn = $(p.nn)") + println(io, "delta_mlow = $(p.delta_mlow)") + println(io, "delta_mhigh = $(p.delta_mhigh)") + println(io, "mtheta_vacuum = 512") + println(io, "singfac_min = 1e-4") + println(io) + println(io, "[Riccati]") + println(io, "eulerlagrange_tolerance = 1e-7") + println(io, "save_interval = $(p.euler_stride)") + println(io) + println(io, "[ForcingTerms]") + println(io, "forcing_data_file = \"forcing.dat\"") + println(io, "forcing_data_format = \"ascii\"") + println(io) + println(io, "[PerturbedEquilibrium]") + println(io, "compute_singular_coupling = true") + end +end + +# ─── Equilibrium setup helper ───────────────────────────────────────────────── + +function setup_equil(p::FortranRunParams, fortran_dir::String) + eq_dict = Dict{String,Any}( + "eq_type" => p.eq_type, + "eq_filename" => p.eq_filename, + "jac_type" => p.jac_type, + "psilow" => p.psilow, + "psihigh" => p.psihigh, + "mpsi" => p.mpsi, + "mtheta" => p.mtheta_equil, + "grid_type" => p.grid_type, + "flux_surface_rtol" => 1e-7 + ) + eq_config = GeneralizedPerturbedEquilibrium.equilibrium_config(Dict{String,Any}("Equilibrium" => eq_dict), fortran_dir) + return Equilibrium.PlasmaEquilibrium(eq_config) +end + +# ─── Phi_x comparison (coil mode) ──────────────────────────────────────────── + +""" +Compute Julia Phi_x via Biot-Savart and compare with Fortran Phi_coil. +Returns (julia_amps, fortran_amps, m_vals, equil). +""" +function compare_phix( + p::FortranRunParams, + fort::Dict, + fortran_dir::String, + coil_dat_dir::String +) + println(" Loading equilibrium for Phi_x check...") + equil = setup_equil(p, fortran_dir) + + psilim = fort["psilim"] + m_vals = fort["m_vals"] + mlow = minimum(m_vals) + mhigh = maximum(m_vals) + + coil_sets = ForcingTerms.CoilSet[] + for (name, currents) in zip(p.coil_names, p.coil_currents) + dat_path = joinpath(coil_dat_dir, "$(p.machine)_$(name).dat") + isfile(dat_path) || error("Coil dat file not found: $dat_path") + raw = ForcingTerms.read_coil_dat(dat_path) + push!(coil_sets, ForcingTerms.CoilSet(raw.name, raw.ncoil, raw.s, raw.nw, raw.nsec, + raw.x, raw.y, raw.z, Float64.(currents[1:raw.ncoil]))) + end + + grid = ForcingTerms.sample_boundary_grid(equil, p.mtheta_coil, p.nzeta_coil; psi=psilim) + nobs = p.mtheta_coil * p.nzeta_coil + obs_R = zeros(nobs) + obs_phi = zeros(nobs) + obs_Z = zeros(nobs) + for j in 1:p.nzeta_coil, i in 1:p.mtheta_coil + idx = i + (j - 1) * p.mtheta_coil + obs_R[idx] = grid.R[i] + obs_phi[idx] = grid.phi_grid[j] + grid.phi_offset[i] + obs_Z[idx] = grid.Z[i] + end + B_R = zeros(nobs) + B_phi = zeros(nobs) + B_Z = zeros(nobs) + ForcingTerms.compute_biot_savart_boundary!(B_R, B_phi, B_Z, obs_R, obs_phi, obs_Z, coil_sets) + bn = zeros(p.mtheta_coil, p.nzeta_coil) + ForcingTerms.project_normal_flux!(bn, B_R, B_Z, grid) + julia_modes = ForcingTerms.fourier_decompose_bn(bn, grid, p.nn, mlow, mhigh) + + julia_amps = Dict(md.m => abs(md.amplitude) for md in julia_modes) + fort_amps = Dict(m => fort["Phi_x_amp"][k] for (k, m) in enumerate(m_vals)) + + return julia_amps, fort_amps, m_vals, equil +end + +# ─── Comparison table ──────────────────────────────────────────────────────── + +rel_pct(a, b) = abs(b) > 1e-30 ? @sprintf("%.1f%%", 100 * abs(a - b) / abs(b)) : "N/A" + +function step_series(m_vals, amps) + m_ext = [m_vals[1] - 1; m_vals; m_vals[end] + 1] + amp_ext = [0.0; amps; 0.0] + return m_ext, amp_ext +end + +function find_julia_row(julia, q_int::Int, nn::Int) + rat_q = julia["rational_q"] + rat_n = julia["rational_n"] + isempty(rat_q) && return 0 + for row in eachindex(rat_q) + row > length(rat_n) && break + rat_n[row] == nn && round(Int, rat_q[row]) == q_int && return row + end + return 0 +end + +function build_comparison_table(fort, julia, fortran_dir, bench_dir, nn) + lines = String[] + push!(lines, "="^72) + push!(lines, "GPEC FORTRAN vs JULIA COMPARISON") + push!(lines, "="^72) + push!(lines, "Fortran : $fortran_dir") + push!(lines, "Julia : $(joinpath(bench_dir, "gpec.h5"))") + push!(lines, "Date : $(Dates.format(Dates.now(), "yyyy-mm-dd HH:MM"))") + push!(lines, "") + + push!(lines, "--- Scalar Parameters ---") + push!(lines, @sprintf("%-12s %10s %10s %10s %6s", "", "Fortran", "Julia", "AbsDiff", "Rel%")) + for (label, fv, jv) in [ + ("psilim", fort["psilim"], julia["psilim"]), + ("qlim", fort["qlim"], julia["qlim"]) + ] + push!(lines, @sprintf("%-12s %10.6f %10.6f %10.6f %6s", + label, fv, jv, abs(fv - jv), rel_pct(fv, jv))) + end + push!(lines, "") + + push!(lines, "--- W_t Eigenvalue (least stable mode) ---") + push!(lines, @sprintf("%-8s %10s %10s %6s", "", "Fortran", "Julia", "Rel%")) + f_wt = fort["W_t_eigenvalue"] + j_et = julia["et"] + fv = isempty(f_wt) ? NaN : f_wt[argmin(real.(f_wt))] + jv = isempty(j_et) ? NaN : j_et[1] + push!(lines, @sprintf("|W_t| %10.4f %10.4f %6s", abs(fv), abs(jv), rel_pct(abs(fv), abs(jv)))) + push!(lines, @sprintf("Re(W_t) %10.4f %10.4f %6s", real(fv), real(jv), rel_pct(real(fv), real(jv)))) + push!(lines, "") + + push!(lines, "--- q Profile (RMS on common grid) ---") + fq_psi = fort["psi_n_dcon"] + fq = fort["q_dcon"] + jq_psi = julia["psi_q"] + jq = julia["q"] + if !isempty(jq) && !isempty(fq) + jq_on_fgrid = [_interp1(jq_psi, jq, p) for p in fq_psi] + rms = sqrt(mean((fq .- jq_on_fgrid) .^ 2)) + push!(lines, @sprintf("RMS(q_fort - q_julia) = %.5f (%.2f%% relative)", + rms, rms / mean(abs.(fq)) * 100)) + else + push!(lines, "q profile unavailable") + end + push!(lines, "") + + push!(lines, "--- Mercier Criterion ---") + fdi = fort["di"] + fdr = fort["dr"] + jdi = julia["di"] + jdr = julia["dr"] + if !isempty(jdi) && !isempty(fdi) + jdi_on_fg = [_interp1(jq_psi, jdi, p) for p in fq_psi] + jdr_on_fg = !isempty(jdr) ? + [_interp1(jq_psi, jdr, p) for p in fq_psi] : fill(NaN, length(fq_psi)) + push!(lines, @sprintf("RMS(di_fort - di_julia) = %.5e", sqrt(mean((fdi .- jdi_on_fg) .^ 2)))) + push!(lines, @sprintf("RMS(dr_fort - dr_julia) = %.5e", sqrt(mean((fdr .- jdr_on_fg) .^ 2)))) + else + push!(lines, "Mercier data unavailable") + end + push!(lines, "") + + push!(lines, "--- Phi_x and Phi_tot Spectrum (n=$nn) ---") + m_ctrl = fort["m_vals_ctrl"] + f_phi_x = fort["Phi_x"] + f_phi_tot = fort["Phi_tot"] + j_fvec = julia["forcing_vec"] + j_rvec = julia["response_vec"] + j_mmodes = julia["m_modes"] + if !isempty(j_fvec) && !isempty(f_phi_x) + push!(lines, @sprintf("%-5s %-14s %-14s %-6s %-14s %-14s %-6s", + "m", "|Phi_x_fort|", "|Phi_x_julia|", "Rel%", "|Phi_tot_fort|", "|Phi_tot_julia|", "Rel%")) + for (k, m) in enumerate(m_ctrl) + j_idx = findfirst(==(m), j_mmodes) + j_fx = isnothing(j_idx) ? NaN : abs(j_fvec[j_idx]) + j_ft = isnothing(j_idx) ? NaN : abs(j_rvec[j_idx]) + f_fx = abs(f_phi_x[k]) + f_ft = abs(f_phi_tot[k]) + push!( + lines, + @sprintf("%-5d %14.4e %14s %-6s %14.4e %14s %-6s", + m, f_fx, + isnan(j_fx) ? "N/A" : @sprintf("%.4e", j_fx), + isnan(j_fx) ? "N/A" : rel_pct(f_fx, j_fx), + f_ft, + isnan(j_ft) ? "N/A" : @sprintf("%.4e", j_ft), + isnan(j_ft) ? "N/A" : rel_pct(f_ft, j_ft)) + ) + end + else + push!(lines, "Phi_tot data unavailable") + end + push!(lines, "") + + push!(lines, "--- P Matrix |diagonal| comparison (n=$nn) ---") + f_P = fort["P"] + j_P = julia["permeability"] + if !isempty(f_P) && !isempty(j_P) && size(f_P) == size(j_P) + mpert = size(f_P, 1) + push!(lines, @sprintf("%-5s %-14s %-14s %-6s", "m", "|P_fort[i,i]|", "|P_julia[i,i]|", "Rel%")) + m_ctrl = fort["m_vals_ctrl"] + for i in 1:mpert + m = i <= length(m_ctrl) ? m_ctrl[i] : i + fp = abs(f_P[i, i]) + jp = abs(j_P[i, i]) + push!(lines, @sprintf("%-5d %14.4e %14.4e %-6s", m, fp, jp, rel_pct(fp, jp))) + end + else + push!(lines, "P matrix unavailable or size mismatch") + end + push!(lines, "") + + push!(lines, "--- Lambda (plasma inductance) |diagonal| comparison (n=$nn) ---") + f_L = fort["Lambda"] + j_L = julia["plasma_inductance"] + if !isempty(f_L) && !isempty(j_L) && size(f_L) == size(j_L) + mpert = size(f_L, 1) + push!(lines, @sprintf("%-5s %-14s %-14s %-6s %-10s", "m", "|Λ_fort[i,i]|", "|Λ_julia[i,i]|", "Rel%", "Ratio j/f")) + m_ctrl = fort["m_vals_ctrl"] + for i in 1:mpert + m = i <= length(m_ctrl) ? m_ctrl[i] : i + fl = abs(f_L[i, i]) + jl = abs(j_L[i, i]) + ratio = fl > 0 ? jl / fl : NaN + push!(lines, @sprintf("%-5d %14.4e %14.4e %-6s %10.3f", m, fl, jl, rel_pct(fl, jl), ratio)) + end + else + push!(lines, "Lambda matrix unavailable or size mismatch (fort=$(size(f_L)), julia=$(size(j_L)))") + end + push!(lines, "") + + push!(lines, "--- L (surface inductance) |diagonal| comparison (n=$nn) ---") + f_LS = fort["L_surf"] + j_LS = julia["surface_inductance"] + if !isempty(f_LS) && !isempty(j_LS) && size(f_LS) == size(j_LS) + mpert = size(f_LS, 1) + push!(lines, @sprintf("%-5s %-14s %-14s %-6s %-10s", "m", "|L_fort[i,i]|", "|L_julia[i,i]|", "Rel%", "Ratio j/f")) + m_ctrl = fort["m_vals_ctrl"] + for i in 1:mpert + m = i <= length(m_ctrl) ? m_ctrl[i] : i + fl = abs(f_LS[i, i]) + jl = abs(j_LS[i, i]) + ratio = fl > 0 ? jl / fl : NaN + push!(lines, @sprintf("%-5d %14.4e %14.4e %-6s %10.3f", m, fl, jl, rel_pct(fl, jl), ratio)) + end + else + push!(lines, "L matrix unavailable or size mismatch (fort=$(size(f_LS)), julia=$(size(j_LS)))") + end + push!(lines, "") + + push!(lines, "--- Resonant Surface Comparison (n=$nn) ---") + push!( + lines, + @sprintf( + "%-4s %-8s %-8s %-14s %-14s %-6s %-10s %-10s %-6s %-7s %-7s %-6s", + "q", "psi_fort", "psi_julia", "|Phi_res_fort|", "|Phi_res_julia|", "Rel%", + "w_isl_fort", "w_isl_julia", "Rel%", "K_fort", "K_julia", "Rel%") + ) + + f_psi_rat = fort["psi_n_rational"] + f_q_rat = fort["q_rational"] + f_phi_res = fort["Phi_res"] + f_w_isl = fort["w_isl"] + f_K_isl = fort["K_isl"] + + for (i, (fq_r, fp_r)) in enumerate(zip(f_q_rat, f_psi_rat)) + q_int = round(Int, fq_r) + row = find_julia_row(julia, q_int, nn) + + jp_r = (row > 0 && !isempty(julia["rational_psi"])) ? julia["rational_psi"][row] : NaN + j_phi = (row > 0 && !isempty(julia["resonant_area_weighted_field"])) ? abs(julia["resonant_area_weighted_field"][row]) : NaN + j_wisl = (row > 0 && !isempty(julia["island_half_width"])) ? 2 * julia["island_half_width"][row] : NaN + j_kch = (row > 0 && !isempty(julia["chirikov_parameter"])) ? julia["chirikov_parameter"][row] : NaN + + push!( + lines, + @sprintf( + "%-4d %8.4f %8s %14.4e %14s %-6s %10.4e %10s %-6s %7.4f %7s %-6s", + q_int, fp_r, + isnan(jp_r) ? "N/A" : @sprintf("%.4f", jp_r), + abs(f_phi_res[i]), + isnan(j_phi) ? "N/A" : @sprintf("%.4e", j_phi), + isnan(j_phi) ? "N/A" : rel_pct(abs(f_phi_res[i]), j_phi), + f_w_isl[i], + isnan(j_wisl) ? "N/A" : @sprintf("%.4e", j_wisl), + isnan(j_wisl) ? "N/A" : rel_pct(f_w_isl[i], j_wisl), + f_K_isl[i], + isnan(j_kch) ? "N/A" : @sprintf("%.4f", j_kch), + isnan(j_kch) ? "N/A" : rel_pct(f_K_isl[i], j_kch), + ) + ) + end + + push!(lines, "") + + push!(lines, "--- j_c Diagnostic (resonant current coefficient, inferred from I_res/Delta) ---") + psio = julia["psio"] + chi1 = 2π * psio + push!(lines, @sprintf(" chi1 = 2π * psio = %.6e (psio = %.6e)", chi1, psio)) + push!(lines, @sprintf(" j_c inferred as |I_res| * |m_res| / (|Δ'| * chi1)")) + push!(lines, @sprintf("%-4s %-8s %-14s %-14s %-8s", + "q", "psi_fort", "j_c_fort", "j_c_julia", "ratio j/f")) + + f_delta = fort["Delta"] + f_i_res = fort["I_res"] + j_delta = julia["delta_prime"] + j_i_res = julia["resonant_current"] + j_m_res = julia["rational_m_res"] + + for (i, (fq_r, fp_r)) in enumerate(zip(f_q_rat, f_psi_rat)) + q_int = round(Int, fq_r) + m_res_fort = q_int * nn # m_res = q * n at rational surface + row = find_julia_row(julia, q_int, nn) + + jc_fort = (i <= length(f_delta) && i <= length(f_i_res) && + abs(f_delta[i]) > 1e-30 && chi1 > 0) ? + abs(f_i_res[i]) * abs(m_res_fort) / (abs(f_delta[i]) * chi1) : NaN + + jc_julia = + (row > 0 && row <= length(j_delta) && row <= length(j_i_res) && + row <= length(j_m_res) && abs(j_delta[row]) > 1e-30 && chi1 > 0) ? + abs(j_i_res[row]) * abs(j_m_res[row]) / (abs(j_delta[row]) * chi1) : NaN + + ratio = (!isnan(jc_fort) && !isnan(jc_julia) && jc_fort > 0) ? jc_julia / jc_fort : NaN + + push!( + lines, + @sprintf("%-4d %8.4f %14s %14s %8s", + q_int, fp_r, + isnan(jc_fort) ? "N/A" : @sprintf("%.4e", jc_fort), + isnan(jc_julia) ? "N/A" : @sprintf("%.4e", jc_julia), + isnan(ratio) ? "N/A" : @sprintf("%.4f", ratio)) + ) + end + + push!(lines, "") + push!(lines, "--- Delta' Comparison (isolates jump vs L_mm/area error) ---") + push!(lines, @sprintf("%-4s %-8s %-14s %-14s %-8s %-14s %-14s %-8s", + "q", "psi_fort", "|Delta_fort|", "|Delta_julia|", "Rel%", + "|I_res_fort|", "|I_res_julia|", "Rel%")) + for (i, (fq_r, fp_r)) in enumerate(zip(f_q_rat, f_psi_rat)) + q_int = round(Int, fq_r) + row = find_julia_row(julia, q_int, nn) + fd = (i <= length(f_delta)) ? abs(f_delta[i]) : NaN + jd = (row > 0 && row <= length(j_delta)) ? abs(j_delta[row]) : NaN + fi = (!isempty(f_i_res) && i <= length(f_i_res)) ? abs(f_i_res[i]) : NaN + ji = (row > 0 && row <= length(j_i_res)) ? abs(j_i_res[row]) : NaN + push!( + lines, + @sprintf("%-4d %8.4f %14s %14s %8s %14s %14s %8s", + q_int, fp_r, + isnan(fd) ? "N/A" : @sprintf("%.4e", fd), + isnan(jd) ? "N/A" : @sprintf("%.4e", jd), + (isnan(fd) || isnan(jd)) ? "N/A" : rel_pct(fd, jd), + isnan(fi) ? "N/A" : @sprintf("%.4e", fi), + isnan(ji) ? "N/A" : @sprintf("%.4e", ji), + (isnan(fi) || isnan(ji)) ? "N/A" : rel_pct(fi, ji)) + ) + end + + push!(lines, "="^72) + return lines +end + +function _interp1(x::AbstractVector, y::AbstractVector, xi::Real) + n = length(x) + n < 2 && return y[1] + xi <= x[1] && return y[1] + xi >= x[end] && return y[end] + i = clamp(searchsortedlast(x, xi), 1, n - 1) + t = (xi - x[i]) / (x[i+1] - x[i]) + return y[i] + t * (y[i+1] - y[i]) +end + +# ─── Plot generation ───────────────────────────────────────────────────────── + +function generate_plots(fort, julia, bench_dir, nn) + f_q_rat = fort["q_rational"] + n_surf = length(f_q_rat) + + # --- Row 1: q profile, Mercier, W_t scatter, |P| diagonal --- + p1 = plot(fort["psi_n_dcon"], fort["q_dcon"]; + label="Fortran", xlabel="ψ_n", ylabel="q", title="q profile", lw=2) + plot!(p1, julia["psi_q"], julia["q"]; label="Julia", linestyle=:dash, lw=2) + + p2 = plot(fort["psi_n_dcon"], fort["di"]; + label="Fortran", xlabel="ψ_n", ylabel="D_I", title="Mercier D_I", lw=2) + !isempty(julia["di"]) && plot!(p2, julia["psi_q"], julia["di"]; label="Julia", linestyle=:dash, lw=2) + hline!(p2, [0.0]; color=:black, linestyle=:dot, label="") + + p3 = plot(fort["psi_n_dcon"], fort["dr"]; + label="Fortran", xlabel="ψ_n", ylabel="D_R", title="Mercier D_R", lw=2) + !isempty(julia["dr"]) && plot!(p3, julia["psi_q"], julia["dr"]; label="Julia", linestyle=:dash, lw=2) + hline!(p3, [0.0]; color=:black, linestyle=:dot, label="") + + # W_t eigenvalue scatter (log y), all eigenvalues vs index + f_wt = fort["W_t_eigenvalue"] + j_et = julia["et"] + p4 = plot(; xlabel="mode index", ylabel="|W_t|", title="|W_t| eigenvalues", + yscale=:log10, legend=:topright) + if !isempty(f_wt) + f_sorted = sort(abs.(f_wt)) + scatter!(p4, 1:length(f_sorted), f_sorted; color=:steelblue, markersize=5, + markershape=:circle, label="Fortran") + end + if !isempty(j_et) + j_sorted = sort(abs.(j_et)) + scatter!(p4, 1:length(j_sorted), j_sorted; color=:orange, markersize=5, + markershape=:circle, label="Julia", markerstrokewidth=0) + end + + # |P| diagonal comparison + f_P = fort["P"] + j_P = julia["permeability"] + m_ctrl = fort["m_vals_ctrl"] + p5 = plot(; xlabel="Poloidal mode m", ylabel="|P[i,i]|", + title="|P| diagonal (n=$nn)", legend=:topright) + if !isempty(f_P) && !isempty(j_P) + mpert_p = min(size(f_P, 1), size(j_P, 1), length(m_ctrl)) + ms = m_ctrl[1:mpert_p] + fp_diag = [abs(f_P[i, i]) for i in 1:mpert_p] + jp_diag = [abs(j_P[i, i]) for i in 1:mpert_p] + me_f, fe = step_series(ms, fp_diag) + me_j, je = step_series(ms, jp_diag) + plot!(p5, me_f, fe; seriestype=:steppre, lw=2, color=:steelblue, label="Fortran") + plot!(p5, me_j, je; seriestype=:steppre, lw=2, color=:orange, linestyle=:dash, label="Julia") + else + annotate!(p5, 0.5, 0.5, text("data unavailable", 10, :center)) + end + + # --- Row 2: Phi_x/tot spectrum and singular coupling vs psi --- + + # Phi_x & Phi_tot spectrum + m_ctrl = fort["m_vals_ctrl"] + f_phi_x = fort["Phi_x"] + f_phi_tot = fort["Phi_tot"] + j_fvec = julia["forcing_vec"] + j_rvec = julia["response_vec"] + j_mmodes = julia["m_modes"] + p6 = plot(; xlabel="m", ylabel="|b̄| [T]", title="Phi_x & Phi_tot spectrum (n=$nn)", + legend=:topright) + if !isempty(j_fvec) && !isempty(f_phi_x) + jfx_amps = [abs(get(Dict(zip(j_mmodes, j_fvec)), m, NaN + 0im)) for m in m_ctrl] + jft_amps = [abs(get(Dict(zip(j_mmodes, j_rvec)), m, NaN + 0im)) for m in m_ctrl] + ffx_amps = abs.(f_phi_x) + fft_amps = abs.(f_phi_tot) + me, jfe = step_series(m_ctrl, replace(jfx_amps, NaN => 0.0)) + _, jte = step_series(m_ctrl, replace(jft_amps, NaN => 0.0)) + _, ffe = step_series(m_ctrl, ffx_amps) + _, fte = step_series(m_ctrl, fft_amps) + plot!(p6, me, ffe; seriestype=:steppre, lw=2, color=:steelblue, label="Φ_x Fortran") + plot!(p6, me, jfe; seriestype=:steppre, lw=2, color=:steelblue, linestyle=:dash, label="Φ_x Julia") + plot!(p6, me, fte; seriestype=:steppre, lw=2, color=:orange, label="Φ_tot Fortran") + plot!(p6, me, jte; seriestype=:steppre, lw=2, color=:orange, linestyle=:dash, label="Φ_tot Julia") + else + annotate!(p6, 0.5, 0.5, text("data unavailable", 10, :center)) + end + + # Helper: singular coupling vs psi scatter with q-value labels + function _singcoup_panel(ylabel_str, title_str, + f_psi, f_vals, f_q_labels, + j_psi, j_vals) + p = plot(; xlabel="ψ_n", ylabel=ylabel_str, title=title_str, legend=:topright) + if !isempty(f_psi) && !isempty(f_vals) + scatter!(p, f_psi, f_vals; color=:steelblue, markersize=7, markershape=:circle, label="Fortran") + plot!(p, f_psi, f_vals; color=:steelblue, lw=1, linestyle=:dot, label="") + for (xi, yi, qi) in zip(f_psi, f_vals, f_q_labels) + annotate!(p, xi, yi, text(qi, 7, :bottom, :center)) + end + end + if !isempty(j_psi) && !isempty(j_vals) + scatter!(p, j_psi, j_vals; color=:orange, markersize=5, markershape=:circle, + label="Julia", markerstrokewidth=0) + plot!(p, j_psi, j_vals; color=:orange, lw=1, linestyle=:dot, label="") + end + return p + end + + # Collect per-surface values for singular coupling panels + f_psi_rat = fort["psi_n_rational"] + f_q_labels = [@sprintf("q=%d", round(Int, q)) for q in f_q_rat] + + # Delta (Δ') + f_delta_vals = isempty(fort["Delta"]) ? Float64[] : real.(fort["Delta"]) + j_delta_psi = julia["rational_psi"] + j_delta_vals = isempty(julia["delta_prime"]) ? Float64[] : real.(julia["delta_prime"]) + p7 = _singcoup_panel("Re(Δ')", "Δ' vs ψ (n=$nn)", + f_psi_rat, f_delta_vals, f_q_labels, + j_delta_psi, j_delta_vals) + + # I_res (resonant current) + f_ires_vals = isempty(fort["I_res"]) ? Float64[] : abs.(fort["I_res"]) + j_ires_psi = julia["rational_psi"] + j_ires_vals = isempty(julia["resonant_current"]) ? Float64[] : abs.(julia["resonant_current"]) + p8 = _singcoup_panel("|I_res| [A]", "|I_res| vs ψ (n=$nn)", + f_psi_rat, f_ires_vals, f_q_labels, + j_ires_psi, j_ires_vals) + + # Phi_res (resonant flux / area) + f_phires_vals = isempty(fort["Phi_res"]) ? Float64[] : abs.(fort["Phi_res"]) + j_phires_psi = julia["rational_psi"] + j_phires_vals = isempty(julia["resonant_area_weighted_field"]) ? Float64[] : abs.(julia["resonant_area_weighted_field"]) + p9 = _singcoup_panel("|Φ_res| [T]", "|Φ_res| vs ψ (n=$nn)", + f_psi_rat, f_phires_vals, f_q_labels, + j_phires_psi, j_phires_vals) + + # w_isl (island full width) + f_wisl_vals = fort["w_isl"] + j_wisl_psi = julia["rational_psi"] + j_wisl_vals = isempty(julia["island_half_width"]) ? Float64[] : 2 .* julia["island_half_width"] + p10 = _singcoup_panel("w_isl [ψ_n]", "Island width vs ψ (n=$nn)", + f_psi_rat, f_wisl_vals, f_q_labels, + j_wisl_psi, j_wisl_vals) + + # --- Row 3: b_psi profiles for resonant m harmonics --- + # Identify resonant m values from rational surfaces + res_m_vals = sort(unique(round.(Int, fort["q_rational"]))) # q=m/n → m=round(Int,q) for n=1 + # Clamp to available m range + res_m_vals = filter(m -> m in m_ctrl, res_m_vals) + + # Helper: build a normalised radial profile panel for one mode + function _profile_panel(ylabel_str, title_str, + f_psi, f_prof_complex, f_label, + j_psi, j_prof_complex, j_label) + pb = plot(; xlabel="ψ_n", ylabel=ylabel_str, title=title_str, legend=:topright) + f_prof = abs.(f_prof_complex) + j_prof = abs.(j_prof_complex) + f_max = isempty(f_prof) ? 0.0 : maximum(f_prof) + j_max = isempty(j_prof) ? 0.0 : maximum(j_prof) + f_max > 0 && plot!(pb, f_psi, f_prof ./ f_max; + lw=1.5, color=:steelblue, label=f_label, + markershape=:x, markersize=2, markerstrokewidth=1, markeralpha=0.6) + rms_diff = NaN + if j_max > 0 && f_max > 0 && !isempty(j_psi) + j_on_fg = [_interp1(j_psi, j_prof, p) for p in f_psi] + j_max_on = maximum(j_on_fg) + f_norm = f_prof ./ f_max + if j_max_on > 0 + rms_diff = sqrt(mean((j_on_fg ./ j_max_on .- f_norm) .^ 2)) + end + plot!(pb, j_psi, j_prof ./ j_max; + lw=1.5, color=:orange, linestyle=:dash, label=j_label, + markershape=:x, markersize=4, markerstrokewidth=1.5, markeralpha=0.9) + end + !isnan(rms_diff) && annotate!(pb, 0.8, 0.85, text(@sprintf("RMS=%.3f", rms_diff), 9, :center)) + return pb + end + + j_psi_grid = julia["psi_grid"] + f_psi_prof = fort["psi_n_prof"] + f_m_out = fort["m_out"] + + # Row 3: Jbgradpsi profiles — direct apples-to-apples (both = chi1*singfac*2πi*ξ_ψ in mode space) + jbgp_panels = [] + j_jbgp = julia["Jbgradpsi"] + f_jbgp = fort["Jbgradpsi"] + for m_res in res_m_vals[1:min(end, 4)] + f_midx = findfirst(==(m_res), f_m_out) + j_midx = findfirst(==(m_res), j_mmodes) + fp = (!isnothing(f_midx) && !isempty(f_jbgp)) ? f_jbgp[:, f_midx] : ComplexF64[] + jp = (!isnothing(j_midx) && !isempty(j_jbgp)) ? j_jbgp[:, j_midx] : ComplexF64[] + push!( + jbgp_panels, + _profile_panel( + "|Jbgradpsi| (norm.)", "Jbgradpsi m=$m_res (n=$nn)", + isempty(fp) ? Float64[] : f_psi_prof, fp, "Fortran", + isempty(jp) ? Float64[] : j_psi_grid, jp, "Julia") + ) + end + while length(jbgp_panels) < 4 + push!(jbgp_panels, plot(; title="(no data)", legend=false, axis=false, border=:none)) + end + + # Row 4: b_n profiles — Julia b_n (via IDFT/divide/DFT) vs Fortran b_n + bn_panels = [] + j_bn = julia["b_n"] + f_bn = fort["b_n"] + for m_res in res_m_vals[1:min(end, 4)] + f_midx = findfirst(==(m_res), f_m_out) + j_midx = findfirst(==(m_res), j_mmodes) + fp = (!isnothing(f_midx) && !isempty(f_bn)) ? f_bn[:, f_midx] : ComplexF64[] + jp = (!isnothing(j_midx) && !isempty(j_bn)) ? j_bn[:, j_midx] : ComplexF64[] + push!( + bn_panels, + _profile_panel( + "|b_n| (norm.)", "b_n m=$m_res (n=$nn)", + isempty(fp) ? Float64[] : f_psi_prof, fp, "Fortran", + isempty(jp) ? Float64[] : j_psi_grid, jp, "Julia") + ) + end + while length(bn_panels) < 4 + push!(bn_panels, plot(; title="(no data)", legend=false, axis=false, border=:none)) + end + + # Row 5: xigradpsi (xi_psi) profiles + xigp_panels = [] + j_xigp = julia["xi_psi"] + f_xigp = fort["xigradpsi"] + for m_res in res_m_vals[1:min(end, 4)] + f_midx = findfirst(==(m_res), f_m_out) + j_midx = findfirst(==(m_res), j_mmodes) + fp = (!isnothing(f_midx) && !isempty(f_xigp)) ? f_xigp[:, f_midx] : ComplexF64[] + jp = (!isnothing(j_midx) && !isempty(j_xigp)) ? j_xigp[:, j_midx] : ComplexF64[] + push!( + xigp_panels, + _profile_panel( + "|ξ_ψ| (norm.)", "xigradpsi m=$m_res (n=$nn)", + isempty(fp) ? Float64[] : f_psi_prof, fp, "Fortran", + isempty(jp) ? Float64[] : j_psi_grid, jp, "Julia") + ) + end + while length(xigp_panels) < 4 + push!(xigp_panels, plot(; title="(no data)", legend=false, axis=false, border=:none)) + end + + # Row 6: xi_n profiles + xin_panels = [] + j_xin = julia["xi_n"] + f_xin = fort["xi_n"] + for m_res in res_m_vals[1:min(end, 4)] + f_midx = findfirst(==(m_res), f_m_out) + j_midx = findfirst(==(m_res), j_mmodes) + fp = (!isnothing(f_midx) && !isempty(f_xin)) ? f_xin[:, f_midx] : ComplexF64[] + jp = (!isnothing(j_midx) && !isempty(j_xin)) ? j_xin[:, j_midx] : ComplexF64[] + push!( + xin_panels, + _profile_panel( + "|ξ_n| (norm.)", "xi_n m=$m_res (n=$nn)", + isempty(fp) ? Float64[] : f_psi_prof, fp, "Fortran", + isempty(jp) ? Float64[] : j_psi_grid, jp, "Julia") + ) + end + while length(xin_panels) < 4 + push!(xin_panels, plot(; title="(no data)", legend=false, axis=false, border=:none)) + end + + # Row 7: Clebsch ∂ξ^ψ/∂ψ (regularized) profiles + xigp1_panels = [] + j_xigp1 = julia["clebsch_psi1"] + f_xigp1 = fort["xigradpsi_dpsi"] + for m_res in res_m_vals[1:min(end, 4)] + f_midx = findfirst(==(m_res), f_m_out) + j_midx = findfirst(==(m_res), j_mmodes) + fp = (!isnothing(f_midx) && !isempty(f_xigp1)) ? f_xigp1[:, f_midx] : ComplexF64[] + jp = (!isnothing(j_midx) && !isempty(j_xigp1)) ? j_xigp1[:, j_midx] : ComplexF64[] + push!( + xigp1_panels, + _profile_panel( + "|∂ξ^ψ/∂ψ| (norm.)", "xigradpsi_dpsi m=$m_res (n=$nn)", + isempty(fp) ? Float64[] : f_psi_prof, fp, "Fortran", + isempty(jp) ? Float64[] : j_psi_grid, jp, "Julia") + ) + end + while length(xigp1_panels) < 4 + push!(xigp1_panels, plot(; title="(no data)", legend=false, axis=false, border=:none)) + end + + # Row 8: Clebsch ξ^α (regularized) profiles + xiga_panels = [] + j_xiga = julia["clebsch_alpha"] + f_xiga = fort["xigradalpha"] + for m_res in res_m_vals[1:min(end, 4)] + f_midx = findfirst(==(m_res), f_m_out) + j_midx = findfirst(==(m_res), j_mmodes) + fp = (!isnothing(f_midx) && !isempty(f_xiga)) ? f_xiga[:, f_midx] : ComplexF64[] + jp = (!isnothing(j_midx) && !isempty(j_xiga)) ? j_xiga[:, j_midx] : ComplexF64[] + push!( + xiga_panels, + _profile_panel( + "|ξ^α| (norm.)", "xigradalpha m=$m_res (n=$nn)", + isempty(fp) ? Float64[] : f_psi_prof, fp, "Fortran", + isempty(jp) ? Float64[] : j_psi_grid, jp, "Julia") + ) + end + while length(xiga_panels) < 4 + push!(xiga_panels, plot(; title="(no data)", legend=false, axis=false, border=:none)) + end + + # Pad all profile rows to 5 panels for uniform grid layout + _empty_panel() = plot(; title="", legend=false, axis=false, grid=false, border=:none) + for panels in [jbgp_panels, bn_panels, xigp_panels, xin_panels, xigp1_panels, xiga_panels] + while length(panels) < 5 + push!(panels, _empty_panel()) + end + end + + p = plot(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, + jbgp_panels..., bn_panels..., xigp_panels..., xin_panels..., + xigp1_panels..., xiga_panels...; + layout=(8, 5), size=(2600, 3600), + left_margin=20Plots.mm, right_margin=5Plots.mm, bottom_margin=8Plots.mm, top_margin=5Plots.mm) + outfile = joinpath(bench_dir, "comparison_plots.png") + savefig(p, outfile) + println("Plots saved to: ", abspath(outfile)) + + # ─── R,Z,φ profile comparison figure ────────────────────────────────────── + # Compare θ=0 radial profiles (real & imag) in machine coordinates. + # Julia stores R,Z,φ in mode-space; use modes_to_theta to reconstruct + # theta-space with ν phase + helicity correction for comparison with + # Fortran gpout_xbrzphifun output. + h5_path = julia["h5_path"] + rzphi_panels = [] + for (comp_label, f_key, h5_var) in [ + ("ξ_R", "xi_r_fun", "PerturbedEquilibrium/Response/xi_R"), + ("ξ_Z", "xi_z_fun", "PerturbedEquilibrium/Response/xi_Z"), + ("ξ_φ", "xi_phi_fun", "PerturbedEquilibrium/Response/xi_phi"), + ("b_R", "b_r_fun", "PerturbedEquilibrium/Response/b_R"), + ("b_Z", "b_z_fun", "PerturbedEquilibrium/Response/b_Z"), + ("b_φ", "b_phi_fun", "PerturbedEquilibrium/Response/b_phi")] + f_fun = get(fort, f_key, Matrix{ComplexF64}(undef, 0, 0)) + + # Reconstruct theta-space from mode-space via modes_to_theta (applies ν phase + helicity) + j_fun = Matrix{ComplexF64}(undef, 0, 0) + if isfile(h5_path) + try + j_theta_data, _, _ = PerturbedEquilibriumModes.modes_to_theta(h5_path, h5_var; keep_sfl_phi=false) + j_fun = j_theta_data[:, :, 1] # (npsi, ntheta) for n=1 + catch e + @warn "modes_to_theta failed for $h5_var" exception = e + end + end + + # Extract θ=0 (first column) real and imaginary profiles vs ψ + f_re = isempty(f_fun) ? Float64[] : real.(f_fun[:, 1]) + f_im = isempty(f_fun) ? Float64[] : imag.(f_fun[:, 1]) + j_re = isempty(j_fun) ? Float64[] : real.(j_fun[:, 1]) + j_im = isempty(j_fun) ? Float64[] : imag.(j_fun[:, 1]) + + # Y-axis limits from 2-98 percentiles to avoid spikes + function _percentile_ylims(arrs...) + all_vals = vcat([v for v in arrs if !isempty(v)]...) + isempty(all_vals) && return (-1, 1) + lo = quantile(all_vals, 0.02) + hi = quantile(all_vals, 0.98) + margin = 0.1 * max(hi - lo, eps()) + return (lo - margin, hi + margin) + end + + # Real part panel + yl_re = _percentile_ylims(f_re, j_re) + p_re = plot(; xlabel="ψ_n", ylabel="Re($comp_label)", title="Re($comp_label) θ=0 (n=$nn)", + legend=:topright, ylims=yl_re) + !isempty(f_re) && plot!(p_re, f_psi_prof, f_re; lw=2.5, color=:steelblue, label="Fortran") + !isempty(j_re) && plot!(p_re, j_psi_grid, j_re; lw=1.0, color=:orange, label="Julia") + push!(rzphi_panels, p_re) + + # Imaginary part panel + yl_im = _percentile_ylims(f_im, j_im) + p_im = plot(; xlabel="ψ_n", ylabel="Im($comp_label)", title="Im($comp_label) θ=0 (n=$nn)", + legend=:topright, ylims=yl_im) + !isempty(f_im) && plot!(p_im, f_psi_prof, f_im; lw=2.5, color=:steelblue, label="Fortran") + !isempty(j_im) && plot!(p_im, j_psi_grid, j_im; lw=1.0, color=:orange, label="Julia") + push!(rzphi_panels, p_im) + end + + p_rzphi = plot(rzphi_panels...; + layout=(6, 2), size=(1200, 2700), + left_margin=20Plots.mm, right_margin=5Plots.mm, bottom_margin=8Plots.mm, top_margin=5Plots.mm) + outfile_rzphi = joinpath(bench_dir, "rzphi_comparison.png") + savefig(p_rzphi, outfile_rzphi) + println("R,Z,φ profile plots saved to: ", abspath(outfile_rzphi)) +end + + +# ─── Matrix heatmap figure ─────────────────────────────────────────────────── + +""" + _mat_heatmap(M, title_str, m_vals; rel_err=false) + +Return a heatmap panel for matrix M. Values are plotted on a log₁₀ scale so +that orders-of-magnitude variation is visible. When `rel_err=true` the colour +scale is labelled as log₁₀(rel error). +""" +function _mat_heatmap(M::AbstractMatrix, title_str::String, m_vals; + rel_err::Bool=false, clims=nothing) + vals = log10.(max.(abs.(M), 1e-30)) # floor at 1e-30 to avoid -Inf + label = rel_err ? "log₁₀(|ΔM/M|)" : "log₁₀|M|" + kw = isnothing(clims) ? () : (clims=clims,) + heatmap(m_vals, m_vals, vals; + title=title_str, xlabel="m′", ylabel="m", + color=:viridis, colorbar_title=label, + xrotation=45, yflip=false, kw...) +end + +function _mat_heatmap_empty(title_str) + p = plot(; title=title_str, axis=false, legend=false, border=:none) + annotate!(p, 0.5, 0.5, text("unavailable", 9, :center)) + return p +end + +""" + generate_matrix_plots(fort, julia, bench_dir, nn) + +Save `matrix_comparison_plots.png` showing element-wise |M| heatmaps for the +fundamental response matrices L, Λ, P and the vacuum energy matrix wt0. + +Layout (rows × 3 columns): +• Col 1: |M| Fortran Col 2: |M| Julia Col 3: relative error |ΔM/M_fort| +• Row 4 (wt0): Julia only — no Fortran reference available in NetCDF outputs. +""" +function generate_matrix_plots(fort, julia, bench_dir, _nn) + m_ctrl = fort["m_vals_ctrl"] + + f_L = fort["L_surf"] + f_La = fort["Lambda"] + f_P = fort["P"] + j_L = julia["surface_inductance"] + j_La = julia["plasma_inductance"] + j_P = julia["permeability"] + j_wt0 = julia["wt0"] + + panels = Any[] + + for (name, f_M, j_M) in [("L (surface inductance)", f_L, j_L), + ("Λ (plasma inductance)", f_La, j_La), + ("P (permeability)", f_P, j_P)] + ok = !isempty(f_M) && !isempty(j_M) && size(f_M) == size(j_M) + if ok + # Use a common colour range for the first two panels + all_vals = log10.(max.(abs.([f_M; j_M]), 1e-30)) + clims_mat = (minimum(all_vals), maximum(all_vals)) + push!(panels, _mat_heatmap(f_M, "|$name| — Fortran", m_ctrl; clims=clims_mat)) + push!(panels, _mat_heatmap(j_M, "|$name| — Julia", m_ctrl; clims=clims_mat)) + rel_err = abs.(j_M .- f_M) ./ (abs.(f_M) .+ 1e-30) + push!(panels, _mat_heatmap(rel_err, "Rel. error |$name|", m_ctrl; rel_err=true)) + else + for _ in 1:3 + push!(panels, _mat_heatmap_empty("|$name| (data unavailable)")) + end + end + end + + # wt0 — fundamental vacuum energy matrix, Julia only + if !isempty(j_wt0) + push!(panels, _mat_heatmap(j_wt0, "|wt0| — Julia (no Fortran ref.)", m_ctrl)) + # Fill the remaining two slots to keep the 3-column layout + push!(panels, plot(; title="", axis=false, legend=false, border=:none)) + push!(panels, plot(; title="", axis=false, legend=false, border=:none)) + end + + nrows = length(panels) ÷ 3 + p = plot(panels...; + layout=(nrows, 3), + size=(1800, 420 * nrows), + left_margin=8Plots.mm, bottom_margin=8Plots.mm, top_margin=4Plots.mm) + outfile = joinpath(bench_dir, "matrix_comparison_plots.png") + savefig(p, outfile) + println("Matrix plots saved to: ", abspath(outfile)) +end + + +# ─── Main ──────────────────────────────────────────────────────────────────── + +function main(argv=ARGS) + opts = parse_args(argv) + fortran_dir = opts.fortran_dir + do_plot = opts.do_plot + skip_run = opts.skip_run + use_file_forcing = opts.use_file_forcing + + isdir(fortran_dir) || error("Fortran directory not found: $fortran_dir") + isfile(joinpath(fortran_dir, "equil.in")) || error("equil.in not found in $fortran_dir") + isfile(joinpath(fortran_dir, "dcon.in")) || error("dcon.in not found in $fortran_dir") + + coil_dat_dir = joinpath(@__DIR__, "..", "src", "ForcingTerms", "coil_geometries") + + forcing_mode = use_file_forcing ? "file (Fortran Phi_coil)" : "coil (Julia Biot-Savart)" + println("="^72) + println("GPEC Fortran vs Julia Benchmark") + println(" Fortran dir : $fortran_dir") + println(" Forcing mode : $forcing_mode") + println("="^72) + + println("\n[1/5] Parsing Fortran inputs ...") + p = parse_fortran_run(fortran_dir) + @printf " eq: %s %s (jac=%s, psilow=%.4f, psihigh=%.4f)\n" p.eq_type p.eq_filename p.jac_type p.psilow p.psihigh + @printf " n: %d (delta_mlow=%d, delta_mhigh=%d)\n" p.nn p.delta_mlow p.delta_mhigh + if !use_file_forcing + @printf " machine: %s (%d coil sets, grid %d×%d)\n" p.machine length(p.coil_names) p.mtheta_coil p.nzeta_coil + for (name, curs) in zip(p.coil_names, p.coil_currents) + @printf " %-10s currents: %s A\n" name string(round.(curs; digits=1)) + end + end + + println("\n[2/5] Loading Fortran NetCDF outputs ...") + fort = load_fortran_outputs(fortran_dir, p.nn) + @printf " psilim=%.6f, qlim=%.4f, %d rational surfaces\n" fort["psilim"] fort["qlim"] length(fort["psi_n_rational"]) + @printf " m range: %d:%d\n" fort["mlow"] fort["mhigh"] + + bench_name = "fortran_vs_julia_" * basename(fortran_dir) + bench_dir = joinpath(@__DIR__, bench_name) + mkpath(bench_dir) + println("\nBenchmark directory: $bench_dir") + + eq_filename = p.eq_filename + if !isempty(eq_filename) + src = joinpath(fortran_dir, eq_filename) + dst = joinpath(bench_dir, eq_filename) + if isfile(src) && !isfile(dst) + cp(src, dst) + println("Copied equilibrium file: $eq_filename") + end + end + + # Step 3: Phi_x comparison (coil mode only) + equil = nothing + if use_file_forcing + println("\n[3/5] Skipped Phi_x comparison (file-forcing mode)") + else + println("\n[3/5] Julia Biot-Savart Phi_x vs Fortran Phi_coil ...") + julia_amps, fort_amps, m_vals, equil = compare_phix(p, fort, fortran_dir, coil_dat_dir) + + println(" m Julia |Phi_x| [T·m²] Fortran |Phi_x| [T·m²] ratio") + println(" " * "-"^60) + ratios = Float64[] + for m in sort(m_vals) + ja = get(julia_amps, m, NaN) + fa = get(fort_amps, m, NaN) + ratio = (!isnan(ja) && ja > 1e-40) ? fa / ja : NaN + isnan(ratio) || push!(ratios, ratio) + @printf " %4d %14.4e %14.4e %.4f\n" m ja fa (isnan(ratio) ? 0.0 : ratio) + end + if !isempty(ratios) + @printf " Mean ratio = %.4f ± %.4f (ideal: 1.0000)\n" mean(ratios) std(ratios) + end + end + + # Step 4: Write config and run Julia GPEC + toml_path = joinpath(bench_dir, "gpec.toml") + if use_file_forcing + forcing_path = joinpath(bench_dir, "forcing.dat") + write_forcing_dat(forcing_path, p.nn, fort["m_vals"], fort["Phi_coil_amp"]) + println("\n[4/5] Wrote forcing.dat ($(length(fort["m_vals"])) modes, sfl_flux_Wb convention)") + write_gpec_toml_file(toml_path, fortran_dir, p) + else + write_gpec_toml_coil(toml_path, fortran_dir, p, abspath(coil_dat_dir), fort["qlim"]) + println("\n[4/5] Wrote gpec.toml (coil forcing mode, qhigh=$(fort["qlim"]) to match Fortran sas_flag)") + end + + h5_path = joinpath(bench_dir, "gpec.h5") + if skip_run + println(" --skip-run: skipping Julia GPEC run") + else + println(" Running Julia GPEC (subprocess)...") + isfile(h5_path) && rm(h5_path) + gpec_root = joinpath(@__DIR__, "..") + julia_cmd = Base.julia_cmd() + cmd = `$julia_cmd --project=$gpec_root -e "using GeneralizedPerturbedEquilibrium; GeneralizedPerturbedEquilibrium.main([\"$bench_dir\"])"` + run(cmd) + end + + # Step 5: Compare + println("\n[5/5] Loading Julia outputs and comparing ...") + julia = load_julia_outputs(h5_path) + @printf " psilim=%.6f, qlim=%.4f, %d resonant (surface,n) pairs\n" julia["psilim"] julia["qlim"] length(julia["rational_psi"]) + + println() + table_lines = build_comparison_table(fort, julia, fortran_dir, bench_dir, p.nn) + for line in table_lines + + println(line) + end + + table_path = joinpath(bench_dir, "comparison_table.txt") + open(table_path, "w") do io + for line in table_lines + + println(io, line) + end + end + println("\nComparison table saved to: ", abspath(table_path)) + + do_plot && generate_plots(fort, julia, bench_dir, p.nn) + do_plot && generate_matrix_plots(fort, julia, bench_dir, p.nn) +end + +if abspath(PROGRAM_FILE) == @__FILE__ + main(ARGS) +end diff --git a/benchmarks/benchmark_coil_ForcingTerms_against_fortran.jl b/benchmarks/benchmark_coil_ForcingTerms_against_fortran.jl new file mode 100644 index 0000000000..6b5456de71 --- /dev/null +++ b/benchmarks/benchmark_coil_ForcingTerms_against_fortran.jl @@ -0,0 +1,333 @@ +""" +Coil forcing pipeline validation against a Fortran GPEC run. + +Reads all parameters (coil currents, grid resolution, toroidal mode number, +equilibrium settings) directly from an existing Fortran GPEC run directory, +runs the Julia Biot-Savart pipeline, and produces a 4-panel diagnostic figure: + + Panel 1 (top-left): Coil cross-sections (R, Z) + plasma boundary + Panel 2 (top-right): Normal field bn(θ, ζ) on the plasma boundary + Panel 3 (bot-left): Julia vs Fortran |Phi_x| mode spectrum + Panel 4 (bot-right): Fortran Phi_x / Julia ratio (flat at 1.0 → correct) + +Usage: + julia --project=. benchmarks/check_coil_pipeline.jl [/path/to/fortran/run] + +The Fortran run directory (a DIII-D ideal example run) is taken from the command +line, or from the GPEC_FORTRAN_DIR environment variable if no argument is given. +""" + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.ForcingTerms +using GeneralizedPerturbedEquilibrium.Equilibrium +using GeneralizedPerturbedEquilibrium.Analysis.CoilForcing +using NCDatasets +using Plots +using Printf + +const OUTPUT_DIR = joinpath(@__DIR__, "coil_pipeline") +const COIL_DAT_DIR = joinpath(@__DIR__, "..", "src", "ForcingTerms", "coil_geometries") + +# --------------------------------------------------------------------------- +# Fortran namelist parsing helpers +# --------------------------------------------------------------------------- + +function _strip_fortran_comments(text::String) + join([split(line, '!')[1] for line in split(text, '\n')], '\n') +end + +function _find_scalar(text::String, key::String; default=nothing) + m = match(Regex("\\b$(key)\\s*=\\s*([+-]?[\\d.]+(?:[eE][+-]?\\d+)?)", "i"), text) + isnothing(m) ? default : parse(Float64, m.captures[1]) +end + +function _find_int(text::String, key::String; default::Int=0) + v = _find_scalar(text, key; default=Float64(default)) + isnothing(v) ? default : Int(v) +end + +function _find_string(text::String, key::String; default::String="") + m = match(Regex("\\b$(key)\\s*=\\s*\"([^\"]+)\"", "i"), text) + isnothing(m) ? default : strip(m.captures[1]) +end + +function _find_indexed_string(text::String, name::String, i::Int; default::String="") + m = match(Regex("\\b$(name)\\s*\\(\\s*$(i)\\s*\\)\\s*=\\s*\"([^\"]+)\"", "i"), text) + isnothing(m) ? default : strip(m.captures[1]) +end + +function _find_indexed2_float(text::String, name::String, i::Int, j::Int; default=nothing) + m = match(Regex("\\b$(name)\\s*\\(\\s*$(i)\\s*,\\s*$(j)\\s*\\)\\s*=\\s*([+-]?[\\d.]+(?:[eE][+-]?\\d+)?)", "i"), text) + isnothing(m) ? default : parse(Float64, m.captures[1]) +end + +# --------------------------------------------------------------------------- +# Parse the Fortran run directory +# --------------------------------------------------------------------------- + +struct FortranRunParams + # Equilibrium + eq_type::String + eq_filename::String + jac_type::String + psilow::Float64 + psihigh::Float64 + mtheta_equil::Int + grid_type::String + # Mode number + nn::Int + delta_mlow::Int + delta_mhigh::Int + # Coil grid + mtheta_coil::Int + nzeta_coil::Int + machine::String + # Coil sets: name → current vector + coil_names::Vector{String} + coil_currents::Vector{Vector{Float64}} +end + +function parse_fortran_run(dir::String)::FortranRunParams + equil_file = joinpath(dir, "equil.in") + dcon_file = joinpath(dir, "dcon.in") + coil_file = joinpath(dir, "coil.in") + + isfile(equil_file) || error("equil.in not found in $dir") + isfile(dcon_file) || error("dcon.in not found in $dir") + isfile(coil_file) || error("coil.in not found in $dir") + + eq_text = _strip_fortran_comments(read(equil_file, String)) + dcon_text = _strip_fortran_comments(read(dcon_file, String)) + coil_text = _strip_fortran_comments(read(coil_file, String)) + + # Equilibrium parameters + eq_type = _find_string(eq_text, "eq_type"; default="efit") + eq_file = _find_string(eq_text, "eq_filename"; default="") + jac_type = _find_string(eq_text, "jac_type"; default="hamada") + psilow = _find_scalar(eq_text, "psilow"; default=1e-4) + psihigh = _find_scalar(eq_text, "psihigh"; default=0.99) + mtheta_eq = _find_int(eq_text, "mtheta"; default=256) + grid_type = _find_string(eq_text, "grid_type"; default="ldp") + + # Toroidal mode number and m-range expansion + nn = _find_int(dcon_text, "nn"; default=1) + delta_mlow = _find_int(dcon_text, "delta_mlow"; default=8) + delta_mhigh = _find_int(dcon_text, "delta_mhigh"; default=8) + + # Coil grid parameters + mtheta_coil = _find_int(coil_text, "cmtheta"; default=480) + nzeta_coil = _find_int(coil_text, "cmzeta"; default=40) + machine = _find_string(coil_text, "machine"; default="d3d") + coil_num = _find_int(coil_text, "coil_num"; default=1) + + coil_names = String[] + coil_currents = Vector{Float64}[] + for ci in 1:coil_num + name = _find_indexed_string(coil_text, "coil_name", ci; default="coil$ci") + currents = Float64[] + for cj in 1:48 + cur = _find_indexed2_float(coil_text, "coil_cur", ci, cj) + isnothing(cur) && break + push!(currents, cur) + end + push!(coil_names, name) + push!(coil_currents, currents) + end + + return FortranRunParams(eq_type, eq_file, jac_type, psilow, psihigh, mtheta_eq, + grid_type, nn, delta_mlow, delta_mhigh, + mtheta_coil, nzeta_coil, machine, + coil_names, coil_currents) +end + +function find_output_nc(dir::String, nn::Int) + path = joinpath(dir, "gpec_control_output_n$(nn).nc") + isfile(path) || error("Fortran output not found: $path") + return path +end + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +run_dir = length(ARGS) > 0 ? ARGS[1] : get(ENV, "GPEC_FORTRAN_DIR", "") +isempty(run_dir) && error("No Fortran run directory given: pass it as the first argument or set GPEC_FORTRAN_DIR") +isdir(run_dir) || error("Run directory not found: $run_dir") +mkpath(OUTPUT_DIR) + +println("="^70) +println("Coil Pipeline Validation vs Fortran GPEC") +println(" Run dir: $run_dir") +println("="^70) + +# --------------------------------------------------------------------------- +# [1/4] Parse Fortran inputs +# --------------------------------------------------------------------------- +println("\n[1/4] Parsing Fortran inputs ...") +p = parse_fortran_run(run_dir) + +@printf " eq: %s %s (jac=%s, psilow=%.4f, psihigh=%.4f)\n" p.eq_type p.eq_filename p.jac_type p.psilow p.psihigh +@printf " n: %d (delta_mlow=%d, delta_mhigh=%d)\n" p.nn p.delta_mlow p.delta_mhigh +@printf " grid: mtheta_coil=%d nzeta_coil=%d\n" p.mtheta_coil p.nzeta_coil +@printf " machine: %s (%d coil sets)\n" p.machine length(p.coil_names) +for (name, curs) in zip(p.coil_names, p.coil_currents) + @printf " %-10s currents: %s A\n" name string(round.(curs; digits=1)) +end + +nc_path = find_output_nc(run_dir, p.nn) + +# Read Fortran output (psilim and Phi_x spectrum) +fortran_m = Int[] +fortran_Phix = Float64[] +fortran_psilim = NaN +NCDatasets.Dataset(nc_path, "r") do ds + global fortran_m = Int.(ds["m"][:]) + global fortran_psilim = Float64(ds.attrib["psilim"]) + phi_x_raw = ds["Phi_x"][:, :] # (mpert, 2); col 1=cos, col 2=sin + global fortran_Phix = sqrt.(phi_x_raw[:, 1] .^ 2 .+ phi_x_raw[:, 2] .^ 2) +end +mlow = minimum(fortran_m) +mhigh = maximum(fortran_m) +@printf " Fortran psilim=%.6f, m=%d:%d\n" fortran_psilim mlow mhigh + +# --------------------------------------------------------------------------- +# [2/4] Load equilibrium +# --------------------------------------------------------------------------- +println("\n[2/4] Loading equilibrium ...") +t_equil = @elapsed begin + eq_dict = Dict{String,Any}( + "eq_type" => p.eq_type, + "eq_filename" => p.eq_filename, + "jac_type" => p.jac_type, + "psilow" => p.psilow, + "psihigh" => p.psihigh, + "mtheta" => p.mtheta_equil, + "grid_type" => p.grid_type, + "psi_accuracy" => 0.001, + "flux_surface_rtol" => 1e-7 + ) + eq_config = GeneralizedPerturbedEquilibrium.equilibrium_config(Dict{String,Any}("Equilibrium" => eq_dict), run_dir) + equil = Equilibrium.PlasmaEquilibrium(eq_config) +end +@printf " Done in %.1f s (bt_sign=%d)\n" t_equil equil.params.bt_sign + +# --------------------------------------------------------------------------- +# [3/4] Load coil sets and run Biot-Savart at Fortran psilim surface +# --------------------------------------------------------------------------- +println("\n[3/4] Loading coil sets and computing Biot-Savart ...") +println(" grid: $(p.mtheta_coil)×$(p.nzeta_coil), psi=$(round(fortran_psilim; digits=6))") + +coil_sets = CoilSet[] +for (name, currents) in zip(p.coil_names, p.coil_currents) + dat_path = joinpath(COIL_DAT_DIR, "$(p.machine)_$(name).dat") + isfile(dat_path) || error("Coil geometry file not found: $dat_path") + raw = read_coil_dat(dat_path) + push!(coil_sets, CoilSet(raw.name, raw.ncoil, raw.s, raw.nw, raw.nsec, + raw.x, raw.y, raw.z, Float64.(currents[1:raw.ncoil]))) +end +for cs in coil_sets + @printf " %-10s %d conductors currents: %s A\n" cs.name cs.ncoil string(round.(cs.currents; digits=1)) +end + +julia_modes = ForcingMode[] +bnd_grid = nothing +bn = nothing +t_biot = @elapsed begin + bnd_grid = sample_boundary_grid(equil, p.mtheta_coil, p.nzeta_coil; psi=fortran_psilim) + nobs = p.mtheta_coil * p.nzeta_coil + obs_R = zeros(nobs) + obs_phi = zeros(nobs) + obs_Z = zeros(nobs) + for j in 1:p.nzeta_coil, i in 1:p.mtheta_coil + idx = i + (j - 1) * p.mtheta_coil + obs_R[idx] = bnd_grid.R[i] + obs_phi[idx] = bnd_grid.phi_grid[j] + bnd_grid.phi_offset[i] + obs_Z[idx] = bnd_grid.Z[i] + end + B_R = zeros(nobs) + B_phi = zeros(nobs) + B_Z = zeros(nobs) + compute_biot_savart_boundary!(B_R, B_phi, B_Z, obs_R, obs_phi, obs_Z, coil_sets) + bn = zeros(p.mtheta_coil, p.nzeta_coil) + project_normal_flux!(bn, B_R, B_Z, bnd_grid) + append!(julia_modes, fourier_decompose_bn(bn, bnd_grid, p.nn, mlow, mhigh)) +end +@printf " Done in %.1f s, max|Phi_x| = %.3e T·m²\n" t_biot maximum(abs, bn) + +julia_m = [md.m for md in sort(julia_modes; by=md -> md.m)] +julia_amps = abs.([md.amplitude for md in sort(julia_modes; by=md -> md.m)]) + +# Print comparison table +println("\n m Julia Phi_x [T·m²] Fortran Phi_x [Wb] ratio") +println(" " * "-"^55) +for (jm, ja) in zip(julia_m, julia_amps) + k = findfirst(==(jm), fortran_m) + if !isnothing(k) + fp = fortran_Phix[k] + ratio = ja > 1e-40 ? fp / ja : NaN + @printf " %3d %12.4e %12.4e %.4f\n" jm ja fp ratio + end +end +println(" Expected ratio ≈ 1.000 for all modes.") + +# --------------------------------------------------------------------------- +# [4/4] Generate 4-panel figure +# --------------------------------------------------------------------------- +println("\n[4/4] Generating figure ...") + +function step_series(m_vals, amps) + m_ext = [m_vals[1] - 1; m_vals; m_vals[end] + 1] + amp_ext = [0.0; amps; 0.0] + return m_ext, amp_ext +end + +# Panel 1: coil cross-sections (R, Z) + plasma boundary +p1 = plot_coil_geometry_rz(coil_sets; equil=equil, psi=fortran_psilim) +title!(p1, "Coil cross-sections ($(p.machine), n=$(p.nn))") + +# Panel 2: bn(θ, ζ) heatmap +p2 = plot_bn_contour(bn, p.mtheta_coil, p.nzeta_coil; n=p.nn) +title!(p2, "Normal flux Φₓ(θ, ζ) at ψ=$(round(fortran_psilim; digits=4))") + +# Panel 3: Julia vs Fortran Phi_x spectrum +jm_ext, ja_ext = step_series(julia_m, julia_amps) +fm_ext, fa_ext = step_series(fortran_m, fortran_Phix) +p3 = plot(; xlabel="Poloidal mode m", ylabel="|Phi_x| [T·m²]", + title="Spectrum: Julia vs Fortran (n=$(p.nn))", + legend=:topright) +plot!(p3, jm_ext, ja_ext; seriestype=:steppre, lw=2, color=:blue, + label="Julia ψ=$(round(fortran_psilim; digits=4))") +plot!(p3, fm_ext, fa_ext; seriestype=:steppre, lw=2, color=:orange, + linestyle=:dash, label="Fortran Phi_x") +ylims!(p3, (0, Inf)); +xlims!(p3, mlow - 2, mhigh + 2); + +# Panel 4: Phi_x / Julia ratio +ratio_m = Int[] +ratio_vals = Float64[] +for (jm, ja) in zip(julia_m, julia_amps) + k = findfirst(==(jm), fortran_m) + if !isnothing(k) && ja > 1e-40 + push!(ratio_m, jm) + push!(ratio_vals, fortran_Phix[k] / ja) + end +end +p4 = plot(; xlabel="Poloidal mode m", ylabel="Phi_x / Julia", + title="Ratio (flat at 1.0 → correct)", + legend=:topright) +if !isempty(ratio_m) + scatter!(p4, ratio_m, ratio_vals; color=:blue, label="ratio", markersize=5) + hline!(p4, [1.0]; color=:red, linestyle=:dash, label="expected = 1.0") +end +xlims!(p4, mlow - 2, mhigh + 2) + +run_label = basename(run_dir) +fig = plot(p1, p2, p3, p4; + layout=(2, 2), size=(1400, 900), + plot_title="$run_label | $(p.machine) n=$(p.nn) psilim=$(round(fortran_psilim; digits=4))") + +out_png = joinpath(OUTPUT_DIR, "coil_pipeline_$(run_label)_n$(p.nn).png") +savefig(fig, out_png) +println(" Saved → $out_png") +println("\n" * "="^70) +println("Done.") diff --git a/benchmarks/benchmark_diiid_ideal_ntv_torque.jl b/benchmarks/benchmark_diiid_ideal_ntv_torque.jl new file mode 100644 index 0000000000..3b2ae96fe4 --- /dev/null +++ b/benchmarks/benchmark_diiid_ideal_ntv_torque.jl @@ -0,0 +1,478 @@ +#!/usr/bin/env julia +""" + benchmark_diiid_ideal_ntv_torque.jl + +Benchmark Julia KineticForces NTV against Fortran PENTRC reference values. + +Operates on a Fortran GPEC example directory — uses its equilibrium file (g*), +kinetic file (g*.kin), and Fortran output files (`gpec_xclebsch_n1.out`, +`pentrc_output_n1.nc`). No inputs are duplicated into this repo. A minimal +Julia `gpec.toml` with benchmark-appropriate settings is built in a temp +directory at runtime. + +Loads Fortran `gpec_xclebsch_n1.out` to populate PE Clebsch fields directly, +then runs the KF JBB deweighting + torque integration pipeline — this +isolates KF from the PE pipeline for direct comparison. + +Reference values are read from `pentrc_output_n1.nc` global attributes in +the Fortran example directory. + +Usage: + julia --project=. benchmarks/benchmark_diiid_ideal_ntv_torque.jl [fortran_dir] + + fortran_dir is a Fortran GPEC DIII-D ideal example run directory, taken from + the command line or from \$GPEC_FORTRAN_DIIID. One of the two is required. +""" + +using Printf +using NCDatasets +using Plots + +# Load GPEC +using GeneralizedPerturbedEquilibrium +const GPE = GeneralizedPerturbedEquilibrium +const KF = GPE.KineticForces +const Eq = GPE.Equilibrium +const PE = GPE.PerturbedEquilibrium + +""" +Fortran GPEC DIII-D ideal example run directory, from the environment (no on-disk default). +""" +default_fortran_dir() = get(() -> error("Set GPEC_FORTRAN_DIIID, or pass the Fortran run directory as the first argument"), + ENV, "GPEC_FORTRAN_DIIID") + +""" + discover_inputs(fortran_dir) → (; eq_file, kin_file, xclebsch, pentrc_nc) + +Locate the EFIT g-file, `.kin` kinetic profile, and reference output files +inside a Fortran GPEC example directory. Errors if any required file is +missing or if the g-file cannot be uniquely identified. +""" +function discover_inputs(fortran_dir::String) + isdir(fortran_dir) || error("Fortran example directory not found: $fortran_dir") + # EFIT g-files start with "g" followed by a digit; exclude .kin, backups, + # and gpec_* output files. + eq_candidates = filter(readdir(fortran_dir)) do f + length(f) >= 2 && f[1] == 'g' && isdigit(f[2]) && + !endswith(f, ".kin") && !endswith(f, ".bak") && + !startswith(f, "gpec_") + end + length(eq_candidates) == 1 || + error("Expected exactly one EFIT g-file in $fortran_dir; found: $eq_candidates") + eq_file = joinpath(fortran_dir, eq_candidates[1]) + kin_file = eq_file * ".kin" + xclebsch = joinpath(fortran_dir, "gpec_xclebsch_n1.out") + pentrc_nc = joinpath(fortran_dir, "pentrc_output_n1.nc") + for (label, path) in [("kinetic profile", kin_file), + ("xclebsch output", xclebsch), + ("pentrc NetCDF", pentrc_nc)] + isfile(path) || error("$label not found: $path") + end + return (; eq_file, kin_file, xclebsch, pentrc_nc) +end + +""" + read_pentrc_reference(pentrc_nc) → NamedTuple + +Read `T_total_*` and `dW_total_*` scalar reference values from the Fortran +`pentrc_output_n1.nc` global attributes. +""" +function read_pentrc_reference(pentrc_nc::String) + NCDatasets.Dataset(pentrc_nc, "r") do ds + gatt = ds.attrib + # Reference runs may enable only a subset of methods (e.g. fgar only); missing attributes → NaN. + att(k) = haskey(gatt, k) ? Float64(gatt[k]) : NaN + return ( + T_total_fgar=att("T_total_fgar"), + T_total_tgar=att("T_total_tgar"), + dW_total_fgar=att("dW_total_fgar"), + dW_total_tgar=att("dW_total_tgar") + ) + end +end + +""" + build_benchmark_tomldir(eq_file) → tmpdir + +Construct a temporary directory containing a Julia `gpec.toml` with the +benchmark's fixed Julia-side settings (grid, tolerances, jac_type, etc.) and +a symlink (or copy) to the Fortran EFIT g-file. Returns the tmpdir path so +`GPE.main([tmpdir])` can run against it. +""" +function build_benchmark_tomldir(eq_file::String) + tmpdir = mktempdir(; prefix="diiid_kinetic_bench_") + eq_name = basename(eq_file) + # Symlink is cheap and keeps the dir small. Fall back to copy if the OS + # disallows symlinks (rare on darwin/linux). + try + symlink(eq_file, joinpath(tmpdir, eq_name)) + catch + cp(eq_file, joinpath(tmpdir, eq_name)) + end + open(joinpath(tmpdir, "gpec.toml"), "w") do io + println( + io, + """ +# Auto-generated by benchmark_diiid_ideal_ntv_torque.jl — do not edit. + +[Equilibrium] +eq_filename = "$eq_name" +eq_type = "efit" +jac_type = "hamada" +grid_type = "core_edge_packed" +psilow = 1e-4 +psihigh = 0.993 +mpsi = 128 +mtheta = 256 +newq0 = 0 +flux_surface_rtol = 1e-7 + +[LocalStability] + +[ForceFreeStates] +vac_flag = true + +dW_edge_scan_start = 1.0 # No edge-scan truncation (rational_offset_fraction truncation off) +qlow = 1.02 +qhigh = 1e3 + +nn = 1 +delta_mlow = 8 +delta_mhigh = 8 +mtheta_vacuum = 512 +singfac_min = 1e-4 + +[Riccati] +eulerlagrange_tolerance = 1e-7 +save_interval = 3 +renorm_threshold = 1e4 +""" + ) + end + return tmpdir +end + +""" + load_fortran_xclebsch(filepath) → (psi_grid, clebsch_psi, clebsch_psi1, clebsch_alpha, mpert, mlow) + +Parse Fortran `gpec_xclebsch_n1.out` into arrays matching PE xi_modes layout. + +Header format: +Line 1: title +Line 6: mstep, mpert, mthsurf +Line 8: column headers +Line 9+: data (psi, m, 6 floats) +""" +function load_fortran_xclebsch(filepath::String) + lines = readlines(filepath) + + # Parse mpert from header line 6: " mstep = 1537 mpert = 34 mthsurf = 512" + header_line = lines[6] + m_mpert = match(r"mpert\s*=\s*(\d+)", header_line) + mpert = parse(Int, m_mpert[1]) + + # Find first data line (skip headers) + data_start = 9 + data_lines = lines[data_start:end] + filter!(l -> !isempty(strip(l)), data_lines) + + # Determine mlow from first data block + first_m = parse(Int, split(data_lines[1])[2]) + mlow = first_m + + # Parse data + ndata = length(data_lines) + npsi_total = ndata ÷ mpert + @assert ndata == npsi_total * mpert "Data count $ndata not divisible by mpert=$mpert" + + psi_vals = Float64[] + clebsch_psi1_data = zeros(ComplexF64, npsi_total, mpert) + clebsch_psi_data = zeros(ComplexF64, npsi_total, mpert) + clebsch_alpha_data = zeros(ComplexF64, npsi_total, mpert) + + idx = 0 + for i in 1:npsi_total + for j in 1:mpert + idx += 1 + vals = parse.(Float64, split(data_lines[idx])) + psi = vals[1] + m = Int(vals[2]) + if j == 1 + push!(psi_vals, psi) + end + ipert = m - mlow + 1 + @assert 1 <= ipert <= mpert "Mode m=$m out of range [mlow=$mlow, mhigh=$(mlow+mpert-1)]" + clebsch_psi1_data[i, ipert] = complex(vals[3], vals[4]) # ∂ξ^ψ/∂ψ + clebsch_psi_data[i, ipert] = complex(vals[5], vals[6]) # ξ^ψ + clebsch_alpha_data[i, ipert] = complex(vals[7], vals[8]) # ξ^α + end + end + + return psi_vals, clebsch_psi_data, clebsch_psi1_data, clebsch_alpha_data, mpert, mlow +end + + +_p(args...) = (println(stderr, args...); flush(stderr)) +_pf(fmt, args...) = (print(stderr, Printf.format(Printf.Format(fmt), args...)); flush(stderr)) + +function run_benchmark(fortran_dir::String=default_fortran_dir()) + _p("="^70) + _p(" DIIID Kinetic Benchmark: Julia KineticForces vs Fortran PENTRC") + _p(" Fortran example: $fortran_dir") + _p("="^70) + + inputs = discover_inputs(fortran_dir) + ref = read_pentrc_reference(inputs.pentrc_nc) + _pf(" EFIT g-file: %s\n", basename(inputs.eq_file)) + _pf(" Kinetic: %s\n", basename(inputs.kin_file)) + + tomldir = build_benchmark_tomldir(inputs.eq_file) + + # Run FFS via main() on the tmpdir + _p("\n--- Equilibrium + ForceFreeStates (via main()) ---") + t0 = time() + result = GPE.main([tomldir]) + ffs = result.ffs + equil = ffs.equil + metric = ffs.metric + + _pf(" FFS completed in %.1f s\n", time() - t0) + _pf(" mpert=%d, mlow=%d, mhigh=%d\n", ffs.mpert, ffs.mlow, ffs.mhigh) + + # Load Fortran xclebsch data + _p("\n--- Load Fortran xclebsch ---") + psi_grid_f, clebsch_psi, clebsch_psi1, clebsch_alpha, mpert_f, mlow_f = + load_fortran_xclebsch(inputs.xclebsch) + + npsi_f = length(psi_grid_f) + _pf(" Loaded %d ψ surfaces, mpert=%d, mlow=%d from Fortran xclebsch\n", + npsi_f, mpert_f, mlow_f) + _pf(" ψ range: [%.6f, %.6f]\n", psi_grid_f[1], psi_grid_f[end]) + + if mpert_f != ffs.mpert || mlow_f != ffs.mlow + @warn "Mode ranges differ: Fortran mpert=$mpert_f,mlow=$mlow_f vs Julia mpert=$(ffs.mpert),mlow=$(ffs.mlow)" + end + + # Build PE state from Fortran data and run JBB deweighting + _p("\n--- Build PE state (Fortran Clebsch → dbob_m/divx_m) ---") + t1 = time() + + # Construct PerturbedEquilibriumState with Fortran Clebsch data. + # Fortran gpec_xclebsch already writes ξ^α = xms/χ₁, exactly what the KF operator consumes — feed directly. + pe_state = PE.PerturbedEquilibriumState(; + response=PE.PlasmaResponse(; ffs), + psi_grid=psi_grid_f, + xi_modes=( + clebsch_psi=clebsch_psi, + clebsch_psi1=clebsch_psi1, + clebsch_alpha=clebsch_alpha + ) + ) + + # KF configuration matching Fortran pentrc.in. + # tgar defaults off during perf iteration (same kernels as fgar; see + # memory/feedback_kf_fgar_subsumes_tgar.md). Set BENCHMARK_TGAR=1 to enable + # for the final full-validation run. + tgar_enabled = get(ENV, "BENCHMARK_TGAR", "0") == "1" + kf_ctrl = KF.KineticForcesControl(; + fgar_flag=true, tgar_flag=tgar_enabled, bounce_harmonic_max=4, + nutype="harmonic", f0type="maxwellian", + collisionality_factor=1, psilims=[0.0, 1.0]) + + # Initialize KF internal state + kf_intr = KF.KineticForcesInternal(equil; verbose=false) + + # Run set_perturbation_data! — builds dbob_m, divx_m, xs_m via JBB deweighting + KF.set_perturbation_data!(kf_intr, pe_state, ffs, equil, metric) + + _pf(" JBB deweighting completed in %.1f s\n", time() - t1) + + # Sanity checks + if kf_intr.dbob_m === nothing || kf_intr.divx_m === nothing + error("dbob_m or divx_m is nothing after set_perturbation_data!") + end + + # Sample dbob_m at midplane + psi_mid = 0.5 + dbob_sample = kf_intr.dbob_m(psi_mid) + divx_sample = kf_intr.divx_m(psi_mid) + _pf(" dbob_m(ψ=0.5) max|mode| = %.4e\n", maximum(abs.(dbob_sample))) + _pf(" divx_m(ψ=0.5) max|mode| = %.4e\n", maximum(abs.(divx_sample))) + + # Load kinetic profiles and run torque integration + _p("\n--- Torque integration ---") + t2 = time() + + # The default species (one deuterium main ion, carbon impurity) match pentrc.in. + Eq.attach_kinetic_profiles!(equil, inputs.kin_file) + kinetic_profiles = KF.ntv_profiles(kf_ctrl, equil) + + kf_state = KF.KineticForcesState() + KF.compute_torque_all_methods!(kf_state, kf_intr, kf_ctrl, equil, kinetic_profiles) + + kf_runtime = time() - t2 + _pf(" Torque integration completed in %.1f s\n", kf_runtime) + + # ψ-step count per method — a key KPI for outer-ODE tolerance tuning. + _p("\n ψ-step counts (outer Tsit5 accepted steps per method):") + for (method_key, mr) in kf_state.method_results + _pf(" %-5s: %5d accepted ψ-steps\n", method_key, mr.psi_nsteps) + end + + # Compare results + _p("\n" * "="^70) + _p(" Results Comparison") + _p("="^70) + + results = Dict{String,NamedTuple}() + + for (method_key, ref_T, ref_dW) in [ + ("fgar", ref.T_total_fgar, ref.dW_total_fgar), + ("tgar", ref.T_total_tgar, ref.dW_total_tgar)] + + if haskey(kf_state.method_results, method_key) + mr = kf_state.method_results[method_key] + T_julia = real(mr.total_torque) + dW_julia = mr.total_energy + T_err = abs(T_julia - ref_T) / abs(ref_T) * 100 + dW_err = abs(dW_julia - ref_dW) / abs(ref_dW) * 100 + + results[method_key] = (; T_julia, dW_julia, T_err, dW_err) + + _pf("\n %-5s Torque: Julia = %12.6f Fortran = %12.6f err = %6.2f%%\n", + uppercase(method_key), T_julia, ref_T, T_err) + _pf(" %-5s Energy: Julia = %12.6f Fortran = %12.6f err = %6.2f%%\n", + uppercase(method_key), dW_julia, ref_dW, dW_err) + else + _pf("\n %-5s: NOT RUN\n", uppercase(method_key)) + end + end + + _p("\n" * "="^70) + + # Check pass/fail + all_pass = true + for (method_key, r) in results + if r.T_err > 5.0 || r.dW_err > 5.0 + @warn "$(uppercase(method_key)) exceeds 5% threshold" T_err = r.T_err dW_err = r.dW_err + all_pass = false + end + end + + if all_pass && !isempty(results) + _p(" All methods within 5% of Fortran reference") + elseif isempty(results) + _p(" No methods produced results") + else + _p(" Some methods exceed 5% threshold") + end + + # Plot dT/dψ and T(ψ) overlay vs Fortran PENTRC reference + _p("\n" * "="^70) + _p(" Plotting dT/dψ and T(ψ) overlay vs Fortran") + _p("="^70) + + pentrc_nc = inputs.pentrc_nc + + # Read Fortran reference profiles. PENTRC writes NetCDF in Fortran order + # `dTdpsi_(i, ell, psi)` with i∈{1,2}={real,imag}; NCDatasets + # preserves that order in the Julia array, so we slice [1, :, :] for real + # and sum over ell (dim 1 of the resulting 2D array). + fortran_profiles = Dict{String,NamedTuple}() + NCDatasets.Dataset(pentrc_nc, "r") do ds + for method_key in keys(results) + psi_var = "psi_$method_key" + dt_var = "dTdpsi_$method_key" + T_var = "T_$method_key" + if !(haskey(ds, psi_var) && haskey(ds, dt_var) && haskey(ds, T_var)) + @warn "Fortran NetCDF missing variables for method" method = method_key + continue + end + psi_f = Array(ds[psi_var][:]) + dTdpsi_f = Array(ds[dt_var][:, :, :]) # (i, ell, psi) + T_f = Array(ds[T_var][:, :, :]) + # Take real slice (i=1), sum over ell (dim 1 of the 2D real slice → (psi,)) + dT_re_f = dropdims(sum(dTdpsi_f[1, :, :]; dims=1); dims=1) + T_re_f = dropdims(sum(T_f[1, :, :]; dims=1); dims=1) + fortran_profiles[method_key] = (; psi=psi_f, dTdpsi=dT_re_f, T=T_re_f) + _pf(" Fortran %-5s: %d ψ points, T_final = %.6f N·m\n", + method_key, length(psi_f), T_re_f[end]) + end + end + + # Plot per method: two panels (dT/dψ, cumulative T) saved as separate PNGs. + # Separate files avoid a GR-backend rendering bug that surfaced when + # stacking panels into a layout=(2,N) figure with Float64 arrays of + # different lengths. Simpler is also easier to regenerate outside the full + # benchmark: the raw arrays are dumped to `.dat` so a standalone plot + # script can re-draw without a 5-minute rerun. + method_keys = sort(collect(intersect(keys(results), keys(fortran_profiles)))) + if isempty(method_keys) + @warn "No overlapping methods between Julia and Fortran; skipping plot" + return results + end + + for mkey in method_keys + mr = kf_state.method_results[mkey] + fp = fortran_profiles[mkey] + + _pf(" %s: Julia psi_grid=%d, dtdpsi=%d, t_cum=%d | Fortran psi=%d, dTdpsi=%d, T=%d\n", + mkey, length(mr.psi_grid), length(mr.dtdpsi), length(mr.t_cumulative), + length(fp.psi), length(fp.dTdpsi), length(fp.T)) + + # Dump raw arrays so the plot can be regenerated without rerunning. + open(joinpath(@__DIR__, "kf_$(mkey)_profiles.dat"), "w") do io + println(io, "# psi_julia dTdpsi_julia_re T_julia_re") + for i in eachindex(mr.psi_grid) + @printf(io, "%.8e %.8e %.8e\n", + mr.psi_grid[i], real(mr.dtdpsi[i]), real(mr.t_cumulative[i])) + end + println(io, "# --- fortran ---") + println(io, "# psi_fortran dTdpsi_fortran T_fortran") + for i in eachindex(fp.psi) + @printf(io, "%.8e %.8e %.8e\n", fp.psi[i], fp.dTdpsi[i], fp.T[i]) + end + end + + # dT/dψ panel + p1 = plot(; xlabel="ψ_n", ylabel="dT/dψ [N·m]", + title="$(uppercase(mkey)): dT/dψ", legend=:topleft, + left_margin=12Plots.mm, bottom_margin=4Plots.mm, size=(900, 500)) + plot!(p1, Float64.(fp.psi), Float64.(fp.dTdpsi); + label="Fortran", lw=2, color=:black, linestyle=:dash) + plot!(p1, Float64.(mr.psi_grid), Float64.(real.(mr.dtdpsi)); + label="Julia", lw=1.5, color=:crimson) + path1 = joinpath(@__DIR__, "kf_$(mkey)_dTdpsi_vs_fortran.png") + try + savefig(p1, path1) + _p(" Saved dT/dψ panel → $path1") + catch err + @warn "dT/dψ panel savefig failed" method = mkey err + end + + # Cumulative T panel + p2 = plot(; xlabel="ψ_n", ylabel="T [N·m]", + title="$(uppercase(mkey)): cumulative T", legend=:topleft, + left_margin=12Plots.mm, bottom_margin=4Plots.mm, size=(900, 500)) + plot!(p2, Float64.(fp.psi), Float64.(fp.T); + label="Fortran", lw=2, color=:black, linestyle=:dash) + plot!(p2, Float64.(mr.psi_grid), Float64.(real.(mr.t_cumulative)); + label="Julia", lw=1.5, color=:crimson) + path2 = joinpath(@__DIR__, "kf_$(mkey)_T_vs_fortran.png") + try + savefig(p2, path2) + _p(" Saved cumulative T panel → $path2") + catch err + @warn "cumulative T panel savefig failed" method = mkey err + end + end + + return results +end + +# Run only when invoked as a script (so other benchmarks can `include()` this +# file to reuse `load_fortran_xclebsch` without triggering the full run). +if abspath(PROGRAM_FILE) == @__FILE__ + fortran_dir = length(ARGS) >= 1 ? ARGS[1] : default_fortran_dir() + results = run_benchmark(fortran_dir) +end diff --git a/benchmarks/benchmark_diiid_kinetic_stability.jl b/benchmarks/benchmark_diiid_kinetic_stability.jl new file mode 100644 index 0000000000..46d970b372 --- /dev/null +++ b/benchmarks/benchmark_diiid_kinetic_stability.jl @@ -0,0 +1,252 @@ +#!/usr/bin/env julia +""" + benchmark_diiid_kinetic_stability.jl + +Benchmark the self-consistent kinetic-DCON path (KF → FFS → PE) against +Fortran GPEC's kinetic DCON reference. + +Runs `GPE.main()` with `kinetic = "KineticForces"` +against the EFIT g-file and `.kin` profile taken from a Fortran GPEC +kinetic example directory, then compares the least-stable total-energy +eigenvalue `ForceFreeStates/FreeBoundaryStability/eigenmode_energies[1]` against `W_t_eigenvalue[:, 0]` in the Fortran +`dcon_output_n1.nc`. + +No inputs are duplicated into this repo — everything is read from the +Fortran example directory, and the Julia `gpec.toml` is written to a +`mktempdir()` scratch directory at runtime. + +Acceptance: + - Re(et[1]) within 5 % of Fortran + - Im(et[1]) within 20 % of Fortran (stretch: 5 %) +Im is the damping-rate component — smaller magnitude, more sensitive to +the non-Hermitian FKG reduction and the inner kinetic-matrix tolerances. + +Usage: + julia --project=. benchmarks/benchmark_diiid_kinetic_stability.jl [fortran_dir] + + fortran_dir is a Fortran GPEC DIII-D kinetic example run directory, taken from + the command line or from \$GPEC_FORTRAN_DIIID_DCON. One of the two is required. +""" + +using Printf +using HDF5 +using NCDatasets + +using GeneralizedPerturbedEquilibrium +const GPE = GeneralizedPerturbedEquilibrium +const AnalysisFFS = GPE.Analysis.ForceFreeStates + +""" +Fortran GPEC DIII-D kinetic example run directory, from the environment (no on-disk default). +""" +default_fortran_dir() = get(() -> error("Set GPEC_FORTRAN_DIIID_DCON, or pass the Fortran run directory as the first argument"), + ENV, "GPEC_FORTRAN_DIIID_DCON") + +""" + discover_inputs(fortran_dir) → (; eq_file, kin_file, dcon_nc) + +Locate the EFIT g-file, its sibling `.kin` kinetic profile, and the +Fortran `dcon_output_n1.nc` reference NetCDF inside a Fortran GPEC +kinetic example directory. Errors if any required file is missing or if +the g-file cannot be uniquely identified. +""" +function discover_inputs(fortran_dir::String) + isdir(fortran_dir) || error("Fortran example directory not found: $fortran_dir") + # EFIT g-files start with "g" followed by a digit; exclude .kin, backups, + # and gpec_* output files. + eq_candidates = filter(readdir(fortran_dir)) do f + length(f) >= 2 && f[1] == 'g' && isdigit(f[2]) && + !endswith(f, ".kin") && !endswith(f, ".bak") && + !startswith(f, "gpec_") + end + length(eq_candidates) == 1 || + error("Expected exactly one EFIT g-file in $fortran_dir; found: $eq_candidates") + eq_file = joinpath(fortran_dir, eq_candidates[1]) + kin_file = eq_file * ".kin" + dcon_nc = joinpath(fortran_dir, "dcon_output_n1.nc") + for (label, path) in [("kinetic profile", kin_file), + ("dcon NetCDF", dcon_nc)] + isfile(path) || error("$label not found: $path") + end + return (; eq_file, kin_file, dcon_nc) +end + +""" + read_dcon_reference(dcon_nc) → NamedTuple + +Read the Fortran kinetic-DCON least-stable total-energy eigenvalue from +`dcon_output_n1.nc`. Also returns the plasma and vacuum least-stable +eigenvalues for diagnostic printout (the three are *separately* +diagonalised matrices in DCON, so they do not sum to the total — the +partition is informational only). + +NetCDF layout (dims in Fortran order, preserved by NCDatasets): +W_t_eigenvalue(mode, i) — total energy, i=1 real, i=2 imag +W_p_eigenvalue(mode, i) — plasma energy +W_v_eigenvalue(mode, i) — vacuum energy +Mode index 1 is the least-stable mode. +""" +function read_dcon_reference(dcon_nc::String) + NCDatasets.Dataset(dcon_nc, "r") do ds + Wt = Array(ds["W_t_eigenvalue"][:, :]) + Wp = Array(ds["W_p_eigenvalue"][:, :]) + Wv = Array(ds["W_v_eigenvalue"][:, :]) + return ( + W_t_re=Wt[1, 1], W_t_im=Wt[1, 2], + W_p_re=Wp[1, 1], W_p_im=Wp[1, 2], + W_v_re=Wv[1, 1], W_v_im=Wv[1, 2] + ) + end +end + +""" + build_benchmark_tomldir(eq_file, kin_file) → tmpdir + +Construct a temporary directory containing a Julia `gpec.toml` with +kinetic-DCON settings that mirror the Fortran `dcon.in` + `equil.in` + +`pentrc.in` from the kinetic example, plus a symlink (or copy) to the +EFIT g-file. The `[KineticForces]` section points at the Fortran `.kin` +file by absolute path. +""" +function build_benchmark_tomldir(eq_file::String, kin_file::String) + tmpdir = mktempdir(; prefix="diiid_kinetic_dcon_bench_") + eq_name = basename(eq_file) + try + symlink(eq_file, joinpath(tmpdir, eq_name)) + catch + cp(eq_file, joinpath(tmpdir, eq_name)) + end + open(joinpath(tmpdir, "gpec.toml"), "w") do io + println( + io, + """ +# Auto-generated by benchmark_diiid_kinetic_stability.jl — do not edit. + +[Equilibrium] +eq_filename = "$eq_name" +kinetic_file = "$kin_file" +grid_type = "pow1" # Matches Fortran DIIID_kinetic_example (equil/grid.f90 powspace) +psilow = 0.01 +psihigh = 0.993 +mpsi = 128 +mtheta = 256 +newq0 = 0 +flux_surface_rtol = 1e-7 + +[LocalStability] + +[ForceFreeStates] +vac_flag = true + +dW_edge_scan_start = 1.0 # No edge-scan truncation (rational_offset_fraction truncation off) +qlow = 1.02 +qhigh = 1e3 + +nn = 1 +delta_mlow = 8 +delta_mhigh = 8 +mtheta_vacuum = 512 + +integrator = "Forward" +kinetic = "KineticForces" +singfac_min = 1e-4 + +[Forward] +eulerlagrange_tolerance = 1e-7 +renorm_threshold = 1e4 + +[KineticForces] +bounce_harmonic_max = 4 +nutype = "harmonic" +f0type = "maxwellian" +""" + ) + end + return tmpdir +end + +_p(args...) = (println(stderr, args...); flush(stderr)) +_pf(fmt, args...) = (print(stderr, Printf.format(Printf.Format(fmt), args...)); flush(stderr)) + +function run_benchmark(fortran_dir::String=default_fortran_dir()) + _p("="^70) + _p(" DIIID Kinetic-DCON Benchmark: Julia KF→FFS vs Fortran DCON") + _p(" Fortran example: $fortran_dir") + _p("="^70) + + inputs = discover_inputs(fortran_dir) + ref = read_dcon_reference(inputs.dcon_nc) + _pf(" EFIT g-file: %s\n", basename(inputs.eq_file)) + _pf(" Kinetic: %s\n", basename(inputs.kin_file)) + _pf(" Reference (W_t_eigenvalue[1]): Re = %.6f Im = %.6f\n", + ref.W_t_re, ref.W_t_im) + + tomldir = build_benchmark_tomldir(inputs.eq_file, inputs.kin_file) + + _p("\n--- Running GPE.main with kinetic = \"KineticForces\" ---") + t0 = time() + GPE.main([tomldir]) + wall = time() - t0 + _pf(" GPE.main completed in %.1f s\n", wall) + + # Read Julia result + h5path = joinpath(tomldir, "gpec.h5") + isfile(h5path) || error("Expected Julia output not found: $h5path") + + et = h5open(h5path, "r") do h5 + read(h5["ForceFreeStates/FreeBoundaryStability/eigenmode_energies"]) + end + isempty(et) && error("ForceFreeStates/FreeBoundaryStability/eigenmode_energies is empty in $h5path") + # et is stored as a length-2*N real array (re,im interleaved) by HDF5.jl + # when the underlying Julia array is ComplexF64. NCDatasets and HDF5 give + # us a ComplexF64 array directly here. + julia_re = real(et[1]) + julia_im = imag(et[1]) + + re_err = abs(julia_re - ref.W_t_re) / abs(ref.W_t_re) * 100 + im_err = abs(julia_im - ref.W_t_im) / abs(ref.W_t_im) * 100 + + _p("\n" * "="^70) + _p(" Results Comparison (kinetic-DCON least-stable eigenvalue)") + _p("="^70) + _pf(" Re(et[1]): Julia = %12.6f Fortran = %12.6f err = %6.2f%%\n", + julia_re, ref.W_t_re, re_err) + _pf(" Im(et[1]): Julia = %12.6f Fortran = %12.6f err = %6.2f%%\n", + julia_im, ref.W_t_im, im_err) + _p("\n Plasma/vacuum partition (Fortran least-stable, separate diag.):") + _pf(" W_p[1] = %.6f + %.6fi\n", ref.W_p_re, ref.W_p_im) + _pf(" W_v[1] = %.6f + %.6fi\n", ref.W_v_re, ref.W_v_im) + + # Acceptance check + re_pass = re_err <= 5.0 + im_pass = im_err <= 20.0 + im_stretch = im_err <= 5.0 + + _p("\n" * "="^70) + if re_pass && im_pass + _p(" PASS — Re within 5 %, Im within 20 %" * + (im_stretch ? " (stretch: Im within 5 %)" : "")) + else + _p(" FAIL — thresholds exceeded:") + re_pass || _pf(" Re err %.2f%% > 5%%\n", re_err) + im_pass || _pf(" Im err %.2f%% > 20%%\n", im_err) + end + _p("="^70) + + # cond(F̄) vs ψ diagnostic — shows whether kinetic singular surfaces + # (det(F̄)=0) were found and why peaks were/weren't accepted. + png_path = joinpath(@__DIR__, "diiid_kinetic_cond_fbar.png") + if AnalysisFFS.plot_cond_fbar(h5path; save_path=png_path) !== nothing + println(" cond(F̄) plot: ", abspath(png_path)) + end + + return (; julia_re, julia_im, + fortran_re=ref.W_t_re, fortran_im=ref.W_t_im, + re_err, im_err, wall, tomldir) +end + +# Run only when invoked as a script. +if abspath(PROGRAM_FILE) == @__FILE__ + fortran_dir = length(ARGS) >= 1 ? ARGS[1] : default_fortran_dir() + run_benchmark(fortran_dir) +end diff --git a/benchmarks/benchmark_git_branches.jl b/benchmarks/benchmark_git_branches.jl new file mode 100755 index 0000000000..3daf54d5d5 --- /dev/null +++ b/benchmarks/benchmark_git_branches.jl @@ -0,0 +1,406 @@ +#!/usr/bin/env julia +""" +benchmark_git_branches.jl - Generic Git branch benchmarking tool for GPEC + +Compares performance between two Git branches or commits by running a specified +example multiple times to warm up Julia's JIT compiler, then measuring runtime. + +# Usage + +Basic comparison of two branches: +```bash +julia benchmarks/benchmark_git_branches.jl --example examples/DIIID-like_ideal_example --branch1 develop --branch2 feature-branch +``` + +Compare specific commits: +```bash +julia benchmarks/benchmark_git_branches.jl --example examples/DIIID-like_ideal_example --commit1 abc123 --commit2 def456 +``` + +Compare current state against old state of same branch: +```bash +julia benchmarks/benchmark_git_branches.jl --example examples/DIIID-like_ideal_example --branch1 develop --commit1 HEAD~10 --branch2 develop +``` + +# Options + +- `--example PATH`: Path to example directory (required) +- `--branch1 NAME`: First branch name (default: "develop") +- `--branch2 NAME`: Second branch name (default: current branch) +- `--commit1 HASH`: Specific commit for branch1 (default: branch HEAD) +- `--commit2 HASH`: Specific commit for branch2 (default: branch HEAD) +- `--runs N`: Number of warm runs to average (default: 2) +- `--output FILE`: Write results to file (default: stdout only) + +# Requirements + +- Example directory must contain a `gpec.toml` config file runnable via: `using GeneralizedPerturbedEquilibrium; GeneralizedPerturbedEquilibrium.main(["path"])` +- Working directory must be clean or changes will be stashed during branch switching + +# Output Metrics + +For each branch/commit, reports: +- Eigenmode energy (et[1]) from gpec.h5 +- Integration steps from gpec.h5 +- Runtime (averaged over warm runs) +- Per-stage runtime breakdown from `Info/Runtimes` (averaged over warm runs, when both refs record it) +- Git commit hash + +# Example Output + +``` +============================================================ +GPEC Branch Benchmark Comparison +============================================================ +Example: examples/DIIID-like_ideal_example + +Branch 1: develop @ abc123 + Eigenmode energy (et[1]): 1.7005 + Integration steps: 911 + Runtime (warmed, avg): 145.3 s + +Branch 2: feature-branch @ def456 + Eigenmode energy (et[1]): 1.7072 + Integration steps: 136 + Runtime (warmed, avg): 142.8 s + +Comparison: + Eigenmode Δ: +0.0067 (+0.4%) + Steps Δ: -775 (-85.1%) + Runtime Δ: -2.5 s (-1.7% faster) + +Per-stage runtime (avg over warm runs): + Stage Branch 1 Branch 2 Δ s Δ % + equilibrium 2.10s 2.08s -0.02 -1.0% + force_free_states 100.40s 98.10s -2.30 -2.3% + total 145.30s 142.80s -2.50 -1.7% +``` +""" + +using HDF5, Printf + +# Parse command-line arguments +function parse_args(args) + options = Dict( + "example" => nothing, + "branch1" => "develop", + "branch2" => nothing, # Default to current branch + "commit1" => nothing, + "commit2" => nothing, + "runs" => 2, + "output" => nothing + ) + + i = 1 + while i <= length(args) + if startswith(args[i], "--") + key = args[i][3:end] + if haskey(options, key) + if i < length(args) && !startswith(args[i+1], "--") + value = args[i+1] + if key == "runs" + options[key] = parse(Int, value) + else + options[key] = value + end + i += 2 + else + error("Option --$key requires a value") + end + else + error("Unknown option: $(args[i])") + end + else + error("Unexpected argument: $(args[i])") + end + end + + # Validate required arguments + if options["example"] === nothing + error("--example is required") + end + + # Default branch2 to current branch if not specified + if options["branch2"] === nothing + options["branch2"] = strip(read(`git branch --show-current`, String)) + end + + return options +end + +# Get commit hash for a branch/commit reference +function get_commit_hash(ref) + return strip(read(`git rev-parse --short $ref`, String)) +end + +# Check if working directory is clean +function check_clean_workdir() + status = read(`git status --porcelain`, String) + if !isempty(status) + println("Warning: Working directory has uncommitted changes. Stashing...") + run(`git stash`) + return true + end + return false +end + +# Restore stashed changes +function restore_stash(was_stashed) + if was_stashed + println("Restoring stashed changes...") + run(`git stash pop`) + end +end + +# Checkout a specific branch/commit +function checkout_ref(branch, commit) + if commit !== nothing + println("Checking out $branch @ $commit...") + run(`git checkout $commit`) + else + println("Checking out $branch...") + run(`git checkout $branch`) + end +end + +# Read the per-stage wall-clock seconds a run recorded under Info/Runtimes. +# Returns an empty Dict for refs that predate the feature, so those still benchmark. +function read_stage_runtimes(gpec_path) + stages = Dict{String,Float64}() + isfile(gpec_path) || return stages + h5open(gpec_path, "r") do h5 + haskey(h5, "Info/Runtimes") || return + group = h5["Info/Runtimes"] + for stage in keys(group) + stages[stage] = read(group[stage]) + end + end + return stages +end + +# Average per-stage runtimes over the warm runs, keeping only stages present in every run. +function average_stage_runtimes(stage_times) + isempty(stage_times) && return Dict{String,Float64}() + shared = intersect(map(keys, stage_times)...) + return Dict(stage => sum(s[stage] for s in stage_times) / length(stage_times) for stage in shared) +end + +# Run the example benchmark. +# Each run is a fresh Julia subprocess so that git branch switches take effect +# (using statement only fires once per process; re-checkout without subprocess restart +# would silently benchmark the first branch's code for all subsequent branches). +function run_example_benchmark(example_path, num_runs) + abs_example_path = abspath(example_path) + project_root = abspath(joinpath(example_path, "../..")) + println("\nRunning example: $abs_example_path") + println("Warming up with $(num_runs + 1) runs...") + + # Write a temp script so `using GeneralizedPerturbedEquilibrium` is at top-level in each subprocess. + # Pkg.instantiate() handles branches whose environments haven't been resolved yet. + tmpscript = tempname() * ".jl" + write(tmpscript, "using Pkg; Pkg.instantiate(); using GeneralizedPerturbedEquilibrium; GeneralizedPerturbedEquilibrium.main([ARGS[1]])\n") + + try + # First run for JIT compilation + println("\n[1/$(num_runs+1)] First run (JIT compilation)...") + run(`julia --project=$project_root $tmpscript $abs_example_path`) + + # Warm runs for timing. Each run overwrites gpec.h5, so the per-stage + # Info/Runtimes record must be collected inside the loop, not after it. + gpec_path = joinpath(abs_example_path, "gpec.h5") + runtimes = Float64[] + stage_times = Vector{Dict{String,Float64}}() + for i in 1:num_runs + println("\n[$((i+1))/$(num_runs+1)] Warm run $i...") + t_start = time() + run(`julia --project=$project_root $tmpscript $abs_example_path`) + runtime = time() - t_start + push!(runtimes, runtime) + push!(stage_times, read_stage_runtimes(gpec_path)) + println(" Runtime: $(round(runtime, digits=2)) s") + end + + # Extract metrics from gpec.h5 + if !isfile(gpec_path) + error("gpec.h5 not found at $gpec_path") + end + + h5 = h5open(gpec_path, "r") + et = read(h5["ForceFreeStates/FreeBoundaryStability/eigenmode_energies"]) + nsteps = read(h5["ForceFreeStates/Solutions/ForwardIntegration/nstep"]) + close(h5) + + avg_runtime = sum(runtimes) / length(runtimes) + + return ( + eigenvalue=real(et[1]), + steps=nsteps, + runtime=avg_runtime, + stages=average_stage_runtimes(stage_times) + ) + finally + rm(tmpscript; force=true) + end +end + +# Per-stage runtime breakdown (Info/Runtimes), averaged over the warm runs of each branch. +# Silently skipped when either side lacks the record, so comparisons against older refs work. +function print_stage_comparison(r1, r2) + shared = intersect(keys(r1.metrics.stages), keys(r2.metrics.stages)) + isempty(shared) && return nothing + ordered = sort(collect(shared); by=stage -> (stage == "total", stage)) + + println("\nPer-stage runtime (avg over warm runs):") + @printf(" %-22s %10s %10s %10s %9s\n", "Stage", "Branch 1", "Branch 2", "Δ s", "Δ %") + for stage in ordered + t1 = r1.metrics.stages[stage] + t2 = r2.metrics.stages[stage] + # A stage that ran in no measurable time has no meaningful percentage baseline. + pct = t1 == 0 ? "—" : @sprintf("%+.1f%%", 100 * (t2 - t1) / t1) + @printf(" %-22s %9.2fs %9.2fs %+9.2f %9s\n", stage, t1, t2, t2 - t1, pct) + end + return nothing +end + +# Main benchmarking function +function benchmark_branches(options) + println("="^60) + println("GPEC Branch Benchmark Comparison") + println("="^60) + println("Example: $(options["example"])") + println() + + # Store original branch + original_branch = strip(read(`git branch --show-current`, String)) + was_stashed = check_clean_workdir() + + results = Dict() + + try + # Benchmark branch 1 + println("\n" * "="^60) + println("Benchmarking Branch 1: $(options["branch1"])") + println("="^60) + checkout_ref(options["branch1"], options["commit1"]) + commit1 = get_commit_hash("HEAD") + results["branch1"] = ( + name=options["branch1"], + commit=commit1, + metrics=run_example_benchmark(options["example"], options["runs"]) + ) + + # Benchmark branch 2 + println("\n" * "="^60) + println("Benchmarking Branch 2: $(options["branch2"])") + println("="^60) + checkout_ref(options["branch2"], options["commit2"]) + commit2 = get_commit_hash("HEAD") + results["branch2"] = ( + name=options["branch2"], + commit=commit2, + metrics=run_example_benchmark(options["example"], options["runs"]) + ) + + finally + # Restore original state + println("\nRestoring original branch: $original_branch") + run(`git checkout $original_branch`) + restore_stash(was_stashed) + end + + # Print comparison + println("\n" * "="^60) + println("BENCHMARK RESULTS") + println("="^60) + println() + + r1 = results["branch1"] + r2 = results["branch2"] + + println("Branch 1: $(r1.name) @ $(r1.commit)") + @printf(" Eigenmode energy (et[1]): %.4f\n", r1.metrics.eigenvalue) + @printf(" Integration steps: %d\n", r1.metrics.steps) + @printf(" Runtime (warmed, avg): %.2f s\n", r1.metrics.runtime) + println() + + println("Branch 2: $(r2.name) @ $(r2.commit)") + @printf(" Eigenmode energy (et[1]): %.4f\n", r2.metrics.eigenvalue) + @printf(" Integration steps: %d\n", r2.metrics.steps) + @printf(" Runtime (warmed, avg): %.2f s\n", r2.metrics.runtime) + println() + + # Comparison + println("Comparison (Branch 2 vs Branch 1):") + + ev_delta = r2.metrics.eigenvalue - r1.metrics.eigenvalue + ev_pct = 100 * ev_delta / r1.metrics.eigenvalue + @printf(" Eigenmode Δ: %+.4f (%+.2f%%)\n", ev_delta, ev_pct) + + steps_delta = r2.metrics.steps - r1.metrics.steps + steps_pct = 100 * steps_delta / r1.metrics.steps + @printf(" Steps Δ: %+d (%+.1f%%)\n", steps_delta, steps_pct) + + time_delta = r2.metrics.runtime - r1.metrics.runtime + time_pct = 100 * time_delta / r1.metrics.runtime + speedup_desc = time_delta < 0 ? "faster" : "slower" + @printf(" Runtime Δ: %+.2f s (%+.1f%% %s)\n", time_delta, abs(time_pct), speedup_desc) + + print_stage_comparison(r1, r2) + + println("\n" * "="^60) + + # Write to output file if requested + if options["output"] !== nothing + open(options["output"], "w") do f + write(f, "# GPEC Benchmark Comparison\n") + write(f, "Example: $(options["example"])\n\n") + write(f, "## Branch 1: $(r1.name) @ $(r1.commit)\n") + write(f, @sprintf("- Eigenmode energy: %.4f\n", r1.metrics.eigenvalue)) + write(f, @sprintf("- Integration steps: %d\n", r1.metrics.steps)) + write(f, @sprintf("- Runtime: %.2f s\n\n", r1.metrics.runtime)) + write(f, "## Branch 2: $(r2.name) @ $(r2.commit)\n") + write(f, @sprintf("- Eigenmode energy: %.4f\n", r2.metrics.eigenvalue)) + write(f, @sprintf("- Integration steps: %d\n", r2.metrics.steps)) + write(f, @sprintf("- Runtime: %.2f s\n\n", r2.metrics.runtime)) + write(f, "## Comparison\n") + write(f, @sprintf("- Eigenmode Δ: %+.4f (%+.2f%%)\n", ev_delta, ev_pct)) + write(f, @sprintf("- Steps Δ: %+d (%+.1f%%)\n", steps_delta, steps_pct)) + write(f, @sprintf("- Runtime Δ: %+.2f s (%+.1f%% %s)\n", time_delta, abs(time_pct), speedup_desc)) + end + println("Results written to: $(options["output"])") + end +end + +# Entry point +if abspath(PROGRAM_FILE) == @__FILE__ + if length(ARGS) == 0 + println(""" + Usage: julia benchmarks/benchmark_git_branches.jl [OPTIONS] + + Required: + --example PATH Path to example directory + + Optional: + --branch1 NAME First branch (default: develop) + --branch2 NAME Second branch (default: current branch) + --commit1 HASH Specific commit for branch1 + --commit2 HASH Specific commit for branch2 + --runs N Number of warm runs (default: 2) + --output FILE Write results to file + + Examples: + # Compare two branches + julia benchmarks/benchmark_git_branches.jl --example examples/DIIID-like_ideal_example --branch1 develop --branch2 feature-branch + + # Compare develop now vs 10 commits ago + julia benchmarks/benchmark_git_branches.jl --example examples/DIIID-like_ideal_example --branch1 develop --commit1 HEAD~10 --branch2 develop + + # Compare specific commits + julia benchmarks/benchmark_git_branches.jl --example examples/DIIID-like_ideal_example --commit1 abc123 --commit2 def456 + """) + exit(1) + end + + options = parse_args(ARGS) + benchmark_branches(options) +end diff --git a/benchmarks/benchmark_legendre_2007.jl b/benchmarks/benchmark_legendre_2007.jl new file mode 100644 index 0000000000..184fbe3548 --- /dev/null +++ b/benchmarks/benchmark_legendre_2007.jl @@ -0,0 +1,139 @@ +""" +Benchmark script for comparing Pn_minus_half_1997 and Pn_minus_half_2007 implementations. + +This script: +1. Verifies agreement between 2007 Julia implementation and Fortran +2. Demonstrates expected divergence between 1997 and 2007 at large nloc +3. Benchmarks performance comparison between 1997 and 2007 versions + +Usage: + julia --project=. benchmarks/benchmark_legendre_2007.jl +""" + +using GeneralizedPerturbedEquilibrium +using Printf +using Statistics + +# Access the internal functions +using GeneralizedPerturbedEquilibrium.Vacuum: Pn_minus_half_1997, Pn_minus_half_2007 + +println("="^80) +println("Benchmark: Legendre Function Implementations (1997 vs 2007)") +println("="^80) + +# Test parameters +s_values = [1.5, 2.0, 3.0, 5.0, 10.0] +n_small = [0, 1, 2, 5, 10] +n_large = [20, 50, 100, 200] + +println("\n1. CORRECTNESS: Comparing 1997 and 2007 implementations") +println("-"^80) + +println("\nFor small n (expected good agreement):") +println(@sprintf("%-8s %-8s %-20s %-20s %-20s", "s", "n", "P_1997", "P_2007", "Rel. Diff")) +println("-"^80) + +for s in [2.0, 5.0] + for n in n_small + P_1997 = Pn_minus_half_1997(s, n) + P_2007 = Pn_minus_half_2007(s, n) + + # Compare the n-th order value + val_1997 = P_1997[end-1] + val_2007 = P_2007[end-1] + rel_diff = abs(val_1997 - val_2007) / abs(val_1997) + + println(@sprintf("%-8.2f %-8d %-20.12e %-20.12e %-20.12e", s, n, val_1997, val_2007, rel_diff)) + end +end + +println("\nFor large n (expected divergence):") +println(@sprintf("%-8s %-8s %-20s %-20s %-20s", "s", "n", "P_1997", "P_2007", "Rel. Diff")) +println("-"^80) + +for s in [2.0, 5.0] + for n in n_large + P_1997 = Pn_minus_half_1997(s, n) + P_2007 = Pn_minus_half_2007(s, n) + + # Compare the n-th order value + val_1997 = P_1997[end-1] + val_2007 = P_2007[end-1] + rel_diff = abs(val_1997 - val_2007) / abs(val_1997) + + println(@sprintf("%-8.2f %-8d %-20.12e %-20.12e %-20.12e", s, n, val_1997, val_2007, rel_diff)) + end +end + +println("\n2. PERFORMANCE: Speed comparison") +println("-"^80) + +# Function to benchmark execution time +function benchmark_function(f, s, n, iterations=100) + # Warmup + f(s, n) + + # Benchmark + times = zeros(iterations) + for i in 1:iterations + times[i] = @elapsed f(s, n) + end + + return mean(times), std(times) +end + +println("\nTiming comparison (averaged over 100 runs):") +println(@sprintf("%-8s %-8s %-20s %-20s %-15s", "s", "n", "1997 (ms)", "2007 (ms)", "Speedup")) +println("-"^80) + +test_cases = [ + (2.0, 5), + (2.0, 10), + (2.0, 20), + (2.0, 50), + (5.0, 5), + (5.0, 10), + (5.0, 20), + (5.0, 50) +] + +for (s, n) in test_cases + time_1997, std_1997 = benchmark_function(Pn_minus_half_1997, s, n) + time_2007, std_2007 = benchmark_function(Pn_minus_half_2007, s, n) + + speedup = time_1997 / time_2007 + + println(@sprintf("%-8.2f %-8d %-20.6f %-20.6f %-15.2fx", + s, n, time_1997 * 1000, time_2007 * 1000, speedup)) +end + +println("\n3. VISUAL CHECK: rhohat parameter and integration method selection") +println("-"^80) + +println("\nChecking when Gaussian integration is used (n*rhohat >= 0.1):") +println(@sprintf("%-8s %-8s %-15s %-20s", "s", "n", "rhohat", "Method")) +println("-"^80) + +for s in [1.5, 2.0, 3.0, 5.0, 10.0] + xxq = s * s + ysq = xxq - 1.0 + y = sqrt(ysq) + w = s + y + rhohat = sqrt(1.0 / (2.0 * y * w)) + + for n in [1, 5, 10, 20, 50, 100] + criterion = n * rhohat + method = criterion >= 0.1 ? "Gaussian" : "Recurrence" + println(@sprintf("%-8.2f %-8d %-15.6f %-20s", s, n, rhohat, method)) + end +end + +println("\n" * "="^80) +println("Benchmark complete!") +println("="^80) + +println("\nSummary:") +println("1. For small n, both methods should agree closely (rel. diff < 1e-10)") +println("2. For large n, methods are expected to diverge due to numerical stability") +println("3. The 2007 method uses Gaussian integration when n*rhohat >= 0.1") +println("4. Performance depends on whether Gaussian integration or recurrence is used") diff --git a/benchmarks/benchmark_q_vs_iota_edge.jl b/benchmarks/benchmark_q_vs_iota_edge.jl new file mode 100644 index 0000000000..c4a5b7d0ed --- /dev/null +++ b/benchmarks/benchmark_q_vs_iota_edge.jl @@ -0,0 +1,108 @@ +# Quantifies whether splining iota = 1/q instead of q would reduce the knot count needed +# for a given edge accuracy (GitHub discussion around the auto-grid redesign; PR #179 +# lineage owns any production iota work). For a grid ending at psihigh < 1, interpolation +# error transforms as err_q ≈ err_iota · q², cancelling the flattening of the profile, so +# no leading-order gain is expected — this script documents that with numbers on the +# DIII-D-like example equilibrium. +# +# Usage: julia --project=. benchmarks/benchmark_q_vs_iota_edge.jl +# Outputs (not committed): benchmarks/q_vs_iota_edge.csv, benchmarks/q_vs_iota_edge.png + +using Pkg; +Pkg.activate(joinpath(@__DIR__, "..")) +using GeneralizedPerturbedEquilibrium +using FastInterpolations +using Printf +using Plots + +const GPE = GeneralizedPerturbedEquilibrium +const EXAMPLE_DIR = joinpath(@__DIR__, "..", "examples", "DIIID-like_ideal_example") + +# Dense core_edge_packed reference equilibrium: treat its q(ψ) as ground truth +function reference_q() + inputs, _, additional_input = GPE.build_inputs_from_toml(EXAMPLE_DIR) + equil_dict = merge(inputs["Equilibrium"], Dict{String,Any}("grid_type" => "core_edge_packed", "mpsi" => 1024)) + eq_config = GPE.equilibrium_config(Dict{String,Any}("Equilibrium" => equil_dict), EXAMPLE_DIR) + equil = GPE.Equilibrium.PlasmaEquilibrium(eq_config, additional_input) + # The dict rides along so the mpsi scan can rebuild configs; EquilibriumConfig is immutable. + return equil, equil_dict +end + +# Rational surface ψ_s(q = m/n) and q'(ψ_s) recovered from a fitted spline by bisection +function rational_locations(q_itp, dq_itp, psis, q_targets) + out = Dict{Float64,Tuple{Float64,Float64}}() + for qt in q_targets + lo, hi = psis[1], psis[end] + (q_itp(lo) - qt) * (q_itp(hi) - qt) > 0 && continue + for _ in 1:200 + mid = 0.5 * (lo + hi) + (q_itp(lo) - qt) * (q_itp(mid) - qt) <= 0 ? (hi = mid) : (lo = mid) + end + ps = 0.5 * (lo + hi) + out[qt] = (ps, dq_itp(ps)) + end + return out +end + +function main() + equil, equil_dict = reference_q() + xs_ref = equil.profiles.xs + q_ref_itp = equil.profiles.q_spline + dq_ref_itp = equil.profiles.q_deriv + psilow, psihigh = xs_ref[1], xs_ref[end] + + q_lo = ceil(q_ref_itp(psilow)) + q_hi = floor(q_ref_itp(psihigh)) + q_targets = collect(q_lo:q_hi) # n=1 rationals + ref_rats = rational_locations(q_ref_itp, dq_ref_itp, xs_ref, q_targets) + + psi_eval = collect(range(psilow, psihigh; length=4001)) + edge_band = psi_eval .> 0.9 + + rows = String[] + push!(rows, "N,repr,max_rel_q_err,edge_rel_q_err,edge_rel_qprime_err,max_psi_s_err,max_rel_q1_err") + results = Dict{String,Vector{NTuple{2,Float64}}}("q" => [], "iota" => []) + + for N in (16, 32, 64, 128, 256) + # Same log_asymptotic knot layout the auto grid uses for fixed mpsi + cfg = GPE.equilibrium_config(Dict{String,Any}("Equilibrium" => merge(equil_dict, Dict{String,Any}("mpsi" => N))), EXAMPLE_DIR) + knots = GPE.Equilibrium._build_psi_grid(cfg, psilow, psihigh) + q_nodes = [q_ref_itp(p) for p in knots] + + for repr in ("q", "iota") + itp = repr == "q" ? cubic_interp(knots, q_nodes; extrap=ExtendExtrap()) : + cubic_interp(knots, 1.0 ./ q_nodes; extrap=ExtendExtrap()) + ditp = deriv1(itp) + qf = repr == "q" ? (p -> itp(p)) : (p -> 1.0 / itp(p)) + dqf = repr == "q" ? (p -> ditp(p)) : (p -> -ditp(p) / itp(p)^2) # q' = -ι'/ι² + + q_err = [abs(qf(p) - q_ref_itp(p)) / abs(q_ref_itp(p)) for p in psi_eval] + dq_err = [abs(dqf(p) - dq_ref_itp(p)) / max(abs(dq_ref_itp(p)), 1e-10) for p in psi_eval] + rats = rational_locations(qf, dqf, knots, q_targets) + psi_s_err = maximum(abs(rats[qt][1] - ref_rats[qt][1]) for qt in keys(ref_rats); init=0.0) + q1_err = maximum(abs(rats[qt][2] - ref_rats[qt][2]) / abs(ref_rats[qt][2]) for qt in keys(ref_rats); init=0.0) + + push!(rows, @sprintf("%d,%s,%.3e,%.3e,%.3e,%.3e,%.3e", + N, repr, maximum(q_err), maximum(q_err[edge_band]), maximum(dq_err[edge_band]), psi_s_err, q1_err)) + push!(results[repr], (Float64(N), maximum(q_err[edge_band]))) + end + end + + csv_path = joinpath(@__DIR__, "q_vs_iota_edge.csv") + open(csv_path, "w") do io + foreach(r -> println(io, r), rows) + end + println("Wrote ", abspath(csv_path)) + foreach(println, rows) + + p = plot(; xscale=:log10, yscale=:log10, xlabel="knots N", ylabel="max relative q error, ψ > 0.9", + title="q-spline vs ι-spline edge accuracy", legend=:topright, left_margin=12Plots.mm, bottom_margin=4Plots.mm) + for (repr, pts) in results + plot!(p, first.(pts), last.(pts); marker=:circle, lw=2, label="$repr-spline") + end + png_path = joinpath(@__DIR__, "q_vs_iota_edge.png") + savefig(p, png_path) + println("Wrote ", abspath(png_path)) +end + +main() diff --git a/benchmarks/benchmark_riccati_der.jl b/benchmarks/benchmark_riccati_der.jl new file mode 100644 index 0000000000..9a1d6411b5 --- /dev/null +++ b/benchmarks/benchmark_riccati_der.jl @@ -0,0 +1,117 @@ +# Sanity check: riccati_der! correctly evaluates the explicit Riccati ODE. +# +# riccati_der! implements [Glasser 2018 Phys. Plasmas 25, 032507, Eq. 19]: +# dS/dψ = w†·F̄⁻¹·w - S·Ḡ·S, w = Q - K̄·S +# +# where Q = diag(1/(m - n·q)), F̄ = L·L† (Cholesky), K̄ and Ḡ are the MHD +# metric matrices evaluated at ψ. +# +# NOTE: The identity between this Riccati ODE and the EL chain rule +# dS/dψ = dU₁·U₂⁻¹ - S·dU₂·U₂⁻¹ +# holds ONLY for Hermitian S (physical states evolved from the axis, where +# S†=S is preserved by the EL symmetry). For arbitrary non-Hermitian (U₁, U₂), +# the two expressions differ — so this script compares riccati_der! against the +# explicit formula rather than against sing_der!. +# +# Usage (from the repository root): +# julia --project=. benchmarks/benchmark_riccati_der.jl + +using LinearAlgebra, Random, Printf, TOML +using GeneralizedPerturbedEquilibrium + +const FFS = GeneralizedPerturbedEquilibrium.ForceFreeStates + +function setup_solovev() + GPEC = GeneralizedPerturbedEquilibrium + ex = joinpath(@__DIR__, "..", "test", "test_data", "regression_solovev_ideal_example") + inputs = TOML.parsefile(joinpath(ex, "gpec.toml")) + equil = GPEC.Equilibrium.PlasmaEquilibrium(GPEC.equilibrium_config(inputs, ex), GPEC.analytic_config(inputs)) + prob = GPEC.ForceBalanceProblem(equil; GPEC.force_balance_keywords(inputs, ex)...) + ctrl, intr, equil, _, mats = GPEC._prepare_solve(prob, GPEC.force_balance_algorithm(inputs)) + return ctrl, equil, mats, intr +end + +# Evaluate the Riccati RHS explicitly from splines: dS = w†·F̄⁻¹·w - S·Ḡ·S +function riccati_rhs_manual(S, psi, equil, mats, intr) + N = intr.numpert_total + L = zeros(ComplexF64, N, N) + Kmat = zeros(ComplexF64, N, N) + Gmat = zeros(ComplexF64, N, N) + mats.ideal.F_spline_lower(vec(L), psi; hint=mats._hint) + mats.ideal.K_spline(vec(Kmat), psi; hint=mats._hint) + mats.ideal.G_spline(vec(Gmat), psi; hint=mats._hint) + + q = equil.profiles.q_spline(psi) + singfac = vec(1.0 ./ ((intr.mlow:intr.mhigh) .- q .* (intr.nlow:intr.nhigh)')) + + # w = Q - K̄·S (Q is diagonal; add only the diagonal entries) + w = -Kmat * S + for i in 1:N + w[i, i] += singfac[i] + end + + # v = F̄⁻¹·w via stored Cholesky factor L (L·L† = F̄) + v = copy(w) + ldiv!(LowerTriangular(L), v) + ldiv!(UpperTriangular(L'), v) + + return adjoint(w) * v - S * Gmat * S +end + +println("\n=== riccati_der! formula verification ===") +println("Verifies riccati_der! output matches manual evaluation of Glasser 2018 Eq. 19.") +println("Test state: Hermitian S (physical constraint). Expected error: ~machine epsilon.\n") + +ctrl, equil, mats, intr = setup_solovev() +N = intr.numpert_total + +odet = FFS.OdeState(N, ctrl.algorithm.numsteps_init, ctrl.algorithm.numunorms_init, intr.msing) +FFS.initialize_el_at_axis!(odet, ctrl, mats, equil.profiles, intr) +chunks = FFS.chunk_el_integration_bounds(odet, ctrl, intr) + +# 30% into each chunk: well inside the interval, away from singularities at psi_end +test_psis = [c.psi_start + 0.3 * (c.psi_end - c.psi_start) for c in chunks] + +println(" N=$N modes, $(length(test_psis)) test ψ points (30% into each chunk)\n") +@printf(" %8s %14s %14s %12s\n", "ψ", "‖dS_manual‖", "‖dS_ric‖", "rel error") +println(" " * "-"^54) + +rng = Random.MersenneTwister(42) +threshold = 1e-10 + +max_err = let max_err = 0.0 + for psi in test_psis + # Hermitian S: physical Riccati matrix is Hermitian (preserved by EL symmetry) + A = randn(rng, ComplexF64, N, N) + S = (A + A') / 2 # Hermitian by construction + + # Manual RHS + dS_manual = riccati_rhs_manual(S, psi, equil, mats, intr) + + # riccati_der! RHS + u_ric = zeros(ComplexF64, N, N, 2) + du_ric = zeros(ComplexF64, N, N, 2) + u_ric[:, :, 1] .= S + u_ric[:, :, 2] .= Matrix{ComplexF64}(I, N, N) + dummy_chunk = FFS.IntegrationChunk(psi, psi, false, 0, 1) + params = (ctrl, equil, mats, intr, odet, dummy_chunk) + FFS.riccati_der!(du_ric, u_ric, params, psi) + dS_ric = du_ric[:, :, 1] + + ref = max(norm(dS_manual), 1e-10) + err = norm(dS_ric - dS_manual) / ref + max_err = max(max_err, err) + status = err < threshold ? "" : " ← FAIL" + @printf(" %8.4f %14.4e %14.4e %12.4e%s\n", psi, norm(dS_manual), norm(dS_ric), err, status) + end + max_err +end + +println() +if max_err < threshold + @printf("PASSED — max rel error = %.2e (threshold %.0e)\n", max_err, threshold) +else + @printf("FAILED — max rel error = %.2e exceeds threshold %.0e\n", max_err, threshold) + exit(1) +end +println() diff --git a/benchmarks/benchmark_solovev_kinetic_stability.jl b/benchmarks/benchmark_solovev_kinetic_stability.jl new file mode 100644 index 0000000000..0485951bef --- /dev/null +++ b/benchmarks/benchmark_solovev_kinetic_stability.jl @@ -0,0 +1,204 @@ +#!/usr/bin/env julia +""" + benchmark_solovev_kinetic_stability.jl + +Benchmark the self-consistent kinetic-DCON path (`kinetic = "KineticForces"`) +for the analytic **Solovev** equilibrium against Fortran GPEC's kinetic DCON. + +Unlike the DIIID benchmark (which reads a pre-existing `dcon_output_n1.nc`), +no committed Fortran reference exists for this Solovev case, so this script +*generates a matched Fortran input deck* from the Julia regression fixture and +runs Fortran `dcon` itself. The Julia and Fortran cases are made physically +identical: same Solovev parameters, same kinetic profile, same poloidal-mode +band (`delta_mlow=delta_mhigh=0`), same `nl`, collision operator, grid, and +edge truncation (`sas_flag`/`dmlim`). + +The comparison is the least-stable total-energy eigenvalue: + - Julia: `FreeBoundaryStability/eigenmode_energies[1]` in `gpec.h5` + - Fortran: the "Energies: ... real = …, imaginary = …" line printed by `dcon` + (== `W_t_eigenvalue[1]`; parsing the log avoids a NetCDF dependency). + +Acceptance (same thresholds as the DIIID kinetic benchmark): + - Re(et[1]) within 5 % of Fortran + - Im(et[1]) within 20 % of Fortran (the damping rate — smaller, more FP-sensitive) + +Reference result (2026-06, develop, mpert=8, nl=1, nutype="harmonic"): + Re: Julia 15.888 vs Fortran 15.619 → 1.7 % + Im: Julia -0.711 vs Fortran -0.660 → 7.6 % +This confirms the PV+residue energy integral (issue #227) is physically correct; +the pre-rewrite (`ximag`) value of 34.176 was not. + +Usage: + julia --project=. benchmarks/benchmark_solovev_kinetic_stability.jl + +Environment (both required): + GPEC_FORTRAN_DCON path to the Fortran `dcon` executable + GPEC_FORTRAN_SOLDIR Fortran solovev_kinetic_example dir used as deck template +""" + +using Printf +using TOML +using HDF5 + +using GeneralizedPerturbedEquilibrium +const GPE = GeneralizedPerturbedEquilibrium + +const REPO = normpath(joinpath(@__DIR__, "..")) +const JULIA_FIXTURE = joinpath(REPO, "test", "test_data", "regression_solovev_kinetic_calculated") +const FORTRAN_DCON = get(ENV, "GPEC_FORTRAN_DCON", "") +const FORTRAN_SOLDIR = get(ENV, "GPEC_FORTRAN_SOLDIR", "") + +_p(args...) = (println(stderr, args...); flush(stderr)) + +""" + replace_namelist_value!(path, key, value) + +Overwrite `key = …` (Fortran namelist, `=`-separated, value up to a trailing +comment `!` or line end) with `value`, preserving the rest of the line. +""" +function replace_namelist_value!(path::String, key::String, value::AbstractString) + lines = readlines(path) + pat = Regex("^(\\s*" * key * "\\s*=\\s*)([^!]*)(!?.*)\$", "i") + found = false + for (i, ln) in enumerate(lines) + m = match(pat, ln) + if m !== nothing + lines[i] = m.captures[1] * value * " " * + (isempty(m.captures[3]) ? "" : m.captures[3]) + found = true + end + end + found || error("key '$key' not found in $path") + open(path, "w") do io + for ln in lines + println(io, ln) + end + end +end + +""" + build_matched_fortran_deck(workdir) → workdir + +Copy the Fortran solovev_kinetic_example deck into `workdir` and override the +fields that must match the Julia regression fixture (read from its `gpec.toml` +and `sol.toml`). Converts the fixture's `kinetic.dat` into Fortran `kinetic.txt`. +""" +function build_matched_fortran_deck(workdir::String) + isdir(FORTRAN_SOLDIR) || error("Fortran deck template not found: '$FORTRAN_SOLDIR' (set GPEC_FORTRAN_SOLDIR)") + for f in ("sol.in", "equil.in", "dcon.in", "pentrc.in", "vac.in") + cp(joinpath(FORTRAN_SOLDIR, f), joinpath(workdir, f); force=true) + end + + gpec = TOML.parsefile(joinpath(JULIA_FIXTURE, "gpec.toml")) + eq = gpec["Equilibrium"] + ffs = gpec["ForceFreeStates"] + kf = get(gpec, "KineticForces", Dict{String,Any}()) + + # equil.in — grid + coordinate settings to match the Julia [Equilibrium] block. + eqin = joinpath(workdir, "equil.in") + replace_namelist_value!(eqin, "psilow", string(eq["psilow"])) + replace_namelist_value!(eqin, "psihigh", string(eq["psihigh"])) + replace_namelist_value!(eqin, "mpsi", string(eq["mpsi"])) + replace_namelist_value!(eqin, "mtheta", string(eq["mtheta"])) + + # dcon.in — kinetic flags already match; align mode band, edge truncation, n. + dconin = joinpath(workdir, "dcon.in") + replace_namelist_value!(dconin, "nn", string(first(ffs["nn"]))) + replace_namelist_value!(dconin, "delta_mlow", string(get(ffs, "delta_mlow", 0))) + replace_namelist_value!(dconin, "delta_mhigh", string(get(ffs, "delta_mhigh", 0))) + replace_namelist_value!(dconin, "qlow", string(ffs["qlow"])) + replace_namelist_value!(dconin, "qhigh", string(ffs["qhigh"])) + replace_namelist_value!(dconin, "singfac_min", string(ffs["singfac_min"])) + # Julia truncate_at_rational_offset defaults true (sas_flag) with rational_offset_fraction 0.2. + replace_namelist_value!(dconin, "sas_flag", "t") + replace_namelist_value!(dconin, "dmlim", "0.2") + + # pentrc.in — bounce harmonics + species; defaults mirror KineticForcesControl. + pentin = joinpath(workdir, "pentrc.in") + replace_namelist_value!(pentin, "nl", string(get(kf, "nl", 1))) + replace_namelist_value!(pentin, "nutype", "\"" * string(get(kf, "nutype", "harmonic")) * "\"") + replace_namelist_value!(pentin, "f0type", "\"" * string(get(kf, "f0type", "maxwellian")) * "\"") + replace_namelist_value!(pentin, "nufac", string(get(kf, "collisionality_factor", 1))) + replace_namelist_value!(pentin, "kinetic_file", "\"kinetic.txt\"") + + # kinetic.txt — Fortran readtable wants a title line then numeric rows; the + # fixture's kinetic.dat is already the same 6-column ASCII layout. + cp(joinpath(JULIA_FIXTURE, "kinetic.dat"), joinpath(workdir, "kinetic.txt"); force=true) + + return workdir +end + +""" + run_fortran_reference(workdir) → (re, im) + +Run Fortran `dcon` in `workdir` and parse the least-stable total-energy +eigenvalue from its "Energies: ... real = …, imaginary = …" line. +""" +function run_fortran_reference(workdir::String) + isfile(FORTRAN_DCON) || error("Fortran dcon not found: $FORTRAN_DCON (set GPEC_FORTRAN_DCON)") + logpath = joinpath(workdir, "dcon_run.log") + open(logpath, "w") do io + run(pipeline(Cmd(`$FORTRAN_DCON`; dir=workdir); stdout=io, stderr=io)) + end + log = read(logpath, String) + m = match(r"Energies:.*real\s*=\s*([-\d.Ee+]+),\s*imaginary\s*=\s*([-\d.Ee+]+)", log) + m === nothing && error("could not parse Fortran energies from $logpath") + return parse(Float64, m.captures[1]), parse(Float64, m.captures[2]) +end + +function run_julia_reference() + rundir = mktempdir(; prefix="solovev_kinetic_julia_") + for f in ("gpec.toml", "sol.toml", "kinetic.dat") + cp(joinpath(JULIA_FIXTURE, f), joinpath(rundir, f)) + end + t0 = time() + GPE.main([rundir]) + wall = time() - t0 + et = h5open(joinpath(rundir, "gpec.h5"), "r") do h5 + read(h5["ForceFreeStates/FreeBoundaryStability/eigenmode_energies"]) + end + return real(et[1]), imag(et[1]), wall +end + +function run_benchmark() + _p("="^70) + _p(" Solovev Kinetic-DCON Benchmark: Julia KF→FFS vs Fortran DCON") + _p("="^70) + + workdir = mktempdir(; prefix="solovev_kinetic_fortran_") + build_matched_fortran_deck(workdir) + _p(" Matched Fortran deck: $workdir") + fort_re, fort_im = run_fortran_reference(workdir) + _p(@sprintf(" Fortran W_t[1]: Re = %.6f Im = %.6f", fort_re, fort_im)) + + _p("\n--- Running GPE.main with kinetic = \"KineticForces\" ---") + jul_re, jul_im, wall = run_julia_reference() + _p(@sprintf(" GPE.main completed in %.1f s", wall)) + + re_err = abs(jul_re - fort_re) / abs(fort_re) * 100 + im_err = abs(jul_im - fort_im) / abs(fort_im) * 100 + + _p("\n" * "="^70) + _p(" Least-stable total-energy eigenvalue (mpert matched)") + _p("="^70) + _p(@sprintf(" Re(et[1]): Julia = %12.6f Fortran = %12.6f err = %6.2f%%", jul_re, fort_re, re_err)) + _p(@sprintf(" Im(et[1]): Julia = %12.6f Fortran = %12.6f err = %6.2f%%", jul_im, fort_im, im_err)) + + re_pass = re_err <= 5.0 + im_pass = im_err <= 20.0 + _p("\n" * "="^70) + if re_pass && im_pass + _p(" PASS — Re within 5 %, Im within 20 %") + else + _p(" FAIL — thresholds exceeded:") + re_pass || _p(@sprintf(" Re err %.2f%% > 5%%", re_err)) + im_pass || _p(@sprintf(" Im err %.2f%% > 20%%", im_err)) + end + _p("="^70) + + return (; jul_re, jul_im, fort_re, fort_im, re_err, im_err, wall) +end + +if abspath(PROGRAM_FILE) == @__FILE__ + run_benchmark() +end diff --git a/benchmarks/benchmark_threads.jl b/benchmarks/benchmark_threads.jl new file mode 100644 index 0000000000..2dc54c30d6 --- /dev/null +++ b/benchmarks/benchmark_threads.jl @@ -0,0 +1,54 @@ +# Thread-scaling benchmark for the Riccati chunked propagator integration. +# Runs the Solovev (N=8) and DIIID-like (N=26) examples with the Riccati integrator +# across 1, 2, 4, 8 threads and compares against the forward path. The chunk list is +# thread-independent, so et[1] must not move with the thread count — only wall-clock. +# +# Usage (from the repository root): +# for t in 1 2 4 8; do julia -t $t --project=. benchmarks/benchmark_threads.jl; done + +using GeneralizedPerturbedEquilibrium, TOML, Printf, Statistics + +# One solve of the example deck with `integrator` (Forward or Riccati), sharing the deck's ODE numerics. +function run_ffs(ex; integrator) + GPE = GeneralizedPerturbedEquilibrium + inputs, config, analytic = GPE.build_inputs_from_toml(ex) + alg = integrator(; GPE.section_keywords(inputs["Forward"], "Forward", GPE._struct_fields(Forward))...) + ffs = solve(PlasmaEquilibrium(config, analytic), alg; GPE.force_balance_keywords(inputs, ex)...) + return real(ffs.free_boundary.et[1]), ffs.numpert_total +end + +function timed_run(ex; integrator, nwarm=1, nrep=2) + # Warmup + for _ in 1:nwarm + run_ffs(ex; integrator) + end + # Timed runs + times = Float64[] + local et1, N + for _ in 1:nrep + t0 = time() + et1, N = run_ffs(ex; integrator) + push!(times, time() - t0) + end + return mean(times), et1, N +end + +nthreads = Threads.nthreads() +root = joinpath(@__DIR__, "..") +sol_ex = joinpath(root, "test", "test_data", "regression_solovev_ideal_example") +diiid_ex = joinpath(root, "examples", "DIIID-like_ideal_example") + +println("\n=== Thread-scaling benchmark ($(nthreads) thread(s)) ===\n") + +for (label, ex) in [("Solovev", sol_ex), ("DIIID-like", diiid_ex)] + t_fwd, et_fwd, N = timed_run(ex; integrator=Forward) + t_ric, et_ric, _ = timed_run(ex; integrator=Riccati) + + err_ric = abs(et_ric - et_fwd) / abs(et_fwd) * 100 + + println("$label (N=$N, nthreads=$nthreads)") + @printf(" forward et[1]=%.5f t=%.2fs speedup=1.00×\n", et_fwd, t_fwd) + @printf(" riccati et[1]=%.5f t=%.2fs speedup=%.2f× err=%.4f%%\n", + et_ric, t_ric, t_fwd / t_ric, err_ric) + println() +end diff --git a/benchmarks/compare_jbgradpsi_m2.jl b/benchmarks/compare_jbgradpsi_m2.jl new file mode 100644 index 0000000000..e840afe3ed --- /dev/null +++ b/benchmarks/compare_jbgradpsi_m2.jl @@ -0,0 +1,60 @@ +# Compare the area-normalized b^ψ (PerturbedEquilibrium/Response/psi_area = b^ψ/⟨J·|∇ψ|⟩_θ) for one +# poloidal harmonic between two GPEC runs that are identical except for which ξ feeds PerturbedEquilibrium: +# (1) IDEAL galerkin matched ξ (integrator = "Galerkin" with [Match] ideal = true) +# (2) FORWARD ξ (integrator = "Forward", no [Match]) +# +# PE writes no ψ grid, so it's reconstructed: gal-ideal → ForceFreeStates/Solutions/GalerkinIntegration/psi (already excludes on-surface points); +# forward → ForceFreeStates/Solutions/ForwardIntegration/psi. +# Usage: julia --project=. benchmarks/compare_jbgradpsi_m2.jl [gal_h5] [shoot_h5] [out.png] [m] + +using HDF5, Plots, Printf + +gal_h5 = length(ARGS) >= 1 ? ARGS[1] : "/tmp/gal_ideal_test/gpec.h5" +sh_h5 = length(ARGS) >= 2 ? ARGS[2] : "/tmp/forward_test/gpec.h5" +outpng = length(ARGS) >= 3 ? ARGS[3] : joinpath(@__DIR__, "cmp_jbgradpsi_m2.png") +mtarget = length(ARGS) >= 4 ? parse(Int, ARGS[4]) : 2 + +to_c(a) = eltype(a) <: Complex ? ComplexF64.(a) : map(x -> ComplexF64(x.re, x.im), a) + +# gal-ideal run: PE grid = gal solution grid with the on-surface (issing) points dropped +pa_g, psi_g, mlow, sing_psi, sing_m = h5open(gal_h5) do f + pa = to_c(read(f["PerturbedEquilibrium/Response/psi_area"])) # [npsi, mpert] + (pa, read(f["ForceFreeStates/Solutions/GalerkinIntegration/psi"]), read(f["Info/mlow"]), + read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_psi"]), read(f["ForceFreeStates/Solutions/GalerkinIntegration/rational_m"])) +end +# forward run: PE grid = ForceFreeStates/Solutions/ForwardIntegration/psi +pa_s, psi_s = h5open(sh_h5) do f + (to_c(read(f["PerturbedEquilibrium/Response/psi_area"])), read(f["ForceFreeStates/Solutions/ForwardIntegration/psi"])) +end + +size(pa_g, 1) == length(psi_g) || error("gal grid mismatch: npsi=$(size(pa_g,1)) vs grid=$(length(psi_g))") +size(pa_s, 1) == length(psi_s) || error("shoot grid mismatch: npsi=$(size(pa_s,1)) vs grid=$(length(psi_s))") + +col = mtarget - mlow + 1 +@printf("m=%d → column %d (mlow=%d, mpert=%d)\n", mtarget, col, mlow, size(pa_g, 2)) +g = @view pa_g[:, col] +s = @view pa_s[:, col] + +# resonant surface for this m (q = m/n) +psi_res = mtarget in sing_m ? sing_psi[findfirst(==(mtarget), sing_m)] : NaN +# value at the grid point nearest the resonant surface, each run +if !isnan(psi_res) + ig = argmin(abs.(psi_g .- psi_res)) + is = argmin(abs.(psi_s .- psi_res)) + @printf("m=%d resonant surface ψ=%.4f\n", mtarget, psi_res) + @printf(" nearest |b^ψ_area|: gal-ideal=%.4e (ψ=%.4f) forward=%.4e (ψ=%.4f) ratio=%.3f\n", + abs(g[ig]), psi_g[ig], abs(s[is]), psi_s[is], abs(g[ig]) / abs(s[is])) +end + +plt = plot(; size=(1000, 620), xlabel="ψ_N", ylabel="|b^ψ / ⟨J·|∇ψ|⟩| (area-normalized)", + title="m=$mtarget perturbed normal field — gal-ideal vs forward PE", legend=:topleft, + left_margin=13Plots.mm, bottom_margin=5Plots.mm, right_margin=4Plots.mm) +plot!(plt, psi_g, abs.(g); lw=2.2, color=1, label="gal-ideal ξ → PE") +plot!(plt, psi_s, abs.(s); lw=1.8, color=2, ls=:dash, label="forward ξ → PE") +for (j, ps) in enumerate(sing_psi) + ism2 = sing_m[j] == mtarget + vline!(plt, [ps]; color=(ism2 ? :red : :black), ls=:dot, lw=(ism2 ? 1.8 : 1), + label=(ism2 ? "q=$mtarget surface" : (j == 1 ? "rational q" : ""))) +end +savefig(plt, outpng) +println("saved: ", abspath(outpng)) diff --git a/benchmarks/equil_method_comparison.jl b/benchmarks/equil_method_comparison.jl new file mode 100644 index 0000000000..bd6faab8dd --- /dev/null +++ b/benchmarks/equil_method_comparison.jl @@ -0,0 +1,180 @@ +""" +equil_method_comparison.jl + +Compares the three EFIT equilibrium solver methods at default parameters: + - "efit" (geometric-angle field-line ODE, original) + - "efit_arclength" (arc-length level-set ODE, Strategy A) + - "efit_by_inversion" (Contour.jl marching squares, Strategy B) + +Accuracy metrics (all self-consistent, no EFIT external reference for far edge): + - q(ψ) cross-method differences and comparison to EFIT input for ψ < 0.95 + - GSE residual (full domain and outer 10%: ψ > 0.90) + - Roundtrip error: (ψ,θ)→(R,Z)→ψ_spline - ψ_target + - Jacobian consistency: rzphi_jac vs. analytically derived Jacobian + +Usage: + julia --project=. benchmarks/equil_method_comparison.jl [example_path] +""" + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.Equilibrium +using TOML, Printf, Statistics + +example_path = length(ARGS) > 0 ? ARGS[1] : joinpath(@__DIR__, "../examples/DIIID-like_ideal_example") +config_path = joinpath(example_path, "gpec.toml") +psihigh_override = length(ARGS) > 1 ? parse(Float64, ARGS[2]) : nothing + +println("="^65) +println("Equilibrium Method Comparison") +println("Example: $example_path") +println("="^65) + +methods = ["efit", "efit_arclength", "efit_by_inversion"] +results = Dict{String,Any}() + +# ─── Helper: load a named TOML, override eq_type, return EquilibriumConfig ─── +function make_config(path::String, eq_type::String; psihigh=nothing) + raw = TOML.parsefile(path) + raw["Equilibrium"]["eq_type"] = eq_type + psihigh !== nothing && (raw["Equilibrium"]["psihigh"] = psihigh) + base = dirname(path) + return GeneralizedPerturbedEquilibrium.equilibrium_config(raw, base) +end + +# ─── Run each method (3 warm runs, report average of last 2) ───────────────── +for method in methods + println("\n--- Running: $method ---") + cfg = make_config(config_path, method; psihigh=psihigh_override) + local pe = nothing + local t1 = 0.0 + local t2 = 0.0 + local success = true + try + PlasmaEquilibrium(cfg) # JIT warmup + t1 = @elapsed pe = PlasmaEquilibrium(cfg) + t2 = @elapsed PlasmaEquilibrium(cfg) + catch e + @warn " FAILED: $e" + success = false + end + results[method] = Dict( + "success" => success, + "pe" => pe, + "runtime" => (t1 + t2) / 2.0, + "config" => cfg + ) + success && @printf(" Runtime (avg 2 warm): %.3f s\n", (t1 + t2) / 2.0) +end + +# ─── Profile comparison ─────────────────────────────────────────────────────── +println("\n" * "="^65) +println("q(ψ) cross-method comparison") +println("="^65) + +# Reference from the first successful method at shared psi_nodes +ref_method = first(m for m in methods if results[m]["success"]) +ref_pe = results[ref_method]["pe"] +psi_nodes = ref_pe.rzphi_xs + +# Evaluate q at all psi_nodes for each method +q_vals = Dict{String,Vector{Float64}}() +for method in methods + results[method]["success"] || continue + pe = results[method]["pe"] + q_vals[method] = [pe.profiles.q_spline(ψ) for ψ in psi_nodes] +end + +println("\nPairwise max |Δq| (full domain / ψ > 0.90):") +for (i, m1) in enumerate(methods), m2 in methods[(i+1):end] + (results[m1]["success"] && results[m2]["success"]) || continue + Δq = abs.(q_vals[m1] .- q_vals[m2]) + mask_edge = psi_nodes .> 0.90 + @printf(" |q_%s - q_%s|: max=%.2e (full) max=%.2e (ψ>0.90)\n", + m1, m2, maximum(Δq), maximum(Δq[mask_edge])) +end + +# EFIT input q-profile for ψ < 0.95 only. +# The sq_in spline from read_efit already holds the parsed q-profile in column 3: +# sq_in columns = [|F|, mu0*P, q, sqrt(psi_norm)]. +println("\nComparison to EFIT input q-profile (valid for ψ < 0.95 only):") +ref_raw = Equilibrium.read_efit(results[ref_method]["config"]) +f_sq_buf = zeros(4) +efit_q_interp = ψ -> (ref_raw.sq_in(f_sq_buf, ψ); f_sq_buf[3]) +mask_mid = psi_nodes .< 0.95 +for method in methods + results[method]["success"] || continue + Δq = abs.([q_vals[method][i] - efit_q_interp(psi_nodes[i]) for i in 1:length(psi_nodes)]) + @printf(" |q_%s - q_efit| (ψ<0.95): max=%.2e rms=%.2e\n", + method, maximum(Δq[mask_mid]), sqrt(mean(Δq[mask_mid] .^ 2))) +end + +# ─── Roundtrip error ───────────────────────────────────────────────────────── +println("\n" * "="^65) +println("Roundtrip error: (ψ,θ)→(R,Z)→ψ_spline(R,Z)−ψ_target") +println("="^65) + +for method in methods + results[method]["success"] || continue + pe = results[method]["pe"] + cfg = results[method]["config"] + raw_profile = Equilibrium.read_efit(cfg) + # Reconstruct psi_in before direct_position! modifies it + # (psi_in is already renormalized inside pe — test uses it) + psio = pe.psio + psi_xs = pe.rzphi_xs + psi_ys = pe.rzphi_ys + mpsi = length(psi_xs) - 1 + mtheta = length(psi_ys) - 1 + + errors = Float64[] + for ipsi in 1:5:mpsi+1 + ψ_target = psi_xs[ipsi] + for itheta in 1:8:mtheta+1 + θ = psi_ys[itheta] + r2 = pe.rzphi_rsquared((ψ_target, θ)) + off = pe.rzphi_offset((ψ_target, θ)) + rfac = sqrt(max(r2, 0.0)) + η = 2π * (θ + off) + R = pe.ro + rfac * cos(η) + Z = pe.zo + rfac * sin(η) + # Evaluate psi at (R, Z): psi_in returns (sibry - ψ_raw), so + # psi_norm = 1 - psi_in/psio (0 at axis, 1 at boundary). + ψ_reconstructed = 1.0 - raw_profile.psi_in((R, Z)) / psio + push!(errors, abs(ψ_reconstructed - ψ_target)) + end + end + mask_edge_err = [i for (i, ψ) in enumerate(psi_xs[1:5:end]) if ψ > 0.90] + @printf(" %s: max=%.2e rms=%.2e\n", method, maximum(errors), sqrt(mean(errors .^ 2))) +end + +# ─── GSE residual summary ───────────────────────────────────────────────────── +println("\n" * "="^65) +println("Grad-Shafranov residual (from equilibrium_gse! output — see gsec.h5 if diagnose_src=true)") +println("(Showing q-monotonicity near edge as proxy since GSE requires diagnose_src=true)") +println("="^65) + +for method in methods + results[method]["success"] || continue + pe = results[method]["pe"] + psi_xs = pe.rzphi_xs + q_profile = [pe.profiles.q_spline(ψ) for ψ in psi_xs] + dq = diff(q_profile) + n_nonmono = count(dq .< 0) + mask_edge = psi_xs[2:end] .> 0.90 + n_nonmono_edge = count(dq[mask_edge] .< 0) + @printf(" %s: q non-monotone steps: %d total, %d for ψ>0.90\n", + method, n_nonmono, n_nonmono_edge) +end + +# ─── Runtime summary ───────────────────────────────────────────────────────── +println("\n" * "="^65) +println("Runtime summary") +println("="^65) +for method in methods + if results[method]["success"] + @printf(" %-22s %.3f s\n", method, results[method]["runtime"]) + else + @printf(" %-22s FAILED\n", method) + end +end +println() diff --git a/benchmarks/equil_numerical_params.jl b/benchmarks/equil_numerical_params.jl new file mode 100644 index 0000000000..93cff33b32 --- /dev/null +++ b/benchmarks/equil_numerical_params.jl @@ -0,0 +1,164 @@ +""" +equil_numerical_params.jl + +Sweeps numerical parameters to characterize accuracy vs. cost for each method. + +Sweeps: + - mpsi ∈ {64, 128, 256} (all methods) + - mtheta ∈ {128, 256, 512} (all methods) + - ODE reltol ∈ {1e-5, 1e-7, 1e-9} (efit, efit_arclength) + - Contour grid resolution_factor ∈ {2, 4, 8} (efit_by_inversion) + +Accuracy metric: max roundtrip error over a sparse (ψ, θ) sample grid. +Performance metric: wall-clock runtime. + +Usage: + julia --project=. benchmarks/equil_numerical_params.jl [example_path] +""" + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.Equilibrium +using TOML, Printf, Statistics + +example_path = length(ARGS) > 0 ? ARGS[1] : joinpath(@__DIR__, "../examples/DIIID-like_ideal_example") +config_path = joinpath(example_path, "gpec.toml") + +function make_config(path, eq_type, mpsi, mtheta) + raw = TOML.parsefile(path) + raw["Equilibrium"]["eq_type"] = eq_type + raw["Equilibrium"]["mpsi"] = mpsi + raw["Equilibrium"]["mtheta"] = mtheta + return GeneralizedPerturbedEquilibrium.equilibrium_config(raw, dirname(path)) +end + +function roundtrip_error(pe, raw_profile) + psi_xs = pe.rzphi_xs + psio = pe.psio + mtheta = length(pe.rzphi_ys) - 1 + errors = Float64[] + for ipsi in 1:4:length(psi_xs) + ψ = psi_xs[ipsi] + for itheta in 1:8:mtheta+1 + θ = pe.rzphi_ys[itheta] + r2 = pe.rzphi_rsquared((ψ, θ)) + off = pe.rzphi_offset((ψ, θ)) + rfac = sqrt(max(r2, 0.0)) + η = 2π * (θ + off) + R = pe.ro + rfac * cos(η) + Z = pe.zo + rfac * sin(η) + ψ_recon = 1.0 - raw_profile.psi_in((R, Z)) / psio + push!(errors, abs(ψ_recon - ψ)) + end + end + return isempty(errors) ? NaN : maximum(errors) +end + +# JIT warmup +println("JIT warmup...") +warmup_cfg = make_config(config_path, "efit", 64, 128) +PlasmaEquilibrium(warmup_cfg) + +rows = [] + +# ── mpsi scan ────────────────────────────────────────────────────────────────── +println("\n" * "="^65) +println("mpsi scan (mtheta=256, default reltol)") +println("="^65) +for method in ["efit", "efit_arclength", "efit_by_inversion"] + for mpsi in [64, 128, 256] + cfg = make_config(config_path, method, mpsi, 256) + local pe = nothing + local success = false + local runtime = NaN + local rt_err = NaN + try + t = @elapsed pe = PlasmaEquilibrium(cfg) + runtime = t + raw = Equilibrium.read_efit(cfg) + rt_err = roundtrip_error(pe, raw) + success = true + catch e + @warn " $method mpsi=$mpsi FAILED: $e" + end + @printf(" %-22s mpsi=%3d t=%.2fs rt=%.2e\n", method, mpsi, runtime, rt_err) + push!(rows, (sweep="mpsi", method=method, param=Float64(mpsi), param2=256.0, + success=success, runtime=runtime, roundtrip_max=rt_err)) + end +end + +# ── mtheta scan ──────────────────────────────────────────────────────────────── +println("\n" * "="^65) +println("mtheta scan (mpsi=128, default reltol)") +println("="^65) +for method in ["efit", "efit_arclength", "efit_by_inversion"] + for mtheta in [128, 256, 512] + cfg = make_config(config_path, method, 128, mtheta) + local pe = nothing + local success = false + local runtime = NaN + local rt_err = NaN + try + t = @elapsed pe = PlasmaEquilibrium(cfg) + runtime = t + raw = Equilibrium.read_efit(cfg) + rt_err = roundtrip_error(pe, raw) + success = true + catch e + @warn " $method mtheta=$mtheta FAILED: $e" + end + @printf(" %-22s mtheta=%3d t=%.2fs rt=%.2e\n", method, mtheta, runtime, rt_err) + push!(rows, (sweep="mtheta", method=method, param=Float64(mtheta), param2=128.0, + success=success, runtime=runtime, roundtrip_max=rt_err)) + end +end + +# ── ODE reltol scan (efit and efit_arclength only) ──────────────────────────── +# Note: reltol is not exposed through EquilibriumConfig yet. +# This scan patches it by temporarily modifying the solver call. +# TODO: expose etol/reltol as a config parameter when needed. +println("\n" * "="^65) +println("ODE reltol scan — NOTE: not yet exposed via config; using default reltol") +println("(Add reltol to EquilibriumConfig and threading to enable this scan)") +println("="^65) + +# ── Contour grid resolution scan (efit_by_inversion only) ───────────────────── +# resolution_factor scales the adaptive contour grid of equilibrium_solver_by_inversion. +# We call it directly here, bypassing the PlasmaEquilibrium constructor. +println("\n" * "="^65) +println("Contour grid resolution scan (efit_by_inversion only, mpsi=128, mtheta=256)") +println("="^65) +for resolution_factor in [2.0, 4.0, 8.0] + cfg = make_config(config_path, "efit_by_inversion", 128, 256) + raw_input = Equilibrium.read_efit(cfg) + local pe = nothing + local success = false + local runtime = NaN + local rt_err = NaN + try + t = @elapsed begin + pe_raw = Equilibrium.equilibrium_solver_by_inversion(raw_input; resolution_factor) + pe = pe_raw + Equilibrium.equilibrium_global_parameters!(pe) + Equilibrium.equilibrium_qfind!(pe) + end + runtime = t + raw_for_check = Equilibrium.read_efit(cfg) + rt_err = roundtrip_error(pe, raw_for_check) + success = true + catch e + @warn " resolution_factor=$resolution_factor FAILED: $e" + end + @printf(" efit_by_inversion resolution_factor=%3.1f t=%.2fs rt=%.2e\n", resolution_factor, runtime, rt_err) + push!(rows, (sweep="resolution_factor", method="efit_by_inversion", param=resolution_factor, param2=128.0, + success=success, runtime=runtime, roundtrip_max=rt_err)) +end + +output_csv = joinpath(example_path, "equil_numerical_params.csv") +open(output_csv, "w") do io + println(io, join(string.(keys(rows[1])), ",")) + for row in rows + println(io, join(map(v -> v isa AbstractString ? "\"$v\"" : string(v), values(row)), ",")) + end +end +println("\nResults saved to: $output_csv") +println() diff --git a/benchmarks/equil_psihigh_scan.jl b/benchmarks/equil_psihigh_scan.jl new file mode 100644 index 0000000000..2df1356d3a --- /dev/null +++ b/benchmarks/equil_psihigh_scan.jl @@ -0,0 +1,210 @@ +""" +equil_psihigh_scan.jl + +Scans psihigh from 0.980 to 0.9999, testing robustness of all three equilibrium +methods near the separatrix. + +Key outputs: + - Last successful psihigh for each method + - Max roundtrip error in outer 10% (ψ > 0.90) vs. psihigh + - q monotonicity violations in outer 10% + - et[1] (free-boundary MHD stability eigenvalue) vs. psihigh + +Physics note: In a diverted tokamak (DIIID), q → ∞ as ψ → 1. The EFIT q-profile +extrapolates to a finite qa, which is physically wrong near the separatrix. This +benchmark therefore does NOT use the EFIT q-profile as ground truth for ψ > 0.95. +Instead, methods are compared against each other, and the divergence of q toward +the separatrix is tracked as a physical indicator. + +et[1] is obtained by running the full ForceFreeStates pipeline (ODE integration + +free-boundary energy) for each (method, psihigh) combination and reading +FreeBoundaryStability/eigenmode_energies from the HDF5 output. + +Usage: + julia --project=. benchmarks/equil_psihigh_scan.jl [example_path] +""" + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.Equilibrium +using TOML, Printf, Statistics, HDF5 + +example_path = length(ARGS) > 0 ? ARGS[1] : joinpath(@__DIR__, "../examples/DIIID-like_ideal_example") +config_path = joinpath(example_path, "gpec.toml") + +psihigh_values = [0.980, 0.985, 0.990, 0.993, 0.995, 0.996, 0.997, 0.998, 0.999, 0.9995, 0.9999, 1.0] +methods = ["efit", "efit_arclength", "efit_by_inversion"] + +function make_config(path::String, eq_type::String, psihigh::Float64) + raw = TOML.parsefile(path) + raw["Equilibrium"]["eq_type"] = eq_type + raw["Equilibrium"]["psihigh"] = psihigh + base = dirname(path) + return GeneralizedPerturbedEquilibrium.equilibrium_config(raw, base) +end + +""" +Run the full ForceFreeStates pipeline for a given eq_type and psihigh, returning +real(et[1]). Uses a temporary directory so the example directory is not modified. +Returns NaN on failure. +""" +function run_ffs_et1(config_path::String, eq_type::String, psihigh::Float64)::Float64 + example_dir = dirname(config_path) + raw = TOML.parsefile(config_path) + raw["Equilibrium"]["eq_type"] = eq_type + raw["Equilibrium"]["psihigh"] = psihigh + raw["ForceFreeStates"]["vac_flag"] = true + foreach(section -> delete!(raw, section), ("PerturbedEquilibrium", "ErrorFields")) # stop after FFS + + mktempdir() do tmpdir + # Write modified config + open(joinpath(tmpdir, "gpec.toml"), "w") do io + TOML.print(io, raw) + end + # Symlink all non-TOML files from the example directory. + for f in readdir(example_dir) + src = abspath(joinpath(example_dir, f)) + isfile(src) && !endswith(f, ".toml") && symlink(src, joinpath(tmpdir, f)) + end + try + GeneralizedPerturbedEquilibrium.main([tmpdir]) + h5open(joinpath(tmpdir, "gpec.h5"), "r") do h5 + et = read(h5["ForceFreeStates/FreeBoundaryStability/eigenmode_energies"]) + return real(et[1]) + end + catch + return NaN + end + end +end + +# JIT warmup: run equilibrium + FFS once before timing +println("JIT warmup (equilibrium + FFS)...") +run_ffs_et1(config_path, "efit", 0.990) + +println("="^70) +println("psihigh robustness scan (equilibrium + stability)") +println("="^70) + +rows = [] + +for method in methods + println("\n--- Method: $method ---") + for psihigh in psihigh_values + cfg = make_config(config_path, method, psihigh) + local pe = nothing + local runtime = NaN + local success = false + local err_msg = "" + try + t = @elapsed pe = PlasmaEquilibrium(cfg) + runtime = t + success = true + catch e + err_msg = string(e)[1:min(80, length(string(e)))] + end + + roundtrip_max_edge = NaN + q_mono_violations_edge = NaN + q_at_psihigh = NaN + q_edge_slope = NaN + et1 = NaN + + if success + raw_profile = Equilibrium.read_efit(cfg) + psi_xs = pe.rzphi_xs + mtheta = length(pe.rzphi_ys) - 1 + psio = pe.psio + + # Roundtrip error in outer 10% + errors = Float64[] + for ipsi in 1:length(psi_xs) + psi_xs[ipsi] < 0.90 && continue + ψ = psi_xs[ipsi] + for itheta in 1:8:mtheta+1 + θ = pe.rzphi_ys[itheta] + r2 = pe.rzphi_rsquared((ψ, θ)) + off = pe.rzphi_offset((ψ, θ)) + rfac = sqrt(max(r2, 0.0)) + η = 2π * (θ + off) + R = pe.ro + rfac * cos(η) + Z = pe.zo + rfac * sin(η) + ψ_recon = 1.0 - raw_profile.psi_in((R, Z)) / psio + push!(errors, abs(ψ_recon - ψ)) + end + end + roundtrip_max_edge = isempty(errors) ? NaN : maximum(errors) + + # q monotonicity in outer 10% + q_profile = [pe.profiles.q_spline(ψ) for ψ in psi_xs] + dq = diff(q_profile) + mask_edge = psi_xs[2:end] .> 0.90 + q_mono_violations_edge = count(dq[mask_edge] .< 0) + + # q at outermost grid point and slope + q_at_psihigh = pe.profiles.q_spline.y[end] + n = length(psi_xs) + if n >= 2 + q_edge_slope = (pe.profiles.q_spline.y[end] - pe.profiles.q_spline.y[end-1]) / + (psi_xs[end] - psi_xs[end-1]) + end + + # et[1]: full FFS run + et1 = run_ffs_et1(config_path, method, psihigh) + end + + status = success ? "OK" : "FAIL" + et1_str = isnan(et1) ? " FAIL" : @sprintf("%+.4f", et1) + @printf(" psihigh=%.4f %-4s t=%.2fs rt_edge=%.2e q_mono_viol=%d q(end)=%.2f et[1]=%s\n", + psihigh, status, runtime, roundtrip_max_edge, + Int(isnan(q_mono_violations_edge) ? -1 : q_mono_violations_edge), + isnan(q_at_psihigh) ? -1.0 : q_at_psihigh, + et1_str) + + push!( + rows, + ( + method=method, psihigh=psihigh, success=success, runtime=runtime, + roundtrip_max_edge=roundtrip_max_edge, + q_mono_violations_edge=q_mono_violations_edge, + q_at_psihigh=q_at_psihigh, q_edge_slope=q_edge_slope, + et1=et1, error_msg=err_msg + ) + ) + end +end + +output_csv = joinpath(example_path, "equil_psihigh_scan.csv") +open(output_csv, "w") do io + println(io, join(string.(keys(rows[1])), ",")) + for row in rows + println(io, join(map(v -> v isa AbstractString ? "\"$v\"" : string(v), values(row)), ",")) + end +end +println("\nResults saved to: $output_csv") + +# Summary +println("\n" * "="^70) +println("Last successful psihigh per method:") +for method in methods + method_rows = filter(r -> r.method == method && r.success, rows) + if isempty(method_rows) + println(" $method: ALL FAILED") + else + last_ok = maximum(r.psihigh for r in method_rows) + @printf(" %-22s psihigh = %.4f\n", method, last_ok) + end +end + +println("\n" * "="^70) +println("et[1] vs psihigh (free-boundary stability eigenvalue):") +println(" (positive = stable, negative = unstable)") +@printf(" %-22s %s\n", "psihigh", join([@sprintf("%-22s", m) for m in methods])) +for psihigh in psihigh_values + vals = [ + let r = findfirst(x -> x.method == m && x.psihigh == psihigh, rows) + r === nothing || isnan(rows[r].et1) ? " -" : @sprintf("%+.4f", rows[r].et1) + end for m in methods + ] + @printf(" %-22.4f %s\n", psihigh, join([@sprintf("%-22s", v) for v in vals])) +end +println() diff --git a/benchmarks/equil_psilow_scan.jl b/benchmarks/equil_psilow_scan.jl new file mode 100644 index 0000000000..f93476c680 --- /dev/null +++ b/benchmarks/equil_psilow_scan.jl @@ -0,0 +1,133 @@ +""" +equil_psilow_scan.jl + +Scans psilow from 1e-1 down to 1e-4, testing near-axis accuracy and robustness +of all three equilibrium methods. + +Key outputs: + - q0 extrapolation to axis vs. EFIT q-axis value + - Roundtrip error for ψ < 0.10 (near-axis region) + - For efit_by_inversion: smallest psilow where Contour.jl traces a closed curve + (identifies where the near-axis circular fallback kicks in) + +Usage: + julia --project=. benchmarks/equil_psilow_scan.jl [example_path] +""" + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.Equilibrium +using TOML, Printf, Statistics + +example_path = length(ARGS) > 0 ? ARGS[1] : joinpath(@__DIR__, "../examples/DIIID-like_ideal_example") +config_path = joinpath(example_path, "gpec.toml") + +psilow_values = [1e-1, 5e-2, 1e-2, 5e-3, 1e-3, 5e-4, 1e-4] +methods = ["efit", "efit_arclength", "efit_by_inversion"] + +function make_config(path::String, eq_type::String, psilow::Float64) + raw = TOML.parsefile(path) + raw["Equilibrium"]["eq_type"] = eq_type + raw["Equilibrium"]["psilow"] = psilow + base = dirname(path) + return GeneralizedPerturbedEquilibrium.equilibrium_config(raw, base) +end + +# Read EFIT q-axis value from header (simag line) +function read_efit_header_qaxis(eq_filename) + lines = readlines(eq_filename) + # Line 3 has raxis, zaxis, psimag, psibry, bcentr — q_axis is not directly in header. + # The q-profile at ψ_norm=0 is q_axis; extrapolate from the sampled q-profile. + header_parts = split(lines[1]) + nw = parse(Int, header_parts[end-1]) + lines_per_block = cld(nw, 5) + # q-profile starts after 4 header lines + 4 data blocks (fpol,pres,ffprime,pprime) + psi 2D block + nh = parse(Int, header_parts[end]) + q_start = 6 + (4 + cld(nw * nh, 5)) * lines_per_block # rough; parse all blocks + # Simpler: just extrapolate from the first two q-values returned by our config + return nothing # will use profile extrapolation below +end + +# JIT warmup +println("JIT warmup...") +warmup_cfg = make_config(config_path, "efit", 1e-2) +PlasmaEquilibrium(warmup_cfg) + +println("="^70) +println("psilow near-axis accuracy scan") +println("="^70) + +rows = [] + +for method in methods + println("\n--- Method: $method ---") + for psilow in psilow_values + cfg = make_config(config_path, method, psilow) + local pe = nothing + local runtime = NaN + local success = false + local err_msg = "" + try + t = @elapsed pe = PlasmaEquilibrium(cfg) + runtime = t + success = true + catch e + err_msg = string(e)[1:min(80, length(string(e)))] + end + + q0_extrap = NaN + roundtrip_max_axis = NaN + q_axis_rms_axis = NaN + + if success + raw_profile = Equilibrium.read_efit(cfg) + psi_xs = pe.rzphi_xs + mtheta = length(pe.rzphi_ys) - 1 + psio = pe.psio + + # q0 extrapolated to axis (linear from innermost two grid points) + q1 = pe.profiles.q_spline.y[1] + q_slope = pe.profiles.q_deriv(psi_xs[1]; hint=Ref(1)) + q0_extrap = q1 - q_slope * psi_xs[1] + + # Roundtrip error in inner region (ψ < 0.10) + errors = Float64[] + for ipsi in 1:length(psi_xs) + psi_xs[ipsi] > 0.10 && break + ψ = psi_xs[ipsi] + for itheta in 1:8:mtheta+1 + θ = pe.rzphi_ys[itheta] + r2 = pe.rzphi_rsquared((ψ, θ)) + off = pe.rzphi_offset((ψ, θ)) + rfac = sqrt(max(r2, 0.0)) + η = 2π * (θ + off) + R = pe.ro + rfac * cos(η) + Z = pe.zo + rfac * sin(η) + ψ_recon = 1.0 - raw_profile.psi_in((R, Z)) / psio + push!(errors, abs(ψ_recon - ψ)) + end + end + roundtrip_max_axis = isempty(errors) ? NaN : maximum(errors) + end + + status = success ? "OK" : "FAIL" + @printf(" psilow=%.1e %-4s t=%.2fs q0_extrap=%.3f rt_inner=%.2e\n", + psilow, status, runtime, isnan(q0_extrap) ? -1.0 : q0_extrap, roundtrip_max_axis) + !success && println(" Error: $err_msg") + + push!(rows, ( + method=method, psilow=psilow, success=success, runtime=runtime, + q0_extrap=q0_extrap, roundtrip_max_axis=roundtrip_max_axis, + error_msg=err_msg + )) + end +end + +output_csv = joinpath(example_path, "equil_psilow_scan.csv") +open(output_csv, "w") do io + println(io, join(string.(keys(rows[1])), ",")) + for row in rows + println(io, join(map(v -> v isa AbstractString ? "\"$v\"" : string(v), values(row)), ",")) + end +end +println("\nResults saved to: $output_csv") +println() diff --git a/benchmarks/equil_spline_comparison.jl b/benchmarks/equil_spline_comparison.jl new file mode 100644 index 0000000000..19f906c551 --- /dev/null +++ b/benchmarks/equil_spline_comparison.jl @@ -0,0 +1,621 @@ +""" +equil_spline_comparison.jl + +Detailed comparison of equilibrium descriptions produced by all three EFIT equilibrium +construction approaches, using "efit" as the reference. + +Produces (saved to benchmarks/equil_spline_comparison_psihigh/): + 1. 1D profile comparisons: F, P, dV/dψ, q with full-domain, core zoom, edge zoom, and diff subplots + 2. Flux surface contour overlays: psi(R,Z) with SFL grid dots; full, core, and x-point zoom panels + 2b. Theta contour lines: constant-θ spokes in (R,Z) with SFL grid dots; same three panels + 3. 2D rzphi spline profiles at θ = 0, 0.25, 0.5, 0.75 for each of the 4 geometric splines + 4. 2D eqfun physics spline profiles at the same theta slices + 5. Printed numerical summaries of deep-core and far-edge differences for every spline + +Usage: + julia --project=. benchmarks/equil_spline_comparison.jl [example_path] [psihigh] + +psihigh values of interest: + 0.996 → poor et[1] agreement + 0.997 → ok et[1] agreement + 0.999 → poor et[1] agreement again +""" + +using GeneralizedPerturbedEquilibrium +using GeneralizedPerturbedEquilibrium.Equilibrium +using TOML, Printf, Statistics, Plots + +# ─── CLI arguments ───────────────────────────────────────────────────────────── +example_path = length(ARGS) > 0 ? ARGS[1] : + joinpath(@__DIR__, "../examples/DIIID-like_ideal_example") +psihigh_arg = length(ARGS) > 1 ? parse(Float64, ARGS[2]) : 0.997 +config_path = joinpath(example_path, "gpec.toml") + +println("="^70) +println("Equilibrium Spline Comparison: efit / efit_arclength / efit_by_inversion") +println("Example : $example_path") +println("psihigh : $psihigh_arg") +println("="^70) + +# ─── Output directory ────────────────────────────────────────────────────────── +psihigh_tag = replace(string(psihigh_arg), "." => "p") +outdir = joinpath(@__DIR__, "equil_spline_comparison_psihigh$(psihigh_tag)") +mkpath(outdir) +println("Output : $outdir\n") + +# ─── Config factory ──────────────────────────────────────────────────────────── +function make_config(path::String, eq_type::String, psihigh::Float64) + raw = TOML.parsefile(path) + raw["Equilibrium"]["eq_type"] = eq_type + raw["Equilibrium"]["psihigh"] = psihigh + return GeneralizedPerturbedEquilibrium.equilibrium_config(raw, dirname(path)) +end + +# ─── Load all three equilibria ───────────────────────────────────────────────── +# efit is the reference; efit_arclength and efit_by_inversion are compared to it. +ref_method = "efit" +compare_methods = ["efit_arclength", "efit_by_inversion"] +all_methods = vcat(ref_method, compare_methods) + +method_color = Dict( + "efit" => :blue, + "efit_arclength" => :darkorange, + "efit_by_inversion" => :red +) +method_style = Dict( + "efit" => :solid, + "efit_arclength" => :dash, + "efit_by_inversion" => :dot +) +method_label = Dict( + "efit" => "efit", + "efit_arclength" => "efit_arclength", + "efit_by_inversion" => "efit_by_inv" +) +diff_color = Dict( + "efit_arclength" => :darkorange, + "efit_by_inversion" => :red +) + +pes = Dict{String,Any}() +for m in all_methods + println("--- Running: $m ---") + cfg = make_config(config_path, m, psihigh_arg) + try + pes[m] = PlasmaEquilibrium(cfg) + println(" Done.\n") + catch e + println(" FAILED: $e\n") + end +end + +failed = setdiff(all_methods, keys(pes)) +isempty(failed) || println("Skipping failed methods in comparisons: $(join(failed, ", "))") + +haskey(pes, ref_method) || error("Reference method '$ref_method' failed — cannot continue.") +pe_ref = pes[ref_method] +all_methods = filter(m -> haskey(pes, m), all_methods) +compare_methods_active = filter(m -> haskey(pes, m), compare_methods) + +# ─── Helpers ─────────────────────────────────────────────────────────────────── +""" +Convert (ψ, θ) straight-field-line coordinates → (R, Z) physical coordinates. +""" +function psi_theta_to_RZ(pe, psi, theta) + r2 = pe.rzphi_rsquared((psi, theta)) + off = pe.rzphi_offset((psi, theta)) + rfac = sqrt(max(r2, 0.0)) + η = 2π * (theta + off) + R = pe.ro + rfac * cos(η) + Z = pe.zo + rfac * sin(η) + return R, Z +end + +""" +Compute the full (R, Z) trace of each flux surface in psi_vals. +""" +function flux_surface_RZ(pe, psi_vals, theta_range) + R_all = [Float64[] for _ in 1:length(psi_vals)] + Z_all = [Float64[] for _ in 1:length(psi_vals)] + for (k, psi) in enumerate(psi_vals) + for θ in theta_range + R, Z = psi_theta_to_RZ(pe, psi, θ) + push!(R_all[k], R) + push!(Z_all[k], Z) + end + push!(R_all[k], R_all[k][1]) + push!(Z_all[k], Z_all[k][1]) + end + return R_all, Z_all +end + +# Common psi evaluation grid — ldp distribution at 8× oversampling so inter-knot +# ringing is visible in both overplots and diff plots. +psi_lo = pe_ref.rzphi_xs[1] +psi_hi = pe_ref.rzphi_xs[end] +mpsi_eval = 8 * (length(pe_ref.rzphi_xs) - 1) +psi_full = psi_lo .+ (psi_hi - psi_lo) .* sin.(range(0.0, 1.0; length=mpsi_eval + 1) .* (π / 2)) .^ 2 + +mask_core = psi_full .< 0.10 +mask_edge = psi_full .> 0.98 +# Edge coordinate: −ln(1−ψ) linearises the q ~ A·ln(1/(1−ψ)) separatrix asymptotics +t_edge_full = -log.(1.0 .- psi_full[mask_edge]) + +theta_select = [0.0, 0.25, 0.5, 0.75] +theta_colors = [:blue, :green, :darkorange, :purple] + +# ═══════════════════════════════════════════════════════════════════════════════ +# 1. 1D PROFILE COMPARISONS +# ═══════════════════════════════════════════════════════════════════════════════ +println("="^70) +println("1. 1D Profile Comparisons") +println("="^70) + +profile_specs = [ + ("F (2π·R·Bₜ)", pe -> pe.profiles.F_spline), + ("μ₀·P", pe -> pe.profiles.P_spline), + ("dV/dψ", pe -> pe.profiles.dVdpsi_spline), + ("q", pe -> pe.profiles.q_spline) +] +profile_fnames = ["profile_F", "profile_P", "profile_dVdpsi", "profile_q"] + +for (idx, (pname, spl_getter)) in enumerate(profile_specs) + spl_ref = spl_getter(pe_ref) + y_ref = [spl_ref(ψ) for ψ in psi_full] + + p_full = plot(psi_full, y_ref; lw=2, color=method_color[ref_method], + ls=method_style[ref_method], label=method_label[ref_method], + xlabel="ψ (normalized)", ylabel=pname, legend=:best, xlims=(0, 1), + title="$pname — full domain (psihigh=$psihigh_arg)") + p_core = plot(psi_full[mask_core], y_ref[mask_core]; lw=2, + color=method_color[ref_method], ls=method_style[ref_method], + label=method_label[ref_method], xlabel="ψ (log scale)", ylabel=pname, + title="Deep core (ψ < 0.10)", xscale=:log10, legend=:none) + p_edge = plot(t_edge_full, y_ref[mask_edge]; lw=2, + color=method_color[ref_method], ls=method_style[ref_method], + label=method_label[ref_method], xlabel="−ln(1−ψ)", ylabel=pname, + title="Far edge (ψ > 0.98)", legend=:none) + p_diff = plot(; xlabel="ψ", ylabel="Δ$pname", title="Difference (vs efit)", legend=:none, xlims=(0, 1)) + hline!(p_diff, [0.0]; color=:black, lw=1, ls=:dot, label="") + + # Scatter markers at actual ψ grid nodes for reference method + ref_xs = pe_ref.rzphi_xs + ref_ys = [spl_ref(ψ) for ψ in ref_xs] + scatter!(p_full, ref_xs, ref_ys; ms=3, markerstrokewidth=0, color=method_color[ref_method], label="") + let mask = ref_xs .< 0.10 + scatter!(p_core, ref_xs[mask], ref_ys[mask]; ms=3, markerstrokewidth=0, color=method_color[ref_method], label="") + end + let mask = ref_xs .> 0.98 + scatter!(p_edge, -log.(1.0 .- ref_xs[mask]), ref_ys[mask]; ms=3, markerstrokewidth=0, color=method_color[ref_method], label="") + end + + for m in compare_methods_active + spl_m = spl_getter(pes[m]) + y_m = [spl_m(ψ) for ψ in psi_full] + Δy = y_ref .- y_m + + plot!(p_full, psi_full, y_m; lw=1.5, color=method_color[m], ls=method_style[m], + label=method_label[m]) + plot!(p_core, psi_full[mask_core], y_m[mask_core]; lw=1.5, + color=method_color[m], ls=method_style[m], label=method_label[m]) + plot!(p_edge, t_edge_full, y_m[mask_edge]; lw=1.5, + color=method_color[m], ls=method_style[m], label=method_label[m]) + plot!(p_diff, psi_full, Δy; lw=1.5, color=diff_color[m], label="efit − $(method_label[m])") + + # Scatter markers at actual ψ grid nodes for compare method + m_xs = pes[m].rzphi_xs + m_ys = [spl_m(ψ) for ψ in m_xs] + scatter!(p_full, m_xs, m_ys; ms=3, markerstrokewidth=0, color=method_color[m], label="") + let mask = m_xs .< 0.10 + scatter!(p_core, m_xs[mask], m_ys[mask]; ms=3, markerstrokewidth=0, color=method_color[m], label="") + end + let mask = m_xs .> 0.98 + scatter!(p_edge, -log.(1.0 .- m_xs[mask]), m_ys[mask]; ms=3, markerstrokewidth=0, color=method_color[m], label="") + end + + @printf(" %-14s vs %-18s max|Δ|=%.3e rms|Δ|=%.3e edge max|Δ|=%.3e\n", + pname, method_label[m], maximum(abs.(Δy)), sqrt(mean(Δy .^ 2)), + any(mask_edge) ? maximum(abs.(Δy[mask_edge])) : NaN) + end + + p_combo = plot(p_full, p_core, p_edge, p_diff; layout=(2, 2), size=(1400, 900), + left_margin=8Plots.mm, bottom_margin=6Plots.mm) + savefig(p_combo, joinpath(outdir, "$(profile_fnames[idx]).png")) +end + +# ─── Reconstruct efit_by_inversion grids for grid-overplot visualization ─────── +# Mirrors the grid construction in equilibrium_solver_by_inversion so we can +# scatter the actual grid points onto the flux-surface plots. +r_global_grid = Float64[] +z_global_grid = Float64[] +r_zoom_grid_vis = Float64[] +z_zoom_grid_vis = Float64[] +if haskey(pes, "efit_by_inversion") + let + global r_global_grid, z_global_grid, r_zoom_grid_vis, z_zoom_grid_vis + cfg_inv = make_config(config_path, "efit_by_inversion", psihigh_arg) + raw_inv = Equilibrium.read_efit(cfg_inv) + ro_g, zo_g = pes["efit_by_inversion"].ro, pes["efit_by_inversion"].zo + psio_g = raw_inv.psio + psilow_g = cfg_inv.psilow + topology_g = Equilibrium.classify_topology(raw_inv, psio_g) + + ψ_RR = abs(raw_inv.psi_in((ro_g, zo_g); deriv=DerivOp(2, 0))) + ψ_ZZ = abs(raw_inv.psi_in((ro_g, zo_g); deriv=DerivOp(0, 2))) + a_low_g = min(sqrt(2 * psilow_g * psio_g / ψ_RR), sqrt(2 * psilow_g * psio_g / ψ_ZZ)) + + nw_g = length(raw_inv.psi_in_xs) + nh_g = length(raw_inv.psi_in_ys) + β_r_g, β_z_g = 2.0, 2.0 + refine_g = 5 + + nr_g = max(4, round(Int, refine_g * nw_g)) + nz_g = max(4, round(Int, refine_g * nh_g)) + r_g = Equilibrium.make_stretched_r_grid(raw_inv.rmin, raw_inv.rmax, ro_g, nr_g, β_r_g) + z_g = Equilibrium.make_stretched_z_grid(raw_inv.zmin, raw_inv.zmax, zo_g, nz_g, topology_g, β_z_g) + + iro_g = clamp(searchsortedfirst(r_g, ro_g), 2, length(r_g)) + izo_g = clamp(searchsortedfirst(z_g, zo_g), 2, length(z_g)) + dR_ax = r_g[iro_g] - r_g[iro_g-1] + dZ_ax = z_g[izo_g] - z_g[izo_g-1] + sc_r = max(1, ceil(Int, dR_ax / (0.2 * a_low_g))) + sc_z = max(1, ceil(Int, dZ_ax / (0.2 * a_low_g))) + if sc_r > 1 + nr_g = (nr_g - 1) * sc_r + 1 + r_g = Equilibrium.make_stretched_r_grid(raw_inv.rmin, raw_inv.rmax, ro_g, nr_g, β_r_g) + iro_g = clamp(searchsortedfirst(r_g, ro_g), 2, length(r_g)) + dR_ax = r_g[iro_g] - r_g[iro_g-1] + end + if sc_z > 1 + nz_g = (nz_g - 1) * sc_z + 1 + z_g = Equilibrium.make_stretched_z_grid(raw_inv.zmin, raw_inv.zmax, zo_g, nz_g, topology_g, β_z_g) + izo_g = clamp(searchsortedfirst(z_g, zo_g), 2, length(z_g)) + dZ_ax = z_g[izo_g] - z_g[izo_g-1] + end + r_global_grid = r_g + z_global_grid = z_g + + d_max_g = max(dR_ax, dZ_ax) + threshold_g = (10 * d_max_g)^2 * max(ψ_RR, ψ_ZZ) / (2 * psio_g) + psi_zoom_max = 9 * threshold_g + a_zoom_r = sqrt(2 * psi_zoom_max * psio_g / ψ_RR) * 1.2 + a_zoom_z = sqrt(2 * psi_zoom_max * psio_g / ψ_ZZ) * 1.2 + sinh_β = sinh(β_r_g) + nr_z = max(200, ceil(Int, 8 * β_r_g * a_zoom_r * 10 / (sinh_β * a_low_g))) + nz_z = max(200, ceil(Int, 8 * β_r_g * a_zoom_z * 10 / (sinh_β * a_low_g))) + r_zoom_grid_vis = Equilibrium.make_stretched_r_grid( + max(raw_inv.rmin, ro_g - a_zoom_r), min(raw_inv.rmax, ro_g + a_zoom_r), + ro_g, nr_z, β_r_g) + z_zoom_grid_vis = Equilibrium.make_stretched_r_grid( + max(raw_inv.zmin, zo_g - a_zoom_z), min(raw_inv.zmax, zo_g + a_zoom_z), + zo_g, nz_z, β_r_g) + println(" Grid vis: global $(nr_g)×$(nz_g), zoom $(nr_z)×$(nz_z)") + end +end # haskey(pes, "efit_by_inversion") + +# ═══════════════════════════════════════════════════════════════════════════════ +# 2. FLUX SURFACE CONTOUR OVERLAYS +# ═══════════════════════════════════════════════════════════════════════════════ +println("\n" * "="^70) +println("2. Flux Surface Contour Overlays") +println("="^70) + +psi_contours = collect(range(psi_lo, psi_hi; length=20)) +theta_contour = range(0.0, 1.0; length=256) + +R_fs = Dict(m => flux_surface_RZ(pes[m], psi_contours, theta_contour) for m in all_methods) + +p_ctr_full = plot(; aspect_ratio=:equal, xlabel="R [m]", ylabel="Z [m]", + title="Flux surfaces: all methods (psihigh=$psihigh_arg)") +p_ctr_core = plot(; aspect_ratio=:equal, xlabel="R [m]", ylabel="Z [m]", + title="Deep core zoom (innermost 4 surfaces)") +p_ctr_xpt = plot(; aspect_ratio=:equal, xlabel="R [m]", ylabel="Z [m]", + title="Far edge / x-point zoom (outermost 3 surfaces)") + +for m in all_methods + Rs, Zs = R_fs[m] + for k in 1:length(psi_contours) + lbl = k == 1 ? method_label[m] : "" + plot!(p_ctr_full, Rs[k], Zs[k]; color=method_color[m], + ls=method_style[m], lw=0.9, alpha=0.7, label=lbl) + end + n_core = 4 + for k in 1:min(n_core, length(psi_contours)) + lbl = k == 1 ? method_label[m] : "" + plot!(p_ctr_core, Rs[k], Zs[k]; color=method_color[m], + ls=method_style[m], lw=1.5, label=lbl) + end + n_outer = length(psi_contours) + for k in (n_outer-2):n_outer + lbl = k == n_outer - 2 ? method_label[m] : "" + plot!(p_ctr_xpt, Rs[k], Zs[k]; color=method_color[m], + ls=method_style[m], lw=1.5, label=lbl) + end +end + +# Grid overplots: scatter subsampled grid points on each panel (only if efit_by_inversion ran). +# Global grid: every 20th R × 12th Z (grey). Zoom grid: every 3rd R × 3rd Z (darkgrey). +if !isempty(r_global_grid) + let + step_rg, step_zg = 20, 12 + Rg = [r_global_grid[ir] for ir in 1:step_rg:length(r_global_grid) + for _ in 1:step_zg:length(z_global_grid)] + Zg = [z_global_grid[iz] for _ in 1:step_rg:length(r_global_grid) + for iz in 1:step_zg:length(z_global_grid)] + scatter!(p_ctr_full, Rg, Zg; ms=0.8, color=:grey, alpha=0.25, + markerstrokewidth=0, label="global grid") + + step_rz, step_zz = 3, 3 + Rz = [r_zoom_grid_vis[ir] for ir in 1:step_rz:length(r_zoom_grid_vis) + for _ in 1:step_zz:length(z_zoom_grid_vis)] + Zz = [z_zoom_grid_vis[iz] for _ in 1:step_rz:length(r_zoom_grid_vis) + for iz in 1:step_zz:length(z_zoom_grid_vis)] + scatter!(p_ctr_full, Rz, Zz; ms=0.8, color=:darkgrey, alpha=0.4, + markerstrokewidth=0, label="zoom grid") + + # Core panel: show both grids at full density within the core zoom region + scatter!(p_ctr_core, Rg, Zg; ms=1.2, color=:grey, alpha=0.3, + markerstrokewidth=0, label="global grid") + scatter!(p_ctr_core, Rz, Zz; ms=1.2, color=:darkgrey, alpha=0.5, + markerstrokewidth=0, label="zoom grid") + + # X-point panel: global grid only (zoom doesn't extend there) + scatter!(p_ctr_xpt, Rg, Zg; ms=1.2, color=:grey, alpha=0.3, + markerstrokewidth=0, label="global grid") + end +end # !isempty(r_global_grid) + +# Set zoom limits for core and x-point panels using the reference method +Rc, Zc = R_fs[ref_method] +n_core = 4 +all_R_c = vcat(Rc[1:n_core]...); +all_Z_c = vcat(Zc[1:n_core]...); +pad = 0.03 +xlims!(p_ctr_core, minimum(all_R_c) - pad, maximum(all_R_c) + pad) +ylims!(p_ctr_core, minimum(all_Z_c) - pad, maximum(all_Z_c) + pad) + +n_outer = length(psi_contours) +all_R_e = vcat(Rc[(n_outer-2):n_outer]...); +all_Z_e = vcat(Zc[(n_outer-2):n_outer]...); +xpt_idx = argmin(all_Z_e) +xlims!(p_ctr_xpt, all_R_e[xpt_idx] - 0.20, all_R_e[xpt_idx] + 0.20) +ylims!(p_ctr_xpt, all_Z_e[xpt_idx] - 0.05, all_Z_e[xpt_idx] + 0.40) + +p_ctr_combo = plot(p_ctr_full, p_ctr_core, p_ctr_xpt; layout=(1, 3), size=(2100, 800), + left_margin=8Plots.mm, bottom_margin=6Plots.mm) +savefig(p_ctr_combo, joinpath(outdir, "flux_contours.png")) + +# ─── Theta contour plot ───────────────────────────────────────────────────── +# Spoke lines at the subsampled θ grid nodes; dots at (ψ_i, θ_j) intersections. +# Using the actual grid θ values for both ensures every dot lies on a line. +psi_dense = range(psi_lo, psi_hi; length=300) + +p_tht_full = plot(; aspect_ratio=:equal, xlabel="R [m]", ylabel="Z [m]", + title="θ = const lines: all methods (psihigh=$psihigh_arg)") +p_tht_core = plot(; aspect_ratio=:equal, xlabel="R [m]", ylabel="Z [m]", + title="Deep core zoom") +p_tht_xpt = plot(; aspect_ratio=:equal, xlabel="R [m]", ylabel="Z [m]", + title="Far edge / x-point zoom") + +for m in all_methods + pe = pes[m] + θ_all = pe.rzphi_ys + step = max(1, length(θ_all) ÷ 32) + θ_sub = θ_all[1:step:end] # ~32 theta values used for both lines and dots + for (kidx, θ) in enumerate(θ_sub) + lbl = kidx == 1 ? method_label[m] : "" + Rs = [psi_theta_to_RZ(pe, ψ, θ)[1] for ψ in psi_dense] + Zs = [psi_theta_to_RZ(pe, ψ, θ)[2] for ψ in psi_dense] + plot!(p_tht_full, Rs, Zs; color=method_color[m], ls=method_style[m], lw=0.9, alpha=0.7, label=lbl) + plot!(p_tht_core, Rs, Zs; color=method_color[m], ls=method_style[m], lw=1.5, label=lbl) + plot!(p_tht_xpt, Rs, Zs; color=method_color[m], ls=method_style[m], lw=1.5, label=lbl) + end + # Grid dots at every (ψ_i, θ_j) intersection — by construction on the lines above + Rg = [psi_theta_to_RZ(pe, ψ, θ)[1] for ψ in pe.rzphi_xs for θ in θ_sub] + Zg = [psi_theta_to_RZ(pe, ψ, θ)[2] for ψ in pe.rzphi_xs for θ in θ_sub] + scatter!(p_tht_full, Rg, Zg; ms=1.5, color=method_color[m], alpha=0.5, markerstrokewidth=0, label="") + scatter!(p_tht_core, Rg, Zg; ms=2.0, color=method_color[m], alpha=0.6, markerstrokewidth=0, label="") + scatter!(p_tht_xpt, Rg, Zg; ms=2.0, color=method_color[m], alpha=0.6, markerstrokewidth=0, label="") +end + +xlims!(p_tht_core, minimum(all_R_c) - pad, maximum(all_R_c) + pad) +ylims!(p_tht_core, minimum(all_Z_c) - pad, maximum(all_Z_c) + pad) +xlims!(p_tht_xpt, all_R_e[xpt_idx] - 0.20, all_R_e[xpt_idx] + 0.20) +ylims!(p_tht_xpt, all_Z_e[xpt_idx] - 0.05, all_Z_e[xpt_idx] + 0.40) + +p_tht_combo = plot(p_tht_full, p_tht_core, p_tht_xpt; layout=(1, 3), size=(2100, 800), + left_margin=8Plots.mm, bottom_margin=6Plots.mm) +savefig(p_tht_combo, joinpath(outdir, "theta_contours.png")) +println(" Saved flux contour figures.") + +# ═══════════════════════════════════════════════════════════════════════════════ +# 3. 2D RZPHI GEOMETRIC SPLINES AT SELECT THETA SLICES +# ═══════════════════════════════════════════════════════════════════════════════ +println("\n" * "="^70) +println("3. 2D rzphi Geometric Spline Profiles at Select θ Values") +println("="^70) + +rzphi_specs = [ + ("r²=(R-R₀)²+(Z-Z₀)²", pe -> ((ψ, θ) -> pe.rzphi_rsquared((ψ, θ)))), + ("angle offset η/2π−θ", pe -> ((ψ, θ) -> pe.rzphi_offset((ψ, θ)))), + ("ν (toroidal shift)", pe -> ((ψ, θ) -> pe.rzphi_nu((ψ, θ)))), + ("Jacobian", pe -> ((ψ, θ) -> pe.rzphi_jac((ψ, θ)))) +] +rzphi_fnames = ["rzphi_rsquared", "rzphi_angle_offset", "rzphi_nu", "rzphi_jacobian"] + +for (sidx, (sname, fn_getter)) in enumerate(rzphi_specs) + fn_ref = fn_getter(pe_ref) + yd_all = [[fn_ref(ψ, θ) for ψ in psi_full] for θ in theta_select] + + p_full = plot(; xlabel="ψ", ylabel=sname, legend=:best, xlims=(0, 1), + title="$sname — full domain (psihigh=$psihigh_arg)") + p_core = plot(; xlabel="ψ (log scale)", ylabel=sname, title="Deep core (ψ < 0.10)", xscale=:log10, legend=:none) + p_edge = plot(; xlabel="−ln(1−ψ)", ylabel=sname, title="Far edge (ψ > 0.98)", legend=:none) + p_diff = plot(; xlabel="ψ", ylabel="Δ$sname", title="efit − other", legend=:none, xlims=(0, 1)) + hline!(p_diff, [0.0]; color=:black, lw=1, ls=:dot, label="") + + for (tidx, θ) in enumerate(theta_select) + tc = theta_colors[tidx] + yd = yd_all[tidx] + plot!(p_full, psi_full, yd; color=tc, lw=2, label="efit θ=$(θ)") + plot!(p_core, psi_full[mask_core], yd[mask_core]; color=tc, lw=2, label="efit θ=$(θ)") + plot!(p_edge, t_edge_full, yd[mask_edge]; color=tc, lw=2, label="efit θ=$(θ)") + xs = pe_ref.rzphi_xs + ys = [fn_ref(ψ, θ) for ψ in xs] + scatter!(p_full, xs, ys; ms=3, markerstrokewidth=0, color=tc, label="") + let mask = xs .< 0.10 + scatter!(p_core, xs[mask], ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + let mask = xs .> 0.98 + scatter!(p_edge, -log.(1.0 .- xs[mask]), ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + end + + println(" $sname:") + for m in compare_methods_active + fn_m = fn_getter(pes[m]) + yi_all = [[fn_m(ψ, θ) for ψ in psi_full] for θ in theta_select] + for (tidx, θ) in enumerate(theta_select) + tc = theta_colors[tidx] + yi = yi_all[tidx] + Δ = yd_all[tidx] .- yi + plot!(p_full, psi_full, yi; color=tc, lw=1.2, ls=method_style[m], + label="$(method_label[m]) θ=$(θ)") + plot!(p_core, psi_full[mask_core], yi[mask_core]; color=tc, lw=1.2, + ls=method_style[m], label="$(method_label[m]) θ=$(θ)") + plot!(p_edge, t_edge_full, yi[mask_edge]; color=tc, lw=1.2, + ls=method_style[m], label="$(method_label[m]) θ=$(θ)") + plot!(p_diff, psi_full, Δ; color=tc, lw=1.2, ls=method_style[m], + label="(efit−$(method_label[m])) θ=$(θ)") + xs = pes[m].rzphi_xs + ys = [fn_m(ψ, θ) for ψ in xs] + scatter!(p_full, xs, ys; ms=3, markerstrokewidth=0, color=tc, label="") + let mask = xs .< 0.10 + scatter!(p_core, xs[mask], ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + let mask = xs .> 0.98 + scatter!(p_edge, -log.(1.0 .- xs[mask]), ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + + Δc = any(mask_core) ? Δ[mask_core] : [0.0] + Δe = any(mask_edge) ? Δ[mask_edge] : [0.0] + @printf(" vs %-18s θ=%.2f core max|Δ|=%.3e edge max|Δ|=%.3e edge rms=%.3e\n", + method_label[m], θ, maximum(abs.(Δc)), maximum(abs.(Δe)), sqrt(mean(Δe .^ 2))) + end + end + + p_combo = plot(p_full, p_core, p_edge, p_diff; layout=(2, 2), size=(1400, 900), + left_margin=8Plots.mm, bottom_margin=6Plots.mm) + savefig(p_combo, joinpath(outdir, "$(rzphi_fnames[sidx]).png")) +end + +# ═══════════════════════════════════════════════════════════════════════════════ +# 4. EQFUN PHYSICS SPLINES AT SELECT THETA SLICES +# ═══════════════════════════════════════════════════════════════════════════════ +println("\n" * "="^70) +println("4. Physics (eqfun) Spline Profiles at Select θ Values") +println("="^70) + +eqfun_specs = [ + ("|B| (total field [T])", pe -> ((ψ, θ) -> pe.eqfun_B((ψ, θ)))), + ("metric1 (e₁·e₂+q·e₃·e₁)/JB²", pe -> ((ψ, θ) -> pe.eqfun_metric1((ψ, θ)))), + ("metric2 (e₂·e₃+q·e₃²)/JB²", pe -> ((ψ, θ) -> pe.eqfun_metric2((ψ, θ)))) +] +eqfun_fnames = ["eqfun_Bmag", "eqfun_metric1", "eqfun_metric2"] + +for (eidx, (ename, fn_getter)) in enumerate(eqfun_specs) + fn_ref = fn_getter(pe_ref) + yd_all = [[fn_ref(ψ, θ) for ψ in psi_full] for θ in theta_select] + + p_full = plot(; xlabel="ψ", ylabel=ename, legend=:best, xlims=(0, 1), + title="$ename — full domain (psihigh=$psihigh_arg)") + p_core = plot(; xlabel="ψ (log scale)", ylabel=ename, title="Deep core (ψ < 0.10)", xscale=:log10, legend=:none) + p_edge = plot(; xlabel="−ln(1−ψ)", ylabel=ename, title="Far edge (ψ > 0.98)", legend=:none) + p_diff = plot(; xlabel="ψ", ylabel="Δ$ename", title="efit − other", legend=:none, xlims=(0, 1)) + hline!(p_diff, [0.0]; color=:black, lw=1, ls=:dot, label="") + + for (tidx, θ) in enumerate(theta_select) + tc = theta_colors[tidx] + yd = yd_all[tidx] + plot!(p_full, psi_full, yd; color=tc, lw=2, label="efit θ=$(θ)") + plot!(p_core, psi_full[mask_core], yd[mask_core]; color=tc, lw=2, label="efit θ=$(θ)") + plot!(p_edge, t_edge_full, yd[mask_edge]; color=tc, lw=2, label="efit θ=$(θ)") + xs = pe_ref.rzphi_xs + ys = [fn_ref(ψ, θ) for ψ in xs] + scatter!(p_full, xs, ys; ms=3, markerstrokewidth=0, color=tc, label="") + let mask = xs .< 0.10 + scatter!(p_core, xs[mask], ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + let mask = xs .> 0.98 + scatter!(p_edge, -log.(1.0 .- xs[mask]), ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + end + + println(" $ename:") + for m in compare_methods_active + fn_m = fn_getter(pes[m]) + yi_all = [[fn_m(ψ, θ) for ψ in psi_full] for θ in theta_select] + for (tidx, θ) in enumerate(theta_select) + tc = theta_colors[tidx] + yi = yi_all[tidx] + Δ = yd_all[tidx] .- yi + plot!(p_full, psi_full, yi; color=tc, lw=1.2, ls=method_style[m], + label="$(method_label[m]) θ=$(θ)") + plot!(p_core, psi_full[mask_core], yi[mask_core]; color=tc, lw=1.2, + ls=method_style[m], label="$(method_label[m]) θ=$(θ)") + plot!(p_edge, t_edge_full, yi[mask_edge]; color=tc, lw=1.2, + ls=method_style[m], label="$(method_label[m]) θ=$(θ)") + plot!(p_diff, psi_full, Δ; color=tc, lw=1.2, ls=method_style[m], + label="(efit−$(method_label[m])) θ=$(θ)") + xs = pes[m].rzphi_xs + ys = [fn_m(ψ, θ) for ψ in xs] + scatter!(p_full, xs, ys; ms=3, markerstrokewidth=0, color=tc, label="") + let mask = xs .< 0.10 + scatter!(p_core, xs[mask], ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + let mask = xs .> 0.98 + scatter!(p_edge, -log.(1.0 .- xs[mask]), ys[mask]; ms=3, markerstrokewidth=0, color=tc, label="") + end + + Δc = any(mask_core) ? Δ[mask_core] : [0.0] + Δe = any(mask_edge) ? Δ[mask_edge] : [0.0] + @printf(" vs %-18s θ=%.2f core max|Δ|=%.3e edge max|Δ|=%.3e edge rms=%.3e\n", + method_label[m], θ, maximum(abs.(Δc)), maximum(abs.(Δe)), sqrt(mean(Δe .^ 2))) + end + end + + p_combo = plot(p_full, p_core, p_edge, p_diff; layout=(2, 2), size=(1400, 900), + left_margin=8Plots.mm, bottom_margin=6Plots.mm) + savefig(p_combo, joinpath(outdir, "$(eqfun_fnames[eidx]).png")) +end + +# ═══════════════════════════════════════════════════════════════════════════════ +# 5. SUMMARY +# ═══════════════════════════════════════════════════════════════════════════════ +println("\n" * "="^70) +println("Summary: axis / edge parameter comparison") +println("="^70) + +col_labels = [method_label[m] for m in all_methods] +@printf(" %-22s %s\n", "Parameter", join([@sprintf("%18s", l) for l in col_labels])) + +function row(label, vals) + @printf(" %-22s %s\n", label, join([@sprintf("%18.6f", v) for v in vals])) +end +function row_int(label, vals) + @printf(" %-22s %s\n", label, join([@sprintf("%18d", v) for v in vals])) +end + +row("ro [m]", [pes[m].ro for m in all_methods]) +row("zo [m]", [pes[m].zo for m in all_methods]) +row("psio [Wb/rad]", [pes[m].psio for m in all_methods]) +row("psilow", [pes[m].rzphi_xs[1] for m in all_methods]) +row("psihigh", [pes[m].rzphi_xs[end] for m in all_methods]) +row("q(psilow)", [pes[m].profiles.q_spline(pes[m].rzphi_xs[1]) for m in all_methods]) +row("q(psihigh)", [pes[m].profiles.q_spline(pes[m].rzphi_xs[end]) for m in all_methods]) + +q_profiles = Dict(m => [pes[m].profiles.q_spline(ψ) for ψ in psi_full] for m in all_methods) +row_int("q non-mono (edge)", + [count(diff(q_profiles[m])[mask_edge[2:end]] .< 0) for m in all_methods]) + +println("\nAll figures saved to: $outdir") diff --git a/benchmarks/plot_grid_knot_placement.jl b/benchmarks/plot_grid_knot_placement.jl new file mode 100644 index 0000000000..50dc3ef968 --- /dev/null +++ b/benchmarks/plot_grid_knot_placement.jl @@ -0,0 +1,67 @@ +# Visualizes where the two-pass auto grid places its radial knots compared to the fixed +# ldp (sin²) grids, on the DIII-D-like example. Two panels: cumulative knot fraction +# index/mpsi vs ψ_N (packing profile), and local knot spacing Δψ vs ψ_N (log scale), +# with the n=1 rational surfaces marked. Documents the auto-grid PR. +# +# Usage: julia --project=. benchmarks/plot_grid_knot_placement.jl +# Output (not committed): benchmarks/grid_knot_placement.png + +using Pkg; +Pkg.activate(joinpath(@__DIR__, "..")) +using GeneralizedPerturbedEquilibrium +using Plots + +const GPE = GeneralizedPerturbedEquilibrium +const EXAMPLE_DIR = joinpath(@__DIR__, "..", "examples", "DIIID-like_ideal_example") + +function build_grids() + _, eq_config, additional_input = GPE.build_inputs_from_toml(EXAMPLE_DIR) + tau = eq_config.psi_accuracy + + # Pass 1 (coarse layout) and the two-pass refined grid, as the driver builds them + equil1 = GPE.Equilibrium.PlasmaEquilibrium(eq_config, additional_input) + pass1 = copy(equil1.profiles.xs) + rationals = GPE.ForceFreeStates.rational_psi_nodes(equil1; nlow=1, nhigh=1) + auto = GPE.Equilibrium.refined_psi_grid(equil1; tau, mandatory=rationals) + + ldp(mpsi) = [eq_config.psilow + (eq_config.psihigh - eq_config.psilow) * sin((i / mpsi) * (π / 2))^2 for i in 0:mpsi] + + grids = [ + ("auto two-pass, τ=$(tau), mpsi=$(length(auto) - 1)", auto), + ("pass-1 layout, mpsi=$(length(pass1) - 1)", pass1), + ("ldp, mpsi=256", ldp(256)), + ("ldp, mpsi=512", ldp(512)) + ] + return grids, rationals +end + +function main() + grids, rationals = build_grids() + # Okabe-Ito colorblind-safe hues, fixed order; pass-1 recessive gray + colors = ["#0072B2", "#999999", "#E69F00", "#009E73"] + styles = [:solid, :dash, :solid, :solid] + + p1 = plot(; xlabel="ψ_N", ylabel="knot index / mpsi", title="Radial knot packing — DIIID-like example (n=1)", + legend=:bottomright, left_margin=12Plots.mm, bottom_margin=4Plots.mm, size=(900, 420)) + p2 = plot(; xlabel="ψ_N", ylabel="local knot spacing Δψ_N", yscale=:log10, + legend=:bottomleft, left_margin=12Plots.mm, bottom_margin=6Plots.mm, size=(900, 420)) + + for (i, (label, g)) in enumerate(grids) + n = length(g) - 1 + plot!(p1, g, (0:n) ./ n; lw=2, color=colors[i], ls=styles[i], label=label) + mids = 0.5 .* (g[1:(end-1)] .+ g[2:end]) + plot!(p2, mids, diff(g); lw=2, color=colors[i], ls=styles[i], label=label) + end + for (j, r) in enumerate(rationals) + for p in (p1, p2) + vline!(p, [r]; color="#CC79A7", ls=:dot, lw=1.5, label=(j == 1 && p === p1) ? "rational surfaces q=m/1" : "") + end + end + + fig = plot(p1, p2; layout=(2, 1), size=(900, 840)) + out = joinpath(@__DIR__, "grid_knot_placement.png") + savefig(fig, out) + println("Wrote ", abspath(out)) +end + +main() diff --git a/build_docs_local.jl b/build_docs_local.jl index ffab6ab21c..f3cb5d13e2 100644 --- a/build_docs_local.jl +++ b/build_docs_local.jl @@ -5,15 +5,14 @@ using Pkg Pkg.activate(".") Pkg.instantiate() -# Build the package -Pkg.build() - -# Activate and instantiate the docs environment +# Activate the docs environment Pkg.activate("docs") -Pkg.instantiate() -# Add the local package to docs environment -Pkg.develop(PackageSpec(path=".")) +# Add the local package to docs environment first +Pkg.develop(PackageSpec(; path=".")) + +# Now instantiate to get other dependencies +Pkg.instantiate() # Build the documentation include("docs/make.jl") diff --git a/ci/conventions/check_subject.py b/ci/conventions/check_subject.py new file mode 100644 index 0000000000..ecb8b4d8b4 --- /dev/null +++ b/ci/conventions/check_subject.py @@ -0,0 +1,354 @@ +#!/usr/bin/env python3 +# PEP 723 inline metadata, so the script runs anywhere without setting up an +# environment: `uv run ci/conventions/check_subject.py --title "..."`. +# /// script +# requires-python = ">=3.10" +# dependencies = [] +# /// +"""Validate pull request titles, release-note blocks, and commit subjects. + +Two things block on this: the `pr-conventions` CI job, which checks a pull request +title, its release-note block and the freshness of its harness stamp; and the +`naming-table-in-sync` pre-commit hook, which checks the Area table in the docs +against `src/`. Commit subjects follow the same grammar but are not enforced -- +`--commit-msg` and `--stdin-report` exist for checking them by hand or from a +locally installed hook. + +The grammar and its rationale are documented in `docs/development/naming.md`. + +Valid Areas are derived from `src/` on disk rather than hardcoded, so adding or +removing a module updates the vocabulary automatically. +""" + +from __future__ import annotations + +import argparse +import difflib +import re +import sys +from pathlib import Path + +# Tags that name a release-note section. +RELEASE_NOTE_TAGS = ("FEATURE", "BUGFIX", "PERF", "API", "DEPRECATION", "DOCS") + +# Tags whose changes never appear in release notes. +EXCLUDED_TAGS = ("MINOR", "REFACTOR", "TEST") + +ALL_TAGS = RELEASE_NOTE_TAGS + EXCLUDED_TAGS + +# `!` marks a user-visible change; it is incoherent on tags that cannot move +# results or break an interface. An author reaching for one of these has +# mis-tagged the change. +NO_BANG_TAGS = frozenset({"MINOR", "TEST", "DOCS"}) + +# Areas that are not Julia modules. Closed set. +NON_MODULE_AREAS = ( + "Benchmarks", + "Build", + "CI", + "Docs", + "Examples", + "HDF5Schema", + "Regression", + "Repo", + "Test", +) + +# The package entry point is not an Area; a change there is `Repo`. +PACKAGE_ENTRY = "GeneralizedPerturbedEquilibrium.jl" + +# Spellings used before this convention, mapped to their replacement. Purely to +# make the error message instructive; none of these are accepted. +LEGACY_TAGS = { + "IMPROVEMENT": "FEATURE if user-visible, else PERF, REFACTOR or MINOR", + "CLEANUP": "REFACTOR if structural, else MINOR", + "FIX": "BUGFIX", + "NEW": "FEATURE", + "OPTIMIZATION": "PERF", + "PERFORMANCE": "PERF", + "TESTS": "TEST", + "VALIDATION": "TEST", + "WIP": "MINOR", + "CONFIG": "MINOR", + "UPDATE": "MINOR", + "PLANNING": "MINOR", + "REVIEW": "MINOR", + "MMINOR": "MINOR", +} + +LEGACY_AREAS = { + "EQUIL": "Equilibrium", + "VAC": "Vacuum", + "FFS": "ForceFreeStates", + "PE": "PerturbedEquilibrium", + "GPEC": "Repo", + "ALL": "Repo", + "MULTIPLE": "Repo", + "AGENTS": "Docs", + "H5": "HDF5Schema", + "BENCH": "Benchmarks", + "EXAMPLE": "Examples", + "GALERKIN": "ForceFreeStates.Galerkin", + "GGJ": "InnerLayer.GGJ", + "SLAYER": "InnerLayer.SLAYER", + "RICCATI": "ForceFreeStates", + "SINGULARCOUPLING": "PerturbedEquilibrium", + "COILS": "ForcingTerms", + "FOURIER": "Utilities", +} + +# Subjects git or a rebase generates for us are not the author's to format. +EXEMPT_SUBJECT = re.compile(r"^(Merge |Revert |fixup!|squash!|amend!)") + +SUBJECT = re.compile(r"^(?P\S+) - (?P[A-Za-z]+)(?P!?) - (?P\S.*)$") + +RELEASE_NOTE_HEADING = re.compile(r"^##\s+Release note\s*$", re.MULTILINE) +FIELD = r"^\s*[-*]\s*\*\*{label}:\*\*\s*(?P.*?)\s*$" +HARNESS_STAMP = re.compile(r"_\(harness @ (?P[0-9a-fA-F]{7,40})\)_") + +# Template text left unedited. Requires a closing bracket so that prose such as +# "q95 moved (<1%)" is not mistaken for a placeholder. +PLACEHOLDER = re.compile(r"<[^>]{3,}>|\bTODO\b|\bFIXME\b", re.IGNORECASE) + + +def repo_root() -> Path: + """Locate the repository root, preferring the script's own location.""" + candidate = Path(__file__).resolve().parents[2] + if (candidate / "src").is_dir(): + return candidate + return Path.cwd() + + +def module_areas(root: Path) -> list[str]: + """Derive module and submodule Areas from `src/` on disk. + + A subdirectory qualifies as a submodule Area only if it holds Julia sources, + which keeps data directories such as `ForcingTerms/coil_geometries` out of + the vocabulary. File-based submodules are deliberately not Areas: the parent + module is enough for triage and the summary carries the detail. + """ + src = root / "src" + if not src.is_dir(): + return [] + + areas: list[str] = [] + for module in sorted(p for p in src.iterdir() if p.is_dir()): + areas.append(module.name) + for sub in sorted(p for p in module.iterdir() if p.is_dir()): + if any(sub.glob("*.jl")): + areas.append(f"{module.name}.{sub.name}") + + # Top-level sources such as Rerun.jl are components in + # their own right and are committed against directly. + for source in sorted(src.glob("*.jl")): + if source.name != PACKAGE_ENTRY: + areas.append(source.stem) + return areas + + +def valid_areas(root: Path) -> list[str]: + return module_areas(root) + list(NON_MODULE_AREAS) + + +def suggest(token: str, candidates: list[str], legacy: dict[str, str]) -> str: + """Return a ' — use X' hint for a rejected token, or an empty string.""" + replacement = legacy.get(token.upper()) + if replacement is None: + # Catches spellings that differ only in case, e.g. DOCS for Docs. + folded = {c.casefold(): c for c in candidates} + replacement = folded.get(token.casefold()) + if replacement is None: + close = difflib.get_close_matches(token, candidates, n=1, cutoff=0.7) + replacement = close[0] if close else None + return f" — use {replacement}" if replacement else "" + + +def check_subject(subject: str, areas: list[str]) -> list[str]: + """Return a list of violations for one subject line; empty means valid.""" + subject = subject.strip() + if not subject or EXEMPT_SUBJECT.match(subject): + return [] + + match = SUBJECT.match(subject) + if not match: + return ["does not match `Area - TAG - Summary` (single-token Area, single-word TAG, ' - ' separators)"] + + errors: list[str] = [] + + tag = match.group("tag") + if tag not in ALL_TAGS: + errors.append(f"unknown TAG {tag!r}{suggest(tag, list(ALL_TAGS), LEGACY_TAGS)}") + elif match.group("bang") and tag in NO_BANG_TAGS: + errors.append(f"{tag}! is not allowed — {tag} changes cannot move results or break an interface") + + # A change spanning exactly two areas may name both as `A/B`. + parts = match.group("areas").split("/") + if len(parts) > 2: + errors.append("more than two Areas — use `Repo` for a change spanning three or more") + for part in parts: + if part not in areas: + errors.append(f"unknown Area {part!r}{suggest(part, areas, LEGACY_AREAS)}") + + return errors + + +def format_vocabulary(areas: list[str]) -> str: + modules = [a for a in areas if a not in NON_MODULE_AREAS] + return ( + "\nValid TAGs:\n" + f" release-note: {', '.join(RELEASE_NOTE_TAGS)}\n" + f" excluded: {', '.join(EXCLUDED_TAGS)}\n" + f" `!` marks a user-visible change; not allowed on {', '.join(sorted(NO_BANG_TAGS))}\n" + "\nValid Areas:\n" + f" modules: {', '.join(modules)}\n" + f" other: {', '.join(NON_MODULE_AREAS)}\n" + "\nSee docs/development/naming.md\n" + ) + + +def report_subject(subject: str, errors: list[str], areas: list[str]) -> None: + print(f"Invalid subject: {subject}", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) + print(format_vocabulary(areas), file=sys.stderr) + + +def field_value(body: str, label: str) -> str | None: + match = re.search(FIELD.format(label=label), body, re.MULTILINE) + return match.group("value") if match else None + + +def check_body(body: str, title: str | None, touches_src: bool) -> list[str]: + """Validate the `## Release note` block of a PR body.""" + if not RELEASE_NOTE_HEADING.search(body): + return ["missing a `## Release note` section (see .github/PULL_REQUEST_TEMPLATE.md)"] + + errors: list[str] = [] + values: dict[str, str] = {} + for label in ("Audience", "Numerical impact", "Migration"): + value = field_value(body, label) + if value is None: + errors.append(f"`## Release note` has no **{label}:** line") + elif not value or PLACEHOLDER.search(value): + errors.append(f"**{label}:** is empty or still holds template text") + else: + values[label] = value + + audience = values.get("Audience") + if audience and audience not in ("users", "developers"): + errors.append(f"**Audience:** must be `users` or `developers`, not {audience!r}") + + impact = values.get("Numerical impact") + if impact and touches_src and not HARNESS_STAMP.search(impact): + errors.append( + "**Numerical impact:** must carry the commit the harness ran at, " + "e.g. `none _(harness @ a3bdfd21)_`, because this PR changes files under src/" + ) + + if title: + match = SUBJECT.match(title.strip()) + if match and match.group("bang"): + migration = values.get("Migration", "") + if migration.lower().startswith("none"): + errors.append("title carries `!`, so **Migration:** cannot be `none` — say what a user must change") + + return errors + + +def check_table(path: Path, areas: list[str]) -> list[str]: + """Assert the Area table in naming.md lists exactly the modules on disk.""" + if not path.is_file(): + return [f"{path} does not exist"] + + # Only rows pointing at a top-level entry of src/ are compared; submodule + # rows such as `src/InnerLayer/GGJ/` are documented but not required here. + top_level = re.compile(r"`src/([^/`]+?)(?:/|\.jl)`") + + documented = set() + for line in path.read_text(encoding="utf-8").splitlines(): + if not line.startswith("|"): + continue + cells = [c.strip() for c in line.strip("|").split("|")] + if len(cells) < 3: + continue + match = top_level.fullmatch(cells[2]) + if match: + documented.add(match.group(1)) + + on_disk = {a for a in areas if a not in NON_MODULE_AREAS and "." not in a} + errors = [] + for missing in sorted(on_disk - documented): + errors.append(f"module {missing!r} exists in src/ but is not in the Area table") + for extra in sorted(documented - on_disk): + errors.append(f"Area table lists {extra!r}, which is not a directory in src/") + return errors + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument("--commit-msg", metavar="FILE", help="validate the subject line of a commit message file") + parser.add_argument("--title", metavar="TEXT", help="validate a PR title") + parser.add_argument("--body", metavar="FILE", help="validate the release-note block of a PR body file") + parser.add_argument("--touches-src", action="store_true", help="with --body, require the harness stamp") + parser.add_argument("--harness-sha", metavar="FILE", help="print the harness commit stamped in a PR body") + parser.add_argument("--check-table", metavar="FILE", help="check an Area table against src/ on disk") + parser.add_argument("--stdin-report", action="store_true", help="validate subjects piped one per line") + args = parser.parse_args() + + root = repo_root() + areas = valid_areas(root) + failed = False + + if args.harness_sha: + match = HARNESS_STAMP.search(Path(args.harness_sha).read_text(encoding="utf-8")) + print(match.group("sha") if match else "") + return 0 + + if args.commit_msg: + lines = Path(args.commit_msg).read_text(encoding="utf-8").splitlines() + subject = next((line for line in lines if line.strip() and not line.startswith("#")), "") + errors = check_subject(subject, areas) + if errors: + report_subject(subject, errors, areas) + failed = True + + if args.title: + errors = check_subject(args.title, areas) + if errors: + report_subject(args.title, errors, areas) + failed = True + + if args.body: + errors = check_body(Path(args.body).read_text(encoding="utf-8"), args.title, args.touches_src) + if errors: + print("Invalid `## Release note` block:", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) + print("\nSee docs/development/naming.md\n", file=sys.stderr) + failed = True + + if args.check_table: + errors = check_table(Path(args.check_table), areas) + if errors: + print(f"Area table in {args.check_table} is out of sync with src/:", file=sys.stderr) + for error in errors: + print(f" - {error}", file=sys.stderr) + failed = True + + if args.stdin_report: + total = bad = 0 + for line in sys.stdin: + if not line.strip(): + continue + total += 1 + errors = check_subject(line, areas) + if errors: + bad += 1 + print(f"{line.strip()}\n {'; '.join(errors)}") + print(f"\n{total - bad}/{total} subjects valid, {bad} rejected") + + return 1 if failed else 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/ci/local-julia-cache.sh b/ci/local-julia-cache.sh new file mode 100644 index 0000000000..2282e9a444 --- /dev/null +++ b/ci/local-julia-cache.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Publish immutable depot snapshots; restore into each runner's private depot. +set -euo pipefail + +operation=${1:?Expected restore, require, or save} +key=${2:?Expected a cache key} +: "${GPEC_LOCAL_CACHE_DIR:?Set the shared local cache directory}" +[[ "$key" =~ ^[a-zA-Z0-9._-]+$ ]] || { echo 'Invalid cache key' >&2; exit 1; } +entry="$GPEC_LOCAL_CACHE_DIR/$key" +paths=(artifacts packages registries compiled scratchspaces logs) + +case "$operation" in + restore|require) + if [[ ! -f "$entry/complete" ]]; then + echo "Local Julia cache miss: $key" + if [[ "$operation" == require ]]; then + echo 'The precompile job did not publish a usable local cache.' >&2 + exit 1 + fi + exit 0 + fi + mkdir -p "$HOME/.julia" + for path in "${paths[@]}"; do + if [[ -d "$entry/depot/$path" ]]; then + mkdir -p "$HOME/.julia/$path" + rsync -a "$entry/depot/$path/" "$HOME/.julia/$path/" + fi + done + cp "$entry/Manifest.toml" Manifest.toml + echo "Local Julia cache restored: $key" + ;; + save) + mkdir -p "$GPEC_LOCAL_CACHE_DIR" + # Readers only see complete snapshots; concurrent publishers reuse the winner. + exec 9>"$GPEC_LOCAL_CACHE_DIR/.publish.lock" + flock 9 + if [[ -f "$entry/complete" ]]; then + echo "Local Julia cache already published: $key" + exit 0 + fi + staging=$(mktemp -d "$GPEC_LOCAL_CACHE_DIR/.staging.XXXXXXXX") + trap 'rm -rf -- "$staging"' EXIT + mkdir "$staging/depot" + for path in "${paths[@]}"; do + if [[ -d "$HOME/.julia/$path" ]]; then + cp -a --reflink=auto "$HOME/.julia/$path" "$staging/depot/" + fi + done + cp Manifest.toml "$staging/Manifest.toml" + touch "$staging/complete" + mv -T "$staging" "$entry" + echo "Local Julia cache published: $key" + ;; + *) echo "Unknown operation: $operation" >&2; exit 1 ;; +esac diff --git a/ci/manifests/Manifest-v1.11.toml b/ci/manifests/Manifest-v1.11.toml new file mode 100644 index 0000000000..2101791ed7 --- /dev/null +++ b/ci/manifests/Manifest-v1.11.toml @@ -0,0 +1,3126 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.11.9" +manifest_format = "2.0" +project_hash = "0ae359086b0249d78fa4ef29b4162952ca8c0475" + +[[deps.ADTypes]] +deps = ["PrecompileTools"] +git-tree-sha1 = "629de23e1c16911b439dabd2303c08af9575b226" +uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" +version = "1.24.0" + + [deps.ADTypes.extensions] + ADTypesChainRulesCoreExt = "ChainRulesCore" + ADTypesConstructionBaseExt = "ConstructionBase" + ADTypesEnzymeCoreExt = "EnzymeCore" + + [deps.ADTypes.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + +[[deps.AMD]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] +git-tree-sha1 = "344f3d221a6bee75925b5c97a30cfd870f12a138" +uuid = "14f7f29c-3bd6-536c-9a0b-7339e30b5a3e" +version = "0.5.4" + +[[deps.ANSIColoredPrinters]] +git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" +uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" +version = "0.0.1" + +[[deps.AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.5.0" + + [deps.AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + + [deps.AbstractFFTs.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" + +[[deps.Accessors]] +deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "MacroTools"] +git-tree-sha1 = "7063ad1083578215c7c4bf410368150abe8d5524" +uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" +version = "0.1.45" + + [deps.Accessors.extensions] + AxisKeysExt = "AxisKeys" + IntervalSetsExt = "IntervalSets" + LinearAlgebraExt = "LinearAlgebra" + StaticArraysExt = "StaticArrays" + StructArraysExt = "StructArrays" + TestExt = "Test" + UnitfulExt = "Unitful" + + [deps.Accessors.weakdeps] + AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "dee8388a9bec89c8713ad176f96960ae2f98598a" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "4.7.4" +weakdeps = ["SparseArrays", "StaticArrays"] + + [deps.Adapt.extensions] + AdaptSparseArraysExt = "SparseArrays" + AdaptStaticArraysExt = "StaticArrays" + +[[deps.AdaptiveArrayPools]] +deps = ["Preferences", "Printf", "Random"] +git-tree-sha1 = "5feaa5055572e809b69b62f06826b1c1c8d3ebf3" +uuid = "4f381ef7-9af0-4cbe-99d4-cf36d7b0f233" +version = "0.3.11" + + [deps.AdaptiveArrayPools.extensions] + AdaptiveArrayPoolsCUDAExt = "CUDA" + AdaptiveArrayPoolsMetalExt = "Metal" + + [deps.AdaptiveArrayPools.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.2.0" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.2" + +[[deps.ArrayInterface]] +deps = ["Adapt", "LinearAlgebra"] +git-tree-sha1 = "1aec1ff0dcaa83a484e7f72097562fa3102c6722" +uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +version = "7.30.2" + + [deps.ArrayInterface.extensions] + ArrayInterfaceAMDGPUExt = "AMDGPU" + ArrayInterfaceBandedMatricesExt = "BandedMatrices" + ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" + ArrayInterfaceCUDAExt = "CUDA" + ArrayInterfaceCUDSSExt = ["CUDSS", "CUDA"] + ArrayInterfaceChainRulesCoreExt = "ChainRulesCore" + ArrayInterfaceChainRulesExt = "ChainRules" + ArrayInterfaceFillArraysExt = "FillArrays" + ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" + ArrayInterfaceGPUArraysCoreTrackerExt = ["GPUArraysCore", "Tracker"] + ArrayInterfaceMetalExt = "Metal" + ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceSparseArraysExt = "SparseArrays" + ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" + ArrayInterfaceTrackerExt = "Tracker" + + [deps.ArrayInterface.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.ArrayLayouts]] +deps = ["FillArrays", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "b4a2d2dfcaedf8f0cb93a970c4ccba6f632f14cc" +uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" +version = "1.15.1" +weakdeps = ["SparseArrays"] + + [deps.ArrayLayouts.extensions] + ArrayLayoutsSparseArraysExt = "SparseArrays" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" + +[[deps.AssetRegistry]] +deps = ["JSON", "Pidfile", "SHA"] +git-tree-sha1 = "902b85010203830d4bc02259f5450d2e16b316d8" +uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" +version = "0.1.1" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" + +[[deps.BitFlags]] +git-tree-sha1 = "bbe1079eecf9c9fbb52765193ad2bae27ae09bc8" +uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" +version = "0.1.10" + +[[deps.BitTwiddlingConvenienceFunctions]] +deps = ["Static"] +git-tree-sha1 = "f21cfd4950cb9f0587d5067e69405ad2acd27b87" +uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" +version = "0.1.6" + +[[deps.Blink]] +deps = ["Base64", "Distributed", "HTTP", "JSExpr", "JSON", "Lazy", "Logging", "MacroTools", "Mustache", "Mux", "Pkg", "Reexport", "Sockets", "WebIO"] +git-tree-sha1 = "bc93511973d1f949d45b0ea17878e6cb0ad484a1" +uuid = "ad839575-38b3-5650-b840-f874b8c74a25" +version = "0.12.9" + +[[deps.BracketingNonlinearSolve]] +deps = ["CommonSolve", "ConcreteStructs", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "7ad7171d693ae5552ac43862e7f6b61df4471c2b" +uuid = "70df07ce-3d50-431d-a3e7-ca6ddb60ac1e" +version = "1.12.1" + + [deps.BracketingNonlinearSolve.extensions] + BracketingNonlinearSolveChainRulesCoreExt = ["ChainRulesCore", "ForwardDiff"] + BracketingNonlinearSolveForwardDiffExt = "ForwardDiff" + + [deps.BracketingNonlinearSolve.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.9+0" + +[[deps.CPUSummary]] +deps = ["CpuId", "IfElse", "PrecompileTools", "Preferences", "Static"] +git-tree-sha1 = "f3a21d7fc84ba618a779d1ed2fcca2e682865bab" +uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" +version = "0.2.7" + +[[deps.CRlibm]] +deps = ["CRlibm_jll"] +git-tree-sha1 = "66188d9d103b92b6cd705214242e27f5737a1e5e" +uuid = "96374032-68de-5a5b-8d9e-752f78720389" +version = "1.0.2" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "7b841680738c19948120f6e4cf8d0200518bd564" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.18.8+0" + +[[deps.ChunkCodecCore]] +git-tree-sha1 = "3496e2b359c27793d12bec54ec94be7983a6ceb2" +uuid = "0b6fb165-00bc-4d37-ab8b-79f91016dbe1" +version = "1.0.2" + +[[deps.ChunkCodecLibZlib]] +deps = ["ChunkCodecCore", "Zlib_jll"] +git-tree-sha1 = "d4101e848e8d3f585d61d244c2fe0c80a70e6b3b" +uuid = "4c0bbee4-addc-4d73-81a0-b6caacae83c8" +version = "1.1.0" + +[[deps.ChunkCodecLibZstd]] +deps = ["ChunkCodecCore", "Zstd_jll"] +git-tree-sha1 = "34d9873079e4cb3d0c62926a225136824677073f" +uuid = "55437552-ac27-4d47-9aa3-63184e8fd398" +version = "1.0.0" + +[[deps.CloseOpenIntervals]] +deps = ["Static", "StaticArrayInterface"] +git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" +uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" +version = "0.1.13" + +[[deps.CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "970758a3d591a2a5c2a907c53f2e2f8c1b1d3537" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.9" + +[[deps.ColorSchemes]] +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "b0fd3f56fa442f81e0a47815c92245acfaaa4e34" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.31.0" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "a7cf827a79b378a381e31e5c7feca853ec19cb94" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.12.4" +weakdeps = ["StyledStrings"] + + [deps.ColorTypes.extensions] + StyledStringsExt = "StyledStrings" + +[[deps.ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "8b3b6f87ce8f65a2b4f857528fd8d70086cd72b1" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.11.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Reexport"] +git-tree-sha1 = "a633156e74096f90b3b5e833529292b651b5671a" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.13.3" + +[[deps.CommonSolve]] +deps = ["PrecompileTools"] +git-tree-sha1 = "6c389fa857f6ca5a95474b52a52023fd77f24cb7" +uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +version = "0.2.14" + +[[deps.CommonSubexpressions]] +deps = ["MacroTools"] +git-tree-sha1 = "cda2cfaebb4be89c9084adaca7dd7333369715c5" +uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" +version = "0.3.1" + +[[deps.CommonWorldInvalidations]] +deps = ["PrecompileTools"] +git-tree-sha1 = "7c4ea0adfb7238bf4678aa36325863fab6163f6f" +uuid = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8" +version = "1.2.2" + +[[deps.Compat]] +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "9d8a54ce4b17aa5bdce0ea5c34bc5e7c340d16ad" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.18.1" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.1.1+0" + +[[deps.CompositionsBase]] +git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" +uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" +version = "0.1.2" +weakdeps = ["InverseFunctions"] + + [deps.CompositionsBase.extensions] + CompositionsBaseInverseFunctionsExt = "InverseFunctions" + +[[deps.ConcreteStructs]] +deps = ["PrecompileTools"] +git-tree-sha1 = "a72c3b5ce6d2a477f55b5c9b8756e91e695c67f6" +uuid = "2569d6c7-a4a2-43d3-a901-331e8e4be471" +version = "0.2.8" + +[[deps.ConcurrentUtilities]] +deps = ["Serialization", "Sockets"] +git-tree-sha1 = "d5e5fa1f0d6ca517edbbcd734efa93c6639405b0" +uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" +version = "2.7.1" + +[[deps.ConstructionBase]] +git-tree-sha1 = "b4b092499347b18a015186eae3042f72267106cb" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.6.0" + + [deps.ConstructionBase.extensions] + ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseLinearAlgebraExt = "LinearAlgebra" + ConstructionBaseStaticArraysExt = "StaticArrays" + + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.Contour]] +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.6.3" + +[[deps.CoordinateConventions]] +git-tree-sha1 = "10832be51679db21271faac5bad7779a4320ca4b" +uuid = "7204ce3a-f536-43d2-be4a-fbed74e90d86" +version = "1.0.5" + +[[deps.CoreMath]] +deps = ["CoreMath_jll"] +git-tree-sha1 = "8c0480f92b1b1796239156a1b9b1bfb1b39499b4" +uuid = "b7a15901-be09-4a0e-87d2-2e66b0e09b5a" +version = "0.1.0" + +[[deps.CoreMath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a692a4c1dc59a4b8bc0b6403876eb3250fde2bc3" +uuid = "a38c48d9-6df1-5ac9-9223-b6ada3b5572b" +version = "0.1.0+0" + +[[deps.CpuId]] +deps = ["Markdown", "PrecompileTools"] +git-tree-sha1 = "5dbd5150b96f29553fc3b553c6bfb0eadf7ac204" +uuid = "adafc99b-e345-5852-983c-f28acb93d879" +version = "0.3.3" + +[[deps.Crayons]] +git-tree-sha1 = "54b76cbb40d9a0f5368c880725b2f141da77c94f" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.2.0" + +[[deps.DataAPI]] +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.16.0" + +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "5fab31e2e01e70ad66e3e24c968c264d1cf166d6" +uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +version = "1.8.2" + +[[deps.DataInterpolations]] +deps = ["EnumX", "FindFirstFunctions", "ForwardDiff", "LinearAlgebra", "PrettyTables", "RecipesBase", "Reexport"] +git-tree-sha1 = "4d50e83772222ce023efb7d30632198828ea56a6" +uuid = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" +version = "8.10.0" + + [deps.DataInterpolations.extensions] + DataInterpolationsChainRulesCoreExt = "ChainRulesCore" + DataInterpolationsMakieExt = "Makie" + DataInterpolationsMooncakeExt = ["Mooncake", "ChainRulesCore"] + DataInterpolationsOptimExt = "Optim" + DataInterpolationsRegularizationToolsExt = "RegularizationTools" + DataInterpolationsSparseConnectivityTracerExt = ["SparseConnectivityTracer", "FillArrays"] + DataInterpolationsSymbolicsExt = "Symbolics" + + [deps.DataInterpolations.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + Optim = "429524aa-4258-5aef-a3af-852621145aeb" + RegularizationTools = "29dad682-9a27-4bc3-9c72-016788665182" + SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.DataStructures]] +deps = ["OrderedCollections"] +git-tree-sha1 = "b0bc6d2cad1fed8b7fd59a1551a991cb3d2809e6" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.19.6" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" + +[[deps.Dbus_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "473e9afc9cf30814eb67ffa5f2db7df82c3ad9fd" +uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" +version = "1.16.2+0" + +[[deps.DelaunayTriangulation]] +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "4ac548adcad90c1d5d677af13568a748af4c952b" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "1.6.7" + +[[deps.DelimitedFiles]] +deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" + +[[deps.DiffEqBase]] +deps = ["ArrayInterface", "BracketingNonlinearSolve", "ConcreteStructs", "DocStringExtensions", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "9d333db14895e8c7d4857ed228eb1e72d3b302ec" +uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" +version = "6.218.0" + + [deps.DiffEqBase.extensions] + DiffEqBaseCUDAExt = "CUDA" + DiffEqBaseChainRulesCoreExt = "ChainRulesCore" + DiffEqBaseDynamicQuantitiesExt = "DynamicQuantities" + DiffEqBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] + DiffEqBaseFlexUnitsExt = "FlexUnits" + DiffEqBaseForwardDiffExt = ["ForwardDiff"] + DiffEqBaseGTPSAExt = "GTPSA" + DiffEqBaseGeneralizedGeneratedExt = "GeneralizedGenerated" + DiffEqBaseMPIExt = "MPI" + DiffEqBaseMeasurementsExt = "Measurements" + DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + DiffEqBaseMooncakeExt = "Mooncake" + DiffEqBaseReverseDiffExt = "ReverseDiff" + DiffEqBaseSparseArraysExt = "SparseArrays" + DiffEqBaseTrackerExt = "Tracker" + DiffEqBaseUnitfulExt = "Unitful" + + [deps.DiffEqBase.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + FlexUnits = "76e01b6b-c995-4ce6-8559-91e72a3d4e95" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + GTPSA = "b27dd330-f138-47c5-815b-40db9dd9b6e8" + GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.DiffEqCallbacks]] +deps = ["ConcreteStructs", "DataStructures", "DiffEqBase", "DifferentiationInterface", "LinearAlgebra", "Markdown", "PrecompileTools", "RecipesBase", "RecursiveArrayTools", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "88cdec45374d53393bf88268102a2b018c897178" +uuid = "459566f4-90b8-5000-8ac3-15dfb0a30def" +version = "4.19.2" + + [deps.DiffEqCallbacks.extensions] + DiffEqCallbacksFunctorsExt = "Functors" + + [deps.DiffEqCallbacks.weakdeps] + Functors = "d9f16b24-f501-4c13-a1f2-28368ffc5196" + +[[deps.DiffResults]] +deps = ["StaticArraysCore"] +git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" +uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" +version = "1.1.0" + +[[deps.DiffRules]] +deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] +git-tree-sha1 = "79a2aca180a85c690c58a020d47b426954b590f8" +uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" +version = "1.16.0" + +[[deps.DifferentiationInterface]] +deps = ["ADTypes", "LinearAlgebra"] +git-tree-sha1 = "0693d8b0a4608ff289d228ab4c598df5894845cd" +uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" +version = "0.7.21" + + [deps.DifferentiationInterface.extensions] + DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" + DifferentiationInterfaceDiffractorExt = "Diffractor" + DifferentiationInterfaceEnzymeExt = ["EnzymeCore", "Enzyme"] + DifferentiationInterfaceFastDifferentiationExt = "FastDifferentiation" + DifferentiationInterfaceFiniteDiffExt = "FiniteDiff" + DifferentiationInterfaceFiniteDifferencesExt = "FiniteDifferences" + DifferentiationInterfaceForwardDiffExt = ["ForwardDiff", "DiffResults"] + DifferentiationInterfaceGPUArraysCoreExt = ["GPUArraysCore", "Adapt"] + DifferentiationInterfaceGTPSAExt = "GTPSA" + DifferentiationInterfaceHyperHessiansExt = "HyperHessians" + DifferentiationInterfaceMooncakeExt = "Mooncake" + DifferentiationInterfacePolyesterForwardDiffExt = ["PolyesterForwardDiff", "ForwardDiff", "DiffResults"] + DifferentiationInterfaceReverseDiffExt = ["ReverseDiff", "DiffResults"] + DifferentiationInterfaceSparseArraysExt = "SparseArrays" + DifferentiationInterfaceSparseConnectivityTracerExt = "SparseConnectivityTracer" + DifferentiationInterfaceSparseMatrixColoringsExt = "SparseMatrixColorings" + DifferentiationInterfaceStaticArraysExt = "StaticArrays" + DifferentiationInterfaceSymbolicsExt = "Symbolics" + DifferentiationInterfaceTrackerExt = "Tracker" + DifferentiationInterfaceZygoteExt = ["Zygote", "ForwardDiff"] + + [deps.DifferentiationInterface.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" + Diffractor = "9f5e2b26-1114-432f-b630-d3fe2085c51c" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" + FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" + FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + GTPSA = "b27dd330-f138-47c5-815b-40db9dd9b6e8" + HyperHessians = "06b494a0-c8e0-40cc-ad32-d99506a00a6c" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +version = "1.11.0" + +[[deps.DocStringExtensions]] +git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.9.5" + +[[deps.Documenter]] +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] +git-tree-sha1 = "191e6bef0cf32cac3a3913cd4787d851715254c2" +uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +version = "1.19.0" + +[[deps.DoubleFloats]] +deps = ["GenericLinearAlgebra", "GenericSchur", "LinearAlgebra", "Printf", "Quadmath", "Random", "SpecialFunctions"] +git-tree-sha1 = "e9350afd366d2deaaa9b4ed13fd537e2b4dea6e5" +uuid = "497a8b3b-efae-58df-a0af-a86822472b78" +version = "1.11.2" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.6.0" + +[[deps.EnumX]] +git-tree-sha1 = "c49898e8438c828577f04b92fc9368c388ac783c" +uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" +version = "1.0.7" + +[[deps.EnzymeCore]] +git-tree-sha1 = "156ede9181d50b3b21b74d98a7edb08bc7bbdbf1" +uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" +version = "0.8.23" + + [deps.EnzymeCore.extensions] + AdaptExt = "Adapt" + EnzymeCoreChainRulesCoreExt = "ChainRulesCore" + + [deps.EnzymeCore.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.EpollShim_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8a4be429317c42cfae6a7fc03c31bad1970c310d" +uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" +version = "0.0.20230411+1" + +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArrays"] +git-tree-sha1 = "83231673ea4d3d6008ac74dc5079e77ab2209d8f" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.9" + +[[deps.ExceptionUnwrapping]] +deps = ["Test"] +git-tree-sha1 = "4e468f521e1f9f86891cb07186de5df90360a666" +uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" +version = "0.1.12" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "2bfb1e047e2ad0a5ca94365340bde8005d637568" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.8.4+0" + +[[deps.ExponentialUtilities]] +deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "PrecompileTools", "Printf", "SparseArrays", "libblastrampoline_jll"] +git-tree-sha1 = "a3c8b66f8813b721a45bfe3cf32acf793aed78bb" +uuid = "d4d017d3-3776-5f7e-afef-a10c40355c18" +version = "1.31.0" +weakdeps = ["StaticArrays"] + + [deps.ExponentialUtilities.extensions] + ExponentialUtilitiesStaticArraysExt = "StaticArrays" + +[[deps.ExprTools]] +git-tree-sha1 = "d2e49e7efd29719d6f28b891b0e0e159daa9d2b4" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.11" + +[[deps.ExproniconLite]] +git-tree-sha1 = "c13f0b150373771b0fdc1713c97860f8df12e6c2" +uuid = "55351af7-c7e9-48d6-89ff-24e801d99491" +version = "0.10.14" + +[[deps.FFMPEG]] +deps = ["FFMPEG_jll"] +git-tree-sha1 = "7bd13840b4148949e290748071bbe6826b3eabcd" +uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +version = "0.4.6" + +[[deps.FFMPEG_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libva_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "d9d3cd382f2c999f684e6bfa4039fcc16c153b6f" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "9.0.2+0" + +[[deps.FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "Libdl", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "97f08406df914023af55ade2f843c39e99c5d969" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.10.0" + +[[deps.FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6866aec60ef98e3164cd8d6855225684207e9dff" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.12+0" + +[[deps.FastBroadcast]] +deps = ["ArrayInterface", "LinearAlgebra", "PrecompileTools"] +git-tree-sha1 = "c8f8eefadaa330d982bbf5e395c34e53a13ab668" +uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" +version = "1.4.0" +weakdeps = ["Polyester", "Static"] + + [deps.FastBroadcast.extensions] + FastBroadcastPolyesterExt = "Polyester" + FastBroadcastStaticExt = "Static" + +[[deps.FastClosures]] +git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" +uuid = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" +version = "0.3.2" + +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "4916117dd032ec5959b7633aedbbac408ca5ddeb" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "1.3.0" + +[[deps.FastInterpolations]] +deps = ["AdaptiveArrayPools", "HelpPlots", "LinearAlgebra", "Preferences", "Printf"] +git-tree-sha1 = "f7bcd496f0a2e4e1174e7e1f667f27fb671fd7df" +uuid = "9ea80cae-fc13-4c00-8066-6eaedb12f34b" +version = "0.4.20" + + [deps.FastInterpolations.extensions] + FastInterpolationsChainRulesCoreExt = "ChainRulesCore" + FastInterpolationsColorVectorSpaceExt = ["ColorTypes", "ColorVectorSpace"] + FastInterpolationsEnzymeExt = "Enzyme" + FastInterpolationsForwardDiffExt = "ForwardDiff" + FastInterpolationsRecipesBaseExt = "RecipesBase" + FastInterpolationsSymbolicsExt = ["Symbolics", "SymbolicUtils"] + + [deps.FastInterpolations.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" + ColorVectorSpace = "c3611d14-8923-5661-9e6a-0046d554d3a4" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + +[[deps.FastPower]] +deps = ["PrecompileTools"] +git-tree-sha1 = "3c7269c236978d434a16ebe99f9743b9d706270a" +uuid = "a4df4552-cc26-4903-aec0-212e50a0e84b" +version = "1.5.0" + + [deps.FastPower.extensions] + FastPowerEnzymeExt = "Enzyme" + FastPowerForwardDiffExt = "ForwardDiff" + FastPowerMeasurementsExt = "Measurements" + FastPowerMonteCarloMeasurementsExt = "MonteCarloMeasurements" + FastPowerMooncakeExt = "Mooncake" + FastPowerReverseDiffExt = "ReverseDiff" + FastPowerTrackerExt = "Tracker" + + [deps.FastPower.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "6621fef488e496356c9c9625d0562c12a6070819" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.20.0" +weakdeps = ["HTTP"] + + [deps.FileIO.extensions] + HTTPExt = "HTTP" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" + +[[deps.FillArrays]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "417289ac840d201005d9ed16789a46defccbe36d" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.18.1" + + [deps.FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStaticArraysExt = "StaticArrays" + FillArraysStatisticsExt = "Statistics" + + [deps.FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.FindFirstFunctions]] +deps = ["PrecompileTools"] +git-tree-sha1 = "27b495de668ccea58de6b06d6d13181396598ea0" +uuid = "64ca27bc-2ba2-4a57-88aa-44e436879224" +version = "1.8.0" + +[[deps.FiniteDiff]] +deps = ["ArrayInterface", "LinearAlgebra", "Setfield"] +git-tree-sha1 = "5031f23e040bf17082e5b52422d77b5e844eefb1" +uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" +version = "2.33.0" + + [deps.FiniteDiff.extensions] + FiniteDiffBandedMatricesExt = "BandedMatrices" + FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" + FiniteDiffSparseArraysExt = "SparseArrays" + FiniteDiffStaticArraysExt = "StaticArrays" + + [deps.FiniteDiff.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.FixedPointNumbers]] +deps = ["Random", "Statistics"] +git-tree-sha1 = "59af96b98217c6ef4ae0dfe065ac7c20831d1a84" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.6" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "f85dac9a96a01087df6e3a749840015a0ca3817d" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.17.1+0" + +[[deps.Format]] +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" +uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" +version = "1.3.7" + +[[deps.ForwardDiff]] +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "3b0f72e2ffef1a139ac450725c9ecd01c0a3c050" +uuid = "f6369f11-7733-5829-9624-2563aa707210" +version = "1.4.6" +weakdeps = ["StaticArrays"] + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "70329abc09b886fd2c5d94ad2d9527639c421e3e" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.14.3+1" + +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7a214fdac5ed5f59a22c2d9a885a16da1c74bbc7" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.17+0" + +[[deps.FunctionWrappers]] +git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" +uuid = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" +version = "1.1.3" + +[[deps.FunctionWrappersWrappers]] +deps = ["FunctionWrappers", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "2bcce3ad6f6977d617928d7707fdc86ac83cce03" +uuid = "77dc65aa-8811-40c2-897b-53d922fa7daf" +version = "1.13.0" + + [deps.FunctionWrappersWrappers.extensions] + FunctionWrappersWrappersEnzymeExt = ["Enzyme", "EnzymeCore"] + FunctionWrappersWrappersMooncakeExt = "Mooncake" + + [deps.FunctionWrappersWrappers.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + +[[deps.FunctionalCollections]] +deps = ["Test"] +git-tree-sha1 = "04cb9cfaa6ba5311973994fe3496ddec19b6292a" +uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" +version = "0.5.0" + +[[deps.Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" +version = "1.11.0" + +[[deps.GLFW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] +git-tree-sha1 = "64bbbb7d1499297751b536dd39c58b20750ab1db" +uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" +version = "3.5.1+0" + +[[deps.GPUArraysCore]] +deps = ["Adapt"] +git-tree-sha1 = "0b2b3e8b8fb7c1f5ddbf498be2eb2b3b37c9c0ed" +uuid = "46192b85-c4d5-4398-a991-12ede77f4527" +version = "0.2.1" + +[[deps.GR]] +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] +git-tree-sha1 = "4d777f73c46b46b8b5276206059cf8a195499314" +uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" +version = "0.73.27" + + [deps.GR.extensions] + IJuliaExt = "IJulia" + + [deps.GR.weakdeps] + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + +[[deps.GR_jll]] +deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "f8eb8f7ba13ea75083531647fc8faeda8d541f07" +uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" +version = "0.73.27+0" + +[[deps.GenericLinearAlgebra]] +deps = ["LinearAlgebra", "Printf", "Random", "libblastrampoline_jll"] +git-tree-sha1 = "961f8a5d33f043e02cdf2b8020e3a7b0aae78474" +uuid = "14197337-ba66-59df-a3e3-ca00e7dcff7a" +version = "0.4.1" + +[[deps.GenericSchur]] +deps = ["LinearAlgebra", "MatrixFactorizations", "Preferences", "Printf", "UUIDs"] +git-tree-sha1 = "e3d1202ab9ae72edd4c46134ecbb6746ec420486" +uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" +version = "0.5.8" + +[[deps.GettextRuntime_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll"] +git-tree-sha1 = "45288942190db7c5f760f59c04495064eedf9340" +uuid = "b0724c58-0f36-5564-988d-3bb0596ebc4a" +version = "0.22.4+0" + +[[deps.Ghostscript_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Zlib_jll"] +git-tree-sha1 = "38044a04637976140074d0b0621c1edf0eb531fd" +uuid = "61579ee1-b43e-5ca0-a5da-69d92c66a64b" +version = "9.55.1+0" + +[[deps.Git]] +deps = ["Git_LFS_jll", "Git_jll", "JLLWrappers", "OpenSSH_jll"] +git-tree-sha1 = "824a1890086880696fc908fe12a17bcf61738bd8" +uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" +version = "1.5.0" + +[[deps.Git_LFS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8c66e385d631bb934ff05e76d4a566c640c8df69" +uuid = "020c3dae-16b3-5ae5-87b3-4cb189e250b2" +version = "3.7.1+0" + +[[deps.Git_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "3842d761162baf8efce819cdfb5ea070fe44ac40" +uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" +version = "2.56.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "GettextRuntime_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "090526e65de8f69648ac156daae153de8b56df62" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.88.3+0" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "69ffb934a5c5b7e086a0b4fee3427db2556fba6e" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.16+0" + +[[deps.HDF5]] +deps = ["Compat", "HDF5_jll", "Libdl", "MPIPreferences", "Mmap", "Preferences", "Printf", "Random", "Requires", "UUIDs"] +git-tree-sha1 = "26e37af34e8ccb7a8358bd8de1619c7bcb526738" +uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +version = "0.17.4" + + [deps.HDF5.extensions] + MPIExt = "MPI" + + [deps.HDF5.weakdeps] + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + +[[deps.HDF5_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "MPIABI_jll", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "aws_c_s3_jll", "dlfcn_win32_jll", "libaec_jll", "mpif_jll"] +git-tree-sha1 = "58d79d3a61d2fdeda667fead57546ae22a20b27a" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "2.2.3+0" + +[[deps.HTTP]] +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "51059d23c8bb67911a2e6fd5130229113735fc7e" +uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "1.11.0" + +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "9d9531a9cb63a9edc33836414e82a07e81710de2" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "100.14004.0+0" + +[[deps.HashArrayMappedTries]] +git-tree-sha1 = "2eaa69a7cab70a52b9687c8bf950a5a93ec895ae" +uuid = "076d061b-32b6-4027-95e0-9a2c6f6d7e74" +version = "0.2.0" + +[[deps.HelpPlots]] +git-tree-sha1 = "07d389d81ca31e42d68bd88cfa1e900a63b7d378" +uuid = "5880b2e8-ad0a-48c9-bacd-30dc9c1357e0" +version = "1.1.0" +weakdeps = ["Plots"] + + [deps.HelpPlots.extensions] + HelpPlotsExt = "Plots" + +[[deps.Hiccup]] +deps = ["MacroTools", "Test"] +git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" +uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" +version = "0.2.2" + +[[deps.Hwloc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XML2_jll", "Xorg_libpciaccess_jll"] +git-tree-sha1 = "cbbd99d516990d05c02aa132aa077b2bf50ae323" +uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +version = "2.15.0+0" + +[[deps.IMASdd]] +deps = ["AbstractTrees", "AdaptiveArrayPools", "CoordinateConventions", "DataInterpolations", "Dates", "FastInterpolations", "HDF5", "InteractiveUtils", "JSON", "Memoization", "OrderedCollections", "PrecompileTools", "Preferences", "Printf", "StaticArraysCore", "ThreadSafeDicts", "TimerOutputs"] +git-tree-sha1 = "2284c64e11d8fb6225971bcc367c67bae51923f5" +uuid = "c5a45a97-b3f9-491c-b9a7-aa88c3bc0067" +version = "8.6.2" + +[[deps.IOCapture]] +deps = ["Logging", "Random"] +git-tree-sha1 = "0ee181ec08df7d7c911901ea38baf16f755114dc" +uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" +version = "1.0.0" + +[[deps.IfElse]] +git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" +uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" +version = "0.1.1" + +[[deps.InlineStrings]] +git-tree-sha1 = "06b65886c7577a3784d616e29f1302c2e36e389d" +uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" +version = "1.4.6" + + [deps.InlineStrings.extensions] + ArrowTypesExt = "ArrowTypes" + ParsersExt = "Parsers" + + [deps.InlineStrings.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" + +[[deps.IntelOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "ec1debd61c300961f98064cfb21287613ad7f303" +uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" +version = "2025.2.0+0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +version = "1.11.0" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm", "CoreMath", "MacroTools", "OpenBLASConsistentFPCSR_jll", "Printf", "Random", "RoundingEmulator"] +git-tree-sha1 = "1c531bf0f8a5c60a340926e058fd3f209b5eef5d" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "1.0.12" + + [deps.IntervalArithmetic.extensions] + IntervalArithmeticArblibExt = "Arblib" + IntervalArithmeticDiffRulesExt = "DiffRules" + IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticIntervalSetsExt = "IntervalSets" + IntervalArithmeticIrrationalConstantsExt = "IrrationalConstants" + IntervalArithmeticLinearAlgebraExt = "LinearAlgebra" + IntervalArithmeticMakieExt = "Makie" + IntervalArithmeticRecipesBaseExt = "RecipesBase" + IntervalArithmeticSparseArraysExt = "SparseArrays" + + [deps.IntervalArithmetic.weakdeps] + Arblib = "fb37089c-8514-4489-9461-98f9c8763369" + DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.InverseFunctions]] +git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.17" +weakdeps = ["Dates", "Test"] + + [deps.InverseFunctions.extensions] + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" + +[[deps.InvertedIndices]] +git-tree-sha1 = "6da3c4316095de0f5ee2ebd875df8721e7e0bdbe" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.3.1" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "b2d91fe939cae05960e760110b328288867b5758" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.6" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLD2]] +deps = ["ChunkCodecCore", "ChunkCodecLibZlib", "ChunkCodecLibZstd", "FileIO", "MacroTools", "Mmap", "OrderedCollections", "PrecompileTools", "ScopedValues"] +git-tree-sha1 = "9ce2e7c49ae4a7035b7d60db3a553b1f6c16875d" +uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +version = "0.6.7" +weakdeps = ["UnPack"] + + [deps.JLD2.extensions] + UnPackExt = "UnPack" + +[[deps.JLFzf]] +deps = ["REPL", "Random", "fzf_jll"] +git-tree-sha1 = "82f7acdc599b65e0f8ccd270ffa1467c21cb647b" +uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" +version = "0.1.11" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7204148362dafe5fe6a273f855b8ccbe4df8173e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.8.0" + +[[deps.JSExpr]] +deps = ["JSON", "MacroTools", "Observables", "WebIO"] +git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" +uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" +version = "0.5.4" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[deps.Jieko]] +deps = ["ExproniconLite"] +git-tree-sha1 = "2f05ed29618da60c06a87e9c033982d4f71d0b6c" +uuid = "ae98c720-c025-4a4a-838c-29b094483192" +version = "0.2.1" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "037babc10853eeb8e585418922246cb97b8e5b74" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.2.0+1" + +[[deps.Kaleido_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" +uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" +version = "0.2.1+0" + +[[deps.Krylov]] +deps = ["LinearAlgebra", "Printf", "SparseArrays"] +git-tree-sha1 = "e0574e7e2cd5c40d9a6ae444511fbadd51afbf30" +uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +version = "0.10.11" + +[[deps.LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "059aabebaa7c82ccb853dd4a0ee9d17796f7e1bc" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.3+0" + +[[deps.LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "39bca05343661c347aae0bca57a5994a0bf4f08d" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "4.2.0+0" + +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e5b100780d4d30d63b4618d7930d48af409c1772" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "23.1.1+0" + +[[deps.LaTeXStrings]] +git-tree-sha1 = "f88f3ccef05a6a72a0cf0ed417c8fd68530f4ab2" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.4.1" + +[[deps.Latexify]] +deps = ["Format", "Ghostscript_jll", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] +git-tree-sha1 = "df7566479bd64f20bd16b09960145e70160ffb3b" +uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" +version = "0.16.12" + + [deps.Latexify.extensions] + DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" + SymEngineExt = "SymEngine" + TectonicExt = "tectonic_jll" + + [deps.Latexify.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" + tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" + +[[deps.LayoutPointers]] +deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] +git-tree-sha1 = "a9eaadb366f5493a5654e843864c13d8b107548c" +uuid = "10f19ff3-798f-405d-979b-55457f8fc047" +version = "0.1.17" + +[[deps.LazilyInitializedFields]] +git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" +uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" +version = "1.3.0" + +[[deps.Lazy]] +deps = ["MacroTools"] +git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" +uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" +version = "0.15.1" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +version = "1.11.0" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.6.0+0" + +[[deps.LibGit2]] +deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.7.2+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "MbedTLS_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.0+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.4.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "d36c21b9e7c172a44a10484125024495e2625ac0" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.7.1+1" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.18.0+0" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e2b48382276415b5a5d5282a5dd75a0f0bf4ac84" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.42.2+0" + +[[deps.Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "aebd334d06cee9f24cea70bd19a39749daf73881" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.7.3+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d620582b1f0cbe2c72dd1d5bd195a9ce73370ab1" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.42.0+0" + +[[deps.LineSearch]] +deps = ["ADTypes", "CommonSolve", "ConcreteStructs", "FastClosures", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "SciMLBase", "SciMLJacobianOperators", "StaticArraysCore"] +git-tree-sha1 = "2e05027f5a68891d997fcad60d11ce48d09208d0" +uuid = "87fe0de2-c867-4266-b59a-2f0a94fc965b" +version = "0.1.14" +weakdeps = ["LineSearches"] + + [deps.LineSearch.extensions] + LineSearchLineSearchesExt = "LineSearches" + +[[deps.LineSearches]] +deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Printf"] +git-tree-sha1 = "a09a85e94e681def2446752cddaf9401a4d82b6d" +uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" +version = "7.8.2" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +version = "1.11.0" + +[[deps.LinearSolve]] +deps = ["AMD", "ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "GPUArraysCore", "InteractiveUtils", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "OpenBLAS_jll", "PrecompileTools", "Preferences", "PureKLU", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "Setfield", "SparseArrays", "SparseColumnPivotedQR", "StaticArraysCore"] +git-tree-sha1 = "ec49ed72f6024be2f9833fe630fbd72f6048f8ad" +uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" +version = "3.87.0" + + [deps.LinearSolve.extensions] + LinearSolveAMDGPUExt = "AMDGPU" + LinearSolveAlgebraicMultigridExt = "AlgebraicMultigrid" + LinearSolveBLISExt = ["blis_jll", "LAPACK_jll"] + LinearSolveBandedMatricesExt = "BandedMatrices" + LinearSolveBlockDiagonalsExt = "BlockDiagonals" + LinearSolveCUDAExt = ["cuSOLVER"] + LinearSolveCUDSSExt = "CUDSS" + LinearSolveCUSOLVERRFExt = ["CUSOLVERRF", "SparseArrays"] + LinearSolveChainRulesCoreExt = "ChainRulesCore" + LinearSolveCliqueTreesExt = ["CliqueTrees", "SparseArrays"] + LinearSolveElementalExt = "Elemental" + LinearSolveEnzymeExt = ["EnzymeCore", "SparseArrays"] + LinearSolveFastAlmostBandedMatricesExt = "FastAlmostBandedMatrices" + LinearSolveFastLapackInterfaceExt = "FastLapackInterface" + LinearSolveForwardDiffExt = "ForwardDiff" + LinearSolveGinkgoExt = ["Ginkgo", "SparseArrays"] + LinearSolveHSLExt = ["HSL", "SparseArrays"] + LinearSolveHYPREExt = "HYPRE" + LinearSolveIterativeSolversExt = "IterativeSolvers" + LinearSolveKernelAbstractionsExt = "KernelAbstractions" + LinearSolveKrylovKitExt = "KrylovKit" + LinearSolveMUMPSExt = ["MUMPS", "SparseArrays"] + LinearSolveMetalExt = "Metal" + LinearSolveMooncakeExt = "Mooncake" + LinearSolvePETScExt = ["PETSc", "SparseArrays", "SparseMatricesCSR"] + LinearSolvePETScMPIExt = ["PETSc", "PartitionedArrays", "SparseArrays", "SparseMatricesCSR"] + LinearSolveParUExt = ["ParU_jll", "SparseArrays"] + LinearSolvePardisoExt = ["Pardiso", "SparseArrays"] + LinearSolvePartitionedSolversExt = ["PartitionedArrays", "PartitionedSolvers"] + LinearSolvePureUMFPACKExt = ["PureUMFPACK", "SparseArrays"] + LinearSolveRecursiveFactorizationExt = "RecursiveFactorization" + LinearSolveSTRUMPACKExt = ["SparseArrays", "STRUMPACK_jll"] + LinearSolveSparseArraysExt = "SparseArrays" + LinearSolveSparspakExt = ["SparseArrays", "Sparspak"] + LinearSolveSpecializingFactorizationsExt = "SpecializingFactorizations" + LinearSolveSuperLUDISTExt = ["SparseArrays", "SuperLUDIST"] + + [deps.LinearSolve.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + CUSOLVERRF = "a8cc9031-bad2-4722-94f5-40deabb4245c" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" + Elemental = "902c3f28-d1ec-5e7e-8399-a24c3845ee38" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + FastAlmostBandedMatrices = "9d29842c-ecb8-4973-b1e9-a27b1157504e" + FastLapackInterface = "29a986be-02c6-4525-aec4-84b980013641" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Ginkgo = "4c8bd3c9-ead9-4b5e-a625-08f1338ba0ec" + HSL = "34c5aeac-e683-54a6-a0e9-6e0fdc586c50" + HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" + IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" + LAPACK_jll = "51474c39-65e3-53ba-86ba-03b1b862ec14" + MUMPS = "55d2b088-9f4e-11e9-26c0-150b02ea6a46" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" + ParU_jll = "9e0b026c-e8ce-559c-a2c4-6a3d5c955bc9" + Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" + PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9" + PartitionedSolvers = "11b65f7f-80ac-401b-9ef2-3db765482d62" + PureUMFPACK = "b7e1f0a2-3c4d-4e5f-9a0b-1c2d3e4f5a6b" + RecursiveFactorization = "f2c3362d-daeb-58d1-803e-2bc74f2840b4" + STRUMPACK_jll = "86fbd0b9-476f-557c-b766-62c724b42d8c" + SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1" + Sparspak = "e56a9233-b9d6-4f03-8d0f-1825330902ac" + SpecializingFactorizations = "fa08b7a1-13d3-4faf-875d-5cbc1520e3f3" + SuperLUDIST = "4cd002a6-0da4-410d-a012-232df062f478" + blis_jll = "6136c539-28a5-5bf0-87cc-b183200dce32" + cuSOLVER = "887afef0-6a32-4de5-add4-7827692ba8fc" + +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "28d620bcdb37a3fb02de9f521928ee453690f414" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "1.0.3" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" + +[[deps.LoggingExtras]] +deps = ["Dates", "Logging"] +git-tree-sha1 = "f00544d95982ea270145636c181ceda21c4e2575" +uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" +version = "1.2.0" + +[[deps.MKL_jll]] +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "282cadc186e7b2ae0eeadbd7a4dffed4196ae2aa" +uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" +version = "2025.2.0+0" + +[[deps.MPIABI_jll]] +deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "42e2cada9f35500b4d227106ce2b60e85d9f15bc" +uuid = "b5ada748-db0f-5fc0-8972-9331c762740c" +version = "1.0.1+0" + +[[deps.MPICH_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "08cb47f95a2f1b795e43a9e4f9783e68ecdc1ca6" +uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" +version = "5.0.3+0" + +[[deps.MPIPreferences]] +deps = ["Libdl", "Preferences"] +git-tree-sha1 = "8e98d5d80b87403c311fd51e8455d4546ba7a5f8" +uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" +version = "0.1.12" + +[[deps.MPItrampoline_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "09a99227205419c00e734f413ee98c18bc1b02e4" +uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" +version = "5.5.7+0" + +[[deps.MacroTools]] +git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.16" + +[[deps.ManualMemory]] +git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" +uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" +version = "0.1.8" + +[[deps.Markdown]] +deps = ["Base64"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" + +[[deps.MarkdownAST]] +deps = ["AbstractTrees", "Markdown"] +git-tree-sha1 = "93c718d892e73931841089cdc0e982d6dd9cc87b" +uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" +version = "0.1.3" + +[[deps.MatrixFactorizations]] +deps = ["ArrayLayouts", "LinearAlgebra", "Printf", "Random"] +git-tree-sha1 = "076d0f90b1b06e17609cabb48bd2cbef28e7fc77" +uuid = "a3b82374-2e81-5b9e-98ce-41277c0e4c87" +version = "3.2.0" + + [deps.MatrixFactorizations.extensions] + MatrixFactorizationsBandedMatricesExt = "BandedMatrices" + + [deps.MatrixFactorizations.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + +[[deps.MaybeInplace]] +deps = ["ArrayInterface", "LinearAlgebra", "MacroTools", "PrecompileTools"] +git-tree-sha1 = "f2cde0ae772162f20287b803e623966d0d94dea9" +uuid = "bb5d69b7-63fc-4a16-80bd-7e42200c7bdb" +version = "0.1.8" +weakdeps = ["SparseArrays"] + + [deps.MaybeInplace.extensions] + MaybeInplaceSparseArraysExt = "SparseArrays" + +[[deps.MbedTLS]] +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] +git-tree-sha1 = "8785729fa736197687541f7053f6d8ab7fc44f92" +uuid = "739be429-bea8-5141-9913-cc70e7f3736d" +version = "1.1.10" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.6+0" + +[[deps.Measures]] +git-tree-sha1 = "b513cedd20d9c914783d8ad83d08120702bf2c77" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.3" + +[[deps.Memoization]] +deps = ["MacroTools"] +git-tree-sha1 = "7dbf904fa6c4447bd1f1d316886bfbe29feacf45" +uuid = "6fafb56a-5788-4b4e-91ca-c0cea6611c73" +version = "0.2.2" + +[[deps.MicrosoftMPI_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227" +uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" +version = "10.1.4+3" + +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.2.0" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" +version = "1.11.0" + +[[deps.Moshi]] +deps = ["ExproniconLite", "Jieko"] +git-tree-sha1 = "3f234cf937eb1d58f613440ecb022da784ad34d4" +uuid = "2e0e35c7-a2e4-4343-998d-7ef72827ed2d" +version = "0.3.13" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2023.12.12" + +[[deps.MuladdMacro]] +deps = ["PrecompileTools"] +git-tree-sha1 = "283bf85d4a767481dd924dff0eee1735e95f449e" +uuid = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +version = "0.2.7" + +[[deps.Mustache]] +deps = ["DataFrames", "Printf", "Tables"] +git-tree-sha1 = "7437c911e62f50e7a043a774ebdc0dbd780d9fe5" +uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" +version = "1.1.0" + +[[deps.Mux]] +deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "MbedTLS", "Pkg", "Sockets"] +git-tree-sha1 = "7295d849103ac4fcbe3b2e439f229c5cc77b9b69" +uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" +version = "1.0.2" + +[[deps.NLSolversBase]] +deps = ["ADTypes", "DifferentiationInterface", "FiniteDiff", "LinearAlgebra"] +git-tree-sha1 = "f96d38936d92d610318dec4d3b5ef37b0373c20f" +uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" +version = "8.0.1" + +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "dbd2e8cd2c1c27f0b584f6661b4309609c5a685e" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "1.1.4" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.2.0" + +[[deps.NonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "NonlinearSolveBase", "NonlinearSolveFirstOrder", "NonlinearSolveQuasiNewton", "NonlinearSolveSpectralMethods", "PrecompileTools", "Preferences", "Reexport", "SciMLBase", "Setfield", "SimpleNonlinearSolve", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "a6c5719bbb42985c72f4cacbfa49e86bab850d66" +uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" +version = "4.19.1" + + [deps.NonlinearSolve.extensions] + NonlinearSolveFastLevenbergMarquardtExt = "FastLevenbergMarquardt" + NonlinearSolveFixedPointAccelerationExt = "FixedPointAcceleration" + NonlinearSolveLeastSquaresOptimExt = "LeastSquaresOptim" + NonlinearSolveMINPACKExt = "MINPACK" + NonlinearSolveNLSolversExt = "NLSolvers" + NonlinearSolveNLsolveExt = ["NLsolve", "LineSearches"] + NonlinearSolvePETScExt = ["PETSc", "MPI", "SparseArrays"] + NonlinearSolveSIAMFANLEquationsExt = "SIAMFANLEquations" + NonlinearSolveSpeedMappingExt = "SpeedMapping" + NonlinearSolveSundialsExt = "Sundials" + + [deps.NonlinearSolve.weakdeps] + FastLevenbergMarquardt = "7a0df574-e128-4d35-8cbd-3d84502bf7ce" + FixedPointAcceleration = "817d07cb-a79a-5c30-9a31-890123675176" + LeastSquaresOptim = "0fc2ff8b-aaa3-5acd-a817-1944a5e08891" + LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" + MINPACK = "4854310b-de5a-5eb6-a2a5-c1dee2bd17f9" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + NLSolvers = "337daf1e-9722-11e9-073e-8b9effe078ba" + NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" + SIAMFANLEquations = "084e46ad-d928-497d-ad5e-07fa361a48c4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SpeedMapping = "f1835b91-879b-4a3f-a438-e4baacf14412" + Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" + +[[deps.NonlinearSolveBase]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "Compat", "ConcreteStructs", "DifferentiationInterface", "EnzymeCore", "FastClosures", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "LogExpFunctions", "Markdown", "MaybeInplace", "PreallocationTools", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "SciMLBase", "SciMLJacobianOperators", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Setfield", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] +git-tree-sha1 = "cb824030a0d5769555704db95ee3cbe0f76ed53d" +uuid = "be0214bd-f91f-a760-ac4e-3421ce2b2da0" +version = "2.30.3" + + [deps.NonlinearSolveBase.extensions] + NonlinearSolveBaseBandedMatricesExt = "BandedMatrices" + NonlinearSolveBaseChainRulesCoreExt = "ChainRulesCore" + NonlinearSolveBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] + NonlinearSolveBaseForwardDiffExt = "ForwardDiff" + NonlinearSolveBaseLineSearchExt = "LineSearch" + NonlinearSolveBaseLinearSolveExt = "LinearSolve" + NonlinearSolveBaseMooncakeExt = "Mooncake" + NonlinearSolveBaseReverseDiffExt = "ReverseDiff" + NonlinearSolveBaseSparseArraysExt = "SparseArrays" + NonlinearSolveBaseSparseMatrixColoringsExt = "SparseMatrixColorings" + NonlinearSolveBaseTrackerExt = "Tracker" + + [deps.NonlinearSolveBase.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + LineSearch = "87fe0de2-c867-4266-b59a-2f0a94fc965b" + LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.NonlinearSolveFirstOrder]] +deps = ["ADTypes", "ArrayInterface", "CommonSolve", "ConcreteStructs", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLJacobianOperators", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "ce68820a4f421fb5bee7ec4dcf875aff33886bfb" +uuid = "5959db7a-ea39-4486-b5fe-2dd0bf03d60d" +version = "2.1.1" + +[[deps.NonlinearSolveQuasiNewton]] +deps = ["ArrayInterface", "CommonSolve", "ConcreteStructs", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLOperators", "StaticArraysCore"] +git-tree-sha1 = "538432ca1aea8bf63db02929bf870501f8a7c64c" +uuid = "9a2c21bd-3a47-402d-9113-8faf9a0ee114" +version = "1.13.1" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveQuasiNewton.extensions] + NonlinearSolveQuasiNewtonForwardDiffExt = "ForwardDiff" + +[[deps.NonlinearSolveSpectralMethods]] +deps = ["CommonSolve", "ConcreteStructs", "LineSearch", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "a3781e12becdf0ce5520bd97ec617e879bf4e9f2" +uuid = "26075421-4e9a-44e1-8bd1-420ed7ad02b2" +version = "1.7.1" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveSpectralMethods.extensions] + NonlinearSolveSpectralMethodsForwardDiffExt = "ForwardDiff" + +[[deps.Observables]] +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.5" + +[[deps.Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b6aa4566bb7ae78498a5e68943863fa8b5231b59" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.6+0" + +[[deps.OpenBLASConsistentFPCSR_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "38a93f17e431141c6470bb67a88952a7c4f0e928" +uuid = "6cdc7f73-28fd-5e50-80fb-958a8875b1af" +version = "0.3.34+0" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.27+1" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.5+0" + +[[deps.OpenMPI_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] +git-tree-sha1 = "0dba69ef957d790d41484ca7c20212c5ac806738" +uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" +version = "5.0.14+0" + +[[deps.OpenSSH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] +git-tree-sha1 = "1f640b82a627df3dbbaaff756a201a9ceb10dd1f" +uuid = "9bd350c2-7e96-507f-8002-3f2e150b4e1b" +version = "10.6.1+0" + +[[deps.OpenSSL]] +deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "NetworkOptions", "OpenSSL_jll", "Sockets"] +git-tree-sha1 = "5caab1056e0734b87f62ae3a3b520d47d3e994e8" +uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" +version = "1.7.0" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "20dd6a885a6fe8b07584d3eb8073133ad04c72e3" +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "3.5.9+0" + +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.6+0" + +[[deps.Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e2bb57a313a74b8104064b7efd01406c0a50d2ff" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.6.1+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "94ba93778373a53bfd5a0caaf7d809c445292ff4" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.8.2" + +[[deps.OrdinaryDiffEq]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqAdamsBashforthMoulton", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqExplicitRK", "OrdinaryDiffEqExponentialRK", "OrdinaryDiffEqExtrapolation", "OrdinaryDiffEqFIRK", "OrdinaryDiffEqFeagin", "OrdinaryDiffEqFunctionMap", "OrdinaryDiffEqHighOrderRK", "OrdinaryDiffEqIMEXMultistep", "OrdinaryDiffEqLinear", "OrdinaryDiffEqLowOrderRK", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqNordsieck", "OrdinaryDiffEqPDIRK", "OrdinaryDiffEqPRK", "OrdinaryDiffEqQPRK", "OrdinaryDiffEqRKN", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqStabilizedIRK", "OrdinaryDiffEqStabilizedRK", "OrdinaryDiffEqSymplecticRK", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SparseArrays", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "47271adac597af08263b6ea2669e93040b45c4d0" +uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +version = "6.111.0" + +[[deps.OrdinaryDiffEqAdamsBashforthMoulton]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "2e44acb684dfcdc2e41851a988733e30b28a8478" +uuid = "89bda076-bce5-4f1c-845f-551c83cdda9a" +version = "1.11.0" + +[[deps.OrdinaryDiffEqBDF]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "d7f69947e070a6a89aaf583e8aa2fada199fc292" +uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" +version = "1.26.0" + +[[deps.OrdinaryDiffEqCore]] +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "ConcreteStructs", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "Random", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Static", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "c0a22f012d3e98fd78afafe8e5152ace7a2276bc" +uuid = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" +version = "3.33.1" + + [deps.OrdinaryDiffEqCore.extensions] + OrdinaryDiffEqCoreMooncakeExt = "Mooncake" + OrdinaryDiffEqCoreSparseArraysExt = "SparseArrays" + + [deps.OrdinaryDiffEqCore.weakdeps] + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.OrdinaryDiffEqDefault]] +deps = ["ADTypes", "DiffEqBase", "EnumX", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "PrecompileTools", "Preferences", "Reexport", "SciMLBase"] +git-tree-sha1 = "5fbd116f93790ce79c4beff8206e1b8e45c492a2" +uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" +version = "1.14.0" + +[[deps.OrdinaryDiffEqDifferentiation]] +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SciMLOperators", "SparseMatrixColorings", "StaticArraysCore"] +git-tree-sha1 = "f3976b6baeb64051f41ed461a1933b7501a4f38e" +uuid = "4302a76b-040a-498a-8c04-15b101fed76b" +version = "2.9.0" +weakdeps = ["SparseArrays"] + + [deps.OrdinaryDiffEqDifferentiation.extensions] + OrdinaryDiffEqDifferentiationSparseArraysExt = "SparseArrays" + +[[deps.OrdinaryDiffEqExplicitRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "54346c3d7a9932ab11743a99afed8a3ff0a65fea" +uuid = "9286f039-9fbf-40e8-bf65-aa933bdc4db0" +version = "1.12.0" + +[[deps.OrdinaryDiffEqExponentialRK]] +deps = ["ADTypes", "DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "32fb54c8000c82fd32c55f7bf52f4651a3bd0e61" +uuid = "e0540318-69ee-4070-8777-9e2de6de23de" +version = "1.15.0" + +[[deps.OrdinaryDiffEqExtrapolation]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastPower", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "57bf89f5b144f6e5af1146fb7aee1544c6b76950" +uuid = "becaefa8-8ca2-5cf9-886d-c06f3d2bd2c4" +version = "1.18.0" + +[[deps.OrdinaryDiffEqFIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "3339f3a26974a37666c74b95bdb54aefc63b9a56" +uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" +version = "1.26.0" + +[[deps.OrdinaryDiffEqFeagin]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "302999c99bc454cf274d39b9ba3c2415f6fbb1cb" +uuid = "101fe9f7-ebb6-4678-b671-3a81e7194747" +version = "1.10.0" + +[[deps.OrdinaryDiffEqFunctionMap]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "2d2d59f9e530bca2f99934ecfde5430f6ad54bfe" +uuid = "d3585ca7-f5d3-4ba6-8057-292ed1abd90f" +version = "1.11.0" + +[[deps.OrdinaryDiffEqHighOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "a6fef72198944e64e0d818f2cc46275e663370c9" +uuid = "d28bc4f8-55e1-4f49-af69-84c1a99f0f58" +version = "1.12.0" + +[[deps.OrdinaryDiffEqIMEXMultistep]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport", "SciMLBase"] +git-tree-sha1 = "bd5440f7f09ce75df59e7f5d2f5b8e52d24aae3c" +uuid = "9f002381-b378-40b7-97a6-27a27c83f129" +version = "1.14.0" + +[[deps.OrdinaryDiffEqLinear]] +deps = ["DiffEqBase", "ExponentialUtilities", "LinearAlgebra", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "a1217ddfc979da3a2d9cf11b083688562e8ef333" +uuid = "521117fe-8c41-49f8-b3b6-30780b3f0fb5" +version = "1.12.0" + +[[deps.OrdinaryDiffEqLowOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "68a5e7e1b37933841e823a588deff3ec3e757a00" +uuid = "1344f307-1e59-4825-a18e-ace9aa3fa4c6" +version = "1.13.0" + +[[deps.OrdinaryDiffEqLowStorageRK]] +deps = ["Adapt", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "076ac77cee882b2f4858292dde748c8b2ab908e7" +uuid = "b0944070-b475-4768-8dec-fb6eb410534d" +version = "1.15.0" + +[[deps.OrdinaryDiffEqNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FastClosures", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "PreallocationTools", "RecursiveArrayTools", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SparseArrays", "StaticArraysCore"] +git-tree-sha1 = "82dde00dc43d5a4762cf50fa070b654cc38a2e87" +uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" +version = "1.28.0" + +[[deps.OrdinaryDiffEqNordsieck]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "21117475cb0d27942d31e4b8a27337392ae680b0" +uuid = "c9986a66-5c92-4813-8696-a7ec84c806c8" +version = "1.11.0" + +[[deps.OrdinaryDiffEqPDIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "SciMLBase"] +git-tree-sha1 = "1aaaa4851bbe72497c9d01d69fa36396ddeb0a0a" +uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" +version = "1.14.0" + +[[deps.OrdinaryDiffEqPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport", "SciMLBase"] +git-tree-sha1 = "1fe4f58f170c68b187ac42dc5f00932bb162ddfa" +uuid = "5b33eab2-c0f1-4480-b2c3-94bc1e80bda1" +version = "1.10.0" + +[[deps.OrdinaryDiffEqQPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "275900d64ab7be5f1bca8c7bfd48166838b6ab44" +uuid = "04162be5-8125-4266-98ed-640baecc6514" +version = "1.10.0" + +[[deps.OrdinaryDiffEqRKN]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "edf3411246d6030e165a5fba513cbdcde5c72f79" +uuid = "af6ede74-add8-4cfd-b1df-9a4dbb109d7a" +version = "1.12.0" + +[[deps.OrdinaryDiffEqRosenbrock]] +deps = ["ADTypes", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "3579d9272c286ffcceee794c5752e8cc7c3ae514" +uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" +version = "1.31.1" + +[[deps.OrdinaryDiffEqSDIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "c5e26fbadbad137fa2b7e3161a290a1becfec502" +uuid = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" +version = "1.14.0" + +[[deps.OrdinaryDiffEqSSPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "4ec0be19f8115889f61643fc25f6e3af5cd4c9e7" +uuid = "669c94d9-1f4b-4b64-b377-1aa079aa2388" +version = "1.14.0" + +[[deps.OrdinaryDiffEqStabilizedIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqStabilizedRK", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "226226d334fe295885df6ecb2d110c22ead79983" +uuid = "e3e12d00-db14-5390-b879-ac3dd2ef6296" +version = "1.14.0" + +[[deps.OrdinaryDiffEqStabilizedRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "764e11fb6a26ee8f2d2d10778d531fd708f35d28" +uuid = "358294b1-0aab-51c3-aafe-ad5ab194a2ad" +version = "1.11.1" + +[[deps.OrdinaryDiffEqSymplecticRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "b18a4e7973e73e84cfd79222e2389565b323b588" +uuid = "fa646aed-7ef9-47eb-84c4-9443fc8cbfa8" +version = "1.13.0" + +[[deps.OrdinaryDiffEqTsit5]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "6bd89fce8a0b198ac9fe98dc80362a92a2a13ae5" +uuid = "b1df2697-797e-41e3-8120-5422d3b24e4a" +version = "1.12.0" + +[[deps.OrdinaryDiffEqVerner]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "58dc7756825ca46e5a601a5aeba7d26a071d373a" +uuid = "79d7bb75-1356-48c1-b8c0-6832512096c2" +version = "1.14.0" + +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.42.0+1" + +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1912a9f1b9ca55005b03ba075f8e19993583e237" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.58.2+0" + +[[deps.Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "ba0dc8a8a67cacac4842631f960c046e4e563675" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.8" + +[[deps.Pidfile]] +deps = ["FileWatching", "Test"] +git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" +uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" +version = "1.3.0" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "e4a6721aa89e62e5d4217c0b21bd714263779dda" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.46.4+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.11.0" +weakdeps = ["REPL"] + + [deps.Pkg.extensions] + REPLExt = "REPL" + +[[deps.PlotThemes]] +deps = ["PlotUtils", "Statistics"] +git-tree-sha1 = "41031ef3a1be6f5bbbf3e8073f210556daeae5ca" +uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" +version = "3.3.0" + +[[deps.PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Reexport", "Statistics"] +git-tree-sha1 = "ddcc36fd83fb4cd459a53c891981697bde0d3de6" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.5.1" + +[[deps.PlotlyBase]] +deps = ["ColorSchemes", "Colors", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "6256ab3ee24ef079b3afa310593817e069925eeb" +uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +version = "0.8.23" + + [deps.PlotlyBase.extensions] + DataFramesExt = "DataFrames" + DistributionsExt = "Distributions" + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyBase.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyJS]] +deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "PlotlyKaleido", "REPL", "Reexport", "Requires", "WebIO"] +git-tree-sha1 = "4bf6c08295346efba58bb2d5f8c52984efed7f27" +uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" +version = "0.18.18" + + [deps.PlotlyJS.extensions] + CSVExt = "CSV" + DataFramesExt = ["DataFrames", "CSV"] + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyJS.weakdeps] + CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyKaleido]] +deps = ["Artifacts", "Base64", "JSON", "Kaleido_jll"] +git-tree-sha1 = "c7a270d11881c0709052f8e3def3d60d551e99f3" +uuid = "f2990250-8cf9-495f-b13a-cce12b45703c" +version = "2.3.1" + +[[deps.Plots]] +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "Unzip"] +git-tree-sha1 = "83bd514e8ff16b5858ac54c53fa0bcf6002a3b00" +uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +version = "1.41.7" + + [deps.Plots.extensions] + FileIOExt = "FileIO" + GeometryBasicsExt = "GeometryBasics" + IJuliaExt = "IJulia" + ImageInTerminalExt = "ImageInTerminal" + UnitfulExt = "Unitful" + + [deps.Plots.weakdeps] + FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" + GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.Polyester]] +deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] +git-tree-sha1 = "16bbc30b5ebea91e9ce1671adc03de2832cff552" +uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" +version = "0.7.19" + +[[deps.PolyesterWeave]] +deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] +git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" +uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" +version = "0.2.2" + +[[deps.PooledArrays]] +deps = ["DataAPI", "Future"] +git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" +uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" +version = "1.4.3" + +[[deps.PreallocationTools]] +deps = ["Adapt", "ArrayInterface", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "cd00e28071a75c98664663f87c2ae447d25c0503" +uuid = "d236fae5-4411-538c-8e31-a6e3d9e00b46" +version = "1.7.1" + + [deps.PreallocationTools.extensions] + PreallocationToolsEnzymeCoreExt = "EnzymeCore" + PreallocationToolsForwardDiffExt = "ForwardDiff" + PreallocationToolsReverseDiffExt = "ReverseDiff" + + [deps.PreallocationTools.weakdeps] + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.2.1" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "5005266de4bfe50e53ff44a5cb5c540b6e47a254" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.6.0" + +[[deps.PrettyTables]] +deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "StyledStrings", "Tables"] +git-tree-sha1 = "99fd8f32ab6728ff205cddd24e2d9ea6cbb498d3" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "3.5.0" + + [deps.PrettyTables.extensions] + PrettyTablesExcelExt = "XLSX" + PrettyTablesTypstryExt = "Typstry" + PrettyTablesWriteDocxExt = "WriteDocx" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" + WriteDocx = "d049ceea-54ee-41d7-a26f-ba29db3b6599" + XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" + +[[deps.PtrArrays]] +git-tree-sha1 = "4fbbafbc6251b883f4d2705356f3641f3652a7fe" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.4.0" + +[[deps.PureKLU]] +deps = ["LinearAlgebra", "PrecompileTools", "SparseArrays"] +git-tree-sha1 = "ad0e84647fe7b3c2ae02aaeace1522139aaf6a27" +uuid = "0c0d3e7f-3a8b-4f7e-b6f1-9a4d2e7c1f01" +version = "1.6.0" +weakdeps = ["ForwardDiff"] + + [deps.PureKLU.extensions] + PureKLUForwardDiffExt = "ForwardDiff" + +[[deps.Qt6Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] +git-tree-sha1 = "144895f6166994730ee7ff8113b981fc360638f1" +uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" +version = "6.10.2+2" + +[[deps.Qt6Declarative_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll", "Qt6Svg_jll"] +git-tree-sha1 = "159d253ab126d5b29230cf53521899bea4ef4648" +uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" +version = "6.10.2+2" + +[[deps.Qt6ShaderTools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "4d85eedf69d875982c46643f6b4f66919d7e157b" +uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" +version = "6.10.2+1" + +[[deps.Qt6Svg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "81587ff5ff25a4e1115ce191e36285ede0334c9d" +uuid = "6de9746b-f93d-5813-b365-ba18ad4a9cf3" +version = "6.10.2+0" + +[[deps.Qt6Wayland_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] +git-tree-sha1 = "672c938b4b4e3e0169a07a5f227029d4905456f2" +uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" +version = "6.10.2+1" + +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "5e8e8b0ab68215d7a2b14b9921a946fee794749e" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.11.3" + + [deps.QuadGK.extensions] + QuadGKEnzymeExt = "Enzyme" + + [deps.QuadGK.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + +[[deps.Quadmath]] +deps = ["Compat", "Printf", "Random"] +git-tree-sha1 = "fc861299ef1c23d1985de276b2233db4a980a55c" +uuid = "be4d8f0f-7fa4-5f49-b795-2f01399ab2dd" +version = "1.1.0" +weakdeps = ["SpecialFunctions"] + + [deps.Quadmath.extensions] + QuadmathSpecialFunctionsExt = ["SpecialFunctions"] + +[[deps.REPL]] +deps = ["InteractiveUtils", "Markdown", "Sockets", "StyledStrings", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" + +[[deps.Random]] +deps = ["SHA"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" + +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.3.4" + +[[deps.RecipesPipeline]] +deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] +git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" +uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" +version = "0.6.12" + +[[deps.RecursiveArrayTools]] +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "LinearAlgebra", "PrecompileTools", "RecipesBase", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "d0282d612f22dcad7b81cf487b746e63aa2a6709" +uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" +version = "3.54.0" + + [deps.RecursiveArrayTools.extensions] + RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" + RecursiveArrayToolsFastBroadcastPolyesterExt = ["FastBroadcast", "Polyester"] + RecursiveArrayToolsForwardDiffExt = "ForwardDiff" + RecursiveArrayToolsKernelAbstractionsExt = "KernelAbstractions" + RecursiveArrayToolsMeasurementsExt = "Measurements" + RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" + RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] + RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] + RecursiveArrayToolsStatisticsExt = "Statistics" + RecursiveArrayToolsStructArraysExt = "StructArrays" + RecursiveArrayToolsTablesExt = ["Tables"] + RecursiveArrayToolsTrackerExt = "Tracker" + RecursiveArrayToolsZygoteExt = "Zygote" + + [deps.RecursiveArrayTools.weakdeps] + FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.RegistryInstances]] +deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] +git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" +uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" +version = "0.1.0" + +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "1.0.1" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.1" + +[[deps.Roots]] +deps = ["Accessors", "CommonSolve", "Printf"] +git-tree-sha1 = "3eff988b9bd09543783e2e051b0a1eef11f65c2d" +uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +version = "2.3.0" + + [deps.Roots.extensions] + RootsChainRulesCoreExt = "ChainRulesCore" + RootsForwardDiffExt = "ForwardDiff" + RootsIntervalRootFindingExt = "IntervalRootFinding" + RootsSymPyExt = "SymPy" + RootsSymPyPythonCallExt = "SymPyPythonCall" + RootsUnitfulExt = "Unitful" + + [deps.Roots.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalRootFinding = "d2bf35a9-74e0-55ec-b149-d360ff49b807" + SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6" + SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + +[[deps.RuntimeGeneratedFunctions]] +deps = ["ExprTools", "PrecompileTools", "SHA", "Serialization"] +git-tree-sha1 = "9bb075002b40849582905394bb3031fb77aa102d" +uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" +version = "0.5.27" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.SIMDTypes]] +git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" +uuid = "94e857df-77ce-4151-89e5-788b33177be4" +version = "0.1.0" + +[[deps.SciMLBase]] +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PreallocationTools", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLLogging", "SciMLOperators", "SciMLPublic", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] +git-tree-sha1 = "d56cb3b924cdf9297c171113e6a6ae9694e370eb" +uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +version = "2.155.2" + + [deps.SciMLBase.extensions] + SciMLBaseChainRulesCoreExt = "ChainRulesCore" + SciMLBaseDifferentiationInterfaceExt = "DifferentiationInterface" + SciMLBaseDistributionsExt = "Distributions" + SciMLBaseEnzymeExt = "Enzyme" + SciMLBaseForwardDiffExt = "ForwardDiff" + SciMLBaseMLStyleExt = "MLStyle" + SciMLBaseMakieExt = "Makie" + SciMLBaseMeasurementsExt = "Measurements" + SciMLBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + SciMLBaseMooncakeExt = "Mooncake" + SciMLBasePartialFunctionsExt = "PartialFunctions" + SciMLBasePyCallExt = "PyCall" + SciMLBasePythonCallExt = "PythonCall" + SciMLBaseRCallExt = "RCall" + SciMLBaseReverseDiffExt = "ReverseDiff" + SciMLBaseTrackerExt = "Tracker" + SciMLBaseZygoteExt = ["Zygote", "ChainRulesCore"] + + [deps.SciMLBase.weakdeps] + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" + PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" + PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" + RCall = "6f49c342-dc21-5d91-9882-a32aef131414" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.SciMLJacobianOperators]] +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "ad167d716fc134c0873c76ba0469ede56a678732" +uuid = "19f34311-ddf3-4b8b-af20-060888a46c0e" +version = "0.1.17" + +[[deps.SciMLLogging]] +deps = ["Logging", "LoggingExtras", "Preferences"] +git-tree-sha1 = "4e1e21f14a284f892eb62923a356c70a2a0c68e1" +uuid = "a6db7da4-7206-11f0-1eab-35f2a5dbe1d1" +version = "1.10.1" + + [deps.SciMLLogging.extensions] + SciMLLoggingTracyExt = "Tracy" + + [deps.SciMLLogging.weakdeps] + Tracy = "e689c965-62c8-4b79-b2c5-8359227902fd" + +[[deps.SciMLOperators]] +deps = ["Accessors", "Adapt", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "2712b6eeef282b5cd8c38660e68c001a4b9b24e2" +uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" +version = "1.30.3" + + [deps.SciMLOperators.extensions] + SciMLOperatorsLoopVectorizationExt = "LoopVectorization" + SciMLOperatorsSparseArraysExt = "SparseArrays" + + [deps.SciMLOperators.weakdeps] + LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.SciMLPublic]] +deps = ["PrecompileTools"] +git-tree-sha1 = "74685afb51732a464fbce79a72708f7c4203ceb7" +uuid = "431bcebd-1456-4ced-9d72-93c2757fff0b" +version = "1.3.0" + +[[deps.SciMLStructures]] +deps = ["ArrayInterface", "PrecompileTools"] +git-tree-sha1 = "4b8fc0ffe22b4ecba7a02f1fec0b3757db014d0c" +uuid = "53ae85a6-f571-4167-b2af-e1d143709226" +version = "1.10.6" + +[[deps.ScopedValues]] +deps = ["HashArrayMappedTries", "Logging"] +git-tree-sha1 = "67a144433c4ce877ee6d1ada69a124d6b1ecf7be" +uuid = "7e506255-f358-4e82-b7e4-beb19740aa63" +version = "1.6.2" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "9b81b8393e50b7d4e6d0a9f14e192294d3b7c109" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.3.0" + +[[deps.SentinelArrays]] +deps = ["Dates", "Random"] +git-tree-sha1 = "084c47c7c5ce5cfecefa0a98dff69eb3646b5a80" +uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" +version = "1.4.10" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.11.0" + +[[deps.Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] +git-tree-sha1 = "c5391c6ace3bc430ca630251d02ea9687169ca68" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "1.1.2" + +[[deps.Showoff]] +deps = ["Dates"] +git-tree-sha1 = "8238217340ad0aaabe11afe39c1098b5bc9f4c8e" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.1.1" + +[[deps.SimpleBufferStream]] +git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" +uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" +version = "1.2.0" + +[[deps.SimpleNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "08d8bafd57b7ea16dc1f69a8bd57db23a2ceb686" +uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" +version = "2.12.0" + + [deps.SimpleNonlinearSolve.extensions] + SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" + SimpleNonlinearSolveReverseDiffExt = "ReverseDiff" + SimpleNonlinearSolveTrackerExt = "Tracker" + + [deps.SimpleNonlinearSolve.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" + +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "13cd91cc9be159e3f4d95b857fa2aa383b53772a" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.2.3" + +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.11.0" + +[[deps.SparseColumnPivotedQR]] +deps = ["LinearAlgebra", "PrecompileTools", "SparseArrays"] +git-tree-sha1 = "8cf1a59c78dd6f900feb366e5a574f99ed278ef1" +uuid = "a57abbd0-fea5-4d57-96be-5e525945e8e4" +version = "2.1.8" +weakdeps = ["AMD"] + + [deps.SparseColumnPivotedQR.extensions] + SparseColumnPivotedQRAMDExt = "AMD" + +[[deps.SparseMatrixColorings]] +deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "Random", "SparseArrays"] +git-tree-sha1 = "0730f14b07c68fd3b3a6fae89a829a5124a7ae7e" +uuid = "0a514795-09f3-496d-8182-132a7b665d35" +version = "0.4.29" + + [deps.SparseMatrixColorings.extensions] + SparseMatrixColoringsAMDGPUExt = "AMDGPU" + SparseMatrixColoringsCUDAExt = ["CUDA", "cuSPARSE"] + SparseMatrixColoringsCliqueTreesExt = "CliqueTrees" + SparseMatrixColoringsColorsExt = "Colors" + SparseMatrixColoringsGPUArraysExt = "GPUArrays" + SparseMatrixColoringsJuMPExt = ["JuMP", "MathOptInterface"] + + [deps.SparseMatrixColorings.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" + Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" + GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" + JuMP = "4076af6c-e467-56ae-b986-b466b2749572" + MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" + cuSPARSE = "b26da814-b3bc-49ef-b0ee-c816305aa060" + +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "429071b23f4c9a13fb6582f807cc2ef454082408" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.9.0" + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.Static]] +deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "474a5283ad435618090122872eea6a8165ea6bcf" +uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +version = "1.4.6" + +[[deps.StaticArrayInterface]] +deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "SciMLPublic", "Static"] +git-tree-sha1 = "2a635e15d5035c53b345077c947f31ff91744078" +uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" +version = "1.10.0" + + [deps.StaticArrayInterface.extensions] + StaticArrayInterfaceOffsetArraysExt = "OffsetArrays" + StaticArrayInterfaceStaticArraysExt = "StaticArrays" + + [deps.StaticArrayInterface.weakdeps] + OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "de349d5585559b692f518b6d3fc7c179d64b2364" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.9.23" + + [deps.StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [deps.StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "6ab403037779dae8c514bad259f32a447262455a" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.4" + +[[deps.Statistics]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "e2b53ce13a53367e96601081e33d34746b571bad" +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.11.5" +weakdeps = ["SparseArrays"] + + [deps.Statistics.extensions] + SparseArraysExt = ["SparseArrays"] + +[[deps.StatsAPI]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "178ed29fd5b2a2cfc3bd31c13375ae925623ff36" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.8.0" + +[[deps.StatsBase]] +deps = ["AliasTables", "DataAPI", "DataStructures", "IrrationalConstants", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "adb9da019510162e67a4493fc235c23203d8b09e" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.34.13" + +[[deps.StrideArraysCore]] +deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] +git-tree-sha1 = "5316097111523c9a970596a5b33cfea5f92e8581" +uuid = "7792a7ef-975c-4747-a70f-980b88e8d1da" +version = "0.5.9" + +[[deps.StringManipulation]] +deps = ["PrecompileTools", "StyledStrings"] +git-tree-sha1 = "9e3a02d73a8f0a9be04e677dbef758aab40f8bea" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.6.1" + +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.7.0+0" + +[[deps.SymbolicIndexingInterface]] +deps = ["Accessors", "ArrayInterface", "PrecompileTools", "RuntimeGeneratedFunctions", "StaticArraysCore"] +git-tree-sha1 = "7eb6da9656581ac9fbffaef3ef7950a090a5002a" +uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +version = "0.3.55" +weakdeps = ["PrettyTables"] + + [deps.SymbolicIndexingInterface.extensions] + SymbolicIndexingInterfacePrettyTablesExt = "PrettyTables" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "a94d9bdda1b7bed0046cea645639ab3f62196fac" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.14.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" + +[[deps.ThreadSafeDicts]] +git-tree-sha1 = "411010ade7dd5576d693db8737976d805ee9945b" +uuid = "4239201d-c60e-5e0a-9702-85d713665ba7" +version = "0.1.7" + +[[deps.ThreadingUtilities]] +deps = ["ManualMemory"] +git-tree-sha1 = "e12924c87b5476110130841e973ba9b9f20ae599" +uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" +version = "0.5.7" + +[[deps.TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "3748bd928e68c7c346b52125cf41fff0de6937d0" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.29" + + [deps.TimerOutputs.extensions] + FlameGraphsExt = "FlameGraphs" + + [deps.TimerOutputs.weakdeps] + FlameGraphs = "08572546-2f56-4bcf-ba4e-bab62c3a3f89" + +[[deps.TranscodingStreams]] +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.11.3" + +[[deps.TruncatedStacktraces]] +deps = ["InteractiveUtils", "MacroTools", "Preferences"] +git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" +uuid = "781d530d-4396-4725-bb49-402e4bee1e77" +version = "1.4.0" + +[[deps.URIs]] +deps = ["Serialization"] +git-tree-sha1 = "0392e5e3c5327b90a487b0ff3e818c4aecd3660b" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.7.1" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "1.11.0" + +[[deps.UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" + +[[deps.UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[deps.Unzip]] +git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" +uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" +version = "0.2.0" + +[[deps.Vulkan_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] +git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" +uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" +version = "1.3.243+0" + +[[deps.Wayland_jll]] +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "96478df35bbc2f3e1e791bc7a3d0eeee559e60e9" +uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" +version = "1.24.0+0" + +[[deps.WebIO]] +deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] +git-tree-sha1 = "0eef0765186f7452e52236fa42ca8c9b3c11c6e3" +uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" +version = "0.8.21" + +[[deps.WebSockets]] +deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] +git-tree-sha1 = "4162e95e05e79922e44b9952ccbc262832e4ad07" +uuid = "104b5d7c-a370-577a-8038-80a2059c5097" +version = "1.6.0" + +[[deps.Widgets]] +deps = ["Colors", "Dates", "Observables", "OrderedCollections"] +git-tree-sha1 = "cab29ed70d9355a28b95b1798007ff3a00eefc91" +uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" +version = "0.6.8" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "b826aedca5fce2e3c91904e338c279e5c1a2da74" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.15.4+0" + +[[deps.XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e52eca002a11c30a858185efdfb15311e1c7a6bf" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.8.4+0" + +[[deps.Xorg_libICE_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a3ea76ee3f4facd7a64684f9af25310825ee3668" +uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" +version = "1.1.2+0" + +[[deps.Xorg_libSM_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libICE_jll"] +git-tree-sha1 = "9c7ad99c629a44f81e7799eb05ec2746abb5d588" +uuid = "c834827a-8449-5923-a945-d239c165b7dd" +version = "1.2.6+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "808090ede1d41644447dd5cbafced4731c56bd2f" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.13+0" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "aa1261ebbac3ccc8d16558ae6799524c450ed16b" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.13+0" + +[[deps.Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "6c74ca84bbabc18c4547014765d194ff0b4dc9da" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.4+0" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "52858d64353db33a56e13c341d7bf44cd0d7b309" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.6+0" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "1a4a26870bf1e5d26cd585e38038d399d7e65706" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.8+0" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "75e00946e43621e09d431d9b95818ee751e6b2ef" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "6.0.2+0" + +[[deps.Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "dcb316b3ce0941f195537dda56bea4517fcd3ff5" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.8.4+0" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] +git-tree-sha1 = "0ba01bc7396896a4ace8aab67db31403c71628f4" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.7+0" + +[[deps.Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "6c174ef70c96c76f4c3f4d3cfbe09d018bcd1b53" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.6+0" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "7ed9347888fac59a618302ee38216dd0379c480d" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.12+0" + +[[deps.Xorg_libpciaccess_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "58972370b81423fc546c56a60ed1a009450177c3" +uuid = "a65dc6b1-eb27-53a1-bb3e-dea574b5389e" +version = "0.19.0+0" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXau_jll", "Xorg_libXdmcp_jll"] +git-tree-sha1 = "bfcaf7ec088eaba362093393fe11aa141fa15422" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.17.1+0" + +[[deps.Xorg_libxkbfile_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "ed756a03e95fff88d8f738ebc2849431bdd4fd1a" +uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" +version = "1.2.0+0" + +[[deps.Xorg_xcb_util_cursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] +git-tree-sha1 = "9750dc53819eba4e9a20be42349a6d3b86c7cdf8" +uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" +version = "0.1.6+0" + +[[deps.Xorg_xcb_util_image_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f4fc02e384b74418679983a97385644b67e1263b" +uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll"] +git-tree-sha1 = "68da27247e7d8d8dafd1fcf0c3654ad6506f5f97" +uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_keysyms_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "44ec54b0e2acd408b0fb361e1e9244c60c9c3dd4" +uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_renderutil_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "5b0263b6d080716a02544c55fdff2c8d7f9a16a0" +uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" +version = "0.3.10+0" + +[[deps.Xorg_xcb_util_wm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f233c83cad1fa0e70b7771e0e21b061a116f2763" +uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" +version = "0.4.2+0" + +[[deps.Xorg_xkbcomp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "9a5e6e8f1a7c40941465c04840145e527f0d6b4b" +uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" +version = "1.5.0+0" + +[[deps.Xorg_xkeyboard_config_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "df77b22eb77051b3d0ef30aab979684f7482c66f" +uuid = "33bec58e-1273-512f-9401-5d533626f822" +version = "2.48.0+0" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a63799ff68005991f9d9491b6e95bd3478d783cb" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.6.0+0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.2.13+1" + +[[deps.Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "446b23e73536f84e8037f5dce465e92275f6a308" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.7+1" + +[[deps.aws_c_auth_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_cal_jll", "aws_c_http_jll", "aws_c_sdkutils_jll"] +git-tree-sha1 = "8cab83c96af80a1be968251ce1a0548a7545484d" +uuid = "2b3700d1-4306-52e2-a478-c162f0c514be" +version = "0.9.6+0" + +[[deps.aws_c_cal_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "22c0f42f4a1f0dc5dcfa8fd267c4ac407c455e7a" +uuid = "70f11efc-bab2-57f1-b0f3-22aad4e67c4b" +version = "0.9.13+0" + +[[deps.aws_c_common_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a759cb9bf456ad792cc7898a81ae333cce9ef02a" +uuid = "73048d1d-b8c4-5092-a58d-866c5e8d1e50" +version = "0.12.6+0" + +[[deps.aws_c_compression_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "7910c72f45f44afd297c39fe43b99c56d5ed22ec" +uuid = "73a04cd5-f3d7-5bac-9290-e8adb709f224" +version = "0.3.2+0" + +[[deps.aws_c_http_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_compression_jll", "aws_c_io_jll"] +git-tree-sha1 = "3fb8685778068de502c72fec5dd8075e037cee15" +uuid = "3254fc65-9028-534d-aa9d-d76d128babc6" +version = "0.10.15+0" + +[[deps.aws_c_io_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_cal_jll", "aws_c_common_jll", "s2n_tls_jll"] +git-tree-sha1 = "7e481d474b2087ee8bbf55b81bf9119f21e396d9" +uuid = "13c41daa-f319-5298-b5eb-5754e0170d52" +version = "0.26.3+0" + +[[deps.aws_c_s3_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_auth_jll", "aws_c_common_jll", "aws_c_http_jll", "aws_checksums_jll", "s2n_tls_jll"] +git-tree-sha1 = "3e9917ab25114feba657e71be41cad068b9f6595" +uuid = "bd1f34fb-993f-5903-a121-aaf302eed6d4" +version = "0.11.5+0" + +[[deps.aws_c_sdkutils_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "c43dfba2c1ab9ea9f02f2c80e86fa16f6460244e" +uuid = "1282aa60-004d-510b-9f52-12498d409daa" +version = "0.2.4+1" + +[[deps.aws_checksums_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "2570c8e23f4771a087b12a47edcaaa670ac05a01" +uuid = "b2a88e68-78e7-5e94-8c20-c02986ec140e" +version = "0.2.10+0" + +[[deps.dlfcn_win32_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e141d67ffe550eadfb5af1bdbdaf138031e4805f" +uuid = "c4b69c83-5512-53e3-94e6-de98773c479f" +version = "1.4.2+0" + +[[deps.eudev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c3b0e6196d50eab0c5ed34021aaa0bb463489510" +uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" +version = "3.2.14+0" + +[[deps.fzf_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b6a34e0e0960190ac2a4363a1bd003504772d631" +uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" +version = "0.61.1+0" + +[[deps.libaec_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "60f4792734488db6f42e2c7699f1d4594780bd03" +uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" +version = "1.1.7+0" + +[[deps.libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1210ba774d3427387d307bf1f416d699b7c39417" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.15.1+0" + +[[deps.libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "cb007192783c56d8249db4cf0e3495001edfe414" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.17.5+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.11.0+0" + +[[deps.libdecor_jll]] +deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] +git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" +uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" +version = "0.2.2+0" + +[[deps.libdrm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libpciaccess_jll"] +git-tree-sha1 = "28e57478e8a160d346a19c28b3fffb9273bcc9c2" +uuid = "8e53e030-5e6c-5a89-a30b-be5b7263a166" +version = "2.4.134+0" + +[[deps.libevdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "56d643b57b188d30cccc25e331d416d3d358e557" +uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" +version = "1.13.4+0" + +[[deps.libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "646634dd19587a56ee2f1199563ec056c5f228df" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "2.0.4+0" + +[[deps.libinput_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "eudev_jll", "libevdev_jll", "mtdev_jll"] +git-tree-sha1 = "91d05d7f4a9f67205bd6cf395e488009fe85b499" +uuid = "36db933b-70db-51c0-b978-0f229ee0e533" +version = "1.28.1+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "1fcd6cf05d23adab0b5ffc496d020537215751dc" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.59+1" + +[[deps.libva_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "libdrm_jll"] +git-tree-sha1 = "7dbf96baae3310fe2fa0df0ccbb3c6288d5816c9" +uuid = "9a156e7d-b971-5f62-b2c9-67348b8fb97c" +version = "2.23.0+0" + +[[deps.libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll"] +git-tree-sha1 = "11e1772e7f3cc987e9d3de991dd4f6b2602663a5" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.8+0" + +[[deps.mpif_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIABI_jll", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "TOML"] +git-tree-sha1 = "a06fcd368cfe6fe2c0eb7b63320d4d27ddcd010d" +uuid = "9aeb927a-4695-514f-a259-621a69f20ec0" +version = "1.0.0+0" + +[[deps.mtdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b4d631fd51f2e9cdd93724ae25b2efc198b059b1" +uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" +version = "1.1.7+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.59.0+0" + +[[deps.oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "ed2d8d4ad96c03d3abe3382cdbbdea9489b63636" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2023.1.0+0" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.4.0+2" + +[[deps.s2n_tls_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d6cfe0874f96962fe4b20ed8bdb71c467e09c2c6" +uuid = "cddc5d3d-934d-5d3a-9747-62fc12ea3f48" +version = "1.7.11+0" + +[[deps.x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "14cc7083fc6dff3cc44f2bc435ee96d06ed79aa7" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "10164.0.1+0" + +[[deps.x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e7b67590c14d487e734dcb925924c5dc43ec85f3" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "4.1.0+0" + +[[deps.xkbcommon_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] +git-tree-sha1 = "a1fc6507a40bf504527d0d4067d718f8e179b2b8" +uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" +version = "1.13.0+0" diff --git a/ci/manifests/Manifest-v1.12.toml b/ci/manifests/Manifest-v1.12.toml new file mode 100644 index 0000000000..d570e49763 --- /dev/null +++ b/ci/manifests/Manifest-v1.12.toml @@ -0,0 +1,3137 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.12.7" +manifest_format = "2.0" +project_hash = "46cc8ae34afcb7a4b038db845711846a1cfd9266" + +[[deps.ADTypes]] +deps = ["PrecompileTools"] +git-tree-sha1 = "629de23e1c16911b439dabd2303c08af9575b226" +uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" +version = "1.24.0" + + [deps.ADTypes.extensions] + ADTypesChainRulesCoreExt = "ChainRulesCore" + ADTypesConstructionBaseExt = "ConstructionBase" + ADTypesEnzymeCoreExt = "EnzymeCore" + + [deps.ADTypes.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + +[[deps.AMD]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] +git-tree-sha1 = "344f3d221a6bee75925b5c97a30cfd870f12a138" +uuid = "14f7f29c-3bd6-536c-9a0b-7339e30b5a3e" +version = "0.5.4" + +[[deps.ANSIColoredPrinters]] +git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" +uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" +version = "0.0.1" + +[[deps.AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.5.0" + + [deps.AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + + [deps.AbstractFFTs.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" + +[[deps.Accessors]] +deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "MacroTools"] +git-tree-sha1 = "7063ad1083578215c7c4bf410368150abe8d5524" +uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" +version = "0.1.45" + + [deps.Accessors.extensions] + AxisKeysExt = "AxisKeys" + IntervalSetsExt = "IntervalSets" + LinearAlgebraExt = "LinearAlgebra" + StaticArraysExt = "StaticArrays" + StructArraysExt = "StructArrays" + TestExt = "Test" + UnitfulExt = "Unitful" + + [deps.Accessors.weakdeps] + AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "dee8388a9bec89c8713ad176f96960ae2f98598a" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "4.7.4" +weakdeps = ["SparseArrays", "StaticArrays"] + + [deps.Adapt.extensions] + AdaptSparseArraysExt = "SparseArrays" + AdaptStaticArraysExt = "StaticArrays" + +[[deps.AdaptiveArrayPools]] +deps = ["Preferences", "Printf", "Random"] +git-tree-sha1 = "5feaa5055572e809b69b62f06826b1c1c8d3ebf3" +uuid = "4f381ef7-9af0-4cbe-99d4-cf36d7b0f233" +version = "0.3.11" + + [deps.AdaptiveArrayPools.extensions] + AdaptiveArrayPoolsCUDAExt = "CUDA" + AdaptiveArrayPoolsMetalExt = "Metal" + + [deps.AdaptiveArrayPools.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.2.0" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.2" + +[[deps.ArrayInterface]] +deps = ["Adapt", "LinearAlgebra"] +git-tree-sha1 = "1aec1ff0dcaa83a484e7f72097562fa3102c6722" +uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +version = "7.30.2" + + [deps.ArrayInterface.extensions] + ArrayInterfaceAMDGPUExt = "AMDGPU" + ArrayInterfaceBandedMatricesExt = "BandedMatrices" + ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" + ArrayInterfaceCUDAExt = "CUDA" + ArrayInterfaceCUDSSExt = ["CUDSS", "CUDA"] + ArrayInterfaceChainRulesCoreExt = "ChainRulesCore" + ArrayInterfaceChainRulesExt = "ChainRules" + ArrayInterfaceFillArraysExt = "FillArrays" + ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" + ArrayInterfaceGPUArraysCoreTrackerExt = ["GPUArraysCore", "Tracker"] + ArrayInterfaceMetalExt = "Metal" + ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceSparseArraysExt = "SparseArrays" + ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" + ArrayInterfaceTrackerExt = "Tracker" + + [deps.ArrayInterface.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.ArrayLayouts]] +deps = ["FillArrays", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "b4a2d2dfcaedf8f0cb93a970c4ccba6f632f14cc" +uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" +version = "1.15.1" +weakdeps = ["SparseArrays"] + + [deps.ArrayLayouts.extensions] + ArrayLayoutsSparseArraysExt = "SparseArrays" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" + +[[deps.AssetRegistry]] +deps = ["JSON", "Pidfile", "SHA"] +git-tree-sha1 = "902b85010203830d4bc02259f5450d2e16b316d8" +uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" +version = "0.1.1" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" + +[[deps.BitFlags]] +git-tree-sha1 = "bbe1079eecf9c9fbb52765193ad2bae27ae09bc8" +uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" +version = "0.1.10" + +[[deps.BitTwiddlingConvenienceFunctions]] +deps = ["Static"] +git-tree-sha1 = "f21cfd4950cb9f0587d5067e69405ad2acd27b87" +uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" +version = "0.1.6" + +[[deps.Blink]] +deps = ["Base64", "Distributed", "HTTP", "JSExpr", "JSON", "Lazy", "Logging", "MacroTools", "Mustache", "Mux", "Pkg", "Reexport", "Sockets", "WebIO"] +git-tree-sha1 = "bc93511973d1f949d45b0ea17878e6cb0ad484a1" +uuid = "ad839575-38b3-5650-b840-f874b8c74a25" +version = "0.12.9" + +[[deps.BracketingNonlinearSolve]] +deps = ["CommonSolve", "ConcreteStructs", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "7ad7171d693ae5552ac43862e7f6b61df4471c2b" +uuid = "70df07ce-3d50-431d-a3e7-ca6ddb60ac1e" +version = "1.12.1" + + [deps.BracketingNonlinearSolve.extensions] + BracketingNonlinearSolveChainRulesCoreExt = ["ChainRulesCore", "ForwardDiff"] + BracketingNonlinearSolveForwardDiffExt = "ForwardDiff" + + [deps.BracketingNonlinearSolve.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.9+0" + +[[deps.CPUSummary]] +deps = ["CpuId", "IfElse", "PrecompileTools", "Preferences", "Static"] +git-tree-sha1 = "f3a21d7fc84ba618a779d1ed2fcca2e682865bab" +uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" +version = "0.2.7" + +[[deps.CRlibm]] +deps = ["CRlibm_jll"] +git-tree-sha1 = "66188d9d103b92b6cd705214242e27f5737a1e5e" +uuid = "96374032-68de-5a5b-8d9e-752f78720389" +version = "1.0.2" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "7b841680738c19948120f6e4cf8d0200518bd564" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.18.8+0" + +[[deps.ChunkCodecCore]] +git-tree-sha1 = "3496e2b359c27793d12bec54ec94be7983a6ceb2" +uuid = "0b6fb165-00bc-4d37-ab8b-79f91016dbe1" +version = "1.0.2" + +[[deps.ChunkCodecLibZlib]] +deps = ["ChunkCodecCore", "Zlib_jll"] +git-tree-sha1 = "d4101e848e8d3f585d61d244c2fe0c80a70e6b3b" +uuid = "4c0bbee4-addc-4d73-81a0-b6caacae83c8" +version = "1.1.0" + +[[deps.ChunkCodecLibZstd]] +deps = ["ChunkCodecCore", "Zstd_jll"] +git-tree-sha1 = "34d9873079e4cb3d0c62926a225136824677073f" +uuid = "55437552-ac27-4d47-9aa3-63184e8fd398" +version = "1.0.0" + +[[deps.CloseOpenIntervals]] +deps = ["Static", "StaticArrayInterface"] +git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" +uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" +version = "0.1.13" + +[[deps.CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "970758a3d591a2a5c2a907c53f2e2f8c1b1d3537" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.9" + +[[deps.ColorSchemes]] +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "b0fd3f56fa442f81e0a47815c92245acfaaa4e34" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.31.0" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "a7cf827a79b378a381e31e5c7feca853ec19cb94" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.12.4" +weakdeps = ["StyledStrings"] + + [deps.ColorTypes.extensions] + StyledStringsExt = "StyledStrings" + +[[deps.ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "8b3b6f87ce8f65a2b4f857528fd8d70086cd72b1" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.11.0" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Reexport"] +git-tree-sha1 = "a633156e74096f90b3b5e833529292b651b5671a" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.13.3" + +[[deps.CommonSolve]] +deps = ["PrecompileTools"] +git-tree-sha1 = "6c389fa857f6ca5a95474b52a52023fd77f24cb7" +uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +version = "0.2.14" + +[[deps.CommonSubexpressions]] +deps = ["MacroTools"] +git-tree-sha1 = "cda2cfaebb4be89c9084adaca7dd7333369715c5" +uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" +version = "0.3.1" + +[[deps.CommonWorldInvalidations]] +deps = ["PrecompileTools"] +git-tree-sha1 = "7c4ea0adfb7238bf4678aa36325863fab6163f6f" +uuid = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8" +version = "1.2.2" + +[[deps.Compat]] +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "9d8a54ce4b17aa5bdce0ea5c34bc5e7c340d16ad" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.18.1" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.3.1+2" + +[[deps.CompositionsBase]] +git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" +uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" +version = "0.1.2" +weakdeps = ["InverseFunctions"] + + [deps.CompositionsBase.extensions] + CompositionsBaseInverseFunctionsExt = "InverseFunctions" + +[[deps.ConcreteStructs]] +deps = ["PrecompileTools"] +git-tree-sha1 = "a72c3b5ce6d2a477f55b5c9b8756e91e695c67f6" +uuid = "2569d6c7-a4a2-43d3-a901-331e8e4be471" +version = "0.2.8" + +[[deps.ConcurrentUtilities]] +deps = ["Serialization", "Sockets"] +git-tree-sha1 = "d5e5fa1f0d6ca517edbbcd734efa93c6639405b0" +uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" +version = "2.7.1" + +[[deps.ConstructionBase]] +git-tree-sha1 = "b4b092499347b18a015186eae3042f72267106cb" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.6.0" + + [deps.ConstructionBase.extensions] + ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseLinearAlgebraExt = "LinearAlgebra" + ConstructionBaseStaticArraysExt = "StaticArrays" + + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.Contour]] +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.6.3" + +[[deps.CoordinateConventions]] +git-tree-sha1 = "10832be51679db21271faac5bad7779a4320ca4b" +uuid = "7204ce3a-f536-43d2-be4a-fbed74e90d86" +version = "1.0.5" + +[[deps.CoreMath]] +deps = ["CoreMath_jll"] +git-tree-sha1 = "8c0480f92b1b1796239156a1b9b1bfb1b39499b4" +uuid = "b7a15901-be09-4a0e-87d2-2e66b0e09b5a" +version = "0.1.0" + +[[deps.CoreMath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a692a4c1dc59a4b8bc0b6403876eb3250fde2bc3" +uuid = "a38c48d9-6df1-5ac9-9223-b6ada3b5572b" +version = "0.1.0+0" + +[[deps.CpuId]] +deps = ["Markdown", "PrecompileTools"] +git-tree-sha1 = "5dbd5150b96f29553fc3b553c6bfb0eadf7ac204" +uuid = "adafc99b-e345-5852-983c-f28acb93d879" +version = "0.3.3" + +[[deps.Crayons]] +git-tree-sha1 = "54b76cbb40d9a0f5368c880725b2f141da77c94f" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.2.0" + +[[deps.DataAPI]] +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.16.0" + +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "5fab31e2e01e70ad66e3e24c968c264d1cf166d6" +uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +version = "1.8.2" + +[[deps.DataInterpolations]] +deps = ["EnumX", "FindFirstFunctions", "ForwardDiff", "LinearAlgebra", "PrettyTables", "RecipesBase", "Reexport"] +git-tree-sha1 = "4d50e83772222ce023efb7d30632198828ea56a6" +uuid = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" +version = "8.10.0" + + [deps.DataInterpolations.extensions] + DataInterpolationsChainRulesCoreExt = "ChainRulesCore" + DataInterpolationsMakieExt = "Makie" + DataInterpolationsMooncakeExt = ["Mooncake", "ChainRulesCore"] + DataInterpolationsOptimExt = "Optim" + DataInterpolationsRegularizationToolsExt = "RegularizationTools" + DataInterpolationsSparseConnectivityTracerExt = ["SparseConnectivityTracer", "FillArrays"] + DataInterpolationsSymbolicsExt = "Symbolics" + + [deps.DataInterpolations.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + Optim = "429524aa-4258-5aef-a3af-852621145aeb" + RegularizationTools = "29dad682-9a27-4bc3-9c72-016788665182" + SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.DataStructures]] +deps = ["OrderedCollections"] +git-tree-sha1 = "b0bc6d2cad1fed8b7fd59a1551a991cb3d2809e6" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.19.6" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" + +[[deps.Dbus_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "473e9afc9cf30814eb67ffa5f2db7df82c3ad9fd" +uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" +version = "1.16.2+0" + +[[deps.DelaunayTriangulation]] +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "4ac548adcad90c1d5d677af13568a748af4c952b" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "1.6.7" + +[[deps.DelimitedFiles]] +deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" + +[[deps.DiffEqBase]] +deps = ["ArrayInterface", "BracketingNonlinearSolve", "ConcreteStructs", "DocStringExtensions", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "9d333db14895e8c7d4857ed228eb1e72d3b302ec" +uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" +version = "6.218.0" + + [deps.DiffEqBase.extensions] + DiffEqBaseCUDAExt = "CUDA" + DiffEqBaseChainRulesCoreExt = "ChainRulesCore" + DiffEqBaseDynamicQuantitiesExt = "DynamicQuantities" + DiffEqBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] + DiffEqBaseFlexUnitsExt = "FlexUnits" + DiffEqBaseForwardDiffExt = ["ForwardDiff"] + DiffEqBaseGTPSAExt = "GTPSA" + DiffEqBaseGeneralizedGeneratedExt = "GeneralizedGenerated" + DiffEqBaseMPIExt = "MPI" + DiffEqBaseMeasurementsExt = "Measurements" + DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + DiffEqBaseMooncakeExt = "Mooncake" + DiffEqBaseReverseDiffExt = "ReverseDiff" + DiffEqBaseSparseArraysExt = "SparseArrays" + DiffEqBaseTrackerExt = "Tracker" + DiffEqBaseUnitfulExt = "Unitful" + + [deps.DiffEqBase.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + FlexUnits = "76e01b6b-c995-4ce6-8559-91e72a3d4e95" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + GTPSA = "b27dd330-f138-47c5-815b-40db9dd9b6e8" + GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.DiffEqCallbacks]] +deps = ["ConcreteStructs", "DataStructures", "DiffEqBase", "DifferentiationInterface", "LinearAlgebra", "Markdown", "PrecompileTools", "RecipesBase", "RecursiveArrayTools", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "88cdec45374d53393bf88268102a2b018c897178" +uuid = "459566f4-90b8-5000-8ac3-15dfb0a30def" +version = "4.19.2" + + [deps.DiffEqCallbacks.extensions] + DiffEqCallbacksFunctorsExt = "Functors" + + [deps.DiffEqCallbacks.weakdeps] + Functors = "d9f16b24-f501-4c13-a1f2-28368ffc5196" + +[[deps.DiffResults]] +deps = ["StaticArraysCore"] +git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" +uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" +version = "1.1.0" + +[[deps.DiffRules]] +deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] +git-tree-sha1 = "79a2aca180a85c690c58a020d47b426954b590f8" +uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" +version = "1.16.0" + +[[deps.DifferentiationInterface]] +deps = ["ADTypes", "LinearAlgebra"] +git-tree-sha1 = "0693d8b0a4608ff289d228ab4c598df5894845cd" +uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" +version = "0.7.21" + + [deps.DifferentiationInterface.extensions] + DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" + DifferentiationInterfaceDiffractorExt = "Diffractor" + DifferentiationInterfaceEnzymeExt = ["EnzymeCore", "Enzyme"] + DifferentiationInterfaceFastDifferentiationExt = "FastDifferentiation" + DifferentiationInterfaceFiniteDiffExt = "FiniteDiff" + DifferentiationInterfaceFiniteDifferencesExt = "FiniteDifferences" + DifferentiationInterfaceForwardDiffExt = ["ForwardDiff", "DiffResults"] + DifferentiationInterfaceGPUArraysCoreExt = ["GPUArraysCore", "Adapt"] + DifferentiationInterfaceGTPSAExt = "GTPSA" + DifferentiationInterfaceHyperHessiansExt = "HyperHessians" + DifferentiationInterfaceMooncakeExt = "Mooncake" + DifferentiationInterfacePolyesterForwardDiffExt = ["PolyesterForwardDiff", "ForwardDiff", "DiffResults"] + DifferentiationInterfaceReverseDiffExt = ["ReverseDiff", "DiffResults"] + DifferentiationInterfaceSparseArraysExt = "SparseArrays" + DifferentiationInterfaceSparseConnectivityTracerExt = "SparseConnectivityTracer" + DifferentiationInterfaceSparseMatrixColoringsExt = "SparseMatrixColorings" + DifferentiationInterfaceStaticArraysExt = "StaticArrays" + DifferentiationInterfaceSymbolicsExt = "Symbolics" + DifferentiationInterfaceTrackerExt = "Tracker" + DifferentiationInterfaceZygoteExt = ["Zygote", "ForwardDiff"] + + [deps.DifferentiationInterface.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" + Diffractor = "9f5e2b26-1114-432f-b630-d3fe2085c51c" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" + FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" + FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + GTPSA = "b27dd330-f138-47c5-815b-40db9dd9b6e8" + HyperHessians = "06b494a0-c8e0-40cc-ad32-d99506a00a6c" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +version = "1.11.0" + +[[deps.DocStringExtensions]] +git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.9.5" + +[[deps.Documenter]] +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] +git-tree-sha1 = "191e6bef0cf32cac3a3913cd4787d851715254c2" +uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +version = "1.19.0" + +[[deps.DoubleFloats]] +deps = ["GenericLinearAlgebra", "GenericSchur", "LinearAlgebra", "Printf", "Quadmath", "Random", "SpecialFunctions"] +git-tree-sha1 = "e9350afd366d2deaaa9b4ed13fd537e2b4dea6e5" +uuid = "497a8b3b-efae-58df-a0af-a86822472b78" +version = "1.11.2" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.7.0" + +[[deps.EnumX]] +git-tree-sha1 = "c49898e8438c828577f04b92fc9368c388ac783c" +uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" +version = "1.0.7" + +[[deps.EnzymeCore]] +git-tree-sha1 = "156ede9181d50b3b21b74d98a7edb08bc7bbdbf1" +uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" +version = "0.8.23" + + [deps.EnzymeCore.extensions] + AdaptExt = "Adapt" + EnzymeCoreChainRulesCoreExt = "ChainRulesCore" + + [deps.EnzymeCore.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.EpollShim_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8a4be429317c42cfae6a7fc03c31bad1970c310d" +uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" +version = "0.0.20230411+1" + +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArrays"] +git-tree-sha1 = "83231673ea4d3d6008ac74dc5079e77ab2209d8f" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.9" + +[[deps.ExceptionUnwrapping]] +deps = ["Test"] +git-tree-sha1 = "4e468f521e1f9f86891cb07186de5df90360a666" +uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" +version = "0.1.12" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "2bfb1e047e2ad0a5ca94365340bde8005d637568" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.8.4+0" + +[[deps.ExponentialUtilities]] +deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "PrecompileTools", "Printf", "SparseArrays", "libblastrampoline_jll"] +git-tree-sha1 = "a3c8b66f8813b721a45bfe3cf32acf793aed78bb" +uuid = "d4d017d3-3776-5f7e-afef-a10c40355c18" +version = "1.31.0" +weakdeps = ["StaticArrays"] + + [deps.ExponentialUtilities.extensions] + ExponentialUtilitiesStaticArraysExt = "StaticArrays" + +[[deps.ExprTools]] +git-tree-sha1 = "d2e49e7efd29719d6f28b891b0e0e159daa9d2b4" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.11" + +[[deps.ExproniconLite]] +git-tree-sha1 = "c13f0b150373771b0fdc1713c97860f8df12e6c2" +uuid = "55351af7-c7e9-48d6-89ff-24e801d99491" +version = "0.10.14" + +[[deps.FFMPEG]] +deps = ["FFMPEG_jll"] +git-tree-sha1 = "7bd13840b4148949e290748071bbe6826b3eabcd" +uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +version = "0.4.6" + +[[deps.FFMPEG_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libva_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "d9d3cd382f2c999f684e6bfa4039fcc16c153b6f" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "9.0.2+0" + +[[deps.FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "Libdl", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "97f08406df914023af55ade2f843c39e99c5d969" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.10.0" + +[[deps.FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6866aec60ef98e3164cd8d6855225684207e9dff" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.12+0" + +[[deps.FastBroadcast]] +deps = ["ArrayInterface", "LinearAlgebra", "PrecompileTools"] +git-tree-sha1 = "c8f8eefadaa330d982bbf5e395c34e53a13ab668" +uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" +version = "1.4.0" +weakdeps = ["Polyester", "Static"] + + [deps.FastBroadcast.extensions] + FastBroadcastPolyesterExt = "Polyester" + FastBroadcastStaticExt = "Static" + +[[deps.FastClosures]] +git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" +uuid = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" +version = "0.3.2" + +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "4916117dd032ec5959b7633aedbbac408ca5ddeb" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "1.3.0" + +[[deps.FastInterpolations]] +deps = ["AdaptiveArrayPools", "HelpPlots", "LinearAlgebra", "Preferences", "Printf"] +git-tree-sha1 = "f7bcd496f0a2e4e1174e7e1f667f27fb671fd7df" +uuid = "9ea80cae-fc13-4c00-8066-6eaedb12f34b" +version = "0.4.20" + + [deps.FastInterpolations.extensions] + FastInterpolationsChainRulesCoreExt = "ChainRulesCore" + FastInterpolationsColorVectorSpaceExt = ["ColorTypes", "ColorVectorSpace"] + FastInterpolationsEnzymeExt = "Enzyme" + FastInterpolationsForwardDiffExt = "ForwardDiff" + FastInterpolationsRecipesBaseExt = "RecipesBase" + FastInterpolationsSymbolicsExt = ["Symbolics", "SymbolicUtils"] + + [deps.FastInterpolations.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" + ColorVectorSpace = "c3611d14-8923-5661-9e6a-0046d554d3a4" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + +[[deps.FastPower]] +deps = ["PrecompileTools"] +git-tree-sha1 = "3c7269c236978d434a16ebe99f9743b9d706270a" +uuid = "a4df4552-cc26-4903-aec0-212e50a0e84b" +version = "1.5.0" + + [deps.FastPower.extensions] + FastPowerEnzymeExt = "Enzyme" + FastPowerForwardDiffExt = "ForwardDiff" + FastPowerMeasurementsExt = "Measurements" + FastPowerMonteCarloMeasurementsExt = "MonteCarloMeasurements" + FastPowerMooncakeExt = "Mooncake" + FastPowerReverseDiffExt = "ReverseDiff" + FastPowerTrackerExt = "Tracker" + + [deps.FastPower.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "6621fef488e496356c9c9625d0562c12a6070819" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.20.0" +weakdeps = ["HTTP"] + + [deps.FileIO.extensions] + HTTPExt = "HTTP" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" + +[[deps.FillArrays]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "417289ac840d201005d9ed16789a46defccbe36d" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.18.1" + + [deps.FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStaticArraysExt = "StaticArrays" + FillArraysStatisticsExt = "Statistics" + + [deps.FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.FindFirstFunctions]] +deps = ["PrecompileTools"] +git-tree-sha1 = "27b495de668ccea58de6b06d6d13181396598ea0" +uuid = "64ca27bc-2ba2-4a57-88aa-44e436879224" +version = "1.8.0" + +[[deps.FiniteDiff]] +deps = ["ArrayInterface", "LinearAlgebra", "Setfield"] +git-tree-sha1 = "5031f23e040bf17082e5b52422d77b5e844eefb1" +uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" +version = "2.33.0" + + [deps.FiniteDiff.extensions] + FiniteDiffBandedMatricesExt = "BandedMatrices" + FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" + FiniteDiffSparseArraysExt = "SparseArrays" + FiniteDiffStaticArraysExt = "StaticArrays" + + [deps.FiniteDiff.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.FixedPointNumbers]] +deps = ["Random", "Statistics"] +git-tree-sha1 = "59af96b98217c6ef4ae0dfe065ac7c20831d1a84" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.6" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "f85dac9a96a01087df6e3a749840015a0ca3817d" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.17.1+0" + +[[deps.Format]] +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" +uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" +version = "1.3.7" + +[[deps.ForwardDiff]] +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "3b0f72e2ffef1a139ac450725c9ecd01c0a3c050" +uuid = "f6369f11-7733-5829-9624-2563aa707210" +version = "1.4.6" +weakdeps = ["StaticArrays"] + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "70329abc09b886fd2c5d94ad2d9527639c421e3e" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.14.3+1" + +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "7a214fdac5ed5f59a22c2d9a885a16da1c74bbc7" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.17+0" + +[[deps.FunctionWrappers]] +git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" +uuid = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" +version = "1.1.3" + +[[deps.FunctionWrappersWrappers]] +deps = ["FunctionWrappers", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "2bcce3ad6f6977d617928d7707fdc86ac83cce03" +uuid = "77dc65aa-8811-40c2-897b-53d922fa7daf" +version = "1.13.0" + + [deps.FunctionWrappersWrappers.extensions] + FunctionWrappersWrappersEnzymeExt = ["Enzyme", "EnzymeCore"] + FunctionWrappersWrappersMooncakeExt = "Mooncake" + + [deps.FunctionWrappersWrappers.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + +[[deps.FunctionalCollections]] +deps = ["Test"] +git-tree-sha1 = "04cb9cfaa6ba5311973994fe3496ddec19b6292a" +uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" +version = "0.5.0" + +[[deps.Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" +version = "1.11.0" + +[[deps.GLFW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] +git-tree-sha1 = "64bbbb7d1499297751b536dd39c58b20750ab1db" +uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" +version = "3.5.1+0" + +[[deps.GPUArraysCore]] +deps = ["Adapt"] +git-tree-sha1 = "0b2b3e8b8fb7c1f5ddbf498be2eb2b3b37c9c0ed" +uuid = "46192b85-c4d5-4398-a991-12ede77f4527" +version = "0.2.1" + +[[deps.GR]] +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] +git-tree-sha1 = "4d777f73c46b46b8b5276206059cf8a195499314" +uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" +version = "0.73.27" + + [deps.GR.extensions] + IJuliaExt = "IJulia" + + [deps.GR.weakdeps] + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + +[[deps.GR_jll]] +deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "f8eb8f7ba13ea75083531647fc8faeda8d541f07" +uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" +version = "0.73.27+0" + +[[deps.GeneralizedPerturbedEquilibrium]] +deps = ["AdaptiveArrayPools", "CommonSolve", "Contour", "Dates", "DelaunayTriangulation", "DelimitedFiles", "DiffEqCallbacks", "Documenter", "DoubleFloats", "FFTW", "FastGaussQuadrature", "FastInterpolations", "HDF5", "IMASdd", "JLD2", "LaTeXStrings", "LinearAlgebra", "Logging", "OrdinaryDiffEq", "Pkg", "PlotlyJS", "Plots", "PrecompileTools", "Printf", "QuadGK", "Random", "Roots", "SparseArrays", "SpecialFunctions", "StaticArrays", "Statistics", "TOML", "Test"] +path = "." +uuid = "462872dd-e066-4d2e-b993-6468b5239634" +version = "0.1.0" + +[[deps.GenericLinearAlgebra]] +deps = ["LinearAlgebra", "Printf", "Random", "libblastrampoline_jll"] +git-tree-sha1 = "961f8a5d33f043e02cdf2b8020e3a7b0aae78474" +uuid = "14197337-ba66-59df-a3e3-ca00e7dcff7a" +version = "0.4.1" + +[[deps.GenericSchur]] +deps = ["LinearAlgebra", "MatrixFactorizations", "Preferences", "Printf", "UUIDs"] +git-tree-sha1 = "e3d1202ab9ae72edd4c46134ecbb6746ec420486" +uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" +version = "0.5.8" + +[[deps.GettextRuntime_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll"] +git-tree-sha1 = "45288942190db7c5f760f59c04495064eedf9340" +uuid = "b0724c58-0f36-5564-988d-3bb0596ebc4a" +version = "0.22.4+0" + +[[deps.Ghostscript_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Zlib_jll"] +git-tree-sha1 = "38044a04637976140074d0b0621c1edf0eb531fd" +uuid = "61579ee1-b43e-5ca0-a5da-69d92c66a64b" +version = "9.55.1+0" + +[[deps.Git]] +deps = ["Git_LFS_jll", "Git_jll", "JLLWrappers", "OpenSSH_jll"] +git-tree-sha1 = "824a1890086880696fc908fe12a17bcf61738bd8" +uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" +version = "1.5.0" + +[[deps.Git_LFS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8c66e385d631bb934ff05e76d4a566c640c8df69" +uuid = "020c3dae-16b3-5ae5-87b3-4cb189e250b2" +version = "3.7.1+0" + +[[deps.Git_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "3842d761162baf8efce819cdfb5ea070fe44ac40" +uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" +version = "2.56.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "GettextRuntime_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "090526e65de8f69648ac156daae153de8b56df62" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.88.3+0" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "69ffb934a5c5b7e086a0b4fee3427db2556fba6e" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.16+0" + +[[deps.HDF5]] +deps = ["Compat", "HDF5_jll", "Libdl", "MPIPreferences", "Mmap", "Preferences", "Printf", "Random", "Requires", "UUIDs"] +git-tree-sha1 = "26e37af34e8ccb7a8358bd8de1619c7bcb526738" +uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +version = "0.17.4" + + [deps.HDF5.extensions] + MPIExt = "MPI" + + [deps.HDF5.weakdeps] + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + +[[deps.HDF5_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "MPIABI_jll", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "aws_c_s3_jll", "dlfcn_win32_jll", "libaec_jll", "mpif_jll"] +git-tree-sha1 = "58d79d3a61d2fdeda667fead57546ae22a20b27a" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "2.2.3+0" + +[[deps.HTTP]] +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "51059d23c8bb67911a2e6fd5130229113735fc7e" +uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "1.11.0" + +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "9d9531a9cb63a9edc33836414e82a07e81710de2" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "100.14004.0+0" + +[[deps.HashArrayMappedTries]] +git-tree-sha1 = "2eaa69a7cab70a52b9687c8bf950a5a93ec895ae" +uuid = "076d061b-32b6-4027-95e0-9a2c6f6d7e74" +version = "0.2.0" + +[[deps.HelpPlots]] +git-tree-sha1 = "07d389d81ca31e42d68bd88cfa1e900a63b7d378" +uuid = "5880b2e8-ad0a-48c9-bacd-30dc9c1357e0" +version = "1.1.0" +weakdeps = ["Plots"] + + [deps.HelpPlots.extensions] + HelpPlotsExt = "Plots" + +[[deps.Hiccup]] +deps = ["MacroTools", "Test"] +git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" +uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" +version = "0.2.2" + +[[deps.Hwloc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XML2_jll", "Xorg_libpciaccess_jll"] +git-tree-sha1 = "cbbd99d516990d05c02aa132aa077b2bf50ae323" +uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +version = "2.15.0+0" + +[[deps.IMASdd]] +deps = ["AbstractTrees", "AdaptiveArrayPools", "CoordinateConventions", "DataInterpolations", "Dates", "FastInterpolations", "HDF5", "InteractiveUtils", "JSON", "Memoization", "OrderedCollections", "PrecompileTools", "Preferences", "Printf", "StaticArraysCore", "ThreadSafeDicts", "TimerOutputs"] +git-tree-sha1 = "2284c64e11d8fb6225971bcc367c67bae51923f5" +uuid = "c5a45a97-b3f9-491c-b9a7-aa88c3bc0067" +version = "8.6.2" + +[[deps.IOCapture]] +deps = ["Logging", "Random"] +git-tree-sha1 = "0ee181ec08df7d7c911901ea38baf16f755114dc" +uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" +version = "1.0.0" + +[[deps.IfElse]] +git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" +uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" +version = "0.1.1" + +[[deps.InlineStrings]] +git-tree-sha1 = "06b65886c7577a3784d616e29f1302c2e36e389d" +uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" +version = "1.4.6" + + [deps.InlineStrings.extensions] + ArrowTypesExt = "ArrowTypes" + ParsersExt = "Parsers" + + [deps.InlineStrings.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" + +[[deps.IntelOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "ec1debd61c300961f98064cfb21287613ad7f303" +uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" +version = "2025.2.0+0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +version = "1.11.0" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm", "CoreMath", "MacroTools", "OpenBLASConsistentFPCSR_jll", "Printf", "Random", "RoundingEmulator"] +git-tree-sha1 = "1c531bf0f8a5c60a340926e058fd3f209b5eef5d" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "1.0.12" + + [deps.IntervalArithmetic.extensions] + IntervalArithmeticArblibExt = "Arblib" + IntervalArithmeticDiffRulesExt = "DiffRules" + IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticIntervalSetsExt = "IntervalSets" + IntervalArithmeticIrrationalConstantsExt = "IrrationalConstants" + IntervalArithmeticLinearAlgebraExt = "LinearAlgebra" + IntervalArithmeticMakieExt = "Makie" + IntervalArithmeticRecipesBaseExt = "RecipesBase" + IntervalArithmeticSparseArraysExt = "SparseArrays" + + [deps.IntervalArithmetic.weakdeps] + Arblib = "fb37089c-8514-4489-9461-98f9c8763369" + DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.InverseFunctions]] +git-tree-sha1 = "a779299d77cd080bf77b97535acecd73e1c5e5cb" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.17" +weakdeps = ["Dates", "Test"] + + [deps.InverseFunctions.extensions] + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" + +[[deps.InvertedIndices]] +git-tree-sha1 = "6da3c4316095de0f5ee2ebd875df8721e7e0bdbe" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.3.1" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "b2d91fe939cae05960e760110b328288867b5758" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.6" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLD2]] +deps = ["ChunkCodecCore", "ChunkCodecLibZlib", "ChunkCodecLibZstd", "FileIO", "MacroTools", "Mmap", "OrderedCollections", "PrecompileTools", "ScopedValues"] +git-tree-sha1 = "9ce2e7c49ae4a7035b7d60db3a553b1f6c16875d" +uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +version = "0.6.7" +weakdeps = ["UnPack"] + + [deps.JLD2.extensions] + UnPackExt = "UnPack" + +[[deps.JLFzf]] +deps = ["REPL", "Random", "fzf_jll"] +git-tree-sha1 = "82f7acdc599b65e0f8ccd270ffa1467c21cb647b" +uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" +version = "0.1.11" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7204148362dafe5fe6a273f855b8ccbe4df8173e" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.8.0" + +[[deps.JSExpr]] +deps = ["JSON", "MacroTools", "Observables", "WebIO"] +git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" +uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" +version = "0.5.4" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[deps.Jieko]] +deps = ["ExproniconLite"] +git-tree-sha1 = "2f05ed29618da60c06a87e9c033982d4f71d0b6c" +uuid = "ae98c720-c025-4a4a-838c-29b094483192" +version = "0.2.1" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "037babc10853eeb8e585418922246cb97b8e5b74" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.2.0+1" + +[[deps.JuliaSyntaxHighlighting]] +deps = ["StyledStrings"] +uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" +version = "1.12.0" + +[[deps.Kaleido_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" +uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" +version = "0.2.1+0" + +[[deps.Krylov]] +deps = ["LinearAlgebra", "Printf", "SparseArrays"] +git-tree-sha1 = "e0574e7e2cd5c40d9a6ae444511fbadd51afbf30" +uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +version = "0.10.11" + +[[deps.LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "059aabebaa7c82ccb853dd4a0ee9d17796f7e1bc" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.3+0" + +[[deps.LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "39bca05343661c347aae0bca57a5994a0bf4f08d" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "4.2.0+0" + +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e5b100780d4d30d63b4618d7930d48af409c1772" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "23.1.1+0" + +[[deps.LaTeXStrings]] +git-tree-sha1 = "f88f3ccef05a6a72a0cf0ed417c8fd68530f4ab2" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.4.1" + +[[deps.Latexify]] +deps = ["Format", "Ghostscript_jll", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] +git-tree-sha1 = "df7566479bd64f20bd16b09960145e70160ffb3b" +uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" +version = "0.16.12" + + [deps.Latexify.extensions] + DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" + SymEngineExt = "SymEngine" + TectonicExt = "tectonic_jll" + + [deps.Latexify.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" + tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" + +[[deps.LayoutPointers]] +deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] +git-tree-sha1 = "a9eaadb366f5493a5654e843864c13d8b107548c" +uuid = "10f19ff3-798f-405d-979b-55457f8fc047" +version = "0.1.17" + +[[deps.LazilyInitializedFields]] +git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" +uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" +version = "1.3.0" + +[[deps.Lazy]] +deps = ["MacroTools"] +git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" +uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" +version = "0.15.1" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +version = "1.11.0" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "0.6.4" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll", "Zlib_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.15.0+0" + +[[deps.LibGit2]] +deps = ["LibGit2_jll", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "LibSSH2_jll", "Libdl", "OpenSSL_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.9.0+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "Libdl", "OpenSSL_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.3+1" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.4.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "d36c21b9e7c172a44a10484125024495e2625ac0" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.7.1+1" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.18.0+0" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e2b48382276415b5a5d5282a5dd75a0f0bf4ac84" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.42.2+0" + +[[deps.Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "aebd334d06cee9f24cea70bd19a39749daf73881" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.7.3+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d620582b1f0cbe2c72dd1d5bd195a9ce73370ab1" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.42.0+0" + +[[deps.LineSearch]] +deps = ["ADTypes", "CommonSolve", "ConcreteStructs", "FastClosures", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "SciMLBase", "SciMLJacobianOperators", "StaticArraysCore"] +git-tree-sha1 = "2e05027f5a68891d997fcad60d11ce48d09208d0" +uuid = "87fe0de2-c867-4266-b59a-2f0a94fc965b" +version = "0.1.14" +weakdeps = ["LineSearches"] + + [deps.LineSearch.extensions] + LineSearchLineSearchesExt = "LineSearches" + +[[deps.LineSearches]] +deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Printf"] +git-tree-sha1 = "a09a85e94e681def2446752cddaf9401a4d82b6d" +uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" +version = "7.8.2" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +version = "1.12.0" + +[[deps.LinearSolve]] +deps = ["AMD", "ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "GPUArraysCore", "InteractiveUtils", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "OpenBLAS_jll", "PrecompileTools", "Preferences", "PureKLU", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "Setfield", "SparseArrays", "SparseColumnPivotedQR", "StaticArraysCore"] +git-tree-sha1 = "ec49ed72f6024be2f9833fe630fbd72f6048f8ad" +uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" +version = "3.87.0" + + [deps.LinearSolve.extensions] + LinearSolveAMDGPUExt = "AMDGPU" + LinearSolveAlgebraicMultigridExt = "AlgebraicMultigrid" + LinearSolveBLISExt = ["blis_jll", "LAPACK_jll"] + LinearSolveBandedMatricesExt = "BandedMatrices" + LinearSolveBlockDiagonalsExt = "BlockDiagonals" + LinearSolveCUDAExt = ["cuSOLVER"] + LinearSolveCUDSSExt = "CUDSS" + LinearSolveCUSOLVERRFExt = ["CUSOLVERRF", "SparseArrays"] + LinearSolveChainRulesCoreExt = "ChainRulesCore" + LinearSolveCliqueTreesExt = ["CliqueTrees", "SparseArrays"] + LinearSolveElementalExt = "Elemental" + LinearSolveEnzymeExt = ["EnzymeCore", "SparseArrays"] + LinearSolveFastAlmostBandedMatricesExt = "FastAlmostBandedMatrices" + LinearSolveFastLapackInterfaceExt = "FastLapackInterface" + LinearSolveForwardDiffExt = "ForwardDiff" + LinearSolveGinkgoExt = ["Ginkgo", "SparseArrays"] + LinearSolveHSLExt = ["HSL", "SparseArrays"] + LinearSolveHYPREExt = "HYPRE" + LinearSolveIterativeSolversExt = "IterativeSolvers" + LinearSolveKernelAbstractionsExt = "KernelAbstractions" + LinearSolveKrylovKitExt = "KrylovKit" + LinearSolveMUMPSExt = ["MUMPS", "SparseArrays"] + LinearSolveMetalExt = "Metal" + LinearSolveMooncakeExt = "Mooncake" + LinearSolvePETScExt = ["PETSc", "SparseArrays", "SparseMatricesCSR"] + LinearSolvePETScMPIExt = ["PETSc", "PartitionedArrays", "SparseArrays", "SparseMatricesCSR"] + LinearSolveParUExt = ["ParU_jll", "SparseArrays"] + LinearSolvePardisoExt = ["Pardiso", "SparseArrays"] + LinearSolvePartitionedSolversExt = ["PartitionedArrays", "PartitionedSolvers"] + LinearSolvePureUMFPACKExt = ["PureUMFPACK", "SparseArrays"] + LinearSolveRecursiveFactorizationExt = "RecursiveFactorization" + LinearSolveSTRUMPACKExt = ["SparseArrays", "STRUMPACK_jll"] + LinearSolveSparseArraysExt = "SparseArrays" + LinearSolveSparspakExt = ["SparseArrays", "Sparspak"] + LinearSolveSpecializingFactorizationsExt = "SpecializingFactorizations" + LinearSolveSuperLUDISTExt = ["SparseArrays", "SuperLUDIST"] + + [deps.LinearSolve.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + CUSOLVERRF = "a8cc9031-bad2-4722-94f5-40deabb4245c" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" + Elemental = "902c3f28-d1ec-5e7e-8399-a24c3845ee38" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + FastAlmostBandedMatrices = "9d29842c-ecb8-4973-b1e9-a27b1157504e" + FastLapackInterface = "29a986be-02c6-4525-aec4-84b980013641" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Ginkgo = "4c8bd3c9-ead9-4b5e-a625-08f1338ba0ec" + HSL = "34c5aeac-e683-54a6-a0e9-6e0fdc586c50" + HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" + IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" + LAPACK_jll = "51474c39-65e3-53ba-86ba-03b1b862ec14" + MUMPS = "55d2b088-9f4e-11e9-26c0-150b02ea6a46" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" + ParU_jll = "9e0b026c-e8ce-559c-a2c4-6a3d5c955bc9" + Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" + PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9" + PartitionedSolvers = "11b65f7f-80ac-401b-9ef2-3db765482d62" + PureUMFPACK = "b7e1f0a2-3c4d-4e5f-9a0b-1c2d3e4f5a6b" + RecursiveFactorization = "f2c3362d-daeb-58d1-803e-2bc74f2840b4" + STRUMPACK_jll = "86fbd0b9-476f-557c-b766-62c724b42d8c" + SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1" + Sparspak = "e56a9233-b9d6-4f03-8d0f-1825330902ac" + SpecializingFactorizations = "fa08b7a1-13d3-4faf-875d-5cbc1520e3f3" + SuperLUDIST = "4cd002a6-0da4-410d-a012-232df062f478" + blis_jll = "6136c539-28a5-5bf0-87cc-b183200dce32" + cuSOLVER = "887afef0-6a32-4de5-add4-7827692ba8fc" + +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "28d620bcdb37a3fb02de9f521928ee453690f414" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "1.0.3" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" + +[[deps.LoggingExtras]] +deps = ["Dates", "Logging"] +git-tree-sha1 = "f00544d95982ea270145636c181ceda21c4e2575" +uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" +version = "1.2.0" + +[[deps.MKL_jll]] +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "282cadc186e7b2ae0eeadbd7a4dffed4196ae2aa" +uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" +version = "2025.2.0+0" + +[[deps.MPIABI_jll]] +deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "42e2cada9f35500b4d227106ce2b60e85d9f15bc" +uuid = "b5ada748-db0f-5fc0-8972-9331c762740c" +version = "1.0.1+0" + +[[deps.MPICH_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "08cb47f95a2f1b795e43a9e4f9783e68ecdc1ca6" +uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" +version = "5.0.3+0" + +[[deps.MPIPreferences]] +deps = ["Libdl", "Preferences"] +git-tree-sha1 = "8e98d5d80b87403c311fd51e8455d4546ba7a5f8" +uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" +version = "0.1.12" + +[[deps.MPItrampoline_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "09a99227205419c00e734f413ee98c18bc1b02e4" +uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" +version = "5.5.7+0" + +[[deps.MacroTools]] +git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.16" + +[[deps.ManualMemory]] +git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" +uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" +version = "0.1.8" + +[[deps.Markdown]] +deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" + +[[deps.MarkdownAST]] +deps = ["AbstractTrees", "Markdown"] +git-tree-sha1 = "93c718d892e73931841089cdc0e982d6dd9cc87b" +uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" +version = "0.1.3" + +[[deps.MatrixFactorizations]] +deps = ["ArrayLayouts", "LinearAlgebra", "Printf", "Random"] +git-tree-sha1 = "076d0f90b1b06e17609cabb48bd2cbef28e7fc77" +uuid = "a3b82374-2e81-5b9e-98ce-41277c0e4c87" +version = "3.2.0" + + [deps.MatrixFactorizations.extensions] + MatrixFactorizationsBandedMatricesExt = "BandedMatrices" + + [deps.MatrixFactorizations.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + +[[deps.MaybeInplace]] +deps = ["ArrayInterface", "LinearAlgebra", "MacroTools", "PrecompileTools"] +git-tree-sha1 = "f2cde0ae772162f20287b803e623966d0d94dea9" +uuid = "bb5d69b7-63fc-4a16-80bd-7e42200c7bdb" +version = "0.1.8" +weakdeps = ["SparseArrays"] + + [deps.MaybeInplace.extensions] + MaybeInplaceSparseArraysExt = "SparseArrays" + +[[deps.MbedTLS]] +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] +git-tree-sha1 = "8785729fa736197687541f7053f6d8ab7fc44f92" +uuid = "739be429-bea8-5141-9913-cc70e7f3736d" +version = "1.1.10" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "ff69a2b1330bcb730b9ac1ab7dd680176f5896b8" +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.1010+0" + +[[deps.Measures]] +git-tree-sha1 = "b513cedd20d9c914783d8ad83d08120702bf2c77" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.3" + +[[deps.Memoization]] +deps = ["MacroTools"] +git-tree-sha1 = "7dbf904fa6c4447bd1f1d316886bfbe29feacf45" +uuid = "6fafb56a-5788-4b4e-91ca-c0cea6611c73" +version = "0.2.2" + +[[deps.MicrosoftMPI_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227" +uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" +version = "10.1.4+3" + +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.2.0" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" +version = "1.11.0" + +[[deps.Moshi]] +deps = ["ExproniconLite", "Jieko"] +git-tree-sha1 = "3f234cf937eb1d58f613440ecb022da784ad34d4" +uuid = "2e0e35c7-a2e4-4343-998d-7ef72827ed2d" +version = "0.3.13" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2025.11.4" + +[[deps.MuladdMacro]] +deps = ["PrecompileTools"] +git-tree-sha1 = "283bf85d4a767481dd924dff0eee1735e95f449e" +uuid = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +version = "0.2.7" + +[[deps.Mustache]] +deps = ["DataFrames", "Printf", "Tables"] +git-tree-sha1 = "7437c911e62f50e7a043a774ebdc0dbd780d9fe5" +uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" +version = "1.1.0" + +[[deps.Mux]] +deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "MbedTLS", "Pkg", "Sockets"] +git-tree-sha1 = "7295d849103ac4fcbe3b2e439f229c5cc77b9b69" +uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" +version = "1.0.2" + +[[deps.NLSolversBase]] +deps = ["ADTypes", "DifferentiationInterface", "FiniteDiff", "LinearAlgebra"] +git-tree-sha1 = "f96d38936d92d610318dec4d3b5ef37b0373c20f" +uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" +version = "8.0.1" + +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "dbd2e8cd2c1c27f0b584f6661b4309609c5a685e" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "1.1.4" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.3.0" + +[[deps.NonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "NonlinearSolveBase", "NonlinearSolveFirstOrder", "NonlinearSolveQuasiNewton", "NonlinearSolveSpectralMethods", "PrecompileTools", "Preferences", "Reexport", "SciMLBase", "Setfield", "SimpleNonlinearSolve", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "a6c5719bbb42985c72f4cacbfa49e86bab850d66" +uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" +version = "4.19.1" + + [deps.NonlinearSolve.extensions] + NonlinearSolveFastLevenbergMarquardtExt = "FastLevenbergMarquardt" + NonlinearSolveFixedPointAccelerationExt = "FixedPointAcceleration" + NonlinearSolveLeastSquaresOptimExt = "LeastSquaresOptim" + NonlinearSolveMINPACKExt = "MINPACK" + NonlinearSolveNLSolversExt = "NLSolvers" + NonlinearSolveNLsolveExt = ["NLsolve", "LineSearches"] + NonlinearSolvePETScExt = ["PETSc", "MPI", "SparseArrays"] + NonlinearSolveSIAMFANLEquationsExt = "SIAMFANLEquations" + NonlinearSolveSpeedMappingExt = "SpeedMapping" + NonlinearSolveSundialsExt = "Sundials" + + [deps.NonlinearSolve.weakdeps] + FastLevenbergMarquardt = "7a0df574-e128-4d35-8cbd-3d84502bf7ce" + FixedPointAcceleration = "817d07cb-a79a-5c30-9a31-890123675176" + LeastSquaresOptim = "0fc2ff8b-aaa3-5acd-a817-1944a5e08891" + LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" + MINPACK = "4854310b-de5a-5eb6-a2a5-c1dee2bd17f9" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + NLSolvers = "337daf1e-9722-11e9-073e-8b9effe078ba" + NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" + SIAMFANLEquations = "084e46ad-d928-497d-ad5e-07fa361a48c4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SpeedMapping = "f1835b91-879b-4a3f-a438-e4baacf14412" + Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" + +[[deps.NonlinearSolveBase]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "Compat", "ConcreteStructs", "DifferentiationInterface", "EnzymeCore", "FastClosures", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "LogExpFunctions", "Markdown", "MaybeInplace", "PreallocationTools", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "SciMLBase", "SciMLJacobianOperators", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Setfield", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] +git-tree-sha1 = "cb824030a0d5769555704db95ee3cbe0f76ed53d" +uuid = "be0214bd-f91f-a760-ac4e-3421ce2b2da0" +version = "2.30.3" + + [deps.NonlinearSolveBase.extensions] + NonlinearSolveBaseBandedMatricesExt = "BandedMatrices" + NonlinearSolveBaseChainRulesCoreExt = "ChainRulesCore" + NonlinearSolveBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] + NonlinearSolveBaseForwardDiffExt = "ForwardDiff" + NonlinearSolveBaseLineSearchExt = "LineSearch" + NonlinearSolveBaseLinearSolveExt = "LinearSolve" + NonlinearSolveBaseMooncakeExt = "Mooncake" + NonlinearSolveBaseReverseDiffExt = "ReverseDiff" + NonlinearSolveBaseSparseArraysExt = "SparseArrays" + NonlinearSolveBaseSparseMatrixColoringsExt = "SparseMatrixColorings" + NonlinearSolveBaseTrackerExt = "Tracker" + + [deps.NonlinearSolveBase.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + LineSearch = "87fe0de2-c867-4266-b59a-2f0a94fc965b" + LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.NonlinearSolveFirstOrder]] +deps = ["ADTypes", "ArrayInterface", "CommonSolve", "ConcreteStructs", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLJacobianOperators", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "ce68820a4f421fb5bee7ec4dcf875aff33886bfb" +uuid = "5959db7a-ea39-4486-b5fe-2dd0bf03d60d" +version = "2.1.1" + +[[deps.NonlinearSolveQuasiNewton]] +deps = ["ArrayInterface", "CommonSolve", "ConcreteStructs", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLOperators", "StaticArraysCore"] +git-tree-sha1 = "538432ca1aea8bf63db02929bf870501f8a7c64c" +uuid = "9a2c21bd-3a47-402d-9113-8faf9a0ee114" +version = "1.13.1" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveQuasiNewton.extensions] + NonlinearSolveQuasiNewtonForwardDiffExt = "ForwardDiff" + +[[deps.NonlinearSolveSpectralMethods]] +deps = ["CommonSolve", "ConcreteStructs", "LineSearch", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "a3781e12becdf0ce5520bd97ec617e879bf4e9f2" +uuid = "26075421-4e9a-44e1-8bd1-420ed7ad02b2" +version = "1.7.1" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveSpectralMethods.extensions] + NonlinearSolveSpectralMethodsForwardDiffExt = "ForwardDiff" + +[[deps.Observables]] +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.5" + +[[deps.Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b6aa4566bb7ae78498a5e68943863fa8b5231b59" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.6+0" + +[[deps.OpenBLASConsistentFPCSR_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "38a93f17e431141c6470bb67a88952a7c4f0e928" +uuid = "6cdc7f73-28fd-5e50-80fb-958a8875b1af" +version = "0.3.34+0" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.29+0" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.7+0" + +[[deps.OpenMPI_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] +git-tree-sha1 = "0dba69ef957d790d41484ca7c20212c5ac806738" +uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" +version = "5.0.14+0" + +[[deps.OpenSSH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] +git-tree-sha1 = "1f640b82a627df3dbbaaff756a201a9ceb10dd1f" +uuid = "9bd350c2-7e96-507f-8002-3f2e150b4e1b" +version = "10.6.1+0" + +[[deps.OpenSSL]] +deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "NetworkOptions", "OpenSSL_jll", "Sockets"] +git-tree-sha1 = "5caab1056e0734b87f62ae3a3b520d47d3e994e8" +uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" +version = "1.7.0" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "3.5.6+0" + +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.6+0" + +[[deps.Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e2bb57a313a74b8104064b7efd01406c0a50d2ff" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.6.1+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "94ba93778373a53bfd5a0caaf7d809c445292ff4" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.8.2" + +[[deps.OrdinaryDiffEq]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqAdamsBashforthMoulton", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqExplicitRK", "OrdinaryDiffEqExponentialRK", "OrdinaryDiffEqExtrapolation", "OrdinaryDiffEqFIRK", "OrdinaryDiffEqFeagin", "OrdinaryDiffEqFunctionMap", "OrdinaryDiffEqHighOrderRK", "OrdinaryDiffEqIMEXMultistep", "OrdinaryDiffEqLinear", "OrdinaryDiffEqLowOrderRK", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqNordsieck", "OrdinaryDiffEqPDIRK", "OrdinaryDiffEqPRK", "OrdinaryDiffEqQPRK", "OrdinaryDiffEqRKN", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqStabilizedIRK", "OrdinaryDiffEqStabilizedRK", "OrdinaryDiffEqSymplecticRK", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SparseArrays", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "47271adac597af08263b6ea2669e93040b45c4d0" +uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +version = "6.111.0" + +[[deps.OrdinaryDiffEqAdamsBashforthMoulton]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "2e44acb684dfcdc2e41851a988733e30b28a8478" +uuid = "89bda076-bce5-4f1c-845f-551c83cdda9a" +version = "1.11.0" + +[[deps.OrdinaryDiffEqBDF]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "d7f69947e070a6a89aaf583e8aa2fada199fc292" +uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" +version = "1.26.0" + +[[deps.OrdinaryDiffEqCore]] +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "ConcreteStructs", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "Random", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Static", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "c0a22f012d3e98fd78afafe8e5152ace7a2276bc" +uuid = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" +version = "3.33.1" + + [deps.OrdinaryDiffEqCore.extensions] + OrdinaryDiffEqCoreMooncakeExt = "Mooncake" + OrdinaryDiffEqCoreSparseArraysExt = "SparseArrays" + + [deps.OrdinaryDiffEqCore.weakdeps] + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.OrdinaryDiffEqDefault]] +deps = ["ADTypes", "DiffEqBase", "EnumX", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "PrecompileTools", "Preferences", "Reexport", "SciMLBase"] +git-tree-sha1 = "5fbd116f93790ce79c4beff8206e1b8e45c492a2" +uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" +version = "1.14.0" + +[[deps.OrdinaryDiffEqDifferentiation]] +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SciMLOperators", "SparseMatrixColorings", "StaticArraysCore"] +git-tree-sha1 = "f3976b6baeb64051f41ed461a1933b7501a4f38e" +uuid = "4302a76b-040a-498a-8c04-15b101fed76b" +version = "2.9.0" +weakdeps = ["SparseArrays"] + + [deps.OrdinaryDiffEqDifferentiation.extensions] + OrdinaryDiffEqDifferentiationSparseArraysExt = "SparseArrays" + +[[deps.OrdinaryDiffEqExplicitRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "54346c3d7a9932ab11743a99afed8a3ff0a65fea" +uuid = "9286f039-9fbf-40e8-bf65-aa933bdc4db0" +version = "1.12.0" + +[[deps.OrdinaryDiffEqExponentialRK]] +deps = ["ADTypes", "DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "32fb54c8000c82fd32c55f7bf52f4651a3bd0e61" +uuid = "e0540318-69ee-4070-8777-9e2de6de23de" +version = "1.15.0" + +[[deps.OrdinaryDiffEqExtrapolation]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastPower", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "57bf89f5b144f6e5af1146fb7aee1544c6b76950" +uuid = "becaefa8-8ca2-5cf9-886d-c06f3d2bd2c4" +version = "1.18.0" + +[[deps.OrdinaryDiffEqFIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "3339f3a26974a37666c74b95bdb54aefc63b9a56" +uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" +version = "1.26.0" + +[[deps.OrdinaryDiffEqFeagin]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "302999c99bc454cf274d39b9ba3c2415f6fbb1cb" +uuid = "101fe9f7-ebb6-4678-b671-3a81e7194747" +version = "1.10.0" + +[[deps.OrdinaryDiffEqFunctionMap]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "2d2d59f9e530bca2f99934ecfde5430f6ad54bfe" +uuid = "d3585ca7-f5d3-4ba6-8057-292ed1abd90f" +version = "1.11.0" + +[[deps.OrdinaryDiffEqHighOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "a6fef72198944e64e0d818f2cc46275e663370c9" +uuid = "d28bc4f8-55e1-4f49-af69-84c1a99f0f58" +version = "1.12.0" + +[[deps.OrdinaryDiffEqIMEXMultistep]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport", "SciMLBase"] +git-tree-sha1 = "bd5440f7f09ce75df59e7f5d2f5b8e52d24aae3c" +uuid = "9f002381-b378-40b7-97a6-27a27c83f129" +version = "1.14.0" + +[[deps.OrdinaryDiffEqLinear]] +deps = ["DiffEqBase", "ExponentialUtilities", "LinearAlgebra", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "a1217ddfc979da3a2d9cf11b083688562e8ef333" +uuid = "521117fe-8c41-49f8-b3b6-30780b3f0fb5" +version = "1.12.0" + +[[deps.OrdinaryDiffEqLowOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "68a5e7e1b37933841e823a588deff3ec3e757a00" +uuid = "1344f307-1e59-4825-a18e-ace9aa3fa4c6" +version = "1.13.0" + +[[deps.OrdinaryDiffEqLowStorageRK]] +deps = ["Adapt", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "076ac77cee882b2f4858292dde748c8b2ab908e7" +uuid = "b0944070-b475-4768-8dec-fb6eb410534d" +version = "1.15.0" + +[[deps.OrdinaryDiffEqNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FastClosures", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "PreallocationTools", "RecursiveArrayTools", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SparseArrays", "StaticArraysCore"] +git-tree-sha1 = "82dde00dc43d5a4762cf50fa070b654cc38a2e87" +uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" +version = "1.28.0" + +[[deps.OrdinaryDiffEqNordsieck]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "21117475cb0d27942d31e4b8a27337392ae680b0" +uuid = "c9986a66-5c92-4813-8696-a7ec84c806c8" +version = "1.11.0" + +[[deps.OrdinaryDiffEqPDIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "SciMLBase"] +git-tree-sha1 = "1aaaa4851bbe72497c9d01d69fa36396ddeb0a0a" +uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" +version = "1.14.0" + +[[deps.OrdinaryDiffEqPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport", "SciMLBase"] +git-tree-sha1 = "1fe4f58f170c68b187ac42dc5f00932bb162ddfa" +uuid = "5b33eab2-c0f1-4480-b2c3-94bc1e80bda1" +version = "1.10.0" + +[[deps.OrdinaryDiffEqQPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "275900d64ab7be5f1bca8c7bfd48166838b6ab44" +uuid = "04162be5-8125-4266-98ed-640baecc6514" +version = "1.10.0" + +[[deps.OrdinaryDiffEqRKN]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "edf3411246d6030e165a5fba513cbdcde5c72f79" +uuid = "af6ede74-add8-4cfd-b1df-9a4dbb109d7a" +version = "1.12.0" + +[[deps.OrdinaryDiffEqRosenbrock]] +deps = ["ADTypes", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "3579d9272c286ffcceee794c5752e8cc7c3ae514" +uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" +version = "1.31.1" + +[[deps.OrdinaryDiffEqSDIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "c5e26fbadbad137fa2b7e3161a290a1becfec502" +uuid = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" +version = "1.14.0" + +[[deps.OrdinaryDiffEqSSPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "4ec0be19f8115889f61643fc25f6e3af5cd4c9e7" +uuid = "669c94d9-1f4b-4b64-b377-1aa079aa2388" +version = "1.14.0" + +[[deps.OrdinaryDiffEqStabilizedIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqStabilizedRK", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "226226d334fe295885df6ecb2d110c22ead79983" +uuid = "e3e12d00-db14-5390-b879-ac3dd2ef6296" +version = "1.14.0" + +[[deps.OrdinaryDiffEqStabilizedRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "764e11fb6a26ee8f2d2d10778d531fd708f35d28" +uuid = "358294b1-0aab-51c3-aafe-ad5ab194a2ad" +version = "1.11.1" + +[[deps.OrdinaryDiffEqSymplecticRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "b18a4e7973e73e84cfd79222e2389565b323b588" +uuid = "fa646aed-7ef9-47eb-84c4-9443fc8cbfa8" +version = "1.13.0" + +[[deps.OrdinaryDiffEqTsit5]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "6bd89fce8a0b198ac9fe98dc80362a92a2a13ae5" +uuid = "b1df2697-797e-41e3-8120-5422d3b24e4a" +version = "1.12.0" + +[[deps.OrdinaryDiffEqVerner]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "58dc7756825ca46e5a601a5aeba7d26a071d373a" +uuid = "79d7bb75-1356-48c1-b8c0-6832512096c2" +version = "1.14.0" + +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.44.0+1" + +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1912a9f1b9ca55005b03ba075f8e19993583e237" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.58.2+0" + +[[deps.Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "ba0dc8a8a67cacac4842631f960c046e4e563675" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.8" + +[[deps.Pidfile]] +deps = ["FileWatching", "Test"] +git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" +uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" +version = "1.3.0" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "e4a6721aa89e62e5d4217c0b21bd714263779dda" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.46.4+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.12.1" +weakdeps = ["REPL"] + + [deps.Pkg.extensions] + REPLExt = "REPL" + +[[deps.PlotThemes]] +deps = ["PlotUtils", "Statistics"] +git-tree-sha1 = "41031ef3a1be6f5bbbf3e8073f210556daeae5ca" +uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" +version = "3.3.0" + +[[deps.PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Reexport", "Statistics"] +git-tree-sha1 = "ddcc36fd83fb4cd459a53c891981697bde0d3de6" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.5.1" + +[[deps.PlotlyBase]] +deps = ["ColorSchemes", "Colors", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "6256ab3ee24ef079b3afa310593817e069925eeb" +uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +version = "0.8.23" + + [deps.PlotlyBase.extensions] + DataFramesExt = "DataFrames" + DistributionsExt = "Distributions" + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyBase.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyJS]] +deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "PlotlyKaleido", "REPL", "Reexport", "Requires", "WebIO"] +git-tree-sha1 = "4bf6c08295346efba58bb2d5f8c52984efed7f27" +uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" +version = "0.18.18" + + [deps.PlotlyJS.extensions] + CSVExt = "CSV" + DataFramesExt = ["DataFrames", "CSV"] + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyJS.weakdeps] + CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyKaleido]] +deps = ["Artifacts", "Base64", "JSON", "Kaleido_jll"] +git-tree-sha1 = "c7a270d11881c0709052f8e3def3d60d551e99f3" +uuid = "f2990250-8cf9-495f-b13a-cce12b45703c" +version = "2.3.1" + +[[deps.Plots]] +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "Unzip"] +git-tree-sha1 = "83bd514e8ff16b5858ac54c53fa0bcf6002a3b00" +uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +version = "1.41.7" + + [deps.Plots.extensions] + FileIOExt = "FileIO" + GeometryBasicsExt = "GeometryBasics" + IJuliaExt = "IJulia" + ImageInTerminalExt = "ImageInTerminal" + UnitfulExt = "Unitful" + + [deps.Plots.weakdeps] + FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" + GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.Polyester]] +deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] +git-tree-sha1 = "16bbc30b5ebea91e9ce1671adc03de2832cff552" +uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" +version = "0.7.19" + +[[deps.PolyesterWeave]] +deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] +git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" +uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" +version = "0.2.2" + +[[deps.PooledArrays]] +deps = ["DataAPI", "Future"] +git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" +uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" +version = "1.4.3" + +[[deps.PreallocationTools]] +deps = ["Adapt", "ArrayInterface", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "cd00e28071a75c98664663f87c2ae447d25c0503" +uuid = "d236fae5-4411-538c-8e31-a6e3d9e00b46" +version = "1.7.1" + + [deps.PreallocationTools.extensions] + PreallocationToolsEnzymeCoreExt = "EnzymeCore" + PreallocationToolsForwardDiffExt = "ForwardDiff" + PreallocationToolsReverseDiffExt = "ReverseDiff" + + [deps.PreallocationTools.weakdeps] + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "edbeefc7a4889f528644251bdb5fc9ab5348bc2c" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.3.4" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "5005266de4bfe50e53ff44a5cb5c540b6e47a254" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.6.0" + +[[deps.PrettyTables]] +deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "StyledStrings", "Tables"] +git-tree-sha1 = "99fd8f32ab6728ff205cddd24e2d9ea6cbb498d3" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "3.5.0" + + [deps.PrettyTables.extensions] + PrettyTablesExcelExt = "XLSX" + PrettyTablesTypstryExt = "Typstry" + PrettyTablesWriteDocxExt = "WriteDocx" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" + WriteDocx = "d049ceea-54ee-41d7-a26f-ba29db3b6599" + XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" + +[[deps.PtrArrays]] +git-tree-sha1 = "4fbbafbc6251b883f4d2705356f3641f3652a7fe" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.4.0" + +[[deps.PureKLU]] +deps = ["LinearAlgebra", "PrecompileTools", "SparseArrays"] +git-tree-sha1 = "ad0e84647fe7b3c2ae02aaeace1522139aaf6a27" +uuid = "0c0d3e7f-3a8b-4f7e-b6f1-9a4d2e7c1f01" +version = "1.6.0" +weakdeps = ["ForwardDiff"] + + [deps.PureKLU.extensions] + PureKLUForwardDiffExt = "ForwardDiff" + +[[deps.Qt6Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] +git-tree-sha1 = "144895f6166994730ee7ff8113b981fc360638f1" +uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" +version = "6.10.2+2" + +[[deps.Qt6Declarative_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll", "Qt6Svg_jll"] +git-tree-sha1 = "159d253ab126d5b29230cf53521899bea4ef4648" +uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" +version = "6.10.2+2" + +[[deps.Qt6ShaderTools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "4d85eedf69d875982c46643f6b4f66919d7e157b" +uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" +version = "6.10.2+1" + +[[deps.Qt6Svg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "81587ff5ff25a4e1115ce191e36285ede0334c9d" +uuid = "6de9746b-f93d-5813-b365-ba18ad4a9cf3" +version = "6.10.2+0" + +[[deps.Qt6Wayland_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] +git-tree-sha1 = "672c938b4b4e3e0169a07a5f227029d4905456f2" +uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" +version = "6.10.2+1" + +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "5e8e8b0ab68215d7a2b14b9921a946fee794749e" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.11.3" + + [deps.QuadGK.extensions] + QuadGKEnzymeExt = "Enzyme" + + [deps.QuadGK.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + +[[deps.Quadmath]] +deps = ["Compat", "Printf", "Random"] +git-tree-sha1 = "fc861299ef1c23d1985de276b2233db4a980a55c" +uuid = "be4d8f0f-7fa4-5f49-b795-2f01399ab2dd" +version = "1.1.0" +weakdeps = ["SpecialFunctions"] + + [deps.Quadmath.extensions] + QuadmathSpecialFunctionsExt = ["SpecialFunctions"] + +[[deps.REPL]] +deps = ["InteractiveUtils", "JuliaSyntaxHighlighting", "Markdown", "Sockets", "StyledStrings", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" + +[[deps.Random]] +deps = ["SHA"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" + +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "0c0dcffc464cda2ff33fc8ffc1dd1bdd53cd98be" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.4.0" + +[[deps.RecipesPipeline]] +deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] +git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" +uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" +version = "0.6.12" + +[[deps.RecursiveArrayTools]] +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "LinearAlgebra", "PrecompileTools", "RecipesBase", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "d0282d612f22dcad7b81cf487b746e63aa2a6709" +uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" +version = "3.54.0" + + [deps.RecursiveArrayTools.extensions] + RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" + RecursiveArrayToolsFastBroadcastPolyesterExt = ["FastBroadcast", "Polyester"] + RecursiveArrayToolsForwardDiffExt = "ForwardDiff" + RecursiveArrayToolsKernelAbstractionsExt = "KernelAbstractions" + RecursiveArrayToolsMeasurementsExt = "Measurements" + RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" + RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] + RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] + RecursiveArrayToolsStatisticsExt = "Statistics" + RecursiveArrayToolsStructArraysExt = "StructArrays" + RecursiveArrayToolsTablesExt = ["Tables"] + RecursiveArrayToolsTrackerExt = "Tracker" + RecursiveArrayToolsZygoteExt = "Zygote" + + [deps.RecursiveArrayTools.weakdeps] + FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.RegistryInstances]] +deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] +git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" +uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" +version = "0.1.0" + +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "1.0.1" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.1" + +[[deps.Roots]] +deps = ["Accessors", "CommonSolve", "Printf"] +git-tree-sha1 = "3eff988b9bd09543783e2e051b0a1eef11f65c2d" +uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +version = "2.3.0" + + [deps.Roots.extensions] + RootsChainRulesCoreExt = "ChainRulesCore" + RootsForwardDiffExt = "ForwardDiff" + RootsIntervalRootFindingExt = "IntervalRootFinding" + RootsSymPyExt = "SymPy" + RootsSymPyPythonCallExt = "SymPyPythonCall" + RootsUnitfulExt = "Unitful" + + [deps.Roots.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalRootFinding = "d2bf35a9-74e0-55ec-b149-d360ff49b807" + SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6" + SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + +[[deps.RuntimeGeneratedFunctions]] +deps = ["ExprTools", "PrecompileTools", "SHA", "Serialization"] +git-tree-sha1 = "9bb075002b40849582905394bb3031fb77aa102d" +uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" +version = "0.5.27" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "0.7.0" + +[[deps.SIMDTypes]] +git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" +uuid = "94e857df-77ce-4151-89e5-788b33177be4" +version = "0.1.0" + +[[deps.SciMLBase]] +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PreallocationTools", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLLogging", "SciMLOperators", "SciMLPublic", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] +git-tree-sha1 = "d56cb3b924cdf9297c171113e6a6ae9694e370eb" +uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +version = "2.155.2" + + [deps.SciMLBase.extensions] + SciMLBaseChainRulesCoreExt = "ChainRulesCore" + SciMLBaseDifferentiationInterfaceExt = "DifferentiationInterface" + SciMLBaseDistributionsExt = "Distributions" + SciMLBaseEnzymeExt = "Enzyme" + SciMLBaseForwardDiffExt = "ForwardDiff" + SciMLBaseMLStyleExt = "MLStyle" + SciMLBaseMakieExt = "Makie" + SciMLBaseMeasurementsExt = "Measurements" + SciMLBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + SciMLBaseMooncakeExt = "Mooncake" + SciMLBasePartialFunctionsExt = "PartialFunctions" + SciMLBasePyCallExt = "PyCall" + SciMLBasePythonCallExt = "PythonCall" + SciMLBaseRCallExt = "RCall" + SciMLBaseReverseDiffExt = "ReverseDiff" + SciMLBaseTrackerExt = "Tracker" + SciMLBaseZygoteExt = ["Zygote", "ChainRulesCore"] + + [deps.SciMLBase.weakdeps] + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" + PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" + PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" + RCall = "6f49c342-dc21-5d91-9882-a32aef131414" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.SciMLJacobianOperators]] +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "ad167d716fc134c0873c76ba0469ede56a678732" +uuid = "19f34311-ddf3-4b8b-af20-060888a46c0e" +version = "0.1.17" + +[[deps.SciMLLogging]] +deps = ["Logging", "LoggingExtras", "Preferences"] +git-tree-sha1 = "4e1e21f14a284f892eb62923a356c70a2a0c68e1" +uuid = "a6db7da4-7206-11f0-1eab-35f2a5dbe1d1" +version = "1.10.1" + + [deps.SciMLLogging.extensions] + SciMLLoggingTracyExt = "Tracy" + + [deps.SciMLLogging.weakdeps] + Tracy = "e689c965-62c8-4b79-b2c5-8359227902fd" + +[[deps.SciMLOperators]] +deps = ["Accessors", "Adapt", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "2712b6eeef282b5cd8c38660e68c001a4b9b24e2" +uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" +version = "1.30.3" + + [deps.SciMLOperators.extensions] + SciMLOperatorsLoopVectorizationExt = "LoopVectorization" + SciMLOperatorsSparseArraysExt = "SparseArrays" + + [deps.SciMLOperators.weakdeps] + LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.SciMLPublic]] +deps = ["PrecompileTools"] +git-tree-sha1 = "74685afb51732a464fbce79a72708f7c4203ceb7" +uuid = "431bcebd-1456-4ced-9d72-93c2757fff0b" +version = "1.3.0" + +[[deps.SciMLStructures]] +deps = ["ArrayInterface", "PrecompileTools"] +git-tree-sha1 = "4b8fc0ffe22b4ecba7a02f1fec0b3757db014d0c" +uuid = "53ae85a6-f571-4167-b2af-e1d143709226" +version = "1.10.6" + +[[deps.ScopedValues]] +deps = ["HashArrayMappedTries", "Logging"] +git-tree-sha1 = "67a144433c4ce877ee6d1ada69a124d6b1ecf7be" +uuid = "7e506255-f358-4e82-b7e4-beb19740aa63" +version = "1.6.2" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "9b81b8393e50b7d4e6d0a9f14e192294d3b7c109" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.3.0" + +[[deps.SentinelArrays]] +deps = ["Dates", "Random"] +git-tree-sha1 = "084c47c7c5ce5cfecefa0a98dff69eb3646b5a80" +uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" +version = "1.4.10" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.11.0" + +[[deps.Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] +git-tree-sha1 = "c5391c6ace3bc430ca630251d02ea9687169ca68" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "1.1.2" + +[[deps.Showoff]] +deps = ["Dates"] +git-tree-sha1 = "8238217340ad0aaabe11afe39c1098b5bc9f4c8e" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.1.1" + +[[deps.SimpleBufferStream]] +git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" +uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" +version = "1.2.0" + +[[deps.SimpleNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "08d8bafd57b7ea16dc1f69a8bd57db23a2ceb686" +uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" +version = "2.12.0" + + [deps.SimpleNonlinearSolve.extensions] + SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" + SimpleNonlinearSolveReverseDiffExt = "ReverseDiff" + SimpleNonlinearSolveTrackerExt = "Tracker" + + [deps.SimpleNonlinearSolve.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" + +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "13cd91cc9be159e3f4d95b857fa2aa383b53772a" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.2.3" + +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.12.0" + +[[deps.SparseColumnPivotedQR]] +deps = ["LinearAlgebra", "PrecompileTools", "SparseArrays"] +git-tree-sha1 = "8cf1a59c78dd6f900feb366e5a574f99ed278ef1" +uuid = "a57abbd0-fea5-4d57-96be-5e525945e8e4" +version = "2.1.8" +weakdeps = ["AMD"] + + [deps.SparseColumnPivotedQR.extensions] + SparseColumnPivotedQRAMDExt = "AMD" + +[[deps.SparseMatrixColorings]] +deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "Random", "SparseArrays"] +git-tree-sha1 = "0730f14b07c68fd3b3a6fae89a829a5124a7ae7e" +uuid = "0a514795-09f3-496d-8182-132a7b665d35" +version = "0.4.29" + + [deps.SparseMatrixColorings.extensions] + SparseMatrixColoringsAMDGPUExt = "AMDGPU" + SparseMatrixColoringsCUDAExt = ["CUDA", "cuSPARSE"] + SparseMatrixColoringsCliqueTreesExt = "CliqueTrees" + SparseMatrixColoringsColorsExt = "Colors" + SparseMatrixColoringsGPUArraysExt = "GPUArrays" + SparseMatrixColoringsJuMPExt = ["JuMP", "MathOptInterface"] + + [deps.SparseMatrixColorings.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" + Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" + GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" + JuMP = "4076af6c-e467-56ae-b986-b466b2749572" + MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" + cuSPARSE = "b26da814-b3bc-49ef-b0ee-c816305aa060" + +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "429071b23f4c9a13fb6582f807cc2ef454082408" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.9.0" + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.Static]] +deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "474a5283ad435618090122872eea6a8165ea6bcf" +uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +version = "1.4.6" + +[[deps.StaticArrayInterface]] +deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "SciMLPublic", "Static"] +git-tree-sha1 = "2a635e15d5035c53b345077c947f31ff91744078" +uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" +version = "1.10.0" + + [deps.StaticArrayInterface.extensions] + StaticArrayInterfaceOffsetArraysExt = "OffsetArrays" + StaticArrayInterfaceStaticArraysExt = "StaticArrays" + + [deps.StaticArrayInterface.weakdeps] + OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "de349d5585559b692f518b6d3fc7c179d64b2364" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.9.23" + + [deps.StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [deps.StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "6ab403037779dae8c514bad259f32a447262455a" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.4" + +[[deps.Statistics]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "e2b53ce13a53367e96601081e33d34746b571bad" +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.11.5" +weakdeps = ["SparseArrays"] + + [deps.Statistics.extensions] + SparseArraysExt = ["SparseArrays"] + +[[deps.StatsAPI]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "178ed29fd5b2a2cfc3bd31c13375ae925623ff36" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.8.0" + +[[deps.StatsBase]] +deps = ["AliasTables", "DataAPI", "DataStructures", "IrrationalConstants", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "adb9da019510162e67a4493fc235c23203d8b09e" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.34.13" + +[[deps.StrideArraysCore]] +deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] +git-tree-sha1 = "5316097111523c9a970596a5b33cfea5f92e8581" +uuid = "7792a7ef-975c-4747-a70f-980b88e8d1da" +version = "0.5.9" + +[[deps.StringManipulation]] +deps = ["PrecompileTools", "StyledStrings"] +git-tree-sha1 = "9e3a02d73a8f0a9be04e677dbef758aab40f8bea" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.6.1" + +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.8.3+2" + +[[deps.SymbolicIndexingInterface]] +deps = ["Accessors", "ArrayInterface", "PrecompileTools", "RuntimeGeneratedFunctions", "StaticArraysCore"] +git-tree-sha1 = "7eb6da9656581ac9fbffaef3ef7950a090a5002a" +uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +version = "0.3.55" +weakdeps = ["PrettyTables"] + + [deps.SymbolicIndexingInterface.extensions] + SymbolicIndexingInterfacePrettyTablesExt = "PrettyTables" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "a94d9bdda1b7bed0046cea645639ab3f62196fac" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.14.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" + +[[deps.ThreadSafeDicts]] +git-tree-sha1 = "411010ade7dd5576d693db8737976d805ee9945b" +uuid = "4239201d-c60e-5e0a-9702-85d713665ba7" +version = "0.1.7" + +[[deps.ThreadingUtilities]] +deps = ["ManualMemory"] +git-tree-sha1 = "e12924c87b5476110130841e973ba9b9f20ae599" +uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" +version = "0.5.7" + +[[deps.TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "3748bd928e68c7c346b52125cf41fff0de6937d0" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.29" + + [deps.TimerOutputs.extensions] + FlameGraphsExt = "FlameGraphs" + + [deps.TimerOutputs.weakdeps] + FlameGraphs = "08572546-2f56-4bcf-ba4e-bab62c3a3f89" + +[[deps.TranscodingStreams]] +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.11.3" + +[[deps.TruncatedStacktraces]] +deps = ["InteractiveUtils", "MacroTools", "Preferences"] +git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" +uuid = "781d530d-4396-4725-bb49-402e4bee1e77" +version = "1.4.0" + +[[deps.URIs]] +deps = ["Serialization"] +git-tree-sha1 = "0392e5e3c5327b90a487b0ff3e818c4aecd3660b" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.7.1" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "1.11.0" + +[[deps.UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" + +[[deps.UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[deps.Unzip]] +git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" +uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" +version = "0.2.0" + +[[deps.Vulkan_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] +git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" +uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" +version = "1.3.243+0" + +[[deps.Wayland_jll]] +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "96478df35bbc2f3e1e791bc7a3d0eeee559e60e9" +uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" +version = "1.24.0+0" + +[[deps.WebIO]] +deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] +git-tree-sha1 = "0eef0765186f7452e52236fa42ca8c9b3c11c6e3" +uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" +version = "0.8.21" + +[[deps.WebSockets]] +deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] +git-tree-sha1 = "4162e95e05e79922e44b9952ccbc262832e4ad07" +uuid = "104b5d7c-a370-577a-8038-80a2059c5097" +version = "1.6.0" + +[[deps.Widgets]] +deps = ["Colors", "Dates", "Observables", "OrderedCollections"] +git-tree-sha1 = "cab29ed70d9355a28b95b1798007ff3a00eefc91" +uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" +version = "0.6.8" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "b826aedca5fce2e3c91904e338c279e5c1a2da74" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.15.4+0" + +[[deps.XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e52eca002a11c30a858185efdfb15311e1c7a6bf" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.8.4+0" + +[[deps.Xorg_libICE_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a3ea76ee3f4facd7a64684f9af25310825ee3668" +uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" +version = "1.1.2+0" + +[[deps.Xorg_libSM_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libICE_jll"] +git-tree-sha1 = "9c7ad99c629a44f81e7799eb05ec2746abb5d588" +uuid = "c834827a-8449-5923-a945-d239c165b7dd" +version = "1.2.6+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "808090ede1d41644447dd5cbafced4731c56bd2f" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.13+0" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "aa1261ebbac3ccc8d16558ae6799524c450ed16b" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.13+0" + +[[deps.Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "6c74ca84bbabc18c4547014765d194ff0b4dc9da" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.4+0" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "52858d64353db33a56e13c341d7bf44cd0d7b309" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.6+0" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "1a4a26870bf1e5d26cd585e38038d399d7e65706" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.8+0" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "75e00946e43621e09d431d9b95818ee751e6b2ef" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "6.0.2+0" + +[[deps.Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "dcb316b3ce0941f195537dda56bea4517fcd3ff5" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.8.4+0" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] +git-tree-sha1 = "0ba01bc7396896a4ace8aab67db31403c71628f4" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.7+0" + +[[deps.Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "6c174ef70c96c76f4c3f4d3cfbe09d018bcd1b53" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.6+0" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "7ed9347888fac59a618302ee38216dd0379c480d" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.12+0" + +[[deps.Xorg_libpciaccess_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "58972370b81423fc546c56a60ed1a009450177c3" +uuid = "a65dc6b1-eb27-53a1-bb3e-dea574b5389e" +version = "0.19.0+0" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXau_jll", "Xorg_libXdmcp_jll"] +git-tree-sha1 = "bfcaf7ec088eaba362093393fe11aa141fa15422" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.17.1+0" + +[[deps.Xorg_libxkbfile_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "ed756a03e95fff88d8f738ebc2849431bdd4fd1a" +uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" +version = "1.2.0+0" + +[[deps.Xorg_xcb_util_cursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] +git-tree-sha1 = "9750dc53819eba4e9a20be42349a6d3b86c7cdf8" +uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" +version = "0.1.6+0" + +[[deps.Xorg_xcb_util_image_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f4fc02e384b74418679983a97385644b67e1263b" +uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll"] +git-tree-sha1 = "68da27247e7d8d8dafd1fcf0c3654ad6506f5f97" +uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_keysyms_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "44ec54b0e2acd408b0fb361e1e9244c60c9c3dd4" +uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_renderutil_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "5b0263b6d080716a02544c55fdff2c8d7f9a16a0" +uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" +version = "0.3.10+0" + +[[deps.Xorg_xcb_util_wm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f233c83cad1fa0e70b7771e0e21b061a116f2763" +uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" +version = "0.4.2+0" + +[[deps.Xorg_xkbcomp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "9a5e6e8f1a7c40941465c04840145e527f0d6b4b" +uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" +version = "1.5.0+0" + +[[deps.Xorg_xkeyboard_config_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "df77b22eb77051b3d0ef30aab979684f7482c66f" +uuid = "33bec58e-1273-512f-9401-5d533626f822" +version = "2.48.0+0" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a63799ff68005991f9d9491b6e95bd3478d783cb" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.6.0+0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.3.1+2" + +[[deps.Zstd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "446b23e73536f84e8037f5dce465e92275f6a308" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.7+1" + +[[deps.aws_c_auth_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_cal_jll", "aws_c_http_jll", "aws_c_sdkutils_jll"] +git-tree-sha1 = "8cab83c96af80a1be968251ce1a0548a7545484d" +uuid = "2b3700d1-4306-52e2-a478-c162f0c514be" +version = "0.9.6+0" + +[[deps.aws_c_cal_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "22c0f42f4a1f0dc5dcfa8fd267c4ac407c455e7a" +uuid = "70f11efc-bab2-57f1-b0f3-22aad4e67c4b" +version = "0.9.13+0" + +[[deps.aws_c_common_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a759cb9bf456ad792cc7898a81ae333cce9ef02a" +uuid = "73048d1d-b8c4-5092-a58d-866c5e8d1e50" +version = "0.12.6+0" + +[[deps.aws_c_compression_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "7910c72f45f44afd297c39fe43b99c56d5ed22ec" +uuid = "73a04cd5-f3d7-5bac-9290-e8adb709f224" +version = "0.3.2+0" + +[[deps.aws_c_http_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_compression_jll", "aws_c_io_jll"] +git-tree-sha1 = "3fb8685778068de502c72fec5dd8075e037cee15" +uuid = "3254fc65-9028-534d-aa9d-d76d128babc6" +version = "0.10.15+0" + +[[deps.aws_c_io_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_cal_jll", "aws_c_common_jll", "s2n_tls_jll"] +git-tree-sha1 = "7e481d474b2087ee8bbf55b81bf9119f21e396d9" +uuid = "13c41daa-f319-5298-b5eb-5754e0170d52" +version = "0.26.3+0" + +[[deps.aws_c_s3_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_auth_jll", "aws_c_common_jll", "aws_c_http_jll", "aws_checksums_jll", "s2n_tls_jll"] +git-tree-sha1 = "3e9917ab25114feba657e71be41cad068b9f6595" +uuid = "bd1f34fb-993f-5903-a121-aaf302eed6d4" +version = "0.11.5+0" + +[[deps.aws_c_sdkutils_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "c43dfba2c1ab9ea9f02f2c80e86fa16f6460244e" +uuid = "1282aa60-004d-510b-9f52-12498d409daa" +version = "0.2.4+1" + +[[deps.aws_checksums_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "2570c8e23f4771a087b12a47edcaaa670ac05a01" +uuid = "b2a88e68-78e7-5e94-8c20-c02986ec140e" +version = "0.2.10+0" + +[[deps.dlfcn_win32_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e141d67ffe550eadfb5af1bdbdaf138031e4805f" +uuid = "c4b69c83-5512-53e3-94e6-de98773c479f" +version = "1.4.2+0" + +[[deps.eudev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c3b0e6196d50eab0c5ed34021aaa0bb463489510" +uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" +version = "3.2.14+0" + +[[deps.fzf_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b6a34e0e0960190ac2a4363a1bd003504772d631" +uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" +version = "0.61.1+0" + +[[deps.libaec_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "60f4792734488db6f42e2c7699f1d4594780bd03" +uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" +version = "1.1.7+0" + +[[deps.libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1210ba774d3427387d307bf1f416d699b7c39417" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.15.1+0" + +[[deps.libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "cb007192783c56d8249db4cf0e3495001edfe414" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.17.5+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.15.0+0" + +[[deps.libdecor_jll]] +deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] +git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" +uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" +version = "0.2.2+0" + +[[deps.libdrm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libpciaccess_jll"] +git-tree-sha1 = "28e57478e8a160d346a19c28b3fffb9273bcc9c2" +uuid = "8e53e030-5e6c-5a89-a30b-be5b7263a166" +version = "2.4.134+0" + +[[deps.libevdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "56d643b57b188d30cccc25e331d416d3d358e557" +uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" +version = "1.13.4+0" + +[[deps.libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "646634dd19587a56ee2f1199563ec056c5f228df" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "2.0.4+0" + +[[deps.libinput_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "eudev_jll", "libevdev_jll", "mtdev_jll"] +git-tree-sha1 = "91d05d7f4a9f67205bd6cf395e488009fe85b499" +uuid = "36db933b-70db-51c0-b978-0f229ee0e533" +version = "1.28.1+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "1fcd6cf05d23adab0b5ffc496d020537215751dc" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.59+1" + +[[deps.libva_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "libdrm_jll"] +git-tree-sha1 = "7dbf96baae3310fe2fa0df0ccbb3c6288d5816c9" +uuid = "9a156e7d-b971-5f62-b2c9-67348b8fb97c" +version = "2.23.0+0" + +[[deps.libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll"] +git-tree-sha1 = "11e1772e7f3cc987e9d3de991dd4f6b2602663a5" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.8+0" + +[[deps.mpif_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIABI_jll", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "TOML"] +git-tree-sha1 = "a06fcd368cfe6fe2c0eb7b63320d4d27ddcd010d" +uuid = "9aeb927a-4695-514f-a259-621a69f20ec0" +version = "1.0.0+0" + +[[deps.mtdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b4d631fd51f2e9cdd93724ae25b2efc198b059b1" +uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" +version = "1.1.7+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.64.0+1" + +[[deps.oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "ed2d8d4ad96c03d3abe3382cdbbdea9489b63636" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2023.1.0+0" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.7.0+0" + +[[deps.s2n_tls_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d6cfe0874f96962fe4b20ed8bdb71c467e09c2c6" +uuid = "cddc5d3d-934d-5d3a-9747-62fc12ea3f48" +version = "1.7.11+0" + +[[deps.x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "14cc7083fc6dff3cc44f2bc435ee96d06ed79aa7" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "10164.0.1+0" + +[[deps.x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e7b67590c14d487e734dcb925924c5dc43ec85f3" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "4.1.0+0" + +[[deps.xkbcommon_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] +git-tree-sha1 = "a1fc6507a40bf504527d0d4067d718f8e179b2b8" +uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" +version = "1.13.0+0" diff --git a/ci/manifests/Manifest-v1.13.toml b/ci/manifests/Manifest-v1.13.toml new file mode 100644 index 0000000000..459066e96d --- /dev/null +++ b/ci/manifests/Manifest-v1.13.toml @@ -0,0 +1,3501 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.13.3" +manifest_format = "2.1" +project_hash = "46cc8ae34afcb7a4b038db845711846a1cfd9266" + +[[deps.ADTypes]] +deps = ["PrecompileTools"] +git-tree-sha1 = "629de23e1c16911b439dabd2303c08af9575b226" +registries = "General" +uuid = "47edcb42-4c32-4615-8424-f2b9edc5f35b" +version = "1.24.0" + + [deps.ADTypes.extensions] + ADTypesChainRulesCoreExt = "ChainRulesCore" + ADTypesConstructionBaseExt = "ConstructionBase" + ADTypesEnzymeCoreExt = "EnzymeCore" + + [deps.ADTypes.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + +[[deps.AMD]] +deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse_jll"] +git-tree-sha1 = "344f3d221a6bee75925b5c97a30cfd870f12a138" +registries = "General" +uuid = "14f7f29c-3bd6-536c-9a0b-7339e30b5a3e" +version = "0.5.4" + +[[deps.ANSIColoredPrinters]] +git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" +registries = "General" +uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" +version = "0.0.1" + +[[deps.AbstractFFTs]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" +registries = "General" +uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" +version = "1.5.0" + + [deps.AbstractFFTs.extensions] + AbstractFFTsChainRulesCoreExt = "ChainRulesCore" + AbstractFFTsTestExt = "Test" + + [deps.AbstractFFTs.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[[deps.AbstractTrees]] +git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" +registries = "General" +uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" +version = "0.4.5" + +[[deps.Accessors]] +deps = ["CompositionsBase", "ConstructionBase", "Dates", "InverseFunctions", "MacroTools"] +git-tree-sha1 = "8c0bc12397dabf5d2dfc4b5763ec463854cc44d3" +registries = "General" +uuid = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697" +version = "0.1.46" + + [deps.Accessors.extensions] + AxisKeysExt = "AxisKeys" + IntervalSetsExt = "IntervalSets" + LinearAlgebraExt = "LinearAlgebra" + StaticArraysExt = "StaticArrays" + StructArraysExt = "StructArrays" + TestExt = "Test" + UnitfulExt = "Unitful" + + [deps.Accessors.weakdeps] + AxisKeys = "94b1ba4f-4ee9-5380-92f1-94cde586c3c5" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.Adapt]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "dee8388a9bec89c8713ad176f96960ae2f98598a" +registries = "General" +uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" +version = "4.7.4" +weakdeps = ["SparseArrays", "StaticArrays"] + + [deps.Adapt.extensions] + AdaptSparseArraysExt = "SparseArrays" + AdaptStaticArraysExt = "StaticArrays" + +[[deps.AdaptiveArrayPools]] +deps = ["Preferences", "Printf", "Random"] +git-tree-sha1 = "5feaa5055572e809b69b62f06826b1c1c8d3ebf3" +registries = "General" +uuid = "4f381ef7-9af0-4cbe-99d4-cf36d7b0f233" +version = "0.3.11" + + [deps.AdaptiveArrayPools.extensions] + AdaptiveArrayPoolsCUDAExt = "CUDA" + AdaptiveArrayPoolsMetalExt = "Metal" + + [deps.AdaptiveArrayPools.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + +[[deps.AdaptivePredicates]] +git-tree-sha1 = "7e651ea8d262d2d74ce75fdf47c4d63c07dba7a6" +registries = "General" +uuid = "35492f91-a3bd-45ad-95db-fcad7dcfedb7" +version = "1.2.0" + +[[deps.AliasTables]] +deps = ["PtrArrays", "Random"] +git-tree-sha1 = "9876e1e164b144ca45e9e3198d0b689cadfed9ff" +registries = "General" +uuid = "66dad0bd-aa9a-41b7-9441-69ab47430ed8" +version = "1.1.3" + +[[deps.ArgTools]] +uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" +version = "1.1.2" + +[[deps.ArrayInterface]] +deps = ["Adapt", "LinearAlgebra"] +git-tree-sha1 = "1aec1ff0dcaa83a484e7f72097562fa3102c6722" +registries = "General" +uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" +version = "7.30.2" + + [deps.ArrayInterface.extensions] + ArrayInterfaceAMDGPUExt = "AMDGPU" + ArrayInterfaceBandedMatricesExt = "BandedMatrices" + ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" + ArrayInterfaceCUDAExt = "CUDA" + ArrayInterfaceCUDSSExt = ["CUDSS", "CUDA"] + ArrayInterfaceChainRulesCoreExt = "ChainRulesCore" + ArrayInterfaceChainRulesExt = "ChainRules" + ArrayInterfaceFillArraysExt = "FillArrays" + ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" + ArrayInterfaceGPUArraysCoreTrackerExt = ["GPUArraysCore", "Tracker"] + ArrayInterfaceMetalExt = "Metal" + ArrayInterfaceReverseDiffExt = "ReverseDiff" + ArrayInterfaceSparseArraysExt = "SparseArrays" + ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" + ArrayInterfaceTrackerExt = "Tracker" + + [deps.ArrayInterface.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.ArrayLayouts]] +deps = ["FillArrays", "LinearAlgebra", "StaticArrays"] +git-tree-sha1 = "8fde25d92e917350570f1203e47c933d16b77dce" +registries = "General" +uuid = "4c555306-a7a7-4459-81d9-ec55ddd5c99a" +version = "1.16.0" +weakdeps = ["SparseArrays"] + + [deps.ArrayLayouts.extensions] + ArrayLayoutsSparseArraysExt = "SparseArrays" + +[[deps.Artifacts]] +uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" +version = "1.11.0" + +[[deps.AssetRegistry]] +deps = ["JSON", "Pidfile", "SHA"] +git-tree-sha1 = "902b85010203830d4bc02259f5450d2e16b316d8" +registries = "General" +uuid = "bf4720bc-e11a-5d0c-854e-bdca1663c893" +version = "0.1.1" + +[[deps.Base64]] +uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" +version = "1.11.0" + +[[deps.BitFlags]] +git-tree-sha1 = "bbe1079eecf9c9fbb52765193ad2bae27ae09bc8" +registries = "General" +uuid = "d1d4a3ce-64b1-5f1a-9ba4-7e7e69966f35" +version = "0.1.10" + +[[deps.BitTwiddlingConvenienceFunctions]] +deps = ["Static"] +git-tree-sha1 = "f21cfd4950cb9f0587d5067e69405ad2acd27b87" +registries = "General" +uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" +version = "0.1.6" + +[[deps.Blink]] +deps = ["Base64", "Distributed", "HTTP", "JSExpr", "JSON", "Lazy", "Logging", "MacroTools", "Mustache", "Mux", "Pkg", "Reexport", "Sockets", "WebIO"] +git-tree-sha1 = "bc93511973d1f949d45b0ea17878e6cb0ad484a1" +registries = "General" +uuid = "ad839575-38b3-5650-b840-f874b8c74a25" +version = "0.12.9" + +[[deps.BracketingNonlinearSolve]] +deps = ["CommonSolve", "ConcreteStructs", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "7ad7171d693ae5552ac43862e7f6b61df4471c2b" +registries = "General" +uuid = "70df07ce-3d50-431d-a3e7-ca6ddb60ac1e" +version = "1.12.1" + + [deps.BracketingNonlinearSolve.extensions] + BracketingNonlinearSolveChainRulesCoreExt = ["ChainRulesCore", "ForwardDiff"] + BracketingNonlinearSolveForwardDiffExt = "ForwardDiff" + + [deps.BracketingNonlinearSolve.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + +[[deps.Bzip2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1b96ea4a01afe0ea4090c5c8039690672dd13f2e" +registries = "General" +uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" +version = "1.0.9+0" + +[[deps.CPUSummary]] +deps = ["CpuId", "IfElse", "PrecompileTools", "Preferences", "Static"] +git-tree-sha1 = "f3a21d7fc84ba618a779d1ed2fcca2e682865bab" +registries = "General" +uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" +version = "0.2.7" + +[[deps.CRlibm]] +deps = ["CRlibm_jll"] +git-tree-sha1 = "66188d9d103b92b6cd705214242e27f5737a1e5e" +registries = "General" +uuid = "96374032-68de-5a5b-8d9e-752f78720389" +version = "1.0.2" + +[[deps.CRlibm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "e329286945d0cfc04456972ea732551869af1cfc" +registries = "General" +uuid = "4e9b3aee-d8a1-5a3d-ad8b-7d824db253f0" +version = "1.0.1+0" + +[[deps.Cairo_jll]] +deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "0cfb779e24fdced77a05df1dbe99200821a9f563" +registries = "General" +uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" +version = "1.18.8+1" + +[[deps.ChunkCodecCore]] +git-tree-sha1 = "3496e2b359c27793d12bec54ec94be7983a6ceb2" +registries = "General" +uuid = "0b6fb165-00bc-4d37-ab8b-79f91016dbe1" +version = "1.0.2" + +[[deps.ChunkCodecLibZlib]] +deps = ["ChunkCodecCore", "Zlib_jll"] +git-tree-sha1 = "d4101e848e8d3f585d61d244c2fe0c80a70e6b3b" +registries = "General" +uuid = "4c0bbee4-addc-4d73-81a0-b6caacae83c8" +version = "1.1.0" + +[[deps.ChunkCodecLibZstd]] +deps = ["ChunkCodecCore", "Zstd_jll"] +git-tree-sha1 = "34d9873079e4cb3d0c62926a225136824677073f" +registries = "General" +uuid = "55437552-ac27-4d47-9aa3-63184e8fd398" +version = "1.0.0" + +[[deps.CloseOpenIntervals]] +deps = ["Static", "StaticArrayInterface"] +git-tree-sha1 = "05ba0d07cd4fd8b7a39541e31a7b0254704ea581" +registries = "General" +uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" +version = "0.1.13" + +[[deps.CodecZlib]] +deps = ["TranscodingStreams", "Zlib_jll"] +git-tree-sha1 = "970758a3d591a2a5c2a907c53f2e2f8c1b1d3537" +registries = "General" +uuid = "944b1d66-785c-5afd-91f1-9de20f533193" +version = "0.7.9" + +[[deps.ColorSchemes]] +deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] +git-tree-sha1 = "b0fd3f56fa442f81e0a47815c92245acfaaa4e34" +registries = "General" +uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" +version = "3.31.0" + +[[deps.ColorTypes]] +deps = ["FixedPointNumbers", "Random"] +git-tree-sha1 = "a7cf827a79b378a381e31e5c7feca853ec19cb94" +registries = "General" +uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" +version = "0.12.4" +weakdeps = ["StyledStrings"] + + [deps.ColorTypes.extensions] + StyledStringsExt = "StyledStrings" + +[[deps.ColorVectorSpace]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Requires", "Statistics", "TensorCore"] +git-tree-sha1 = "a4e53eb7a1779254f92c1cc1302c2bc6acdba560" +registries = "General" +uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" +version = "0.11.1" +weakdeps = ["SpecialFunctions"] + + [deps.ColorVectorSpace.extensions] + SpecialFunctionsExt = "SpecialFunctions" + +[[deps.Colors]] +deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "Reexport"] +git-tree-sha1 = "a633156e74096f90b3b5e833529292b651b5671a" +registries = "General" +uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" +version = "0.13.3" + +[[deps.CommonSolve]] +deps = ["PrecompileTools"] +git-tree-sha1 = "6c389fa857f6ca5a95474b52a52023fd77f24cb7" +registries = "General" +uuid = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" +version = "0.2.14" + +[[deps.CommonSubexpressions]] +deps = ["MacroTools"] +git-tree-sha1 = "cda2cfaebb4be89c9084adaca7dd7333369715c5" +registries = "General" +uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" +version = "0.3.1" + +[[deps.CommonWorldInvalidations]] +deps = ["PrecompileTools"] +git-tree-sha1 = "7c4ea0adfb7238bf4678aa36325863fab6163f6f" +registries = "General" +uuid = "f70d9fcc-98c5-4d4a-abd7-e4cdeebd8ca8" +version = "1.2.2" + +[[deps.Compat]] +deps = ["TOML", "UUIDs"] +git-tree-sha1 = "9d8a54ce4b17aa5bdce0ea5c34bc5e7c340d16ad" +registries = "General" +uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" +version = "4.18.1" +weakdeps = ["Dates", "LinearAlgebra"] + + [deps.Compat.extensions] + CompatLinearAlgebraExt = "LinearAlgebra" + +[[deps.CompilerSupportLibraries_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" +version = "1.5.5+2" + +[[deps.CompositionsBase]] +git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" +registries = "General" +uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" +version = "0.1.2" +weakdeps = ["InverseFunctions"] + + [deps.CompositionsBase.extensions] + CompositionsBaseInverseFunctionsExt = "InverseFunctions" + +[[deps.ConcreteStructs]] +deps = ["PrecompileTools"] +git-tree-sha1 = "a72c3b5ce6d2a477f55b5c9b8756e91e695c67f6" +registries = "General" +uuid = "2569d6c7-a4a2-43d3-a901-331e8e4be471" +version = "0.2.8" + +[[deps.ConcurrentUtilities]] +deps = ["Serialization", "Sockets"] +git-tree-sha1 = "d5e5fa1f0d6ca517edbbcd734efa93c6639405b0" +registries = "General" +uuid = "f0e56b4a-5159-44fe-b623-3e5288b988bb" +version = "2.7.1" + +[[deps.ConstructionBase]] +git-tree-sha1 = "b4b092499347b18a015186eae3042f72267106cb" +registries = "General" +uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" +version = "1.6.0" + + [deps.ConstructionBase.extensions] + ConstructionBaseIntervalSetsExt = "IntervalSets" + ConstructionBaseLinearAlgebraExt = "LinearAlgebra" + ConstructionBaseStaticArraysExt = "StaticArrays" + + [deps.ConstructionBase.weakdeps] + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.Contour]] +git-tree-sha1 = "439e35b0b36e2e5881738abc8857bd92ad6ff9a8" +registries = "General" +uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" +version = "0.6.3" + +[[deps.CoordinateConventions]] +git-tree-sha1 = "10832be51679db21271faac5bad7779a4320ca4b" +registries = "General" +uuid = "7204ce3a-f536-43d2-be4a-fbed74e90d86" +version = "1.0.5" + +[[deps.CoreMath]] +deps = ["CoreMath_jll"] +git-tree-sha1 = "8c0480f92b1b1796239156a1b9b1bfb1b39499b4" +registries = "General" +uuid = "b7a15901-be09-4a0e-87d2-2e66b0e09b5a" +version = "0.1.0" + +[[deps.CoreMath_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a692a4c1dc59a4b8bc0b6403876eb3250fde2bc3" +registries = "General" +uuid = "a38c48d9-6df1-5ac9-9223-b6ada3b5572b" +version = "0.1.0+0" + +[[deps.CpuId]] +deps = ["Markdown", "PrecompileTools"] +git-tree-sha1 = "5dbd5150b96f29553fc3b553c6bfb0eadf7ac204" +registries = "General" +uuid = "adafc99b-e345-5852-983c-f28acb93d879" +version = "0.3.3" + +[[deps.Crayons]] +git-tree-sha1 = "54b76cbb40d9a0f5368c880725b2f141da77c94f" +registries = "General" +uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" +version = "4.2.0" + +[[deps.DataAPI]] +git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" +registries = "General" +uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" +version = "1.16.0" + +[[deps.DataFrames]] +deps = ["Compat", "DataAPI", "DataStructures", "Future", "InlineStrings", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrecompileTools", "PrettyTables", "Printf", "Random", "Reexport", "SentinelArrays", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] +git-tree-sha1 = "5fab31e2e01e70ad66e3e24c968c264d1cf166d6" +registries = "General" +uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +version = "1.8.2" + +[[deps.DataInterpolations]] +deps = ["EnumX", "FindFirstFunctions", "ForwardDiff", "LinearAlgebra", "PrettyTables", "RecipesBase", "Reexport"] +git-tree-sha1 = "4d50e83772222ce023efb7d30632198828ea56a6" +registries = "General" +uuid = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" +version = "8.10.0" + + [deps.DataInterpolations.extensions] + DataInterpolationsChainRulesCoreExt = "ChainRulesCore" + DataInterpolationsMakieExt = "Makie" + DataInterpolationsMooncakeExt = ["Mooncake", "ChainRulesCore"] + DataInterpolationsOptimExt = "Optim" + DataInterpolationsRegularizationToolsExt = "RegularizationTools" + DataInterpolationsSparseConnectivityTracerExt = ["SparseConnectivityTracer", "FillArrays"] + DataInterpolationsSymbolicsExt = "Symbolics" + + [deps.DataInterpolations.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + Optim = "429524aa-4258-5aef-a3af-852621145aeb" + RegularizationTools = "29dad682-9a27-4bc3-9c72-016788665182" + SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.DataStructures]] +deps = ["OrderedCollections"] +git-tree-sha1 = "b0bc6d2cad1fed8b7fd59a1551a991cb3d2809e6" +registries = "General" +uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" +version = "0.19.6" + +[[deps.DataValueInterfaces]] +git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" +registries = "General" +uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" +version = "1.0.0" + +[[deps.Dates]] +deps = ["Printf"] +uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" +version = "1.11.0" + +[[deps.Dbus_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "473e9afc9cf30814eb67ffa5f2db7df82c3ad9fd" +registries = "General" +uuid = "ee1fde0b-3d02-5ea6-8484-8dfef6360eab" +version = "1.16.2+0" + +[[deps.DelaunayTriangulation]] +deps = ["AdaptivePredicates", "EnumX", "ExactPredicates", "Random"] +git-tree-sha1 = "e1e28f37cbdca4b594ad86ecfe2ee8f25796ebff" +registries = "General" +uuid = "927a84f5-c5f4-47a5-9785-b46e178433df" +version = "1.7.0" + +[[deps.DelimitedFiles]] +deps = ["Mmap"] +git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" +registries = "General" +uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" +version = "1.9.1" + +[[deps.DiffEqBase]] +deps = ["ArrayInterface", "BracketingNonlinearSolve", "ConcreteStructs", "DocStringExtensions", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "Logging", "Markdown", "MuladdMacro", "PrecompileTools", "Printf", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Setfield", "Static", "StaticArraysCore", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "9d333db14895e8c7d4857ed228eb1e72d3b302ec" +registries = "General" +uuid = "2b5f629d-d688-5b77-993f-72d75c75574e" +version = "6.218.0" + + [deps.DiffEqBase.extensions] + DiffEqBaseCUDAExt = "CUDA" + DiffEqBaseChainRulesCoreExt = "ChainRulesCore" + DiffEqBaseDynamicQuantitiesExt = "DynamicQuantities" + DiffEqBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] + DiffEqBaseFlexUnitsExt = "FlexUnits" + DiffEqBaseForwardDiffExt = ["ForwardDiff"] + DiffEqBaseGTPSAExt = "GTPSA" + DiffEqBaseGeneralizedGeneratedExt = "GeneralizedGenerated" + DiffEqBaseMPIExt = "MPI" + DiffEqBaseMeasurementsExt = "Measurements" + DiffEqBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + DiffEqBaseMooncakeExt = "Mooncake" + DiffEqBaseReverseDiffExt = "ReverseDiff" + DiffEqBaseSparseArraysExt = "SparseArrays" + DiffEqBaseTrackerExt = "Tracker" + DiffEqBaseUnitfulExt = "Unitful" + + [deps.DiffEqBase.weakdeps] + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + DynamicQuantities = "06fc5a27-2a28-4c7c-a15d-362465fb6821" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + FlexUnits = "76e01b6b-c995-4ce6-8559-91e72a3d4e95" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + GTPSA = "b27dd330-f138-47c5-815b-40db9dd9b6e8" + GeneralizedGenerated = "6b9d7cbe-bcb9-11e9-073f-15a7a543e2eb" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.DiffEqCallbacks]] +deps = ["ConcreteStructs", "DataStructures", "DiffEqBase", "DifferentiationInterface", "LinearAlgebra", "Markdown", "PrecompileTools", "RecipesBase", "RecursiveArrayTools", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "88cdec45374d53393bf88268102a2b018c897178" +registries = "General" +uuid = "459566f4-90b8-5000-8ac3-15dfb0a30def" +version = "4.19.2" + + [deps.DiffEqCallbacks.extensions] + DiffEqCallbacksFunctorsExt = "Functors" + + [deps.DiffEqCallbacks.weakdeps] + Functors = "d9f16b24-f501-4c13-a1f2-28368ffc5196" + +[[deps.DiffResults]] +deps = ["StaticArraysCore"] +git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" +registries = "General" +uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" +version = "1.1.0" + +[[deps.DiffRules]] +deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] +git-tree-sha1 = "79a2aca180a85c690c58a020d47b426954b590f8" +registries = "General" +uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" +version = "1.16.0" + +[[deps.DifferentiationInterface]] +deps = ["ADTypes", "LinearAlgebra"] +git-tree-sha1 = "0693d8b0a4608ff289d228ab4c598df5894845cd" +registries = "General" +uuid = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" +version = "0.7.21" + + [deps.DifferentiationInterface.extensions] + DifferentiationInterfaceChainRulesCoreExt = "ChainRulesCore" + DifferentiationInterfaceDiffractorExt = "Diffractor" + DifferentiationInterfaceEnzymeExt = ["EnzymeCore", "Enzyme"] + DifferentiationInterfaceFastDifferentiationExt = "FastDifferentiation" + DifferentiationInterfaceFiniteDiffExt = "FiniteDiff" + DifferentiationInterfaceFiniteDifferencesExt = "FiniteDifferences" + DifferentiationInterfaceForwardDiffExt = ["ForwardDiff", "DiffResults"] + DifferentiationInterfaceGPUArraysCoreExt = ["GPUArraysCore", "Adapt"] + DifferentiationInterfaceGTPSAExt = "GTPSA" + DifferentiationInterfaceHyperHessiansExt = "HyperHessians" + DifferentiationInterfaceMooncakeExt = "Mooncake" + DifferentiationInterfacePolyesterForwardDiffExt = ["PolyesterForwardDiff", "ForwardDiff", "DiffResults"] + DifferentiationInterfaceReverseDiffExt = ["ReverseDiff", "DiffResults"] + DifferentiationInterfaceSparseArraysExt = "SparseArrays" + DifferentiationInterfaceSparseConnectivityTracerExt = "SparseConnectivityTracer" + DifferentiationInterfaceSparseMatrixColoringsExt = "SparseMatrixColorings" + DifferentiationInterfaceStaticArraysExt = "StaticArrays" + DifferentiationInterfaceSymbolicsExt = "Symbolics" + DifferentiationInterfaceTrackerExt = "Tracker" + DifferentiationInterfaceZygoteExt = ["Zygote", "ForwardDiff"] + + [deps.DifferentiationInterface.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DiffResults = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" + Diffractor = "9f5e2b26-1114-432f-b630-d3fe2085c51c" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + FastDifferentiation = "eb9bf01b-bf85-4b60-bf87-ee5de06c00be" + FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41" + FiniteDifferences = "26cc04aa-876d-5657-8c51-4c34ba976000" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" + GTPSA = "b27dd330-f138-47c5-815b-40db9dd9b6e8" + HyperHessians = "06b494a0-c8e0-40cc-ad32-d99506a00a6c" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PolyesterForwardDiff = "98d1487c-24ca-40b6-b7ab-df2af84e126b" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.Distributed]] +deps = ["Random", "Serialization", "Sockets"] +uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" +version = "1.11.0" + +[[deps.DocStringExtensions]] +git-tree-sha1 = "7442a5dfe1ebb773c29cc2962a8980f47221d76c" +registries = "General" +uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" +version = "0.9.5" + +[[deps.Documenter]] +deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] +git-tree-sha1 = "191e6bef0cf32cac3a3913cd4787d851715254c2" +registries = "General" +uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" +version = "1.19.0" + +[[deps.DoubleFloats]] +deps = ["GenericLinearAlgebra", "GenericSchur", "LinearAlgebra", "Printf", "Quadmath", "Random", "SpecialFunctions"] +git-tree-sha1 = "00720e467f998dc28d6bd3ec2fe33b1d04155f6c" +registries = "General" +uuid = "497a8b3b-efae-58df-a0af-a86822472b78" +version = "1.12.0" + +[[deps.Downloads]] +deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] +uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" +version = "1.7.0" + +[[deps.EnumX]] +git-tree-sha1 = "c49898e8438c828577f04b92fc9368c388ac783c" +registries = "General" +uuid = "4e289a0a-7415-4d19-859d-a7e5c4648b56" +version = "1.0.7" + +[[deps.EnzymeCore]] +git-tree-sha1 = "04c4269956f90d8f5c9acc73b774c5293481f4d7" +registries = "General" +uuid = "f151be2c-9106-41f4-ab19-57ee4f262869" +version = "0.8.26" + + [deps.EnzymeCore.extensions] + AdaptExt = "Adapt" + EnzymeCoreChainRulesCoreExt = "ChainRulesCore" + + [deps.EnzymeCore.weakdeps] + Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.EpollShim_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "8a4be429317c42cfae6a7fc03c31bad1970c310d" +registries = "General" +uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" +version = "0.0.20230411+1" + +[[deps.ExactPredicates]] +deps = ["IntervalArithmetic", "Random", "StaticArrays"] +git-tree-sha1 = "83231673ea4d3d6008ac74dc5079e77ab2209d8f" +registries = "General" +uuid = "429591f6-91af-11e9-00e2-59fbe8cec110" +version = "2.2.9" + +[[deps.ExceptionUnwrapping]] +deps = ["Test"] +git-tree-sha1 = "4e468f521e1f9f86891cb07186de5df90360a666" +registries = "General" +uuid = "460bff9d-24e4-43bc-9d9f-a8973cb893f4" +version = "0.1.12" + +[[deps.Expat_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d0f666deaf8099a7d7ac229a74402ada88898be1" +registries = "General" +uuid = "2e619515-83b5-522b-bb60-26c02a35a201" +version = "2.9.0+0" + +[[deps.ExponentialUtilities]] +deps = ["Adapt", "ArrayInterface", "GPUArraysCore", "GenericSchur", "LinearAlgebra", "PrecompileTools", "Printf", "SparseArrays", "libblastrampoline_jll"] +git-tree-sha1 = "a3c8b66f8813b721a45bfe3cf32acf793aed78bb" +registries = "General" +uuid = "d4d017d3-3776-5f7e-afef-a10c40355c18" +version = "1.31.0" +weakdeps = ["StaticArrays"] + + [deps.ExponentialUtilities.extensions] + ExponentialUtilitiesStaticArraysExt = "StaticArrays" + +[[deps.ExprTools]] +git-tree-sha1 = "d2e49e7efd29719d6f28b891b0e0e159daa9d2b4" +registries = "General" +uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" +version = "0.1.11" + +[[deps.ExproniconLite]] +git-tree-sha1 = "c13f0b150373771b0fdc1713c97860f8df12e6c2" +registries = "General" +uuid = "55351af7-c7e9-48d6-89ff-24e801d99491" +version = "0.10.14" + +[[deps.FFMPEG]] +deps = ["FFMPEG_jll"] +git-tree-sha1 = "7bd13840b4148949e290748071bbe6826b3eabcd" +registries = "General" +uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" +version = "0.4.6" + +[[deps.FFMPEG_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libva_jll", "libvorbis_jll", "x264_jll", "x265_jll"] +git-tree-sha1 = "d9d3cd382f2c999f684e6bfa4039fcc16c153b6f" +registries = "General" +uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" +version = "9.0.2+0" + +[[deps.FFTW]] +deps = ["AbstractFFTs", "FFTW_jll", "Libdl", "LinearAlgebra", "MKL_jll", "Preferences", "Reexport"] +git-tree-sha1 = "97f08406df914023af55ade2f843c39e99c5d969" +registries = "General" +uuid = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" +version = "1.10.0" + +[[deps.FFTW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6866aec60ef98e3164cd8d6855225684207e9dff" +registries = "General" +uuid = "f5851436-0d7a-5f13-b9de-f02708fd171a" +version = "3.3.12+0" + +[[deps.FastBroadcast]] +deps = ["ArrayInterface", "LinearAlgebra", "PrecompileTools"] +git-tree-sha1 = "b90aee4f191474eeda3dfa0cb588635f80820cf1" +registries = "General" +uuid = "7034ab61-46d4-4ed7-9d0f-46aef9175898" +version = "1.5.0" + + [deps.FastBroadcast.extensions] + FastBroadcastPolyesterExt = "Polyester" + FastBroadcastStaticExt = "Static" + FixedSizeArraysExt = "FixedSizeArrays" + + [deps.FastBroadcast.weakdeps] + FixedSizeArrays = "3821ddf9-e5b5-40d5-8e25-6813ab96b5e2" + Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" + Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" + +[[deps.FastClosures]] +git-tree-sha1 = "acebe244d53ee1b461970f8910c235b259e772ef" +registries = "General" +uuid = "9aa1b823-49e4-5ca5-8b0f-3971ec8bab6a" +version = "0.3.2" + +[[deps.FastGaussQuadrature]] +deps = ["LinearAlgebra", "SpecialFunctions", "StaticArrays"] +git-tree-sha1 = "4916117dd032ec5959b7633aedbbac408ca5ddeb" +registries = "General" +uuid = "442a2c76-b920-505d-bb47-c5924d526838" +version = "1.3.0" + +[[deps.FastInterpolations]] +deps = ["AdaptiveArrayPools", "HelpPlots", "LinearAlgebra", "Preferences", "Printf"] +git-tree-sha1 = "f7bcd496f0a2e4e1174e7e1f667f27fb671fd7df" +registries = "General" +uuid = "9ea80cae-fc13-4c00-8066-6eaedb12f34b" +version = "0.4.20" + + [deps.FastInterpolations.extensions] + FastInterpolationsChainRulesCoreExt = "ChainRulesCore" + FastInterpolationsColorVectorSpaceExt = ["ColorTypes", "ColorVectorSpace"] + FastInterpolationsEnzymeExt = "Enzyme" + FastInterpolationsForwardDiffExt = "ForwardDiff" + FastInterpolationsRecipesBaseExt = "RecipesBase" + FastInterpolationsSymbolicsExt = ["Symbolics", "SymbolicUtils"] + + [deps.FastInterpolations.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" + ColorVectorSpace = "c3611d14-8923-5661-9e6a-0046d554d3a4" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" + Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7" + +[[deps.FastPower]] +deps = ["PrecompileTools"] +git-tree-sha1 = "a6bcbd090fc6b6f022fc0ad96f77ba31b9577282" +registries = "General" +uuid = "a4df4552-cc26-4903-aec0-212e50a0e84b" +version = "1.5.1" + + [deps.FastPower.extensions] + FastPowerEnzymeExt = "Enzyme" + FastPowerForwardDiffExt = "ForwardDiff" + FastPowerMeasurementsExt = "Measurements" + FastPowerMonteCarloMeasurementsExt = "MonteCarloMeasurements" + FastPowerMooncakeExt = "Mooncake" + FastPowerReverseDiffExt = "ReverseDiff" + FastPowerTrackerExt = "Tracker" + + [deps.FastPower.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.FileIO]] +deps = ["Pkg", "Requires", "UUIDs"] +git-tree-sha1 = "6621fef488e496356c9c9625d0562c12a6070819" +registries = "General" +uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" +version = "1.20.0" +weakdeps = ["HTTP"] + + [deps.FileIO.extensions] + HTTPExt = "HTTP" + +[[deps.FileWatching]] +uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" +version = "1.11.0" + +[[deps.FillArrays]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "831f86fde78ab7f6470d6ba4d1161a11fb6b4ec0" +registries = "General" +uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" +version = "1.18.2" + + [deps.FillArrays.extensions] + FillArraysPDMatsExt = "PDMats" + FillArraysSparseArraysExt = "SparseArrays" + FillArraysStaticArraysExt = "StaticArrays" + FillArraysStatisticsExt = "Statistics" + + [deps.FillArrays.weakdeps] + PDMats = "90014a1f-27ba-587c-ab20-58faa44d9150" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.FindFirstFunctions]] +deps = ["PrecompileTools"] +git-tree-sha1 = "27b495de668ccea58de6b06d6d13181396598ea0" +registries = "General" +uuid = "64ca27bc-2ba2-4a57-88aa-44e436879224" +version = "1.8.0" + +[[deps.FiniteDiff]] +deps = ["ArrayInterface", "LinearAlgebra", "Setfield"] +git-tree-sha1 = "5031f23e040bf17082e5b52422d77b5e844eefb1" +registries = "General" +uuid = "6a86dc24-6348-571c-b903-95158fe2bd41" +version = "2.33.0" + + [deps.FiniteDiff.extensions] + FiniteDiffBandedMatricesExt = "BandedMatrices" + FiniteDiffBlockBandedMatricesExt = "BlockBandedMatrices" + FiniteDiffSparseArraysExt = "SparseArrays" + FiniteDiffStaticArraysExt = "StaticArrays" + + [deps.FiniteDiff.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.FixedPointNumbers]] +deps = ["Random", "Statistics"] +git-tree-sha1 = "59af96b98217c6ef4ae0dfe065ac7c20831d1a84" +registries = "General" +uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" +version = "0.8.6" + +[[deps.Fontconfig_jll]] +deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Zlib_jll"] +git-tree-sha1 = "e1e9ba3a8f5d71e6ae698f138a1362aeabb42e9f" +registries = "General" +uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" +version = "2.18.3+0" + +[[deps.Format]] +git-tree-sha1 = "9c68794ef81b08086aeb32eeaf33531668d5f5fc" +registries = "General" +uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" +version = "1.3.7" + +[[deps.ForwardDiff]] +deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] +git-tree-sha1 = "3b0f72e2ffef1a139ac450725c9ecd01c0a3c050" +registries = "General" +uuid = "f6369f11-7733-5829-9624-2563aa707210" +version = "1.4.6" +weakdeps = ["StaticArrays"] + + [deps.ForwardDiff.extensions] + ForwardDiffStaticArraysExt = "StaticArrays" + +[[deps.FreeType2_jll]] +deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "d95a7a3a7e0f2fffc6291bd33035830aab01310b" +registries = "General" +uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" +version = "2.15.0+0" + +[[deps.FriBidi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "f3f50c8001bdfa84f312234fa86715b7c9243771" +registries = "General" +uuid = "559328eb-81f9-559d-9380-de523a88c83c" +version = "1.0.18+0" + +[[deps.FunctionWrappers]] +git-tree-sha1 = "d62485945ce5ae9c0c48f124a84998d755bae00e" +registries = "General" +uuid = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e" +version = "1.1.3" + +[[deps.FunctionWrappersWrappers]] +deps = ["FunctionWrappers", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "2bcce3ad6f6977d617928d7707fdc86ac83cce03" +registries = "General" +uuid = "77dc65aa-8811-40c2-897b-53d922fa7daf" +version = "1.13.0" + + [deps.FunctionWrappersWrappers.extensions] + FunctionWrappersWrappersEnzymeExt = ["Enzyme", "EnzymeCore"] + FunctionWrappersWrappersMooncakeExt = "Mooncake" + + [deps.FunctionWrappersWrappers.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + +[[deps.FunctionalCollections]] +deps = ["Test"] +git-tree-sha1 = "04cb9cfaa6ba5311973994fe3496ddec19b6292a" +registries = "General" +uuid = "de31a74c-ac4f-5751-b3fd-e18cd04993ca" +version = "0.5.0" + +[[deps.Future]] +deps = ["Random"] +uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" +version = "1.11.0" + +[[deps.GLFW_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll", "libdecor_jll", "xkbcommon_jll"] +git-tree-sha1 = "64bbbb7d1499297751b536dd39c58b20750ab1db" +registries = "General" +uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" +version = "3.5.1+0" + +[[deps.GPUArraysCore]] +deps = ["Adapt"] +git-tree-sha1 = "0b2b3e8b8fb7c1f5ddbf498be2eb2b3b37c9c0ed" +registries = "General" +uuid = "46192b85-c4d5-4398-a991-12ede77f4527" +version = "0.2.1" + +[[deps.GR]] +deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "JSON", "Libdl", "LinearAlgebra", "Preferences", "Printf", "Qt6Wayland_jll", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "p7zip_jll"] +git-tree-sha1 = "4d777f73c46b46b8b5276206059cf8a195499314" +registries = "General" +uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" +version = "0.73.27" + + [deps.GR.extensions] + IJuliaExt = "IJulia" + + [deps.GR.weakdeps] + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + +[[deps.GR_jll]] +deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] +git-tree-sha1 = "f8eb8f7ba13ea75083531647fc8faeda8d541f07" +registries = "General" +uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" +version = "0.73.27+0" + +[[deps.GeneralizedPerturbedEquilibrium]] +deps = ["AdaptiveArrayPools", "CommonSolve", "Contour", "Dates", "DelaunayTriangulation", "DelimitedFiles", "DiffEqCallbacks", "Documenter", "DoubleFloats", "FFTW", "FastGaussQuadrature", "FastInterpolations", "HDF5", "IMASdd", "JLD2", "LaTeXStrings", "LinearAlgebra", "Logging", "OrdinaryDiffEq", "Pkg", "PlotlyJS", "Plots", "PrecompileTools", "Printf", "QuadGK", "Random", "Roots", "SparseArrays", "SpecialFunctions", "StaticArrays", "Statistics", "TOML", "Test"] +path = "." +uuid = "462872dd-e066-4d2e-b993-6468b5239634" +version = "0.1.0" + +[[deps.GenericLinearAlgebra]] +deps = ["LinearAlgebra", "Printf", "Random", "libblastrampoline_jll"] +git-tree-sha1 = "961f8a5d33f043e02cdf2b8020e3a7b0aae78474" +registries = "General" +uuid = "14197337-ba66-59df-a3e3-ca00e7dcff7a" +version = "0.4.1" + +[[deps.GenericSchur]] +deps = ["LinearAlgebra", "MatrixFactorizations", "Preferences", "Printf", "UUIDs"] +git-tree-sha1 = "e3d1202ab9ae72edd4c46134ecbb6746ec420486" +registries = "General" +uuid = "c145ed77-6b09-5dd9-b285-bf645a82121e" +version = "0.5.8" + +[[deps.GettextRuntime_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll"] +git-tree-sha1 = "45288942190db7c5f760f59c04495064eedf9340" +registries = "General" +uuid = "b0724c58-0f36-5564-988d-3bb0596ebc4a" +version = "0.22.4+0" + +[[deps.Ghostscript_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Zlib_jll"] +git-tree-sha1 = "38044a04637976140074d0b0621c1edf0eb531fd" +registries = "General" +uuid = "61579ee1-b43e-5ca0-a5da-69d92c66a64b" +version = "9.55.1+0" + +[[deps.Git]] +deps = ["Git_LFS_jll", "Git_jll", "JLLWrappers", "OpenSSH_jll"] +git-tree-sha1 = "824a1890086880696fc908fe12a17bcf61738bd8" +registries = "General" +uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" +version = "1.5.0" + +[[deps.Git_LFS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "d310b22511a51569401e975194cc76077bcd3786" +registries = "General" +uuid = "020c3dae-16b3-5ae5-87b3-4cb189e250b2" +version = "3.8.1+0" + +[[deps.Git_jll]] +deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "3842d761162baf8efce819cdfb5ea070fe44ac40" +registries = "General" +uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" +version = "2.56.0+0" + +[[deps.Glib_jll]] +deps = ["Artifacts", "GettextRuntime_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] +git-tree-sha1 = "ea9f1fb37ff10597353b56f3ba0ebd525acef6e0" +registries = "General" +uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" +version = "2.90.1+0" + +[[deps.Graphite2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "69ffb934a5c5b7e086a0b4fee3427db2556fba6e" +registries = "General" +uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" +version = "1.3.16+0" + +[[deps.HDF5]] +deps = ["Compat", "HDF5_jll", "Libdl", "MPIPreferences", "Mmap", "Preferences", "Printf", "Random", "Requires", "UUIDs"] +git-tree-sha1 = "26e37af34e8ccb7a8358bd8de1619c7bcb526738" +registries = "General" +uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" +version = "0.17.4" + + [deps.HDF5.extensions] + MPIExt = "MPI" + + [deps.HDF5.weakdeps] + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + +[[deps.HDF5_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "MPIABI_jll", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "aws_c_s3_jll", "dlfcn_win32_jll", "libaec_jll", "mpif_jll"] +git-tree-sha1 = "58d79d3a61d2fdeda667fead57546ae22a20b27a" +registries = "General" +uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" +version = "2.2.3+0" + +[[deps.HTTP]] +deps = ["Base64", "CodecZlib", "ConcurrentUtilities", "Dates", "ExceptionUnwrapping", "Logging", "LoggingExtras", "MbedTLS", "NetworkOptions", "OpenSSL", "PrecompileTools", "Random", "SimpleBufferStream", "Sockets", "URIs", "UUIDs"] +git-tree-sha1 = "49a7885f0e1c0ecabd51b25f2fd14f26357777e0" +registries = "General" +uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" +version = "1.11.1" + +[[deps.HarfBuzz_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "f700c32c1e9e11b576592043f270e6b33868a56f" +registries = "General" +uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" +version = "100.15000.0+0" + +[[deps.HashArrayMappedTries]] +git-tree-sha1 = "2eaa69a7cab70a52b9687c8bf950a5a93ec895ae" +registries = "General" +uuid = "076d061b-32b6-4027-95e0-9a2c6f6d7e74" +version = "0.2.0" + +[[deps.HelpPlots]] +git-tree-sha1 = "07d389d81ca31e42d68bd88cfa1e900a63b7d378" +registries = "General" +uuid = "5880b2e8-ad0a-48c9-bacd-30dc9c1357e0" +version = "1.1.0" +weakdeps = ["Plots"] + + [deps.HelpPlots.extensions] + HelpPlotsExt = "Plots" + +[[deps.Hiccup]] +deps = ["MacroTools", "Test"] +git-tree-sha1 = "6187bb2d5fcbb2007c39e7ac53308b0d371124bd" +registries = "General" +uuid = "9fb69e20-1954-56bb-a84f-559cc56a8ff7" +version = "0.2.2" + +[[deps.Hwloc_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "XML2_jll", "Xorg_libpciaccess_jll"] +git-tree-sha1 = "cbbd99d516990d05c02aa132aa077b2bf50ae323" +registries = "General" +uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +version = "2.15.0+0" + +[[deps.IMASdd]] +deps = ["AbstractTrees", "AdaptiveArrayPools", "CoordinateConventions", "DataInterpolations", "Dates", "FastInterpolations", "HDF5", "InteractiveUtils", "JSON", "Memoization", "OrderedCollections", "PrecompileTools", "Preferences", "Printf", "StaticArraysCore", "ThreadSafeDicts", "TimerOutputs"] +git-tree-sha1 = "2284c64e11d8fb6225971bcc367c67bae51923f5" +registries = "General" +uuid = "c5a45a97-b3f9-491c-b9a7-aa88c3bc0067" +version = "8.6.2" + +[[deps.IOCapture]] +deps = ["Logging", "Random"] +git-tree-sha1 = "0ee181ec08df7d7c911901ea38baf16f755114dc" +registries = "General" +uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" +version = "1.0.0" + +[[deps.IfElse]] +git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" +registries = "General" +uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" +version = "0.1.1" + +[[deps.InlineStrings]] +git-tree-sha1 = "06b65886c7577a3784d616e29f1302c2e36e389d" +registries = "General" +uuid = "842dd82b-1e85-43dc-bf29-5d0ee9dffc48" +version = "1.4.6" + + [deps.InlineStrings.extensions] + ArrowTypesExt = "ArrowTypes" + ParsersExt = "Parsers" + + [deps.InlineStrings.weakdeps] + ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" + Parsers = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" + +[[deps.IntelOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "ec1debd61c300961f98064cfb21287613ad7f303" +registries = "General" +uuid = "1d5cc7b8-4909-519e-a0f8-d0f5ad9712d0" +version = "2025.2.0+0" + +[[deps.InteractiveUtils]] +deps = ["Markdown"] +uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" +version = "1.11.0" + +[[deps.IntervalArithmetic]] +deps = ["CRlibm", "CoreMath", "MacroTools", "OpenBLASConsistentFPCSR_jll", "Printf", "Random", "RoundingEmulator"] +git-tree-sha1 = "1c531bf0f8a5c60a340926e058fd3f209b5eef5d" +registries = "General" +uuid = "d1acc4aa-44c8-5952-acd4-ba5d80a2a253" +version = "1.0.12" + + [deps.IntervalArithmetic.extensions] + IntervalArithmeticArblibExt = "Arblib" + IntervalArithmeticDiffRulesExt = "DiffRules" + IntervalArithmeticForwardDiffExt = "ForwardDiff" + IntervalArithmeticIntervalSetsExt = "IntervalSets" + IntervalArithmeticIrrationalConstantsExt = "IrrationalConstants" + IntervalArithmeticLinearAlgebraExt = "LinearAlgebra" + IntervalArithmeticMakieExt = "Makie" + IntervalArithmeticRecipesBaseExt = "RecipesBase" + IntervalArithmeticSparseArraysExt = "SparseArrays" + + [deps.IntervalArithmetic.weakdeps] + Arblib = "fb37089c-8514-4489-9461-98f9c8763369" + DiffRules = "b552c78f-8df3-52c6-915a-8e097449b14b" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" + IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6" + LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.InverseFunctions]] +git-tree-sha1 = "5cc6ad3e2bfc75a11eed92632ee341d37f4a4536" +registries = "General" +uuid = "3587e190-3f89-42d0-90ee-14403ec27112" +version = "0.1.18" +weakdeps = ["Dates", "Test"] + + [deps.InverseFunctions.extensions] + InverseFunctionsDatesExt = "Dates" + InverseFunctionsTestExt = "Test" + +[[deps.InvertedIndices]] +git-tree-sha1 = "6da3c4316095de0f5ee2ebd875df8721e7e0bdbe" +registries = "General" +uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" +version = "1.3.1" + +[[deps.IrrationalConstants]] +git-tree-sha1 = "d22e8bdb3f03c300a103ff23c0dd5a654c6701de" +registries = "General" +uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" +version = "0.2.7" + +[[deps.IteratorInterfaceExtensions]] +git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" +registries = "General" +uuid = "82899510-4779-5014-852e-03e436cf321d" +version = "1.0.0" + +[[deps.JLD2]] +deps = ["ChunkCodecCore", "ChunkCodecLibZlib", "ChunkCodecLibZstd", "FileIO", "MacroTools", "Mmap", "OrderedCollections", "PrecompileTools", "ScopedValues"] +git-tree-sha1 = "9ce2e7c49ae4a7035b7d60db3a553b1f6c16875d" +registries = "General" +uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" +version = "0.6.7" +weakdeps = ["UnPack"] + + [deps.JLD2.extensions] + UnPackExt = "UnPack" + +[[deps.JLFzf]] +deps = ["REPL", "Random", "fzf_jll"] +git-tree-sha1 = "82f7acdc599b65e0f8ccd270ffa1467c21cb647b" +registries = "General" +uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" +version = "0.1.11" + +[[deps.JLLWrappers]] +deps = ["Artifacts", "Preferences"] +git-tree-sha1 = "7204148362dafe5fe6a273f855b8ccbe4df8173e" +registries = "General" +uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" +version = "1.8.0" + +[[deps.JSExpr]] +deps = ["JSON", "MacroTools", "Observables", "WebIO"] +git-tree-sha1 = "b413a73785b98474d8af24fd4c8a975e31df3658" +registries = "General" +uuid = "97c1335a-c9c5-57fe-bc5d-ec35cebe8660" +version = "0.5.4" + +[[deps.JSON]] +deps = ["Dates", "Mmap", "Parsers", "Unicode"] +git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" +registries = "General" +uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +version = "0.21.4" + +[[deps.Jieko]] +deps = ["ExproniconLite"] +git-tree-sha1 = "2f05ed29618da60c06a87e9c033982d4f71d0b6c" +registries = "General" +uuid = "ae98c720-c025-4a4a-838c-29b094483192" +version = "0.2.1" + +[[deps.JpegTurbo_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "037babc10853eeb8e585418922246cb97b8e5b74" +registries = "General" +uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" +version = "3.2.0+1" + +[[deps.JuliaSyntaxHighlighting]] +deps = ["StyledStrings"] +uuid = "ac6e5ff7-fb65-4e79-a425-ec3bc9c03011" +version = "1.12.0" + +[[deps.Kaleido_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "43032da5832754f58d14a91ffbe86d5f176acda9" +registries = "General" +uuid = "f7e6163d-2fa5-5f23-b69c-1db539e41963" +version = "0.2.1+0" + +[[deps.Krylov]] +deps = ["LinearAlgebra", "Printf", "SparseArrays"] +git-tree-sha1 = "b13076e4fff6ea0d0a5ddd9b939a11ca2027755a" +registries = "General" +uuid = "ba0b0d4f-ebba-5204-a429-3ac8c609bfb7" +version = "0.10.12" + +[[deps.LAME_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "059aabebaa7c82ccb853dd4a0ee9d17796f7e1bc" +registries = "General" +uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" +version = "3.100.3+0" + +[[deps.LERC_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "39bca05343661c347aae0bca57a5994a0bf4f08d" +registries = "General" +uuid = "88015f11-f218-50d7-93a8-a6af411a945d" +version = "4.2.0+0" + +[[deps.LLVMOpenMP_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e5b100780d4d30d63b4618d7930d48af409c1772" +registries = "General" +uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" +version = "23.1.1+0" + +[[deps.LaTeXStrings]] +git-tree-sha1 = "f88f3ccef05a6a72a0cf0ed417c8fd68530f4ab2" +registries = "General" +uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" +version = "1.4.1" + +[[deps.Latexify]] +deps = ["Format", "Ghostscript_jll", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] +git-tree-sha1 = "df7566479bd64f20bd16b09960145e70160ffb3b" +registries = "General" +uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" +version = "0.16.12" + + [deps.Latexify.extensions] + DataFramesExt = "DataFrames" + SparseArraysExt = "SparseArrays" + SymEngineExt = "SymEngine" + TectonicExt = "tectonic_jll" + + [deps.Latexify.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" + tectonic_jll = "d7dd28d6-a5e6-559c-9131-7eb760cdacc5" + +[[deps.LayoutPointers]] +deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] +git-tree-sha1 = "a9eaadb366f5493a5654e843864c13d8b107548c" +registries = "General" +uuid = "10f19ff3-798f-405d-979b-55457f8fc047" +version = "0.1.17" + +[[deps.LazilyInitializedFields]] +git-tree-sha1 = "0f2da712350b020bc3957f269c9caad516383ee0" +registries = "General" +uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" +version = "1.3.0" + +[[deps.Lazy]] +deps = ["MacroTools"] +git-tree-sha1 = "1370f8202dac30758f3c345f9909b97f53d87d3f" +registries = "General" +uuid = "50d2b5c4-7a5e-59d5-8109-a42b560f39c0" +version = "0.15.1" + +[[deps.LazyArtifacts]] +deps = ["Artifacts", "Pkg"] +uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" +version = "1.11.0" + +[[deps.LibCURL]] +deps = ["LibCURL_jll", "MozillaCACerts_jll"] +uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" +version = "1.0.0" + +[[deps.LibCURL_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "LibSSH2_jll", "Libdl", "OpenSSL_jll", "Zlib_jll", "Zstd_jll", "nghttp2_jll"] +uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" +version = "8.22.0+0" + +[[deps.LibGit2]] +deps = ["LibGit2_jll", "NetworkOptions", "Printf", "SHA"] +uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" +version = "1.11.0" + +[[deps.LibGit2_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "LibSSH2_jll", "Libdl", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] +uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" +version = "1.9.7+0" + +[[deps.LibSSH2_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl", "OpenSSL_jll", "Zlib_jll"] +uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" +version = "1.11.104+0" + +[[deps.Libdl]] +uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" +version = "1.11.0" + +[[deps.Libffi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c8da7e6a91781c41a863611c7e966098d783c57a" +registries = "General" +uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" +version = "3.4.7+0" + +[[deps.Libglvnd_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll"] +git-tree-sha1 = "d36c21b9e7c172a44a10484125024495e2625ac0" +registries = "General" +uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" +version = "1.7.1+1" + +[[deps.Libiconv_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "be484f5c92fad0bd8acfef35fe017900b0b73809" +registries = "General" +uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" +version = "1.18.0+0" + +[[deps.Libmount_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b8a91037379e1fd661e9b5ff3d7a770424b1f9f2" +registries = "General" +uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" +version = "2.42.4+0" + +[[deps.Libtiff_jll]] +deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] +git-tree-sha1 = "aebd334d06cee9f24cea70bd19a39749daf73881" +registries = "General" +uuid = "89763e89-9b03-5906-acba-b20f662cd828" +version = "4.7.3+0" + +[[deps.Libuuid_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "9e887184f306b28dbfcff88cefbb0ec80f1aa9e0" +registries = "General" +uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" +version = "2.42.4+0" + +[[deps.LineSearch]] +deps = ["ADTypes", "CommonSolve", "ConcreteStructs", "FastClosures", "LinearAlgebra", "MaybeInplace", "PrecompileTools", "SciMLBase", "SciMLJacobianOperators", "StaticArraysCore"] +git-tree-sha1 = "2e05027f5a68891d997fcad60d11ce48d09208d0" +registries = "General" +uuid = "87fe0de2-c867-4266-b59a-2f0a94fc965b" +version = "0.1.14" +weakdeps = ["LineSearches"] + + [deps.LineSearch.extensions] + LineSearchLineSearchesExt = "LineSearches" + +[[deps.LineSearches]] +deps = ["LinearAlgebra", "NLSolversBase", "NaNMath", "Printf"] +git-tree-sha1 = "a09a85e94e681def2446752cddaf9401a4d82b6d" +registries = "General" +uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" +version = "7.8.2" + +[[deps.LinearAlgebra]] +deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] +uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +version = "1.13.0" + +[[deps.LinearSolve]] +deps = ["AMD", "ArrayInterface", "ConcreteStructs", "DocStringExtensions", "EnumX", "GPUArraysCore", "InteractiveUtils", "Krylov", "Libdl", "LinearAlgebra", "MKL_jll", "Markdown", "OpenBLAS_jll", "PrecompileTools", "Preferences", "PureKLU", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "Setfield", "SparseArrays", "SparseColumnPivotedQR", "StaticArraysCore"] +git-tree-sha1 = "ec49ed72f6024be2f9833fe630fbd72f6048f8ad" +registries = "General" +uuid = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" +version = "3.87.0" + + [deps.LinearSolve.extensions] + LinearSolveAMDGPUExt = "AMDGPU" + LinearSolveAlgebraicMultigridExt = "AlgebraicMultigrid" + LinearSolveBLISExt = ["blis_jll", "LAPACK_jll"] + LinearSolveBandedMatricesExt = "BandedMatrices" + LinearSolveBlockDiagonalsExt = "BlockDiagonals" + LinearSolveCUDAExt = ["cuSOLVER"] + LinearSolveCUDSSExt = "CUDSS" + LinearSolveCUSOLVERRFExt = ["CUSOLVERRF", "SparseArrays"] + LinearSolveChainRulesCoreExt = "ChainRulesCore" + LinearSolveCliqueTreesExt = ["CliqueTrees", "SparseArrays"] + LinearSolveElementalExt = "Elemental" + LinearSolveEnzymeExt = ["EnzymeCore", "SparseArrays"] + LinearSolveFastAlmostBandedMatricesExt = "FastAlmostBandedMatrices" + LinearSolveFastLapackInterfaceExt = "FastLapackInterface" + LinearSolveForwardDiffExt = "ForwardDiff" + LinearSolveGinkgoExt = ["Ginkgo", "SparseArrays"] + LinearSolveHSLExt = ["HSL", "SparseArrays"] + LinearSolveHYPREExt = "HYPRE" + LinearSolveIterativeSolversExt = "IterativeSolvers" + LinearSolveKernelAbstractionsExt = "KernelAbstractions" + LinearSolveKrylovKitExt = "KrylovKit" + LinearSolveMUMPSExt = ["MUMPS", "SparseArrays"] + LinearSolveMetalExt = "Metal" + LinearSolveMooncakeExt = "Mooncake" + LinearSolvePETScExt = ["PETSc", "SparseArrays", "SparseMatricesCSR"] + LinearSolvePETScMPIExt = ["PETSc", "PartitionedArrays", "SparseArrays", "SparseMatricesCSR"] + LinearSolveParUExt = ["ParU_jll", "SparseArrays"] + LinearSolvePardisoExt = ["Pardiso", "SparseArrays"] + LinearSolvePartitionedSolversExt = ["PartitionedArrays", "PartitionedSolvers"] + LinearSolvePureUMFPACKExt = ["PureUMFPACK", "SparseArrays"] + LinearSolveRecursiveFactorizationExt = "RecursiveFactorization" + LinearSolveSTRUMPACKExt = ["SparseArrays", "STRUMPACK_jll"] + LinearSolveSparseArraysExt = "SparseArrays" + LinearSolveSparspakExt = ["SparseArrays", "Sparspak"] + LinearSolveSpecializingFactorizationsExt = "SpecializingFactorizations" + LinearSolveSuperLUDISTExt = ["SparseArrays", "SuperLUDIST"] + + [deps.LinearSolve.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + AlgebraicMultigrid = "2169fc97-5a83-5252-b627-83903c6c433c" + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + BlockDiagonals = "0a1fb500-61f7-11e9-3c65-f5ef3456f9f0" + CUDSS = "45b445bb-4962-46a0-9369-b4df9d0f772e" + CUSOLVERRF = "a8cc9031-bad2-4722-94f5-40deabb4245c" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" + Elemental = "902c3f28-d1ec-5e7e-8399-a24c3845ee38" + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + FastAlmostBandedMatrices = "9d29842c-ecb8-4973-b1e9-a27b1157504e" + FastLapackInterface = "29a986be-02c6-4525-aec4-84b980013641" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + Ginkgo = "4c8bd3c9-ead9-4b5e-a625-08f1338ba0ec" + HSL = "34c5aeac-e683-54a6-a0e9-6e0fdc586c50" + HYPRE = "b5ffcf37-a2bd-41ab-a3da-4bd9bc8ad771" + IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" + LAPACK_jll = "51474c39-65e3-53ba-86ba-03b1b862ec14" + MUMPS = "55d2b088-9f4e-11e9-26c0-150b02ea6a46" + Metal = "dde4c033-4e86-420c-a63e-0dd931031962" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" + ParU_jll = "9e0b026c-e8ce-559c-a2c4-6a3d5c955bc9" + Pardiso = "46dd5b70-b6fb-5a00-ae2d-e8fea33afaf2" + PartitionedArrays = "5a9dfac6-5c52-46f7-8278-5e2210713be9" + PartitionedSolvers = "11b65f7f-80ac-401b-9ef2-3db765482d62" + PureUMFPACK = "b7e1f0a2-3c4d-4e5f-9a0b-1c2d3e4f5a6b" + RecursiveFactorization = "f2c3362d-daeb-58d1-803e-2bc74f2840b4" + STRUMPACK_jll = "86fbd0b9-476f-557c-b766-62c724b42d8c" + SparseMatricesCSR = "a0a7dd2c-ebf4-11e9-1f05-cf50bc540ca1" + Sparspak = "e56a9233-b9d6-4f03-8d0f-1825330902ac" + SpecializingFactorizations = "fa08b7a1-13d3-4faf-875d-5cbc1520e3f3" + SuperLUDIST = "4cd002a6-0da4-410d-a012-232df062f478" + blis_jll = "6136c539-28a5-5bf0-87cc-b183200dce32" + cuSOLVER = "887afef0-6a32-4de5-add4-7827692ba8fc" + +[[deps.LogExpFunctions]] +deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] +git-tree-sha1 = "28d620bcdb37a3fb02de9f521928ee453690f414" +registries = "General" +uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" +version = "1.0.3" + + [deps.LogExpFunctions.extensions] + LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" + LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" + LogExpFunctionsInverseFunctionsExt = "InverseFunctions" + + [deps.LogExpFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" + InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" + +[[deps.Logging]] +uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" +version = "1.11.0" + +[[deps.LoggingExtras]] +deps = ["Dates", "Logging"] +git-tree-sha1 = "f00544d95982ea270145636c181ceda21c4e2575" +registries = "General" +uuid = "e6f89c97-d47a-5376-807f-9c37f3926c36" +version = "1.2.0" + +[[deps.MKL_jll]] +deps = ["Artifacts", "IntelOpenMP_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "oneTBB_jll"] +git-tree-sha1 = "282cadc186e7b2ae0eeadbd7a4dffed4196ae2aa" +registries = "General" +uuid = "856f044c-d86e-5d09-b602-aeab76dc8ba7" +version = "2025.2.0+0" + +[[deps.MPIABI_jll]] +deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "42e2cada9f35500b4d227106ce2b60e85d9f15bc" +registries = "General" +uuid = "b5ada748-db0f-5fc0-8972-9331c762740c" +version = "1.0.1+0" + +[[deps.MPICH_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "08cb47f95a2f1b795e43a9e4f9783e68ecdc1ca6" +registries = "General" +uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" +version = "5.0.3+0" + +[[deps.MPIPreferences]] +deps = ["Libdl", "Preferences"] +git-tree-sha1 = "8e98d5d80b87403c311fd51e8455d4546ba7a5f8" +registries = "General" +uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" +version = "0.1.12" + +[[deps.MPItrampoline_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] +git-tree-sha1 = "09a99227205419c00e734f413ee98c18bc1b02e4" +registries = "General" +uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" +version = "5.5.7+0" + +[[deps.MacroTools]] +git-tree-sha1 = "1e0228a030642014fe5cfe68c2c0a818f9e3f522" +registries = "General" +uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" +version = "0.5.16" + +[[deps.ManualMemory]] +git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" +registries = "General" +uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" +version = "0.1.8" + +[[deps.Markdown]] +deps = ["Base64", "JuliaSyntaxHighlighting", "StyledStrings"] +uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" +version = "1.11.0" + +[[deps.MarkdownAST]] +deps = ["AbstractTrees", "Markdown"] +git-tree-sha1 = "93c718d892e73931841089cdc0e982d6dd9cc87b" +registries = "General" +uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" +version = "0.1.3" + +[[deps.MatrixFactorizations]] +deps = ["ArrayLayouts", "LinearAlgebra", "Printf", "Random"] +git-tree-sha1 = "076d0f90b1b06e17609cabb48bd2cbef28e7fc77" +registries = "General" +uuid = "a3b82374-2e81-5b9e-98ce-41277c0e4c87" +version = "3.2.0" + + [deps.MatrixFactorizations.extensions] + MatrixFactorizationsBandedMatricesExt = "BandedMatrices" + + [deps.MatrixFactorizations.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + +[[deps.MaybeInplace]] +deps = ["ArrayInterface", "LinearAlgebra", "MacroTools", "PrecompileTools"] +git-tree-sha1 = "f2cde0ae772162f20287b803e623966d0d94dea9" +registries = "General" +uuid = "bb5d69b7-63fc-4a16-80bd-7e42200c7bdb" +version = "0.1.8" +weakdeps = ["SparseArrays"] + + [deps.MaybeInplace.extensions] + MaybeInplaceSparseArraysExt = "SparseArrays" + +[[deps.MbedTLS]] +deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] +git-tree-sha1 = "0e4b139a1f0f0b8c054fcdfd3be5f610cfb8c787" +registries = "General" +uuid = "739be429-bea8-5141-9913-cc70e7f3736d" +version = "1.2.0" + +[[deps.MbedTLS_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "ff69a2b1330bcb730b9ac1ab7dd680176f5896b8" +registries = "General" +uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" +version = "2.28.1010+0" + +[[deps.Measures]] +git-tree-sha1 = "b513cedd20d9c914783d8ad83d08120702bf2c77" +registries = "General" +uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" +version = "0.3.3" + +[[deps.Memoization]] +deps = ["MacroTools"] +git-tree-sha1 = "7dbf904fa6c4447bd1f1d316886bfbe29feacf45" +registries = "General" +uuid = "6fafb56a-5788-4b4e-91ca-c0cea6611c73" +version = "0.2.2" + +[[deps.MicrosoftMPI_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] +git-tree-sha1 = "bc95bf4149bf535c09602e3acdf950d9b4376227" +registries = "General" +uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" +version = "10.1.4+3" + +[[deps.Missings]] +deps = ["DataAPI"] +git-tree-sha1 = "ec4f7fbeab05d7747bdf98eb74d130a2a2ed298d" +registries = "General" +uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" +version = "1.2.0" + +[[deps.Mmap]] +uuid = "a63ad114-7e13-5084-954f-fe012c677804" +version = "1.11.0" + +[[deps.Moshi]] +deps = ["ExproniconLite", "Jieko"] +git-tree-sha1 = "3f234cf937eb1d58f613440ecb022da784ad34d4" +registries = "General" +uuid = "2e0e35c7-a2e4-4343-998d-7ef72827ed2d" +version = "0.3.13" + +[[deps.MozillaCACerts_jll]] +uuid = "14a3606d-f60d-562e-9121-12d972cd8159" +version = "2026.10.20" + +[[deps.MuladdMacro]] +deps = ["PrecompileTools"] +git-tree-sha1 = "283bf85d4a767481dd924dff0eee1735e95f449e" +registries = "General" +uuid = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221" +version = "0.2.7" + +[[deps.Mustache]] +deps = ["DataFrames", "Printf", "Tables"] +git-tree-sha1 = "7437c911e62f50e7a043a774ebdc0dbd780d9fe5" +registries = "General" +uuid = "ffc61752-8dc7-55ee-8c37-f3e9cdd09e70" +version = "1.1.0" + +[[deps.Mux]] +deps = ["AssetRegistry", "Base64", "HTTP", "Hiccup", "MbedTLS", "Pkg", "Sockets"] +git-tree-sha1 = "7295d849103ac4fcbe3b2e439f229c5cc77b9b69" +registries = "General" +uuid = "a975b10e-0019-58db-a62f-e48ff68538c9" +version = "1.0.2" + +[[deps.NLSolversBase]] +deps = ["ADTypes", "DifferentiationInterface", "FiniteDiff", "LinearAlgebra"] +git-tree-sha1 = "f96d38936d92d610318dec4d3b5ef37b0373c20f" +registries = "General" +uuid = "d41bc354-129a-5804-8e4c-c37616107c6c" +version = "8.0.1" + +[[deps.NaNMath]] +deps = ["OpenLibm_jll"] +git-tree-sha1 = "676c0f1c1c34224d6869675047a54b42fdc7b8c5" +registries = "General" +uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" +version = "1.1.5" + +[[deps.NetworkOptions]] +uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" +version = "1.3.0" + +[[deps.NonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "NonlinearSolveBase", "NonlinearSolveFirstOrder", "NonlinearSolveQuasiNewton", "NonlinearSolveSpectralMethods", "PrecompileTools", "Preferences", "Reexport", "SciMLBase", "Setfield", "SimpleNonlinearSolve", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "a6c5719bbb42985c72f4cacbfa49e86bab850d66" +registries = "General" +uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" +version = "4.19.1" + + [deps.NonlinearSolve.extensions] + NonlinearSolveFastLevenbergMarquardtExt = "FastLevenbergMarquardt" + NonlinearSolveFixedPointAccelerationExt = "FixedPointAcceleration" + NonlinearSolveLeastSquaresOptimExt = "LeastSquaresOptim" + NonlinearSolveMINPACKExt = "MINPACK" + NonlinearSolveNLSolversExt = "NLSolvers" + NonlinearSolveNLsolveExt = ["NLsolve", "LineSearches"] + NonlinearSolvePETScExt = ["PETSc", "MPI", "SparseArrays"] + NonlinearSolveSIAMFANLEquationsExt = "SIAMFANLEquations" + NonlinearSolveSpeedMappingExt = "SpeedMapping" + NonlinearSolveSundialsExt = "Sundials" + + [deps.NonlinearSolve.weakdeps] + FastLevenbergMarquardt = "7a0df574-e128-4d35-8cbd-3d84502bf7ce" + FixedPointAcceleration = "817d07cb-a79a-5c30-9a31-890123675176" + LeastSquaresOptim = "0fc2ff8b-aaa3-5acd-a817-1944a5e08891" + LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255" + MINPACK = "4854310b-de5a-5eb6-a2a5-c1dee2bd17f9" + MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" + NLSolvers = "337daf1e-9722-11e9-073e-8b9effe078ba" + NLsolve = "2774e3e8-f4cf-5e23-947b-6d7e65073b56" + PETSc = "ace2c81b-2b5f-4b1e-a30d-d662738edfe0" + SIAMFANLEquations = "084e46ad-d928-497d-ad5e-07fa361a48c4" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SpeedMapping = "f1835b91-879b-4a3f-a438-e4baacf14412" + Sundials = "c3572dad-4567-51f8-b174-8c6c989267f4" + +[[deps.NonlinearSolveBase]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "Compat", "ConcreteStructs", "DifferentiationInterface", "EnzymeCore", "FastClosures", "FunctionWrappers", "FunctionWrappersWrappers", "LinearAlgebra", "LogExpFunctions", "Markdown", "MaybeInplace", "PreallocationTools", "PrecompileTools", "Preferences", "Printf", "RecursiveArrayTools", "SciMLBase", "SciMLJacobianOperators", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Setfield", "StaticArraysCore", "SymbolicIndexingInterface", "TimerOutputs"] +git-tree-sha1 = "cb824030a0d5769555704db95ee3cbe0f76ed53d" +registries = "General" +uuid = "be0214bd-f91f-a760-ac4e-3421ce2b2da0" +version = "2.30.3" + + [deps.NonlinearSolveBase.extensions] + NonlinearSolveBaseBandedMatricesExt = "BandedMatrices" + NonlinearSolveBaseChainRulesCoreExt = "ChainRulesCore" + NonlinearSolveBaseEnzymeExt = ["ChainRulesCore", "Enzyme"] + NonlinearSolveBaseForwardDiffExt = "ForwardDiff" + NonlinearSolveBaseLineSearchExt = "LineSearch" + NonlinearSolveBaseLinearSolveExt = "LinearSolve" + NonlinearSolveBaseMooncakeExt = "Mooncake" + NonlinearSolveBaseReverseDiffExt = "ReverseDiff" + NonlinearSolveBaseSparseArraysExt = "SparseArrays" + NonlinearSolveBaseSparseMatrixColoringsExt = "SparseMatrixColorings" + NonlinearSolveBaseTrackerExt = "Tracker" + + [deps.NonlinearSolveBase.weakdeps] + BandedMatrices = "aae01518-5342-5314-be14-df237901396f" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + LineSearch = "87fe0de2-c867-4266-b59a-2f0a94fc965b" + LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.NonlinearSolveFirstOrder]] +deps = ["ADTypes", "ArrayInterface", "CommonSolve", "ConcreteStructs", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLJacobianOperators", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "ce68820a4f421fb5bee7ec4dcf875aff33886bfb" +registries = "General" +uuid = "5959db7a-ea39-4486-b5fe-2dd0bf03d60d" +version = "2.1.1" + +[[deps.NonlinearSolveQuasiNewton]] +deps = ["ArrayInterface", "CommonSolve", "ConcreteStructs", "LinearAlgebra", "LinearSolve", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "SciMLOperators", "StaticArraysCore"] +git-tree-sha1 = "538432ca1aea8bf63db02929bf870501f8a7c64c" +registries = "General" +uuid = "9a2c21bd-3a47-402d-9113-8faf9a0ee114" +version = "1.13.1" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveQuasiNewton.extensions] + NonlinearSolveQuasiNewtonForwardDiffExt = "ForwardDiff" + +[[deps.NonlinearSolveSpectralMethods]] +deps = ["CommonSolve", "ConcreteStructs", "LineSearch", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "a3781e12becdf0ce5520bd97ec617e879bf4e9f2" +registries = "General" +uuid = "26075421-4e9a-44e1-8bd1-420ed7ad02b2" +version = "1.7.1" +weakdeps = ["ForwardDiff"] + + [deps.NonlinearSolveSpectralMethods.extensions] + NonlinearSolveSpectralMethodsForwardDiffExt = "ForwardDiff" + +[[deps.Observables]] +git-tree-sha1 = "7438a59546cf62428fc9d1bc94729146d37a7225" +registries = "General" +uuid = "510215fc-4207-5dde-b226-833fc4488ee2" +version = "0.5.5" + +[[deps.Ogg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b6aa4566bb7ae78498a5e68943863fa8b5231b59" +registries = "General" +uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" +version = "1.3.6+0" + +[[deps.OpenBLASConsistentFPCSR_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "38a93f17e431141c6470bb67a88952a7c4f0e928" +registries = "General" +uuid = "6cdc7f73-28fd-5e50-80fb-958a8875b1af" +version = "0.3.34+0" + +[[deps.OpenBLAS_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" +version = "0.3.30+0" + +[[deps.OpenLibm_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "05823500-19ac-5b8b-9628-191a04bc5112" +version = "0.8.7+0" + +[[deps.OpenMPI_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML", "Zlib_jll"] +git-tree-sha1 = "0dba69ef957d790d41484ca7c20212c5ac806738" +registries = "General" +uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" +version = "5.0.14+0" + +[[deps.OpenSSH_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll", "Zlib_jll"] +git-tree-sha1 = "1f640b82a627df3dbbaaff756a201a9ceb10dd1f" +registries = "General" +uuid = "9bd350c2-7e96-507f-8002-3f2e150b4e1b" +version = "10.6.1+0" + +[[deps.OpenSSL]] +deps = ["BitFlags", "Dates", "MozillaCACerts_jll", "NetworkOptions", "OpenSSL_jll", "Sockets"] +git-tree-sha1 = "f1cffefcdc73bbf17875e7c1cb062ecb000c1552" +registries = "General" +uuid = "4d8831e6-92b7-49fb-bdf8-b643e874388c" +version = "1.7.1" + +[[deps.OpenSSL_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" +version = "3.5.8+0" + +[[deps.OpenSpecFun_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1346c9208249809840c91b26703912dff463d335" +registries = "General" +uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" +version = "0.5.6+0" + +[[deps.Opus_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e2bb57a313a74b8104064b7efd01406c0a50d2ff" +registries = "General" +uuid = "91d4177d-7536-5919-b921-800302f37372" +version = "1.6.1+0" + +[[deps.OrderedCollections]] +git-tree-sha1 = "94ba93778373a53bfd5a0caaf7d809c445292ff4" +registries = "General" +uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +version = "1.8.2" + +[[deps.OrdinaryDiffEq]] +deps = ["ADTypes", "Adapt", "ArrayInterface", "CommonSolve", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "ExponentialUtilities", "FastBroadcast", "FastClosures", "FillArrays", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "InteractiveUtils", "LineSearches", "LinearAlgebra", "LinearSolve", "Logging", "MacroTools", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqAdamsBashforthMoulton", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqDefault", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqExplicitRK", "OrdinaryDiffEqExponentialRK", "OrdinaryDiffEqExtrapolation", "OrdinaryDiffEqFIRK", "OrdinaryDiffEqFeagin", "OrdinaryDiffEqFunctionMap", "OrdinaryDiffEqHighOrderRK", "OrdinaryDiffEqIMEXMultistep", "OrdinaryDiffEqLinear", "OrdinaryDiffEqLowOrderRK", "OrdinaryDiffEqLowStorageRK", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqNordsieck", "OrdinaryDiffEqPDIRK", "OrdinaryDiffEqPRK", "OrdinaryDiffEqQPRK", "OrdinaryDiffEqRKN", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqSDIRK", "OrdinaryDiffEqSSPRK", "OrdinaryDiffEqStabilizedIRK", "OrdinaryDiffEqStabilizedRK", "OrdinaryDiffEqSymplecticRK", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "Polyester", "PreallocationTools", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SparseArrays", "Static", "StaticArrayInterface", "StaticArrays", "TruncatedStacktraces"] +git-tree-sha1 = "47271adac597af08263b6ea2669e93040b45c4d0" +registries = "General" +uuid = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +version = "6.111.0" + +[[deps.OrdinaryDiffEqAdamsBashforthMoulton]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqLowOrderRK", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "2e44acb684dfcdc2e41851a988733e30b28a8478" +registries = "General" +uuid = "89bda076-bce5-4f1c-845f-551c83cdda9a" +version = "1.11.0" + +[[deps.OrdinaryDiffEqBDF]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqSDIRK", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "d7f69947e070a6a89aaf583e8aa2fada199fc292" +registries = "General" +uuid = "6ad6398a-0878-4a85-9266-38940aa047c8" +version = "1.26.0" + +[[deps.OrdinaryDiffEqCore]] +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "ConcreteStructs", "DataStructures", "DiffEqBase", "DocStringExtensions", "EnumX", "EnzymeCore", "FastBroadcast", "FastClosures", "FastPower", "FunctionWrappersWrappers", "InteractiveUtils", "LinearAlgebra", "Logging", "MacroTools", "MuladdMacro", "Polyester", "PrecompileTools", "Preferences", "Random", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLLogging", "SciMLOperators", "SciMLStructures", "Static", "SymbolicIndexingInterface", "TruncatedStacktraces"] +git-tree-sha1 = "c0a22f012d3e98fd78afafe8e5152ace7a2276bc" +registries = "General" +uuid = "bbf590c4-e513-4bbe-9b18-05decba2e5d8" +version = "3.33.1" + + [deps.OrdinaryDiffEqCore.extensions] + OrdinaryDiffEqCoreMooncakeExt = "Mooncake" + OrdinaryDiffEqCoreSparseArraysExt = "SparseArrays" + + [deps.OrdinaryDiffEqCore.weakdeps] + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.OrdinaryDiffEqDefault]] +deps = ["ADTypes", "DiffEqBase", "EnumX", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqBDF", "OrdinaryDiffEqCore", "OrdinaryDiffEqRosenbrock", "OrdinaryDiffEqTsit5", "OrdinaryDiffEqVerner", "PrecompileTools", "Preferences", "Reexport", "SciMLBase"] +git-tree-sha1 = "5fbd116f93790ce79c4beff8206e1b8e45c492a2" +registries = "General" +uuid = "50262376-6c5a-4cf5-baba-aaf4f84d72d7" +version = "1.14.0" + +[[deps.OrdinaryDiffEqDifferentiation]] +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "FunctionWrappersWrappers", "LinearAlgebra", "LinearSolve", "OrdinaryDiffEqCore", "SciMLBase", "SciMLOperators", "SparseMatrixColorings", "StaticArraysCore"] +git-tree-sha1 = "f3976b6baeb64051f41ed461a1933b7501a4f38e" +registries = "General" +uuid = "4302a76b-040a-498a-8c04-15b101fed76b" +version = "2.9.0" +weakdeps = ["SparseArrays"] + + [deps.OrdinaryDiffEqDifferentiation.extensions] + OrdinaryDiffEqDifferentiationSparseArraysExt = "SparseArrays" + +[[deps.OrdinaryDiffEqExplicitRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "54346c3d7a9932ab11743a99afed8a3ff0a65fea" +registries = "General" +uuid = "9286f039-9fbf-40e8-bf65-aa933bdc4db0" +version = "1.12.0" + +[[deps.OrdinaryDiffEqExponentialRK]] +deps = ["ADTypes", "DiffEqBase", "ExponentialUtilities", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "32fb54c8000c82fd32c55f7bf52f4651a3bd0e61" +registries = "General" +uuid = "e0540318-69ee-4070-8777-9e2de6de23de" +version = "1.15.0" + +[[deps.OrdinaryDiffEqExtrapolation]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastPower", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "57bf89f5b144f6e5af1146fb7aee1544c6b76950" +registries = "General" +uuid = "becaefa8-8ca2-5cf9-886d-c06f3d2bd2c4" +version = "1.18.0" + +[[deps.OrdinaryDiffEqFIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "FastGaussQuadrature", "FastPower", "LinearAlgebra", "LinearSolve", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "3339f3a26974a37666c74b95bdb54aefc63b9a56" +registries = "General" +uuid = "5960d6e9-dd7a-4743-88e7-cf307b64f125" +version = "1.26.0" + +[[deps.OrdinaryDiffEqFeagin]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "302999c99bc454cf274d39b9ba3c2415f6fbb1cb" +registries = "General" +uuid = "101fe9f7-ebb6-4678-b671-3a81e7194747" +version = "1.10.0" + +[[deps.OrdinaryDiffEqFunctionMap]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "2d2d59f9e530bca2f99934ecfde5430f6ad54bfe" +registries = "General" +uuid = "d3585ca7-f5d3-4ba6-8057-292ed1abd90f" +version = "1.11.0" + +[[deps.OrdinaryDiffEqHighOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "a6fef72198944e64e0d818f2cc46275e663370c9" +registries = "General" +uuid = "d28bc4f8-55e1-4f49-af69-84c1a99f0f58" +version = "1.12.0" + +[[deps.OrdinaryDiffEqIMEXMultistep]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Reexport", "SciMLBase"] +git-tree-sha1 = "bd5440f7f09ce75df59e7f5d2f5b8e52d24aae3c" +registries = "General" +uuid = "9f002381-b378-40b7-97a6-27a27c83f129" +version = "1.14.0" + +[[deps.OrdinaryDiffEqLinear]] +deps = ["DiffEqBase", "ExponentialUtilities", "LinearAlgebra", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "a1217ddfc979da3a2d9cf11b083688562e8ef333" +registries = "General" +uuid = "521117fe-8c41-49f8-b3b6-30780b3f0fb5" +version = "1.12.0" + +[[deps.OrdinaryDiffEqLowOrderRK]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "68a5e7e1b37933841e823a588deff3ec3e757a00" +registries = "General" +uuid = "1344f307-1e59-4825-a18e-ace9aa3fa4c6" +version = "1.13.0" + +[[deps.OrdinaryDiffEqLowStorageRK]] +deps = ["Adapt", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "076ac77cee882b2f4858292dde748c8b2ab908e7" +registries = "General" +uuid = "b0944070-b475-4768-8dec-fb6eb410534d" +version = "1.15.0" + +[[deps.OrdinaryDiffEqNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "DiffEqBase", "FastBroadcast", "FastClosures", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MuladdMacro", "NonlinearSolve", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "PreallocationTools", "RecursiveArrayTools", "SciMLBase", "SciMLOperators", "SciMLStructures", "SimpleNonlinearSolve", "SparseArrays", "StaticArraysCore"] +git-tree-sha1 = "82dde00dc43d5a4762cf50fa070b654cc38a2e87" +registries = "General" +uuid = "127b3ac7-2247-4354-8eb6-78cf4e7c58e8" +version = "1.28.0" + +[[deps.OrdinaryDiffEqNordsieck]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqTsit5", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "21117475cb0d27942d31e4b8a27337392ae680b0" +registries = "General" +uuid = "c9986a66-5c92-4813-8696-a7ec84c806c8" +version = "1.11.0" + +[[deps.OrdinaryDiffEqPDIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "Polyester", "Reexport", "SciMLBase"] +git-tree-sha1 = "1aaaa4851bbe72497c9d01d69fa36396ddeb0a0a" +registries = "General" +uuid = "5dd0a6cf-3d4b-4314-aa06-06d4e299bc89" +version = "1.14.0" + +[[deps.OrdinaryDiffEqPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "Reexport", "SciMLBase"] +git-tree-sha1 = "1fe4f58f170c68b187ac42dc5f00932bb162ddfa" +registries = "General" +uuid = "5b33eab2-c0f1-4480-b2c3-94bc1e80bda1" +version = "1.10.0" + +[[deps.OrdinaryDiffEqQPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "275900d64ab7be5f1bca8c7bfd48166838b6ab44" +registries = "General" +uuid = "04162be5-8125-4266-98ed-640baecc6514" +version = "1.10.0" + +[[deps.OrdinaryDiffEqRKN]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "edf3411246d6030e165a5fba513cbdcde5c72f79" +registries = "General" +uuid = "af6ede74-add8-4cfd-b1df-9a4dbb109d7a" +version = "1.12.0" + +[[deps.OrdinaryDiffEqRosenbrock]] +deps = ["ADTypes", "DiffEqBase", "DifferentiationInterface", "FastBroadcast", "FiniteDiff", "ForwardDiff", "LinearAlgebra", "LinearSolve", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "3579d9272c286ffcceee794c5752e8cc7c3ae514" +registries = "General" +uuid = "43230ef6-c299-4910-a778-202eb28ce4ce" +version = "1.31.1" + +[[deps.OrdinaryDiffEqSDIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "LinearAlgebra", "MacroTools", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "TruncatedStacktraces"] +git-tree-sha1 = "c5e26fbadbad137fa2b7e3161a290a1becfec502" +registries = "General" +uuid = "2d112036-d095-4a1e-ab9a-08536f3ecdbf" +version = "1.14.0" + +[[deps.OrdinaryDiffEqSSPRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static"] +git-tree-sha1 = "4ec0be19f8115889f61643fc25f6e3af5cd4c9e7" +registries = "General" +uuid = "669c94d9-1f4b-4b64-b377-1aa079aa2388" +version = "1.14.0" + +[[deps.OrdinaryDiffEqStabilizedIRK]] +deps = ["ADTypes", "DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "OrdinaryDiffEqDifferentiation", "OrdinaryDiffEqNonlinearSolve", "OrdinaryDiffEqStabilizedRK", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "226226d334fe295885df6ecb2d110c22ead79983" +registries = "General" +uuid = "e3e12d00-db14-5390-b879-ac3dd2ef6296" +version = "1.14.0" + +[[deps.OrdinaryDiffEqStabilizedRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "RecursiveArrayTools", "Reexport", "SciMLBase", "StaticArraysCore"] +git-tree-sha1 = "764e11fb6a26ee8f2d2d10778d531fd708f35d28" +registries = "General" +uuid = "358294b1-0aab-51c3-aafe-ad5ab194a2ad" +version = "1.11.1" + +[[deps.OrdinaryDiffEqSymplecticRK]] +deps = ["DiffEqBase", "FastBroadcast", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "RecursiveArrayTools", "Reexport", "SciMLBase"] +git-tree-sha1 = "b18a4e7973e73e84cfd79222e2389565b323b588" +registries = "General" +uuid = "fa646aed-7ef9-47eb-84c4-9443fc8cbfa8" +version = "1.13.0" + +[[deps.OrdinaryDiffEqTsit5]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "6bd89fce8a0b198ac9fe98dc80362a92a2a13ae5" +registries = "General" +uuid = "b1df2697-797e-41e3-8120-5422d3b24e4a" +version = "1.12.0" + +[[deps.OrdinaryDiffEqVerner]] +deps = ["DiffEqBase", "FastBroadcast", "LinearAlgebra", "MuladdMacro", "OrdinaryDiffEqCore", "Polyester", "PrecompileTools", "Preferences", "RecursiveArrayTools", "Reexport", "SciMLBase", "Static", "TruncatedStacktraces"] +git-tree-sha1 = "58dc7756825ca46e5a601a5aeba7d26a071d373a" +registries = "General" +uuid = "79d7bb75-1356-48c1-b8c0-6832512096c2" +version = "1.14.0" + +[[deps.PCRE2_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" +version = "10.47.0+0" + +[[deps.Pango_jll]] +deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "FriBidi_jll", "Glib_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl"] +git-tree-sha1 = "1912a9f1b9ca55005b03ba075f8e19993583e237" +registries = "General" +uuid = "36c8627f-9965-5494-a995-c6b170f724f3" +version = "1.58.2+0" + +[[deps.Parameters]] +deps = ["OrderedCollections", "UnPack"] +git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" +registries = "General" +uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" +version = "0.12.3" + +[[deps.Parsers]] +deps = ["Dates", "PrecompileTools", "UUIDs"] +git-tree-sha1 = "ba0dc8a8a67cacac4842631f960c046e4e563675" +registries = "General" +uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" +version = "2.8.8" + +[[deps.Pidfile]] +deps = ["FileWatching", "Test"] +git-tree-sha1 = "2d8aaf8ee10df53d0dfb9b8ee44ae7c04ced2b03" +registries = "General" +uuid = "fa939f87-e72e-5be4-a000-7fc836dbe307" +version = "1.3.0" + +[[deps.Pixman_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] +git-tree-sha1 = "e4a6721aa89e62e5d4217c0b21bd714263779dda" +registries = "General" +uuid = "30392449-352a-5448-841d-b1acce4e97dc" +version = "0.46.4+0" + +[[deps.Pkg]] +deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "Random", "SHA", "TOML", "Tar", "UUIDs", "Zstd_jll", "p7zip_jll"] +uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" +version = "1.13.0" +weakdeps = ["REPL"] + + [deps.Pkg.extensions] + REPLExt = "REPL" + +[[deps.PlotThemes]] +deps = ["PlotUtils", "Statistics"] +git-tree-sha1 = "41031ef3a1be6f5bbbf3e8073f210556daeae5ca" +registries = "General" +uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" +version = "3.3.0" + +[[deps.PlotUtils]] +deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Reexport", "Statistics"] +git-tree-sha1 = "ddcc36fd83fb4cd459a53c891981697bde0d3de6" +registries = "General" +uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" +version = "1.5.1" + +[[deps.PlotlyBase]] +deps = ["ColorSchemes", "Colors", "Dates", "DelimitedFiles", "DocStringExtensions", "JSON", "LaTeXStrings", "Logging", "Parameters", "Pkg", "REPL", "Requires", "Statistics", "UUIDs"] +git-tree-sha1 = "6256ab3ee24ef079b3afa310593817e069925eeb" +registries = "General" +uuid = "a03496cd-edff-5a9b-9e67-9cda94a718b5" +version = "0.8.23" + + [deps.PlotlyBase.extensions] + DataFramesExt = "DataFrames" + DistributionsExt = "Distributions" + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyBase.weakdeps] + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyJS]] +deps = ["Base64", "Blink", "DelimitedFiles", "JSExpr", "JSON", "Kaleido_jll", "Markdown", "Pkg", "PlotlyBase", "PlotlyKaleido", "REPL", "Reexport", "Requires", "WebIO"] +git-tree-sha1 = "4bf6c08295346efba58bb2d5f8c52984efed7f27" +registries = "General" +uuid = "f0f68f2c-4968-5e81-91da-67840de0976a" +version = "0.18.18" + + [deps.PlotlyJS.extensions] + CSVExt = "CSV" + DataFramesExt = ["DataFrames", "CSV"] + IJuliaExt = "IJulia" + JSON3Ext = "JSON3" + + [deps.PlotlyJS.weakdeps] + CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" + DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" + +[[deps.PlotlyKaleido]] +deps = ["Artifacts", "Base64", "JSON", "Kaleido_jll"] +git-tree-sha1 = "c7a270d11881c0709052f8e3def3d60d551e99f3" +registries = "General" +uuid = "f2990250-8cf9-495f-b13a-cce12b45703c" +version = "2.3.1" + +[[deps.Plots]] +deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "TOML", "UUIDs", "UnicodeFun", "Unzip"] +git-tree-sha1 = "83bd514e8ff16b5858ac54c53fa0bcf6002a3b00" +registries = "General" +uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" +version = "1.41.7" + + [deps.Plots.extensions] + FileIOExt = "FileIO" + GeometryBasicsExt = "GeometryBasics" + IJuliaExt = "IJulia" + ImageInTerminalExt = "ImageInTerminal" + UnitfulExt = "Unitful" + + [deps.Plots.weakdeps] + FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" + GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" + IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.Polyester]] +deps = ["ArrayInterface", "BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "ManualMemory", "PolyesterWeave", "Static", "StaticArrayInterface", "StrideArraysCore", "ThreadingUtilities"] +git-tree-sha1 = "16bbc30b5ebea91e9ce1671adc03de2832cff552" +registries = "General" +uuid = "f517fe37-dbe3-4b94-8317-1923a5111588" +version = "0.7.19" + +[[deps.PolyesterWeave]] +deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] +git-tree-sha1 = "645bed98cd47f72f67316fd42fc47dee771aefcd" +registries = "General" +uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" +version = "0.2.2" + +[[deps.PooledArrays]] +deps = ["DataAPI", "Future"] +git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" +registries = "General" +uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" +version = "1.4.3" + +[[deps.PreallocationTools]] +deps = ["Adapt", "ArrayInterface", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "cd00e28071a75c98664663f87c2ae447d25c0503" +registries = "General" +uuid = "d236fae5-4411-538c-8e31-a6e3d9e00b46" +version = "1.7.1" + + [deps.PreallocationTools.extensions] + PreallocationToolsEnzymeCoreExt = "EnzymeCore" + PreallocationToolsForwardDiffExt = "ForwardDiff" + PreallocationToolsReverseDiffExt = "ReverseDiff" + + [deps.PreallocationTools.weakdeps] + EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + +[[deps.PrecompileTools]] +deps = ["Preferences"] +git-tree-sha1 = "edbeefc7a4889f528644251bdb5fc9ab5348bc2c" +registries = "General" +uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" +version = "1.3.4" + +[[deps.Preferences]] +deps = ["TOML"] +git-tree-sha1 = "aef1987d90dcc2e2cbd919ad10b2d25bcce4f418" +registries = "General" +uuid = "21216c6a-2e73-6563-6e65-726566657250" +version = "1.6.1" + +[[deps.PrettyTables]] +deps = ["Crayons", "LaTeXStrings", "Markdown", "PrecompileTools", "Printf", "REPL", "Reexport", "StringManipulation", "StyledStrings", "Tables"] +git-tree-sha1 = "99fd8f32ab6728ff205cddd24e2d9ea6cbb498d3" +registries = "General" +uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" +version = "3.5.0" + + [deps.PrettyTables.extensions] + PrettyTablesExcelExt = "XLSX" + PrettyTablesTypstryExt = "Typstry" + PrettyTablesWriteDocxExt = "WriteDocx" + + [deps.PrettyTables.weakdeps] + Typstry = "f0ed7684-a786-439e-b1e3-3b82803b501e" + WriteDocx = "d049ceea-54ee-41d7-a26f-ba29db3b6599" + XLSX = "fdbf4ff8-1666-58a4-91e7-1b58723a45e0" + +[[deps.Printf]] +deps = ["Unicode"] +uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" +version = "1.11.0" + +[[deps.PtrArrays]] +git-tree-sha1 = "4fbbafbc6251b883f4d2705356f3641f3652a7fe" +registries = "General" +uuid = "43287f4e-b6f4-7ad1-bb20-aadabca52c3d" +version = "1.4.0" + +[[deps.PureKLU]] +deps = ["LinearAlgebra", "PrecompileTools", "SparseArrays"] +git-tree-sha1 = "ad0e84647fe7b3c2ae02aaeace1522139aaf6a27" +registries = "General" +uuid = "0c0d3e7f-3a8b-4f7e-b6f1-9a4d2e7c1f01" +version = "1.6.0" +weakdeps = ["ForwardDiff"] + + [deps.PureKLU.extensions] + PureKLUForwardDiffExt = "ForwardDiff" + +[[deps.Qt6Base_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] +git-tree-sha1 = "144895f6166994730ee7ff8113b981fc360638f1" +registries = "General" +uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" +version = "6.10.2+2" + +[[deps.Qt6Declarative_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6ShaderTools_jll", "Qt6Svg_jll"] +git-tree-sha1 = "159d253ab126d5b29230cf53521899bea4ef4648" +registries = "General" +uuid = "629bc702-f1f5-5709-abd5-49b8460ea067" +version = "6.10.2+2" + +[[deps.Qt6ShaderTools_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "4d85eedf69d875982c46643f6b4f66919d7e157b" +registries = "General" +uuid = "ce943373-25bb-56aa-8eca-768745ed7b5a" +version = "6.10.2+1" + +[[deps.Qt6Svg_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll"] +git-tree-sha1 = "81587ff5ff25a4e1115ce191e36285ede0334c9d" +registries = "General" +uuid = "6de9746b-f93d-5813-b365-ba18ad4a9cf3" +version = "6.10.2+0" + +[[deps.Qt6Wayland_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Qt6Base_jll", "Qt6Declarative_jll"] +git-tree-sha1 = "672c938b4b4e3e0169a07a5f227029d4905456f2" +registries = "General" +uuid = "e99dba38-086e-5de3-a5b1-6e4c66e897c3" +version = "6.10.2+1" + +[[deps.QuadGK]] +deps = ["DataStructures", "LinearAlgebra"] +git-tree-sha1 = "5e8e8b0ab68215d7a2b14b9921a946fee794749e" +registries = "General" +uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" +version = "2.11.3" + + [deps.QuadGK.extensions] + QuadGKEnzymeExt = "Enzyme" + + [deps.QuadGK.weakdeps] + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + +[[deps.Quadmath]] +deps = ["Compat", "Printf", "Random"] +git-tree-sha1 = "fc861299ef1c23d1985de276b2233db4a980a55c" +registries = "General" +uuid = "be4d8f0f-7fa4-5f49-b795-2f01399ab2dd" +version = "1.1.0" +weakdeps = ["SpecialFunctions"] + + [deps.Quadmath.extensions] + QuadmathSpecialFunctionsExt = ["SpecialFunctions"] + +[[deps.REPL]] +deps = ["Base64", "Dates", "FileWatching", "InteractiveUtils", "JuliaSyntaxHighlighting", "Markdown", "Sockets", "StyledStrings", "Unicode"] +uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" +version = "1.11.0" + +[[deps.Random]] +deps = ["SHA"] +uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +version = "1.11.0" + +[[deps.RecipesBase]] +deps = ["PrecompileTools"] +git-tree-sha1 = "0c0dcffc464cda2ff33fc8ffc1dd1bdd53cd98be" +registries = "General" +uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" +version = "1.4.0" + +[[deps.RecipesPipeline]] +deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] +git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" +registries = "General" +uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" +version = "0.6.12" + +[[deps.RecursiveArrayTools]] +deps = ["Adapt", "ArrayInterface", "DocStringExtensions", "GPUArraysCore", "LinearAlgebra", "PrecompileTools", "RecipesBase", "StaticArraysCore", "SymbolicIndexingInterface"] +git-tree-sha1 = "d0282d612f22dcad7b81cf487b746e63aa2a6709" +registries = "General" +uuid = "731186ca-8d62-57ce-b412-fbd966d074cd" +version = "3.54.0" + + [deps.RecursiveArrayTools.extensions] + RecursiveArrayToolsFastBroadcastExt = "FastBroadcast" + RecursiveArrayToolsFastBroadcastPolyesterExt = ["FastBroadcast", "Polyester"] + RecursiveArrayToolsForwardDiffExt = "ForwardDiff" + RecursiveArrayToolsKernelAbstractionsExt = "KernelAbstractions" + RecursiveArrayToolsMeasurementsExt = "Measurements" + RecursiveArrayToolsMonteCarloMeasurementsExt = "MonteCarloMeasurements" + RecursiveArrayToolsReverseDiffExt = ["ReverseDiff", "Zygote"] + RecursiveArrayToolsSparseArraysExt = ["SparseArrays"] + RecursiveArrayToolsStatisticsExt = "Statistics" + RecursiveArrayToolsStructArraysExt = "StructArrays" + RecursiveArrayToolsTablesExt = ["Tables"] + RecursiveArrayToolsTrackerExt = "Tracker" + RecursiveArrayToolsZygoteExt = "Zygote" + + [deps.RecursiveArrayTools.weakdeps] + FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + KernelAbstractions = "63c18a36-062a-441e-b654-da1e3ab1ce7c" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" + Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.Reexport]] +git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" +registries = "General" +uuid = "189a3867-3050-52da-a836-e630ba90ab69" +version = "1.2.2" + +[[deps.RegistryInstances]] +deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] +git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" +registries = "General" +uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" +version = "0.1.0" + +[[deps.RelocatableFolders]] +deps = ["SHA", "Scratch"] +git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" +registries = "General" +uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" +version = "1.0.1" + +[[deps.Requires]] +deps = ["UUIDs"] +git-tree-sha1 = "62389eeff14780bfe55195b7204c0d8738436d64" +registries = "General" +uuid = "ae029012-a4dd-5104-9daa-d747884805df" +version = "1.3.1" + +[[deps.Roots]] +deps = ["Accessors", "CommonSolve", "Printf"] +git-tree-sha1 = "3eff988b9bd09543783e2e051b0a1eef11f65c2d" +registries = "General" +uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" +version = "2.3.0" + + [deps.Roots.extensions] + RootsChainRulesCoreExt = "ChainRulesCore" + RootsForwardDiffExt = "ForwardDiff" + RootsIntervalRootFindingExt = "IntervalRootFinding" + RootsSymPyExt = "SymPy" + RootsSymPyPythonCallExt = "SymPyPythonCall" + RootsUnitfulExt = "Unitful" + + [deps.Roots.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + IntervalRootFinding = "d2bf35a9-74e0-55ec-b149-d360ff49b807" + SymPy = "24249f21-da20-56a4-8eb1-6a02cf4ae2e6" + SymPyPythonCall = "bc8888f7-b21e-4b7c-a06a-5d9c9496438c" + Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" + +[[deps.RoundingEmulator]] +git-tree-sha1 = "40b9edad2e5287e05bd413a38f61a8ff55b9557b" +registries = "General" +uuid = "5eaf0fd0-dfba-4ccb-bf02-d820a40db705" +version = "0.2.1" + +[[deps.RuntimeGeneratedFunctions]] +deps = ["ExprTools", "PrecompileTools", "SHA", "Serialization"] +git-tree-sha1 = "9bb075002b40849582905394bb3031fb77aa102d" +registries = "General" +uuid = "7e49a35a-f44a-4d26-94aa-eba1b4ca6b47" +version = "0.5.27" + +[[deps.SHA]] +uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" +version = "1.0.0" + +[[deps.SIMDTypes]] +git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" +registries = "General" +uuid = "94e857df-77ce-4151-89e5-788b33177be4" +version = "0.1.0" + +[[deps.SciMLBase]] +deps = ["ADTypes", "Accessors", "Adapt", "ArrayInterface", "CommonSolve", "ConstructionBase", "Distributed", "DocStringExtensions", "EnumX", "FunctionWrappersWrappers", "IteratorInterfaceExtensions", "LinearAlgebra", "Logging", "Markdown", "Moshi", "PreallocationTools", "PrecompileTools", "Preferences", "Printf", "RecipesBase", "RecursiveArrayTools", "Reexport", "RuntimeGeneratedFunctions", "SciMLLogging", "SciMLOperators", "SciMLPublic", "SciMLStructures", "StaticArraysCore", "Statistics", "SymbolicIndexingInterface"] +git-tree-sha1 = "d56cb3b924cdf9297c171113e6a6ae9694e370eb" +registries = "General" +uuid = "0bca4576-84f4-4d90-8ffe-ffa030f20462" +version = "2.155.2" + + [deps.SciMLBase.extensions] + SciMLBaseChainRulesCoreExt = "ChainRulesCore" + SciMLBaseDifferentiationInterfaceExt = "DifferentiationInterface" + SciMLBaseDistributionsExt = "Distributions" + SciMLBaseEnzymeExt = "Enzyme" + SciMLBaseForwardDiffExt = "ForwardDiff" + SciMLBaseMLStyleExt = "MLStyle" + SciMLBaseMakieExt = "Makie" + SciMLBaseMeasurementsExt = "Measurements" + SciMLBaseMonteCarloMeasurementsExt = "MonteCarloMeasurements" + SciMLBaseMooncakeExt = "Mooncake" + SciMLBasePartialFunctionsExt = "PartialFunctions" + SciMLBasePyCallExt = "PyCall" + SciMLBasePythonCallExt = "PythonCall" + SciMLBaseRCallExt = "RCall" + SciMLBaseReverseDiffExt = "ReverseDiff" + SciMLBaseTrackerExt = "Tracker" + SciMLBaseZygoteExt = ["Zygote", "ChainRulesCore"] + + [deps.SciMLBase.weakdeps] + ChainRules = "082447d4-558c-5d27-93f4-14fc19e9eca2" + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63" + Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" + Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9" + ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" + MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" + Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7" + MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca" + Mooncake = "da2b9cff-9c12-43a0-ae48-6db2b0edb7d6" + PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" + PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" + PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d" + RCall = "6f49c342-dc21-5d91-9882-a32aef131414" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" + +[[deps.SciMLJacobianOperators]] +deps = ["ADTypes", "ArrayInterface", "ConcreteStructs", "ConstructionBase", "DifferentiationInterface", "FastClosures", "LinearAlgebra", "SciMLBase", "SciMLOperators"] +git-tree-sha1 = "ad167d716fc134c0873c76ba0469ede56a678732" +registries = "General" +uuid = "19f34311-ddf3-4b8b-af20-060888a46c0e" +version = "0.1.17" + +[[deps.SciMLLogging]] +deps = ["Logging", "LoggingExtras", "Preferences"] +git-tree-sha1 = "4e1e21f14a284f892eb62923a356c70a2a0c68e1" +registries = "General" +uuid = "a6db7da4-7206-11f0-1eab-35f2a5dbe1d1" +version = "1.10.1" + + [deps.SciMLLogging.extensions] + SciMLLoggingTracyExt = "Tracy" + + [deps.SciMLLogging.weakdeps] + Tracy = "e689c965-62c8-4b79-b2c5-8359227902fd" + +[[deps.SciMLOperators]] +deps = ["Accessors", "Adapt", "ArrayInterface", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "2712b6eeef282b5cd8c38660e68c001a4b9b24e2" +registries = "General" +uuid = "c0aeaf25-5076-4817-a8d5-81caf7dfa961" +version = "1.30.3" + + [deps.SciMLOperators.extensions] + SciMLOperatorsLoopVectorizationExt = "LoopVectorization" + SciMLOperatorsSparseArraysExt = "SparseArrays" + + [deps.SciMLOperators.weakdeps] + LoopVectorization = "bdcacae8-1622-11e9-2a5c-532679323890" + SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" + +[[deps.SciMLPublic]] +deps = ["PrecompileTools"] +git-tree-sha1 = "74685afb51732a464fbce79a72708f7c4203ceb7" +registries = "General" +uuid = "431bcebd-1456-4ced-9d72-93c2757fff0b" +version = "1.3.0" + +[[deps.SciMLStructures]] +deps = ["ArrayInterface", "PrecompileTools"] +git-tree-sha1 = "4b8fc0ffe22b4ecba7a02f1fec0b3757db014d0c" +registries = "General" +uuid = "53ae85a6-f571-4167-b2af-e1d143709226" +version = "1.10.6" + +[[deps.ScopedValues]] +deps = ["HashArrayMappedTries", "Logging"] +git-tree-sha1 = "67a144433c4ce877ee6d1ada69a124d6b1ecf7be" +registries = "General" +uuid = "7e506255-f358-4e82-b7e4-beb19740aa63" +version = "1.6.2" + +[[deps.Scratch]] +deps = ["Dates"] +git-tree-sha1 = "9b81b8393e50b7d4e6d0a9f14e192294d3b7c109" +registries = "General" +uuid = "6c6a2e73-6563-6170-7368-637461726353" +version = "1.3.0" + +[[deps.SentinelArrays]] +deps = ["Dates", "Random"] +git-tree-sha1 = "084c47c7c5ce5cfecefa0a98dff69eb3646b5a80" +registries = "General" +uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" +version = "1.4.10" + +[[deps.Serialization]] +uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" +version = "1.11.0" + +[[deps.Setfield]] +deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] +git-tree-sha1 = "1a17a79e9dffc0d188c81c7ea58e8cc79752df7e" +registries = "General" +uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" +version = "1.1.3" + +[[deps.Showoff]] +deps = ["Dates"] +git-tree-sha1 = "8238217340ad0aaabe11afe39c1098b5bc9f4c8e" +registries = "General" +uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" +version = "1.1.1" + +[[deps.SimpleBufferStream]] +git-tree-sha1 = "f305871d2f381d21527c770d4788c06c097c9bc1" +registries = "General" +uuid = "777ac1f9-54b0-4bf8-805c-2214025038e7" +version = "1.2.0" + +[[deps.SimpleNonlinearSolve]] +deps = ["ADTypes", "ArrayInterface", "BracketingNonlinearSolve", "CommonSolve", "ConcreteStructs", "DifferentiationInterface", "FastClosures", "FiniteDiff", "ForwardDiff", "LineSearch", "LinearAlgebra", "MaybeInplace", "NonlinearSolveBase", "PrecompileTools", "Reexport", "SciMLBase", "Setfield", "StaticArraysCore"] +git-tree-sha1 = "08d8bafd57b7ea16dc1f69a8bd57db23a2ceb686" +registries = "General" +uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7" +version = "2.12.0" + + [deps.SimpleNonlinearSolve.extensions] + SimpleNonlinearSolveChainRulesCoreExt = "ChainRulesCore" + SimpleNonlinearSolveReverseDiffExt = "ReverseDiff" + SimpleNonlinearSolveTrackerExt = "Tracker" + + [deps.SimpleNonlinearSolve.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" + Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" + +[[deps.Sockets]] +uuid = "6462fe0b-24de-5631-8697-dd941f90decc" +version = "1.11.0" + +[[deps.SortingAlgorithms]] +deps = ["DataStructures"] +git-tree-sha1 = "13cd91cc9be159e3f4d95b857fa2aa383b53772a" +registries = "General" +uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" +version = "1.2.3" + +[[deps.SparseArrays]] +deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] +uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" +version = "1.13.0" + +[[deps.SparseColumnPivotedQR]] +deps = ["LinearAlgebra", "PrecompileTools", "SparseArrays"] +git-tree-sha1 = "8cf1a59c78dd6f900feb366e5a574f99ed278ef1" +registries = "General" +uuid = "a57abbd0-fea5-4d57-96be-5e525945e8e4" +version = "2.1.8" +weakdeps = ["AMD"] + + [deps.SparseColumnPivotedQR.extensions] + SparseColumnPivotedQRAMDExt = "AMD" + +[[deps.SparseMatrixColorings]] +deps = ["ADTypes", "DocStringExtensions", "LinearAlgebra", "PrecompileTools", "Random", "SparseArrays"] +git-tree-sha1 = "0730f14b07c68fd3b3a6fae89a829a5124a7ae7e" +registries = "General" +uuid = "0a514795-09f3-496d-8182-132a7b665d35" +version = "0.4.29" + + [deps.SparseMatrixColorings.extensions] + SparseMatrixColoringsAMDGPUExt = "AMDGPU" + SparseMatrixColoringsCUDAExt = ["CUDA", "cuSPARSE"] + SparseMatrixColoringsCliqueTreesExt = "CliqueTrees" + SparseMatrixColoringsColorsExt = "Colors" + SparseMatrixColoringsGPUArraysExt = "GPUArrays" + SparseMatrixColoringsJuMPExt = ["JuMP", "MathOptInterface"] + + [deps.SparseMatrixColorings.weakdeps] + AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" + CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" + CliqueTrees = "60701a23-6482-424a-84db-faee86b9b1f8" + Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" + GPUArrays = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" + JuMP = "4076af6c-e467-56ae-b986-b466b2749572" + MathOptInterface = "b8f27783-ece8-5eb3-8dc8-9495eed66fee" + cuSPARSE = "b26da814-b3bc-49ef-b0ee-c816305aa060" + +[[deps.SpecialFunctions]] +deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] +git-tree-sha1 = "88ef0d98204515bc53dd4aaea5f7788882d1b66c" +registries = "General" +uuid = "276daf66-3868-5448-9aa4-cd146d93841b" +version = "2.9.1" + + [deps.SpecialFunctions.extensions] + SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" + + [deps.SpecialFunctions.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + +[[deps.Static]] +deps = ["CommonWorldInvalidations", "IfElse", "PrecompileTools", "SciMLPublic"] +git-tree-sha1 = "474a5283ad435618090122872eea6a8165ea6bcf" +registries = "General" +uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" +version = "1.4.6" + +[[deps.StaticArrayInterface]] +deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "SciMLPublic", "Static"] +git-tree-sha1 = "2a635e15d5035c53b345077c947f31ff91744078" +registries = "General" +uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" +version = "1.10.0" + + [deps.StaticArrayInterface.extensions] + StaticArrayInterfaceOffsetArraysExt = "OffsetArrays" + StaticArrayInterfaceStaticArraysExt = "StaticArrays" + + [deps.StaticArrayInterface.weakdeps] + OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" + StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" + +[[deps.StaticArrays]] +deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] +git-tree-sha1 = "8a8a52f887f38d44014e24ee46b8a7311c625f6d" +registries = "General" +uuid = "90137ffa-7385-5640-81b9-e52037218182" +version = "1.9.24" + + [deps.StaticArrays.extensions] + StaticArraysChainRulesCoreExt = "ChainRulesCore" + StaticArraysStatisticsExt = "Statistics" + + [deps.StaticArrays.weakdeps] + ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" + Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" + +[[deps.StaticArraysCore]] +git-tree-sha1 = "6ab403037779dae8c514bad259f32a447262455a" +registries = "General" +uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" +version = "1.4.4" + +[[deps.Statistics]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "e2b53ce13a53367e96601081e33d34746b571bad" +registries = "General" +uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +version = "1.11.5" +weakdeps = ["SparseArrays"] + + [deps.Statistics.extensions] + SparseArraysExt = ["SparseArrays"] + +[[deps.StatsAPI]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "178ed29fd5b2a2cfc3bd31c13375ae925623ff36" +registries = "General" +uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" +version = "1.8.0" + +[[deps.StatsBase]] +deps = ["AliasTables", "DataAPI", "DataStructures", "IrrationalConstants", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] +git-tree-sha1 = "adb9da019510162e67a4493fc235c23203d8b09e" +registries = "General" +uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" +version = "0.34.13" + +[[deps.StrideArraysCore]] +deps = ["ArrayInterface", "CloseOpenIntervals", "IfElse", "LayoutPointers", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface", "ThreadingUtilities"] +git-tree-sha1 = "5316097111523c9a970596a5b33cfea5f92e8581" +registries = "General" +uuid = "7792a7ef-975c-4747-a70f-980b88e8d1da" +version = "0.5.9" + +[[deps.StringManipulation]] +deps = ["PrecompileTools", "StyledStrings"] +git-tree-sha1 = "9e3a02d73a8f0a9be04e677dbef758aab40f8bea" +registries = "General" +uuid = "892a3eda-7b42-436c-8928-eab12a02cf0e" +version = "0.6.1" + +[[deps.StyledStrings]] +uuid = "f489334b-da3d-4c2e-b8f0-e476e12c162b" +version = "1.11.0" + +[[deps.SuiteSparse_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl", "libblastrampoline_jll"] +uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" +version = "7.10.1+0" + +[[deps.SymbolicIndexingInterface]] +deps = ["Accessors", "ArrayInterface", "PrecompileTools", "RuntimeGeneratedFunctions", "StaticArraysCore"] +git-tree-sha1 = "7eb6da9656581ac9fbffaef3ef7950a090a5002a" +registries = "General" +uuid = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +version = "0.3.55" +weakdeps = ["PrettyTables"] + + [deps.SymbolicIndexingInterface.extensions] + SymbolicIndexingInterfacePrettyTablesExt = "PrettyTables" + +[[deps.TOML]] +deps = ["Dates"] +uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" +version = "1.0.3" + +[[deps.TableTraits]] +deps = ["IteratorInterfaceExtensions"] +git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" +registries = "General" +uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" +version = "1.0.1" + +[[deps.Tables]] +deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "OrderedCollections", "TableTraits"] +git-tree-sha1 = "a94d9bdda1b7bed0046cea645639ab3f62196fac" +registries = "General" +uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" +version = "1.14.0" + +[[deps.Tar]] +deps = ["ArgTools", "SHA"] +uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +version = "1.10.0" + +[[deps.TensorCore]] +deps = ["LinearAlgebra"] +git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" +registries = "General" +uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" +version = "0.1.1" + +[[deps.Test]] +deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] +uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +version = "1.11.0" + +[[deps.ThreadSafeDicts]] +git-tree-sha1 = "411010ade7dd5576d693db8737976d805ee9945b" +registries = "General" +uuid = "4239201d-c60e-5e0a-9702-85d713665ba7" +version = "0.1.7" + +[[deps.ThreadingUtilities]] +deps = ["ManualMemory"] +git-tree-sha1 = "e12924c87b5476110130841e973ba9b9f20ae599" +registries = "General" +uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" +version = "0.5.7" + +[[deps.TimerOutputs]] +deps = ["ExprTools", "Printf"] +git-tree-sha1 = "3748bd928e68c7c346b52125cf41fff0de6937d0" +registries = "General" +uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" +version = "0.5.29" + + [deps.TimerOutputs.extensions] + FlameGraphsExt = "FlameGraphs" + + [deps.TimerOutputs.weakdeps] + FlameGraphs = "08572546-2f56-4bcf-ba4e-bab62c3a3f89" + +[[deps.TranscodingStreams]] +git-tree-sha1 = "0c45878dcfdcfa8480052b6ab162cdd138781742" +registries = "General" +uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" +version = "0.11.3" + +[[deps.TruncatedStacktraces]] +deps = ["InteractiveUtils", "MacroTools", "Preferences"] +git-tree-sha1 = "ea3e54c2bdde39062abf5a9758a23735558705e1" +registries = "General" +uuid = "781d530d-4396-4725-bb49-402e4bee1e77" +version = "1.4.0" + +[[deps.URIs]] +deps = ["Serialization"] +git-tree-sha1 = "50eda8011f098869a76da69a524ff59e0298711a" +registries = "General" +uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" +version = "1.7.2" + +[[deps.UUIDs]] +deps = ["Random", "SHA"] +uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" +version = "1.11.0" + +[[deps.UnPack]] +git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" +registries = "General" +uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" +version = "1.0.2" + +[[deps.Unicode]] +uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" +version = "1.11.0" + +[[deps.UnicodeFun]] +deps = ["REPL"] +git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" +registries = "General" +uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" +version = "0.4.1" + +[[deps.Unzip]] +git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" +registries = "General" +uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" +version = "0.2.0" + +[[deps.Vulkan_Loader_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] +git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" +registries = "General" +uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" +version = "1.3.243+0" + +[[deps.Wayland_jll]] +deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll"] +git-tree-sha1 = "96478df35bbc2f3e1e791bc7a3d0eeee559e60e9" +registries = "General" +uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" +version = "1.24.0+0" + +[[deps.WebIO]] +deps = ["AssetRegistry", "Base64", "Distributed", "FunctionalCollections", "JSON", "Logging", "Observables", "Pkg", "Random", "Requires", "Sockets", "UUIDs", "WebSockets", "Widgets"] +git-tree-sha1 = "0eef0765186f7452e52236fa42ca8c9b3c11c6e3" +registries = "General" +uuid = "0f1e0344-ec1d-5b48-a673-e5cf874b6c29" +version = "0.8.21" + +[[deps.WebSockets]] +deps = ["Base64", "Dates", "HTTP", "Logging", "Sockets"] +git-tree-sha1 = "4162e95e05e79922e44b9952ccbc262832e4ad07" +registries = "General" +uuid = "104b5d7c-a370-577a-8038-80a2059c5097" +version = "1.6.0" + +[[deps.Widgets]] +deps = ["Colors", "Dates", "Observables", "OrderedCollections"] +git-tree-sha1 = "cab29ed70d9355a28b95b1798007ff3a00eefc91" +registries = "General" +uuid = "cc8bc4a8-27d6-5769-a93b-9d913e69aa62" +version = "0.6.8" + +[[deps.XML2_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] +git-tree-sha1 = "b826aedca5fce2e3c91904e338c279e5c1a2da74" +registries = "General" +uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" +version = "2.15.4+0" + +[[deps.XZ_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e52eca002a11c30a858185efdfb15311e1c7a6bf" +registries = "General" +uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" +version = "5.8.4+0" + +[[deps.Xorg_libICE_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a3ea76ee3f4facd7a64684f9af25310825ee3668" +registries = "General" +uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" +version = "1.1.2+0" + +[[deps.Xorg_libSM_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libICE_jll"] +git-tree-sha1 = "9c7ad99c629a44f81e7799eb05ec2746abb5d588" +registries = "General" +uuid = "c834827a-8449-5923-a945-d239c165b7dd" +version = "1.2.6+0" + +[[deps.Xorg_libX11_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] +git-tree-sha1 = "ce91b54a5f90c4f27df6950706234b34e49cf79e" +registries = "General" +uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" +version = "1.8.14+0" + +[[deps.Xorg_libXau_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "aa1261ebbac3ccc8d16558ae6799524c450ed16b" +registries = "General" +uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" +version = "1.0.13+0" + +[[deps.Xorg_libXcursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "6c74ca84bbabc18c4547014765d194ff0b4dc9da" +registries = "General" +uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" +version = "1.2.4+0" + +[[deps.Xorg_libXdmcp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "52858d64353db33a56e13c341d7bf44cd0d7b309" +registries = "General" +uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" +version = "1.1.6+0" + +[[deps.Xorg_libXext_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "1a4a26870bf1e5d26cd585e38038d399d7e65706" +registries = "General" +uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" +version = "1.3.8+0" + +[[deps.Xorg_libXfixes_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "75e00946e43621e09d431d9b95818ee751e6b2ef" +registries = "General" +uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" +version = "6.0.2+0" + +[[deps.Xorg_libXi_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] +git-tree-sha1 = "704274ca960c42049fd9bda089fc4be32af5cdca" +registries = "General" +uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" +version = "1.8.5+0" + +[[deps.Xorg_libXinerama_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll"] +git-tree-sha1 = "0ba01bc7396896a4ace8aab67db31403c71628f4" +registries = "General" +uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" +version = "1.1.7+0" + +[[deps.Xorg_libXrandr_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXext_jll", "Xorg_libXrender_jll"] +git-tree-sha1 = "6c174ef70c96c76f4c3f4d3cfbe09d018bcd1b53" +registries = "General" +uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" +version = "1.5.6+0" + +[[deps.Xorg_libXrender_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "a54cf54677c9f6eac0d97e9d757c233b56bc21b9" +registries = "General" +uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" +version = "0.9.13+0" + +[[deps.Xorg_libpciaccess_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "58972370b81423fc546c56a60ed1a009450177c3" +registries = "General" +uuid = "a65dc6b1-eb27-53a1-bb3e-dea574b5389e" +version = "0.19.0+0" + +[[deps.Xorg_libxcb_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libXau_jll", "Xorg_libXdmcp_jll"] +git-tree-sha1 = "bfcaf7ec088eaba362093393fe11aa141fa15422" +registries = "General" +uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" +version = "1.17.1+0" + +[[deps.Xorg_libxkbfile_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] +git-tree-sha1 = "ed756a03e95fff88d8f738ebc2849431bdd4fd1a" +registries = "General" +uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" +version = "1.2.0+0" + +[[deps.Xorg_xcb_util_cursor_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] +git-tree-sha1 = "9750dc53819eba4e9a20be42349a6d3b86c7cdf8" +registries = "General" +uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" +version = "0.1.6+0" + +[[deps.Xorg_xcb_util_image_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f4fc02e384b74418679983a97385644b67e1263b" +registries = "General" +uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll"] +git-tree-sha1 = "68da27247e7d8d8dafd1fcf0c3654ad6506f5f97" +registries = "General" +uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_keysyms_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "44ec54b0e2acd408b0fb361e1e9244c60c9c3dd4" +registries = "General" +uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" +version = "0.4.1+0" + +[[deps.Xorg_xcb_util_renderutil_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "5b0263b6d080716a02544c55fdff2c8d7f9a16a0" +registries = "General" +uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" +version = "0.3.10+0" + +[[deps.Xorg_xcb_util_wm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_jll"] +git-tree-sha1 = "f233c83cad1fa0e70b7771e0e21b061a116f2763" +registries = "General" +uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" +version = "0.4.2+0" + +[[deps.Xorg_xkbcomp_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] +git-tree-sha1 = "9a5e6e8f1a7c40941465c04840145e527f0d6b4b" +registries = "General" +uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" +version = "1.5.0+0" + +[[deps.Xorg_xkeyboard_config_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] +git-tree-sha1 = "df77b22eb77051b3d0ef30aab979684f7482c66f" +registries = "General" +uuid = "33bec58e-1273-512f-9401-5d533626f822" +version = "2.48.0+0" + +[[deps.Xorg_xtrans_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a63799ff68005991f9d9491b6e95bd3478d783cb" +registries = "General" +uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" +version = "1.6.0+0" + +[[deps.Zlib_jll]] +deps = ["Libdl"] +uuid = "83775a58-1f1d-513f-b197-d71354ab007a" +version = "1.3.2+0" + +[[deps.Zstd_jll]] +deps = ["CompilerSupportLibraries_jll", "Libdl"] +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.7+1" + +[[deps.aws_c_auth_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_cal_jll", "aws_c_http_jll", "aws_c_sdkutils_jll"] +git-tree-sha1 = "8cab83c96af80a1be968251ce1a0548a7545484d" +registries = "General" +uuid = "2b3700d1-4306-52e2-a478-c162f0c514be" +version = "0.9.6+0" + +[[deps.aws_c_cal_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "22c0f42f4a1f0dc5dcfa8fd267c4ac407c455e7a" +registries = "General" +uuid = "70f11efc-bab2-57f1-b0f3-22aad4e67c4b" +version = "0.9.13+0" + +[[deps.aws_c_common_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "a759cb9bf456ad792cc7898a81ae333cce9ef02a" +registries = "General" +uuid = "73048d1d-b8c4-5092-a58d-866c5e8d1e50" +version = "0.12.6+0" + +[[deps.aws_c_compression_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "7910c72f45f44afd297c39fe43b99c56d5ed22ec" +registries = "General" +uuid = "73a04cd5-f3d7-5bac-9290-e8adb709f224" +version = "0.3.2+0" + +[[deps.aws_c_http_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_compression_jll", "aws_c_io_jll"] +git-tree-sha1 = "3fb8685778068de502c72fec5dd8075e037cee15" +registries = "General" +uuid = "3254fc65-9028-534d-aa9d-d76d128babc6" +version = "0.10.15+0" + +[[deps.aws_c_io_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_cal_jll", "aws_c_common_jll", "s2n_tls_jll"] +git-tree-sha1 = "7e481d474b2087ee8bbf55b81bf9119f21e396d9" +registries = "General" +uuid = "13c41daa-f319-5298-b5eb-5754e0170d52" +version = "0.26.3+0" + +[[deps.aws_c_s3_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_auth_jll", "aws_c_common_jll", "aws_c_http_jll", "aws_checksums_jll", "s2n_tls_jll"] +git-tree-sha1 = "3e9917ab25114feba657e71be41cad068b9f6595" +registries = "General" +uuid = "bd1f34fb-993f-5903-a121-aaf302eed6d4" +version = "0.11.5+0" + +[[deps.aws_c_sdkutils_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "c43dfba2c1ab9ea9f02f2c80e86fa16f6460244e" +registries = "General" +uuid = "1282aa60-004d-510b-9f52-12498d409daa" +version = "0.2.4+1" + +[[deps.aws_checksums_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "aws_c_common_jll"] +git-tree-sha1 = "2570c8e23f4771a087b12a47edcaaa670ac05a01" +registries = "General" +uuid = "b2a88e68-78e7-5e94-8c20-c02986ec140e" +version = "0.2.10+0" + +[[deps.dlfcn_win32_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e141d67ffe550eadfb5af1bdbdaf138031e4805f" +registries = "General" +uuid = "c4b69c83-5512-53e3-94e6-de98773c479f" +version = "1.4.2+0" + +[[deps.eudev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "c3b0e6196d50eab0c5ed34021aaa0bb463489510" +registries = "General" +uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" +version = "3.2.14+0" + +[[deps.fzf_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b6a34e0e0960190ac2a4363a1bd003504772d631" +registries = "General" +uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" +version = "0.61.1+0" + +[[deps.libaec_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "60f4792734488db6f42e2c7699f1d4594780bd03" +registries = "General" +uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" +version = "1.1.7+0" + +[[deps.libaom_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "0343a4dc329ca4dd71bf5c5b3fc1a8c2f6e0692d" +registries = "General" +uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" +version = "3.15.2+0" + +[[deps.libass_jll]] +deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "cb007192783c56d8249db4cf0e3495001edfe414" +registries = "General" +uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" +version = "0.17.5+0" + +[[deps.libblastrampoline_jll]] +deps = ["Artifacts", "Libdl"] +uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" +version = "5.15.0+0" + +[[deps.libdecor_jll]] +deps = ["Artifacts", "Dbus_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "Pango_jll", "Wayland_jll", "xkbcommon_jll"] +git-tree-sha1 = "9bf7903af251d2050b467f76bdbe57ce541f7f4f" +registries = "General" +uuid = "1183f4f0-6f2a-5f1a-908b-139f9cdfea6f" +version = "0.2.2+0" + +[[deps.libdrm_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libpciaccess_jll"] +git-tree-sha1 = "28e57478e8a160d346a19c28b3fffb9273bcc9c2" +registries = "General" +uuid = "8e53e030-5e6c-5a89-a30b-be5b7263a166" +version = "2.4.134+0" + +[[deps.libevdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "56d643b57b188d30cccc25e331d416d3d358e557" +registries = "General" +uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" +version = "1.13.4+0" + +[[deps.libfdk_aac_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "646634dd19587a56ee2f1199563ec056c5f228df" +registries = "General" +uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" +version = "2.0.4+0" + +[[deps.libinput_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "eudev_jll", "libevdev_jll", "mtdev_jll"] +git-tree-sha1 = "91d05d7f4a9f67205bd6cf395e488009fe85b499" +registries = "General" +uuid = "36db933b-70db-51c0-b978-0f229ee0e533" +version = "1.28.1+0" + +[[deps.libpng_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] +git-tree-sha1 = "1fcd6cf05d23adab0b5ffc496d020537215751dc" +registries = "General" +uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" +version = "1.6.59+1" + +[[deps.libva_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll", "Xorg_libXext_jll", "Xorg_libXfixes_jll", "libdrm_jll"] +git-tree-sha1 = "21cbc8fc2ac0bb182ed461e556d9c062ae6a3164" +registries = "General" +uuid = "9a156e7d-b971-5f62-b2c9-67348b8fb97c" +version = "2.24.1+0" + +[[deps.libvorbis_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll"] +git-tree-sha1 = "11e1772e7f3cc987e9d3de991dd4f6b2602663a5" +registries = "General" +uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" +version = "1.3.8+0" + +[[deps.mpif_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "MPIABI_jll", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "TOML"] +git-tree-sha1 = "52e322f7a6d418576cdeb043dd2a7f7ad9a8abd0" +registries = "General" +uuid = "9aeb927a-4695-514f-a259-621a69f20ec0" +version = "1.0.1+0" + +[[deps.mtdev_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "b4d631fd51f2e9cdd93724ae25b2efc198b059b1" +registries = "General" +uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" +version = "1.1.7+0" + +[[deps.nghttp2_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" +version = "1.70.0+0" + +[[deps.oneTBB_jll]] +deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl"] +git-tree-sha1 = "ed2d8d4ad96c03d3abe3382cdbbdea9489b63636" +registries = "General" +uuid = "1317d2d5-d96f-522e-a858-c73665f53c3e" +version = "2023.1.0+0" + +[[deps.p7zip_jll]] +deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] +uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" +version = "17.8.3+0" + +[[deps.s2n_tls_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "6f9611b1b99beb089154f19a4031579f3a818cbd" +registries = "General" +uuid = "cddc5d3d-934d-5d3a-9747-62fc12ea3f48" +version = "1.7.12+0" + +[[deps.x264_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "14cc7083fc6dff3cc44f2bc435ee96d06ed79aa7" +registries = "General" +uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" +version = "10164.0.1+0" + +[[deps.x265_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl"] +git-tree-sha1 = "e7b67590c14d487e734dcb925924c5dc43ec85f3" +registries = "General" +uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" +version = "4.1.0+0" + +[[deps.xkbcommon_jll]] +deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] +git-tree-sha1 = "a1fc6507a40bf504527d0d4067d718f8e179b2b8" +registries = "General" +uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" +version = "1.13.0+0" + +[registries.General] +url = "https://github.com/JuliaRegistries/General.git" +uuid = "23338594-aafe-5451-b93e-139f81909106" diff --git a/ci/manifests/README.md b/ci/manifests/README.md new file mode 100644 index 0000000000..63de455143 --- /dev/null +++ b/ci/manifests/README.md @@ -0,0 +1,111 @@ +# Pinned CI dependency sets + +`Manifest.toml` is gitignored, as it should be for a library: developers resolve +their own environments. CI is the one consumer that wants the opposite, because +an unpinned resolve makes every run non-reproducible and quietly wastes time: + +- Each run re-resolves against the live registry, so two runs an hour apart can + test different dependency versions and a failure may not reproduce. +- Any upstream release invalidates the precompiled objects for that package and + everything downstream of it in the restored depot cache, so the run pays to + recompile a slice of the tree that had not otherwise changed. + +`.github/workflows/test.yaml` therefore copies the pin matching the running Julia +minor version into `Manifest.toml` before building. The copy exists only inside +the CI runner and never reaches a developer's working tree. + +## Compilation caches + +CI uses `JULIA_CPU_TARGET=generic` for portable package images across runner CPUs of the same +architecture. The production depot and source-cache keys record the optimization and bounds-checking +settings, so incompatible images are not restored. Existing production caches remain reusable. + +CI restores the dependency depot first, then overlays GPEC's compiled package directory from a +smaller cache keyed by Julia version, runner architecture, dependency pins, source contents, and +build settings. Cache writes have a run-specific suffix to avoid stale reservations; restores +match the compatible prefix. A compatible snapshot is reused without saving another copy. +Missing or incompatible snapshots are refreshed after successful compilation. Cache saves are +best-effort and limited to three minutes, so a stalled upload cannot block the workflow indefinitely. + +For each Julia version, a production precompile job prepares the environment before releasing +three parallel test groups (`core`, `api`, `errorfields`). On hosted runners it uploads a one-day +workflow artifact containing the resolved manifest and a compressed depot archive. Test groups +first try the compatible depot and exact source-image caches. When either is missing or its restore +fails, they restore the artifact into a fresh depot, preserving permissions and symlinks through tar +and avoiding partially restored read-only files. Its `JULIA_DEPOT_PATH` keeps a trailing `:` to retain +Julia's bundled depots. The small manifest +artifact is always restored. Cache availability therefore affects speed rather than correctness: +a reservation failure or eviction cannot invalidate the job handoff. Artifact publication is required +for precompile to pass. The two Julia versions progress independently. +Cold compilation is paid once per version; warm runs reuse the existing production images. + +Test jobs use optimization level 1 to reduce first-use compilation. Julia can still reuse cached +package images built at level 2; the production build job keeps that default optimization level. +Only the test orchestration modules disable optimization entirely. + +Bounds checking remains enabled. A source change still requires compiling a new GPEC image; +the source cache helps subsequent runs of that source, not the first build. Cache eviction or a +new Julia version likewise requires rebuilding. The test runner logs elapsed and compilation +seconds per file to distinguish this startup cost from work inside the tests. + +## Local cache on self-hosted runners + +Set the repository variable `GPEC_LOCAL_CACHE_DIR` to a shared directory on the runner host. +On self-hosted runners this replaces GitHub cache and environment-artifact transfers with local +snapshots keyed by the exact Julia version, dependencies, source, architecture, and build flags. +Hosted runners continue using GitHub storage. + +The precompile job copies its depot and resolved manifest into a staging directory, then publishes +the complete snapshot with an atomic rename. A lock serializes publishers. Test jobs copy from +that immutable snapshot into their private depots, so concurrent jobs do not write shared images. +An absent test snapshot fails the handoff. Existing snapshots are reused between workflow runs; +obsolete snapshots can be removed when no jobs need them. Snapshots use reflinks when supported. + +All runners eligible for these jobs must have access to the configured directory. On +`pharr-desktop` it is `/home/pharr/Projects/github/julia-cache`; each new runner has its own home +and depot. Unset the variable to return to GitHub storage. + +## Files + +| File | Purpose | +| --- | --- | +| `Manifest-v1.11.toml` | Dependency set for the `1.11` matrix job | +| `Manifest-v1.12.toml` | Dependency set for local Julia 1.12 runs | +| `Manifest-v1.13.toml` | Dependency set for the `1.x` matrix job | +| `project.sha256` | `Project.toml` checksum when the pins were generated | +| `update.jl` | Regenerates the above | + +## Regenerating + +Run once per Julia minor version the test matrix builds, from anywhere in the +repository: + +```bash +julia +1.11 ci/manifests/update.jl +julia +1.12 ci/manifests/update.jl +julia +1.13 ci/manifests/update.jl +``` + +Each run resolves a bare copy of `Project.toml` in a temporary directory, so your +own `Manifest.toml` is left alone. + +Regenerate whenever you change `[deps]` or `[compat]`, and periodically to pick up +upstream releases — a pin that never moves means CI stops noticing that a new +dependency version breaks the package. + +## When CI tells you a pin is stale + +The workflow compares `Project.toml` against `project.sha256` and emits a warning +annotation, not a failure, when they differ; `Pkg` also warns during the build +step that the manifest no longer satisfies the project. In that state CI still +passes, having re-resolved the affected packages, so the only cost is the +determinism and cache reuse the pin was there to provide. Regenerate and commit. + +Because the checksum covers the whole file, bumping `version` in `Project.toml` +also trips the warning even though no dependency changed. Regenerating clears it. + +## Adding a Julia version to the matrix + +Add the matrix entry in `.github/workflows/test.yaml`, then generate the matching +`Manifest-v..toml`. A job with no matching pin is not an error — the +workflow warns and lets `Pkg` resolve normally. diff --git a/ci/manifests/project.sha256 b/ci/manifests/project.sha256 new file mode 100644 index 0000000000..677b6b55ef --- /dev/null +++ b/ci/manifests/project.sha256 @@ -0,0 +1 @@ +f7a12df0a57f6dc6e828822e093b997e7359cd7145772f96a308d6bc9beb8d67 diff --git a/ci/manifests/update.jl b/ci/manifests/update.jl new file mode 100644 index 0000000000..ad204f587e --- /dev/null +++ b/ci/manifests/update.jl @@ -0,0 +1,41 @@ +""" +Regenerate the pinned CI dependency sets in `ci/manifests/`. + +Run once per Julia minor version that `.github/workflows/test.yaml` builds, from +anywhere: + + julia +1.11 ci/manifests/update.jl + julia +1.12 ci/manifests/update.jl + julia +1.13 ci/manifests/update.jl + +Each run resolves `Project.toml` for the running Julia version and writes +`ci/manifests/Manifest-v..toml`, then refreshes +`ci/manifests/project.sha256` so CI can tell when a pin has fallen behind +`Project.toml`. Resolution happens in a temporary directory, so the developer's +own `Manifest.toml` is left untouched. +""" + +using SHA +using Pkg + +const REPO_ROOT = normpath(joinpath(@__DIR__, "..", "..")) +const PIN_DIR = joinpath(REPO_ROOT, "ci", "manifests") +const PROJECT = joinpath(REPO_ROOT, "Project.toml") + +pin = joinpath(PIN_DIR, "Manifest-v$(VERSION.major).$(VERSION.minor).toml") + +# Resolve a bare copy of Project.toml so the pin records a clean maximal resolve +# rather than whatever the working tree happens to be sitting on. +mktempdir() do dir + cp(PROJECT, joinpath(dir, "Project.toml")) + withenv("JULIA_PKG_PRECOMPILE_AUTO" => "0") do + Pkg.activate(dir) + Pkg.resolve() + Pkg.instantiate() + end + cp(joinpath(dir, "Manifest.toml"), pin; force=true) +end + +write(joinpath(PIN_DIR, "project.sha256"), bytes2hex(open(sha256, PROJECT)) * "\n") + +@info "Wrote $(relpath(pin, REPO_ROOT)) for Julia $VERSION" diff --git a/deps/build.jl b/deps/build.jl deleted file mode 100644 index 2c5f6ed213..0000000000 --- a/deps/build.jl +++ /dev/null @@ -1,14 +0,0 @@ -# How to modify build.jl: -# 1) Declare build functions for each Fortran codes as 'build_*_fortran()' -# 2) Add these functions to the results array in build_fortran() -# 3) Export the functions you want to be accessible from outside - - -const parent_dir = joinpath(@__DIR__, "..", "src") - -include(joinpath(@__DIR__, "build_helpers.jl")) - -export build_fortran -export build_spline_fortran, build_vacuum_fortran - -build_fortran() # Call the build function to execute the builds \ No newline at end of file diff --git a/deps/build_helpers.jl b/deps/build_helpers.jl deleted file mode 100644 index 215cfa2bb0..0000000000 --- a/deps/build_helpers.jl +++ /dev/null @@ -1,85 +0,0 @@ -# How to modify build_helpers.jl: -# 1) Declare build functions for each Fortran codes as 'build_*_fortran()' -# 2) Add these functions to the results array in build_fortran() -# 3) Export the functions you want to be accessible from outside - - -const parent_dir = joinpath(@__DIR__, "..", "src") - -export build_spline_fortran, build_vacuum_fortran - -function build_fortran() - ENV["FC"] = get(ENV, "FC", "gfortran") - # Set OS-dependent flags - println("Setting up environment for ", Sys.KERNEL) - if Sys.isapple() - ENV["LIBS"] = "-framework Accelerate" - ENV["LIBSUFFIX"] = ".dylib" - elseif Sys.islinux() - ENV["LIBS"] = "-lopenblas" - ENV["LIBSUFFIX"] = ".so" - elseif Sys.iswindows() - ENV["LIBS"] = "-lopenblas" - ENV["LIBSUFFIX"] = ".dll" - else - error("Unsupported OS for Fortran build") - end - ENV["RECURSFLAG"] = "-frecursive" - ENV["LEGACYFLAG"] = "-std=legacy" - ENV["ZEROFLAG"] = "-finit-local-zero" - ENV["FFLAGS"] = "-fPIC" - ENV["LDFLAGS"] = "-shared" - - results = [ - # build_jpec_fortran() add here - build_spline_fortran(), - build_vacuum_fortran(), - ] - - if all(results) - @info "All Fortran builds succeeded!" - else - @error "Some Fortran builds failed." - end - -end - -function build_spline_fortran() - dir = joinpath(parent_dir, "Splines", "fortran") - try - run(pipeline(`make -C $dir`)) - @info "Splines-fortran compiled well" - return true - catch e - @error "Failed to build Splines-fortran: $e" - return false - end -end - -function build_vacuum_fortran() - dir = joinpath(parent_dir, "Vacuum", "fortran") - try - run(pipeline(`make -C $dir`)) - @info "Vacuum-fortran compiled well" - return true - catch e - @error "Failed to build Vacuum-fortran: $e" - return false - end - -end - -# Example for including more fortran: -# -# function build_jpec_fortran() -# dir = joinpath(parent_dir, "Gpec", "fortran") -# try -# run(pipeline(`make -C $dir`, stdout=DevNull, stderr=DevNull)) -# println("Gpec-fortran compiled well") -# return true -# catch e -# println("Failed to build Gpec-fortran: $e") -# return false -# end -# end - diff --git a/docs/DOC_STANDARD.md b/docs/DOC_STANDARD.md new file mode 100644 index 0000000000..bafe45902f --- /dev/null +++ b/docs/DOC_STANDARD.md @@ -0,0 +1,120 @@ +# GPEC documentation standard + +This is the authoring standard for the GPEC user documentation under +`docs/src/`. The goal is that each physics module page reads like a compact +*Journal of Computational Physics* methods paper: the governing equations, the +numerical method, and the validation that the method works — with figures whose +provenance is explicit. Those module pages **are** built by Documenter and +published on the public documentation site. + +This standard file (`docs/DOC_STANDARD.md`) is itself the one exception: it lives +outside `docs/src/` and is not listed in `make.jl`, so Documenter does not +render it to the website. It is a contributor-facing reference, read here in the +repository — which is why `developer_notes.md` links to it by GitHub URL rather +than as a site-internal page. + +The `Stability` and `Local Stability` pages are +the reference exemplars; new or reworked module pages should follow their shape. + +## Module-page template + +A module reference page (`docs/src/.md`) has these sections, in order. +Not every section is large for every module, but the skeleton is the same. + +1. **What it computes** — the physics of the stage in at most two paragraphs, + and where it sits in the pipeline (a link back to the Workflow page). +2. **Governing equations** — the equations the module discretizes, as display + equations, each citing its paper by "Author (year)" with a link to the + citations page; equation numbers where the paper is in `docs/resources/`. +3. **Running it** — the deck section(s), as a snippet copied from a shipped + example that is named, and the matching scripting-API call. +4. **Outputs** — a table of the `gpec.h5` datasets the stage writes, by full + path, with meaning and units. +5. **Numerical notes** — the discretization and algorithm choices that matter + to a user, kept short, with the validation figures (see below). +6. **API reference** — an `@autodocs` block for the module so every exported + docstring is rendered and `checkdocs=:exports` stays satisfied. + +## Figures + +### Where figures live + +- **Content figures** (anything shown on a page) live in + `docs/src/figures//`. Each committed `.png` sits next to the + script `make_.jl` that produced it — script and figure are always + committed together, so it is always clear how a figure was made. +- `docs/src/assets/` is reserved for **Documenter chrome only** (the site logo, + themes, custom CSS). Content figures never go there. +- The shared helper `docs/figure_tools.jl` (outside `src/`, so it is not + published) provides `save_doc_figure`, provenance stamping, the manifest, and + the `step_series` spectrum helper. Every `make_.jl` `include`s it. + +### Where a figure's data comes from + +Documentation figures get their **own** generator scripts under +`docs/src/figures/`. They are not forced to route through `benchmarks/`. When a +figure genuinely *is* a benchmark or Fortran cross-check result, its +`make_.jl` may call the relevant `benchmarks/` script, and its `depends` +list records that coupling — but the committed PNG and a runnable script still +live together under `docs/src/figures//`. + +`benchmarks/` remains the home of performance comparisons and Fortran +cross-checks (its outputs are git-ignored and may be heavy). `docs/src/figures/` +is the home of the small, purpose-built, committed figures that illustrate a +page. + +### Figures do not build with the docs + +Figure scripts run **manually**, in the root project: + +```bash +julia --project=. docs/src/figures//make_.jl +``` + +They are **never** run at Documenter build time. The build only embeds the +committed PNGs, so `docs/Project.toml` stays minimal (no `Plots`) and the build +stays fast. This is also why we do not regenerate every figure on every docs +change — see the regeneration policy below. + +### Provenance + +`save_doc_figure` stamps a small `GPEC · ` mark in the corner +of every figure (so a figure's age is visible when browsing the rendered site) +and writes a machine-readable entry to `docs/src/figures/manifest.toml`: + +```toml +[inner_layer.rotated_ray_contour] +script = "make_rotated_ray_contour.jl" +commit = "3a0837e3" +date = "2026-07-08" +depends = ["src/InnerLayer/GGJ/Ray.jl"] +``` + +`depends` lists the repo-relative source files whose *numbers* the figure +visualizes. Generate figures as the last step before committing a change; the +stamp then reflects the state they were made against (a `-dirty` suffix marks a +figure generated with an uncommitted working tree, which is honest, not an +error). + +### When to regenerate a figure + +Regenerate a figure (and its manifest entry) only when: + +1. a file in its `depends` list changed the numbers it shows — the regression + harness is the trigger: if a tracked quantity for that module moved, its + figures are suspect; +2. the physics or method the figure illustrates changed; or +3. the figure script itself changed. + +Do **not** regenerate for prose edits, formatting, or changes to unrelated +modules. Because each figure carries its generating commit and a `depends` +list, a reviewer (or a future `docs/check_figures.jl`) can tell when a figure +predates the last change to a file it depends on and is therefore stale. + +### Legacy figures + +A figure migrated from before this system, whose original generator was not +preserved and which cannot be faithfully reproduced from current code, is +registered with `register_legacy_figure` (`commit = "legacy"`, a `note` +explaining why). This is honest bookkeeping for genuinely irreproducible +figures — it is never a substitute for writing a generator for a new figure. diff --git a/docs/Project.toml b/docs/Project.toml index a0026afa76..12f07f7025 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,6 +1,6 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -JPEC = "462872dd-e066-4d2e-b993-6468b5239634" +GeneralizedPerturbedEquilibrium = "462872dd-e066-4d2e-b993-6468b5239634" [compat] Documenter = "1.0" diff --git a/docs/README.md b/docs/README.md index 44a2f400c3..a99dbdde37 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,20 +1,20 @@ -# JPEC.jl Documentation +# GPEC.jl Documentation -This directory contains the documentation source files for JPEC.jl. +This directory contains the documentation source files for GPEC.jl. ## Building Documentation Locally To build the documentation locally: ```bash -julia build_docs_local.jl +julia --project=. build_docs_local.jl ``` Or step by step: ```bash # From the root directory -julia --project=. -e 'using Pkg; Pkg.instantiate(); Pkg.build()' +julia --project=. -e 'using Pkg; Pkg.instantiate()' julia --project=docs -e 'using Pkg; Pkg.instantiate(); Pkg.develop(PackageSpec(path="."))' julia --project=docs docs/make.jl ``` @@ -38,4 +38,4 @@ The documentation is automatically built and deployed to GitHub Pages via GitHub - Julia 1.11+ - Documenter.jl -- All JPEC.jl dependencies (see main Project.toml) +- All GPEC.jl dependencies (see main Project.toml) diff --git a/docs/development/architecture.md b/docs/development/architecture.md new file mode 100644 index 0000000000..781f0b1559 --- /dev/null +++ b/docs/development/architecture.md @@ -0,0 +1,245 @@ +# Architecture + +## Computational Workflow + +GPEC follows a three-stage analysis pipeline: + +1. **Equilibrium** → Solve Grad-Shafranov equation, compute flux surfaces, safety factor q-profile +2. **Stability Analysis** → Solve ideal MHD eigenvalue problem (DCON-style), identify singular surfaces +3. **Perturbed Equilibrium** → Compute plasma response to external fields, analyze singular coupling and island formation + +This workflow is reflected in the modular structure and data flow. + +## Module Structure + +GPEC consists of physics modules organized in `src/`. These names are the canonical Areas used in commit subjects and PR titles; see [`naming.md`](naming.md). + +Splines are provided by the external `FastInterpolations` package rather than by a module here. + +### Foundation Modules + +1. **Utilities** (`src/Utilities/`) - Shared computational tools + - `FourierTransforms.jl` - Efficient Fourier transform utilities with pre-computed basis functions + - Provides type-stable functor pattern for repeated transforms + - Used by Vacuum and PerturbedEquilibrium modules + +### Core Physics Modules + +2. **Equilibrium** (`src/Equilibrium/`) - MHD equilibrium solvers + - Main entry point: `PlasmaEquilibrium(file; eq_type)`, `PlasmaEquilibrium(analytic_config)`, `PlasmaEquilibrium(dd)`, or `PlasmaEquilibrium(config, source)` + - Supports multiple equilibrium types: + - `efit` - EFIT g-file format + - `chease`, `chease2` - CHEASE equilibrium code formats + - analytic models, selected by their deck section rather than `eq_type`: `[Solovev]`, + `[LargeAspectRatio]`, `[TJAnalytic]` + - Key files, by pipeline stage: + - `Config.jl` - `EquilibriumConfig`, `EquilibriumParameters` + - `PlasmaEquilibrium.jl` - The result type and its profile and geometry splines + - `Construction.jl` - Solver input for each source type, the `PlasmaEquilibrium` constructors, `reform` + - `Sources/` - One file per input format (`GFile.jl`, `Chease.jl`, `IFile.jl`, `IMAS.jl`), the ingest and + run-input types (`Ingest.jl`), and `Analytic/` (the analytic registry plus one file per model with its config and solver) + - `Grid/` - First-pass ψ grids (`PsiGrid.jl`) and the two-pass refinement (`GridRefinement.jl`) + - `Axisymmetric/` - The 2D solvers (`Direct.jl`, `DirectArcLength.jl`, `DirectByInversion.jl`, `Inverse.jl`), + the Grad-Shafranov residual, and separatrix finding + - `Derived/` - Global parameters and q extrema, flux-surface metrics, geometry profiles, coordinate-invariant weights + - Status: Stable and feature-complete + +3. **Vacuum** (`src/Vacuum/`) - Vacuum field calculations and Green's functions + - Computes vacuum response matrices for ideal MHD analysis + - Solves the exterior boundary-integral system for the vacuum energy matrix `wv`, and optionally + (`compute_Iv=true`) the interior system as well to build the surface-current matrix `I_v` + (Park 2007 eq. 21b). The interior/exterior Green's functions themselves are internal scratch. + - Main functions: + - `compute_vacuum_response()` / `compute_vacuum_response!()` - allocating and in-place entry points + - Key files: + - `DataTypes.jl` - Data structures (`VacuumInput`, `PlasmaGeometry`, `WallGeometry`) + - `Kernel2D.jl` / `Kernel3D.jl` - Single-/double-layer kernel assembly + - `Field.jl` - Vacuum field and potential evaluation off the surface + - Status: **Pure Julia implementation complete and available** + +4. **ForceFreeStates** (`src/ForceFreeStates/`) - Ideal MHD stability analysis (DCON-style) + - Solves ideal MHD eigenvalue problem with force-free boundary conditions + - Identifies singular surfaces where ξ·∇ψ = 0 + - Key files: + - `CoreTypes.jl` - Module-wide types (`ForceFreeStatesControl`, `ForceFreeStatesInternal`) + - `Result.jl` - `ForceFreeStatesResult`, the published solve product every downstream stage reads + - `EulerLagrange.jl` - ODE integration of the Euler-Lagrange equations (`OdeState`, derivative kernel) + - `Surfaces/` - Singular-surface finding, Frobenius asymptotics, and GGJ coefficients + - `Riccati/` - Chunked fundamental-matrix (STRIDE) driver and Δ' boundary-value problem + - `Galerkin/` - RDCON outer-region singular Galerkin Δ' solver + - `Matching/` - Outer↔inner resistive matching (`DeltaPrimeData`, `MatchProblem`, `MatchResult`) + - `Fourfit.jl` - Fourier fitting routines (`MatrixSplines`) + - `FixedBoundaryStability.jl` - Fixed boundary analysis + - `Free.jl` - Free boundary stability + - Status: Stable, core DCON functionality implemented + +5. **LocalStability** (`src/LocalStability/`) - Local high-n stability + - `Ballooning.jl` - Local stability scan: Mercier D_I, resistive interchange D_R, and high-n ballooning Δ' (s–α). Replaces the former standalone `Mercier.jl`. + - Depends only on Equilibrium (plus math libraries); carries no stability-solver state + - `Problem.jl` - `solve(LocalStabilityProblem(equil), Ballooning())` → `LocalStabilityResult`; a deck's `[LocalStability]` section runs the same solve + - Kernels: `compute_ballooning_stability!`, `ballooning_alpha_boundary` + - Status: Stable + +### Perturbed Equilibrium Modules + +6. **ForcingTerms** (`src/ForcingTerms/`) - External field specification + - Handles external magnetic field perturbations (coils, RMP, etc.) + - Supports ASCII and HDF5 forcing data formats + - `ForcingMode` data structure specifies amplitude and phase for each (m,n) component + - Status: Complete and functional + +7. **PerturbedEquilibrium** (`src/PerturbedEquilibrium/`) - **GPEC-style plasma response** + - Computes plasma response to external forcing + - Calculates singular coupling metrics at rational surfaces + - Key files: + - `PerturbedEquilibrium.jl` - Main entry point + - `PerturbedEquilibriumStructs.jl` - Data structures + - `ResponseMatrices.jl` - Permeability matrix calculation + - `FieldReconstruction.jl` - Mode-space field reconstruction + - `Response.jl` - Plasma response computation + - `SingularCoupling.jl` - **Singular surface analysis** including: + - Delta prime (Δ') tearing stability parameter + - Resonant flux and currents at rational surfaces + - Island half-widths and Chirikov parameters + - Green's functions at interior flux surfaces + - Surface inductance for singular surfaces + - `ResonantCoupling.jl` - `ResonantCoupling` (in-memory or from gpec.h5): windowed SVD for the dominant applied-field mode, and the normalization/overlap helpers that project coil spectra onto it + - `Utils.jl` - Helper functions + - Status: Core plasma response and singular coupling calculations implemented; active area of development + +### Resistive and Kinetic Modules + +8. **InnerLayer** (`src/InnerLayer/`) - Resistive inner-layer physics + - `GGJ/` - Glasser-Greene-Johnson layer model + - `SLAYER/` - Layer solver used for growth-rate extraction + - Both are submodules and are valid Areas in their own right (`InnerLayer.GGJ`, `InnerLayer.SLAYER`) + +9. **Tearing** (`src/Tearing/`) - Tearing mode dispersion and drivers + - `Dispersion/` - Dispersion relation solvers + - `TearingProblem.jl`, `GGJTearing.jl`, `SLAYERTearing.jl` - The tearing problem and its solve with each inner-layer model + - `CriticalResonantField/` - Critical resonant field (field penetration threshold) + - Re-binds `InnerLayer` and exposes it alongside its own submodules + +10. **KineticForces** (`src/KineticForces/`) - Kinetic contributions to the force balance + - Neoclassical toroidal viscosity (NTV) torque and kinetic energy contributions + - Reads kinetic profiles configured under `[KineticForces]` + +11. **ErrorFields** (`src/ErrorFields/`) - Error-field sensitivity to coil misalignment + - `Sensitivity.jl` - central-difference linearization of every coil set's control-surface spectrum with respect to its rigid shifts and tilts, on one shared boundary grid + - `sensitivity_table` projects that linearization onto any windowed dominant mode post hoc (in memory or from `gpec.h5`); consumes `PerturbedEquilibrium.ResonantCoupling` and `ForcingTerms.coil_forcing_modes` + - Configured under `[ErrorFields]`; writes `ErrorFields/CoilSensitivities/` + +### Post-processing + +12. **Analysis** (`src/Analysis/`) - Plotting and post-processing + - Submodules mirror the physics modules they visualize, so their names shadow them + - Not part of the solve path; consumes `gpec.h5` + +## Configuration + +**Unified Configuration File**: `gpec.toml` + +All GPEC modules are configured via a single TOML file with the following sections: + +- `[Equilibrium]` - Equilibrium solver settings +- `[Wall]` - Wall geometry and vacuum region +- `[ForceFreeStates]` - Stability analysis parameters +- `[PerturbedEquilibrium]` - Perturbed equilibrium settings +- `[ForcingTerms]` - External field specification + +The `[Run]` section holds the run-wide settings: `output_file` (default `gpec.h5`), the one file every +stage saves into, and `verbose`. A stage runs when its section is present, so a deck stops early by +omitting the later sections. + +Example configuration files are provided in: +- `examples/Solovev_ideal_example/gpec.toml` +- `examples/DIIID-like_ideal_example/gpec.toml` + +**Note**: Legacy configuration files (`equil.toml`, `vac.in`) are not read; `gpec.toml` is the only input deck. + +## Data Flow + +The complete GPEC analysis pipeline: + +1. **Equilibrium Setup**: + - `PlasmaEquilibrium(config, source)` forms the equilibrium from the `[Equilibrium]` section of `gpec.toml` + - Parses equilibrium data (EFIT, CHEASE, or analytical) + - Runs Grad-Shafranov solver (direct or inverse) + - Computes global parameters: q-profile, pressure, current density, β + - Creates bicubic splines for (ψ, θ, φ) → (R, Z, Φ) mapping + - Outputs: `PlasmaEquilibrium` object + +2. **Vacuum Response**: + - Initialize plasma and wall surfaces from equilibrium + - Compute the vacuum energy matrix `wv` (and `I_v` when `compute_Iv=true`) + - Pure Julia implementation + +3. **Stability Analysis** (ForceFreeStates): + - Solve ideal MHD Euler-Lagrange equations via ODE integration + - Identify singular surfaces where q = m/n + - Compute Δ' at each singular surface + - Calculate potential and kinetic energies + - Check Mercier and ballooning stability criteria + - Outputs: `ForceFreeStatesResult` carrying the eigenmode structure ξ(ψ,θ) and the per-integrator products + +4. **Perturbed Equilibrium** (GPEC-style): + - Load external forcing data (coil fields, RMP configuration) + - Compute plasma response using permeability matrices + - Reconstruct mode-space fields (ξ_modes, b_modes) + - Calculate singular coupling metrics at rational surfaces: + - Δ' (tearing stability parameter) + - Island half-widths + - Chirikov overlap parameter + - Resonant flux and currents + - Outputs: `PerturbedEquilibriumState` with response fields and diagnostics + +5. **Output**: + - All results saved to single HDF5 file (default: `gpec.h5`) + - Top-level HDF5 groups: `Info/`, `Input/`, `Equilibrium/`, `ForceFreeStates/`, `LocalStability/`, `SingularSurfaces/`, `PerturbedEquilibrium/`, `KineticForces/`, `ErrorFields/`, `Tearing/`, `SurfaceGeometries/` (see `docs/development/hdf5-conventions.md`) + +## Key Data Structures + +### Equilibrium +- `PlasmaEquilibrium` - Main equilibrium container with bicubic splines (rzphi), 1D profiles (sq), and global parameters +- `EquilibriumConfig` - Configuration loaded from TOML files + +### Vacuum +- `VacuumInput` - Input parameters for vacuum calculations +- `WallSettings` - Wall geometry configuration + +### Stability +- `SingType` - Singular surface data including: + - Rational surface location (ψ, ρ, q = m/n, dq/dψ) + - Δ' (tearing stability parameter) — **stub**; the valid Δ' is `ForceFreeStatesResult.delta_prime.matrix` + - Asymptotic solution bases at the inner-layer boundaries +- `ForceFreeStatesResult` - Published product of a solve: mode space, metric/matrix fits, singular + surfaces, and the per-integrator products (ξ solution and its basis, free-boundary energies, + STRIDE Δ', Galerkin solve). Optional products are `nothing` when the integrator that ran cannot + supply them, and consumers warn-and-skip via `require` / `require_solution`. +- `ForceFreeStatesInternal` - Solve-time scratch; does not cross a module boundary once the result + is built + +### Perturbed Equilibrium +- `PlasmaResponse` - Source-independent operators of one FFS result (`plasma_response`): inductances, + permeability and reluctance, the singular coupling matrices [n_rational × numpert_total] and their + dominant modes; applied to any number of external fields +- `PerturbedEquilibriumState` - The perturbed equilibrium driven by one field (`perturbed_equilibrium`), including: + - Response fields (ξ_modes, b_modes) in mode space + - Applied resonant vectors and island diagnostics (half-widths, Chirikov parameters) +- `ForcingMode` - External forcing specification (m, n, amplitude, phase) + +## Module Dependencies + +``` +GeneralizedPerturbedEquilibrium +├── Splines (foundation) +├── Utilities (shared tools) +│ └── FourierTransforms +├── Equilibrium (uses Splines) +├── LocalStability (uses Equilibrium) +├── Vacuum (uses Splines, Equilibrium, Utilities) +├── ForcingTerms (data I/O) +├── ForceFreeStates (uses Equilibrium, Vacuum, Splines) +├── PerturbedEquilibrium (uses ForceFreeStates, Vacuum, ForcingTerms, Utilities) +└── ErrorFields (uses PerturbedEquilibrium, ForcingTerms, Equilibrium, Utilities) +``` diff --git a/docs/development/benchmarking.md b/docs/development/benchmarking.md new file mode 100644 index 0000000000..7f30c4b3f0 --- /dev/null +++ b/docs/development/benchmarking.md @@ -0,0 +1,51 @@ +# Benchmarking + +Use the generic benchmarking tool at `benchmarks/benchmark_git_branches.jl` to compare performance between branches or commits. + +**Tool usage:** + +```bash +# Compare feature branch against develop +julia benchmarks/benchmark_git_branches.jl \ + --example examples/DIIID-like_ideal_example \ + --branch1 develop \ + --branch2 feature-branch + +# Compare specific commits +julia benchmarks/benchmark_git_branches.jl \ + --example examples/DIIID-like_ideal_example \ + --commit1 abc123 \ + --commit2 def456 + +# Compare current develop vs develop from 1 month ago +julia benchmarks/benchmark_git_branches.jl \ + --example examples/DIIID-like_ideal_example \ + --branch1 develop \ + --commit1 HEAD~10 \ + --branch2 develop +``` + +**Default benchmark case:** `examples/DIIID-like_ideal_example` + +**Reported metrics:** +1. **Eigenmode energy (`et[1]`)** - First eigenvalue; verifies calculation correctness +2. **Integration steps** - Total ODE solver steps +3. **Runtime (warmed)** - Wall-clock time averaged over multiple warm runs (JIT warmup handled automatically) +4. **Commit hash** - Git commit of code tested + +**The tool automatically:** +- Handles JIT warmup (runs example 3 times, averages last 2) +- Switches between branches/commits +- Stashes uncommitted changes if necessary +- Restores original branch when done +- Reports comparison with percentage differences + +**Important notes:** +- Working directory should be clean or changes will be stashed during branch switching +- Tool requires HDF5.jl for reading `gpec.h5` output +- Each benchmark run takes several minutes per branch (includes compilation + warm runs) + +**Benchmark script conventions:** +- Benchmark scripts must reference input data from `examples/` (e.g., `joinpath(@__DIR__, "..", "examples", "DIIID-like_ideal_example")`). Never duplicate example inputs into `benchmarks/`. +- If a benchmark needs modified TOML settings or a parameter scan, copy inputs to a temporary local directory at runtime — do not commit these copies. +- All outputs (figures, CSVs, HDF5 files) must be saved into `benchmarks/` itself (or a self-described subdirectory within it, e.g., `benchmarks/coil_scan_results/`). Output files are not committed. diff --git a/docs/development/contributors.md b/docs/development/contributors.md new file mode 100644 index 0000000000..4cdb8d6835 --- /dev/null +++ b/docs/development/contributors.md @@ -0,0 +1,23 @@ +# Lead Developers + +A short list of who to suggest as a reviewer or assignee, and the GitHub handle to use, so that a request like "assign this to Nik" resolves to an account without guesswork. Referenced by [`naming.md`](naming.md) when opening a pull request. + +**This is a suggestion list, not a roster.** It names lead developers only — many more people contribute, and their absence here means nothing. It exists mainly so an AI agent has somewhere sensible to start; a human opening a pull request can simply pick from GitHub's own dropdown and does not need this file. + +**Focus names Areas, including the ones outside `src/`.** Focus values are the Area names from [`naming.md`](naming.md), so they cover repository maintenance as well as physics modules: `CI` is the workflows under `.github/`, and `Repo` is the cross-cutting plumbing — conventions, templates, the changelog, and the regression harness. Those rows exist so a tooling change has somewhere to go; without them a CI fix has no obvious reviewer and lands unassigned. + +**Focus is advisory, not ownership.** Nobody is obliged to review a change because they appear in a row, and nobody is barred from one because they do not. There is no `CODEOWNERS` file, so GitHub requests no reviewers automatically — choosing them is the author's job. When it is not obvious who should review, ask rather than guess, and open the pull request as a draft until it has a reviewer and an assignee. + +This file is not published to the documentation site; `docs/development/` sits outside the Documenter source tree. + +| Name | Handle | Focus | +|---|---|---| +| Nikolas Logan | `@logan-nc` | KineticForces, PerturbedEquilibrium, Equilibrium, ForceFreeStates, CI, Repo | +| Matthew Pharr | `@matt-pharr` | ForceFreeStates, InnerLayer, CI, Repo | +| Jake Halpern | `@jhalpern30` | Vacuum, ForceFreeStates, Equilibrium | +| Daniel Burgess | `@d-burg` | Tearing, ForceFreeStates | +| Jaebeom Cho | `@JaeBeom1019` | Vacuum, Equilibrium, ForceFreeStates | +| Jaymyoung Lee | `@jmlmir369` | LocalStability, Equilibrium | +| Sunjae Lee | `@jaesun57` | KineticForces | +| Evan Bursch | `@ebursch` | Equilibrium, PerturbedEquilibrium | +| Min-Gu Yoo | `@mgyoo86` | FastInterpolations (external package) | diff --git a/docs/development/git-workflow.md b/docs/development/git-workflow.md new file mode 100644 index 0000000000..163f550aa5 --- /dev/null +++ b/docs/development/git-workflow.md @@ -0,0 +1,99 @@ +# Git Workflow + +This project uses GitFlow (http://nvie.com/posts/a-successful-git-branching-model): + +- Two permanent branches: `main` and `develop` +- `main` is updated only at release-ready stages via pull request from `develop` +- `develop` is the integration branch — all feature branches merge here + +**IMPORTANT**: All development must be done on feature branches. No commits should be made directly to `develop` or `main`. Always create a branch from `develop`, do all work there, and open a pull request back into `develop`. + +## Branch Naming + +Branches use a typed prefix and a lowercase hyphen-separated description: + +| Prefix | Purpose | Branches from | Merges into | +|---|---|---|---| +| `feature/` | New functionality | `develop` | `develop` | +| `bugfix/` | Non-critical bug fixes | `develop` | `develop` | +| `hotfix/` | Critical production fix | `main` | `main` + `develop` | +| `performance/` | Performance improvements | `develop` | `develop` | +| `refactor/` | Refactoring without behavior change | `develop` | `develop` | +| `docs/` | Documentation only | `develop` | `develop` | +| `test/` | Test additions/improvements | `develop` | `develop` | +| `experiment/` | Exploratory work, may not merge | `develop` | — | + +Examples: `bugfix/sing-lim-bounds-error`, `feature/kinetic-damping`, `performance/green-function-prefactor` + +Author-named branches (e.g. `jmh/`, `nlogan/`) are not used — git history already records authorship on every commit. + +## Stacked Pull Requests + +A change that builds on another unmerged pull request can be opened against that pull request's branch instead of `develop`. When the lower pull request merges, its branch is deleted and GitHub retargets the one above it to `develop`. + +- **Update by merging, not rebasing.** Merge the parent branch into yours when it moves. A rebase rewrites the commit your harness stamp points at, and CI can no longer find it. +- **Merge the lower pull request with a merge commit.** A squash or rebase merge forces every pull request above it to rebase. +- **Run the harness against the base branch:** `regress --cases diiid_n1 --refs ,`, so the report and the release note's **Numerical impact** cover only this pull request. + +CI runs the test suite on every pull request whatever its base. While the base is another feature branch, the harness stamp stays current until this pull request's own `src/` changes differ from those at the stamped commit, so merging an updated parent in does not invalidate it. Once retargeted to `develop`, the usual check applies: nothing under `src/` may differ from the stamped commit. Because the check compares file contents, a report taken on top of the final parent survives the retarget unless `develop` has since gained other `src/` changes. + +## Merge queue + +The `Tests` and `Documentation` workflows handle `merge_group: checks_requested` as well as +pull requests. Queue candidates run the full test battery and build documentation; they do not +deploy documentation. Each queue ref has its own concurrency group. PR conventions remain on +pull-request events because they validate the PR title, body, and regression stamp. + +After these workflows have landed on `develop`, an administrator can enable **Require merge +queue** on its exact branch protection rule. Keep `Tests` and `Documentation` required, retain +at least one human approval, select **merge commits**, and start with build concurrency **1**. +Keep **Only merge non-failing pull requests** enabled. Adding a PR to the queue still requires +human review; queue checks validate it together with the target branch and earlier queued PRs. +The queue does not resolve textual merge conflicts. + +Workflow support does not enable the repository setting. Validate the first actual queue build +when enabling it; a normal PR run cannot exercise GitHub's synthetic merge-group event. + +## Hotfix Workflow + +Hotfixes address critical bugs in production (`main`) that cannot wait for the next release cycle: + +1. Branch `hotfix/description` from the current tagged `main` commit +2. Fix the bug with one or more commits +3. Merge into `main` via pull request; tag the merge commit with a new patch version (e.g. `v0.1.1`) +4. Merge the same branch into `develop` so the fix is not lost in the next release + +## Versioning + +This project uses semantic versioning: `v{major}.{minor}.{patch}` + +- **major**: breaking API or file-format changes +- **minor**: new features, backward-compatible +- **patch**: bug fixes (typically via hotfix branches) + +Tags are applied to merge commits on `main`. + +## Commit Message Format + +``` +Area[.Submodule] - TAG[!] - Imperative summary +``` + +Examples: + +- `PerturbedEquilibrium - FEATURE - Implement singular coupling diagnostics` +- `Vacuum - PERF - Add dual Green's function computation` +- `Equilibrium - BUGFIX! - Fix separatrix finding for high kappa` +- `ForceFreeStates - REFACTOR - Unify singular surface data structure` + +The Area and TAG vocabularies are closed — do not invent new ones. Pull request titles use the same grammar and are checked in CI; commit subjects are not, so follow this by hand. Every pull request body also carries a release-note block, which is what the release notes are compiled from. The full grammar, the Area list, and the abbreviations to use in prose are in [`naming.md`](naming.md). + +## Merge Conflict Resolution Policy + +- When resolving git conflicts, do not simply accept one side. +- Analyze what each side changed and WHY before producing a resolution. +- Produce a merged version incorporating both sets of changes. +- If both sides renamed the same symbol differently, prefer the current (ours) branch convention. +- When a rename on one side conflicts with a logic change on the other, apply the logic change using the renamed symbol. +- If a conflict involves changes to numerical parameters (tolerances, boundary conditions, grid sizes), flag for human review rather than guessing. +- Flag any conflicts where the combination is ambiguous for human review. diff --git a/docs/development/hdf5-conventions.md b/docs/development/hdf5-conventions.md new file mode 100644 index 0000000000..403fb7ffc5 --- /dev/null +++ b/docs/development/hdf5-conventions.md @@ -0,0 +1,76 @@ +# HDF5 Output Schema Conventions + +Conventions for the structure and naming of the `gpec.h5` output file. Every writer that adds a group or dataset must follow these rules; the naming is enforced by `test/runtests_h5_schema.jl`. + +## Governing principle: physics-first organization + +**The schema must be intuitive to a plasma physicist who is not a developer of this code.** The top level largely mirrors the TOML sections / major `src/` modules — which are themselves organized along physics lines — but **physics intuition wins whenever the two diverge**. Worked examples of that rule: + +- All per-rational-surface stability results consolidate under `SingularSurfaces/`, regardless of which algorithm produced them: the GGJ coefficients sit beside the Δ′/PEST-3 matching results, and the Riccati BVP and the Galerkin outer-region solve write the *same* `Delta_prime_matrix`/`Delta_prime_raw`/`Delta_coil`/`pest3_*` paths rather than each owning a producer-named subgroup. +- `EulerLagrangeMatrices` over a bare `Matrices` — group names must say what the data *is*, not which array it came from. Same reasoning renamed `matrices_/` → `KineticMatrices/`. + +Physics-topic groups elevated to top level (rather than nested under their producer): `Info/`, `Input/`, `SingularSurfaces/`, `LocalStability/`, `SurfaceGeometries/`. + +## Naming rules + +These rules govern `gpec.h5` (and any future GPEC-produced HDF5 output); harness-internal synthetic fixtures (e.g. the `ggj/*` reference files written by `regression-harness/src/runner.jl`) are out of scope. + +- **Groups are CamelCase at every level** (`ForceFreeStates/`, `PerSurface/`, `GalerkinIntegration/`). +- **Datasets (leaves) are snake_case** (`eigenmode_energies`, `delta_prime_matrix`). Established physics symbols keep their natural case (`E`, `F`, `Q_root`, `pest3_Delta`, `2piF`). +- **Data-driven tokens are stored verbatim**: coil-set names under `Input/RawInputs/Coils/`, KineticForces method tokens (`fgar`, …), NTV species labels under `KineticForces/PerSpecies/` (`ion_z1_m2`, `impurity_z6_m12`, `electron` — charge and mass identify the species, with a numeric discriminator appended only if a run repeats a `(z, m)` pair), scan indices (`Surface_`, `psi_`). +- **Word-valued names and boolean flags**: multi-word dataset names are snake_case English (`resonance_psi`, `trajectory_offsets`, `layer_widths`), never CamelCase — CamelCase is reserved for groups. A boolean flag is named for the state it asserts when true, with an `is_` prefix only where the bare word would read as a noun or collide with a data family: `is_rational` (bare `rational` would clash with the `rational_*` coordinate family) versus `enabled`, `truncated`, `no_root`, which already read as predicates. +- **Literature capitalization for physics symbols**: names match the standard literature — `D_I`, `D_R`, `Delta_prime`, `tau_R`, `tau_A` (not `di`, `dr`, `delta_prime`, `taur`); lowercase stays where the literature is lowercase (`alpha`, `q`, `beta*`, `delta_s`). +- **Scalar equilibrium parameters spell out the physics**: `R_axis`, `Z_axis`, `B_T_axis`, `a_mean`, `aspect_ratio`, `I_p`, `q_edge`, `beta_N`, `delta_upper`/`delta_lower`. The qualifier is a trailing subscript (`_axis`, `_edge`, `_wall`, `_min`, `_max`, `_upper`, `_lower`, `_extremum`), and a numbered literature definition keeps its number as the last subscript (`beta_p_1`, `l_i_2`). Fortran-era contractions (`bt0`, `amean`, `crnt`, `qa`, `betan`, `li1`) survive only as `Equilibrium.EquilibriumParameters` struct fields: `EQUIL_H5_NAMES` in `src/Utilities/HDF5Schema.jl` maps each field to its dataset name, and `EQUIL_H5_SKIP` drops fields that duplicate another dataset or echo a control flag. +- **Derivatives are `ddpsi`** (`dTdpsi`, `dVdpsi`, `dqdpsi`, `dxidpsi`) — never Fortran `1` suffixes or `_deriv`. +- **"rational" over "singular"** in dataset names (`rational_psi`, `rational_q`, `rational_m`, `rational_n`, `rational_index`, `rational_count`) — kinetic/resistive runs are not singular at the rationals. Specifier order is standardized specifier-first (`rational_psi`, never `psi_rational`). +- **Vector components** follow `[d][_]_[dpsi]` — the variable always comes first and the coordinate is always the trailing subscript. A bare coordinate suffix is the **contravariant** component (`xi_psi` = ξ^ψ), `_cov_` marks the **covariant** one (`b_cov_theta` = b_θ), a leading `J` marks a **Jacobian-weighted** component (`Jxi_theta` = J·ξ^θ), and other representations sit in the same slot (`xi_clebsch_psi`, `dxi_clebsch_psidpsi`). Never drop the variable: `clebsch_psi` is wrong because it does not say *what* is being represented. There is no HDF5/netCDF standard for super- vs subscripts — flat `_` names are universal — so the typeset form always appears in the dataset's `long_name`. +- **Coordinates**: the radial abscissa is `psi` (normalized poloidal flux ψ_N) and the poloidal one is `theta` in every group; never `psi_n`, `xs`, or `ys`. +- **One name per physical quantity**: a quantity written in several groups carries the identical leaf name everywhere (`rational_psi` in `SingularSurfaces/`, `Solutions/GalerkinIntegration/`, and `SingularCoupling/`; `Delta_prime_matrix` in `SingularSurfaces/` and, on the GGJ path, `Tearing/PerSurface/` — the r_s-referenced matrix the slab layer matches is a different quantity and so carries its own name, `Tearing/PerSurface/Delta_prime_matrix_rs`; `dVdpsi` in `Profiles/`, `SingularSurfaces/`, and `KineticForces//`) — the group supplies the context, the leaf supplies the identity. + +## Inputs live only under `Input/` + +`Input/gpec_toml_raw` stores the deck that reproduces the run: each result's save adds the sections stating the problem, algorithm and controls it was computed with (`Utilities.record_inputs!`), so a deck run and a script run of the same workflow record the same deck. `Input/RawInputs/` stores the raw equilibrium/forcing/coil data, the kinetic profiles, the wall file and the tolerance file — together they make `gpec.h5` a self-contained rerun snapshot (`Rerun.jl` reconstructs every control struct from them; the writer and rerun reader carry cross-reference comments marking the mirrored path pair). **Never echo TOML flags or control-struct values into any other group** — every group outside `Input/` is derived output. (The former `kinetic/` and `slayer/settings/` echoes were removed under this rule.) + +## Schema + +Top level (11 groups): + +| Group | Contents | +|---|---| +| `Info/` | Run metadata: `git_version`, mode-number ranges (`mpert`, `mlow`, …, `mn_index`), `psilim`, `qlim`, `Runtimes/` (per-stage wall-clock seconds) | +| `Input/` | Rerun snapshot: `gpec_toml_raw`, `RawInputs/{Equilibrium, ForcingTerms, Coils/, Kinetic, Wall, ErrorFields}` | +| `Equilibrium/` | Scalars (`beta_N`, `q_axis`, `q_95`, `I_p`, …) plus `Profiles/` (1-D on `psi`: 2piF, mu0p, dVdpsi, q) and `Geometry/` (2-D on `psi`×`theta`: rcoords, offset, nu, jac) | +| `ForceFreeStates/` | `Solutions/ForwardIntegration/` (u-solutions), `Solutions/GalerkinIntegration/` (closed ξ profiles in the shared layout, the gal surface list, `gal_basis_output`-gated `Basis/`), `write_el_matrices`-gated `EulerLagrangeMatrices/{Ideal,Kinetic}`, `FreeBoundaryStability/`, `EdgeScan/` | +| `LocalStability/` | Mercier `D_I`, resistive interchange `D_R`, `ballooning_Delta_prime` on `psi`; the ballooning α boundary on `ballooning_psi` | +| `SingularSurfaces/` | Per-rational-surface data: `rational_psi`/`rational_q`/`rational_m`/`rational_n`, GGJ coefficients, `Delta_prime_matrix`/`Delta_prime_raw`/`Delta_coil`/`pest3_A`/`pest3_B`/`pest3_Gamma` (Riccati or Galerkin alike), the inner-layer `Match/` results, `Kinetic/` | +| `PerturbedEquilibrium/` | `ForcingModes/`, `Response/`, `ResponseMatrices/`, `SingularCoupling/`, `Energies/`, `TorqueResponse/` (kinetic forward solves: ψ-resolved `T_xe`, `T_source`), control-surface spectra | +| `KineticForces/` | `/` (torque/energy profiles, `dTdpsi_ell` resolved by bounce harmonic on the `ell` scale, `KineticMatrices/`); multi-ion runs add `PerSpecies///` with the same per-method layout, summing to the top-level total | +| `ErrorFields/` | `CoilSensitivities/` (per-coil-set control-surface spectra and their rigid shift/tilt derivatives, `DominantMode/` full-window projection); `MonteCarlo/` (intrinsic and corrected `\|δ\|` histograms over the sampled tolerances, per batch and averaged); `Risk/` (threshold density, `P(lock\|δ)`, locking probabilities, `ToleranceScan/`); `NTV/` (correction-coil overlap and NTV torque couplings per kAt) | +| `Tearing/` | `PerSurface/` (+ `DpMatrix/`), `Roots/`, `LayerWidths/`, `Diagnostics/{ValidRoots,Poles,FilteredRoots}` (+ `*Winding` with `root_classification = winding`), `Scan/Surface_/`, `CriticalResonantField/` (+ `Scan/Surface_/`) | +| `SurfaceGeometries/` | `{Plasma,Wall}/{x,y,z}` point clouds | + +Reserved (documented, not yet written): `ForceFreeStates/Solutions/RiccatiIntegration/` — the third integrator backend slot alongside `ForwardIntegration` and `GalerkinIntegration`. + +## Metadata contract (self-describing datasets) + +Every dataset outside `Input/` (raw snapshot) must answer "what is this, in what units, plotted against what" without opening the source — enforced by `test/runtests_h5_schema.jl`: + +- **`long_name`** — plain-text physics description. +- **`units`** — SI string (`"T"`, `"Wb/rad"`, `"A"`, `"m"`, `"J"`, `"N*m"`, `"Hz"`, `"Ohm*m"`); `"1"` for dimensionless (CF convention). Normalized quantities state the normalization in `long_name` (e.g. the power-normalized stability energies are per unit ⟨|ξ|²⟩, not joules). +- **`dims`** — required on rank ≥ 2 datasets: a greppable string like `"(psi, m)"` listing axis names in **Julia (column-major) order, axis 1 first**. Note h5py/HDFView users see file dimensions in the reversed (row-major) order. Square-matrix axes use distinct `_row`/`_col` names (`(mode_row, mode_col)`): netCDF permits repeated dimension names, but xarray mangles them on load. +- **HDF5 Dimension Scales** (the netCDF-4 coordinate mechanism): shared coordinate datasets (the `psi` grids, rational-surface `rational_psi`, geometry `psi`/`theta`) are marked with `h5ds_set_scale` and attached per-axis with `h5ds_attach_scale`/`h5ds_set_label`, so h5py `.dims`, xarray, and HDFView resolve axes natively. The H5DS C API indexes file (row-major) dimensions: Julia axis `k` of an `N`-d dataset is C index `N - k`. + +Root-level file attributes: `schema_version` (currently `"2.0"`; bump on breaking schema changes — readers dispatch on it), `Conventions = "GPEC-HDF5-2.0"`, `references`, `title` (run description), `date_created` (ISO 8601 UTC). The code version stays in `Info/git_version`. + +Mechanism: writers stay table-driven — each writer keeps a `path => (; long_name, units, dims, scale, attach)` table next to it (`scale` marks a coordinate dataset as a dimension scale; `attach` binds axes to scales — see the `Utilities.HDF5Annotations.annotate!` docstring) (`src/Utilities/HDF5Schema.jl` for the main writer; alongside `write_galerkin!`, the PerturbedEquilibrium writer, `KineticForces/Output.jl`, and `Tearing/HDF5Output.jl` for the rest) and applies it post-write via `Utilities.HDF5Annotations.annotate!`. Entries for conditionally-written datasets are simply skipped when absent. When adding a dataset, add its table entry in the same commit — the schema test fails otherwise. + +## File-wide conventions + +- Complex quantities are stored as the native HDF5.jl compound type (readable by h5py as a compound dtype) — **never split into `*_real`/`*_imag` dataset pairs**. Sole sanctioned exception: `Input/RawInputs/ForcingTerms/amplitude_{real,imag}`, which mirrors the external forcing ingest-file format and keeps pre-existing snapshots replayable. +- `NaN` is the not-computed sentinel in numeric datasets (e.g. auto-derived settings, rootless growth-rate entries). +- A **zero-extent array** is the not-computed sentinel for whole datasets that a given run never produces (e.g. `SingularSurfaces/ca_left`/`ca_right` on kinetic or galerkin-matched runs, the free-boundary energies when `vac_flag=false`, the on-demand derivative stores). Never write unpopulated (`undef`) memory. +- Ragged (variable-length) data uses the flat-plus-`offsets` companion pattern (`offsets[k+1] - offsets[k]` = length of row `k`) rather than HDF5 VLEN types, e.g. `Tearing/Diagnostics/*`. + +## Back-compatibility policy + +Schema renames are clean breaks everywhere — no dual-path reads, no legacy-path translation layers. When renaming a path, update the writer, all readers, and the regression-harness case TOMLs in the same PR. Cross-commit harness comparisons across a rename boundary work only from already-cached quantities of the pre-rename refs (values are stored by quantity *name*, which renames preserve); fresh extraction of a pre-rename output reports the quantity as missing, and `--ref-range` scans crossing the boundary do the same. Developers should re-baseline old refs with `--force` before a rename lands if they need that history, and treat a schema rename as a `schema_version` bump readers can dispatch on. diff --git a/docs/development/naming.md b/docs/development/naming.md new file mode 100644 index 0000000000..30800cbb0a --- /dev/null +++ b/docs/development/naming.md @@ -0,0 +1,138 @@ +# Naming and Commit Conventions + +Commit subjects, pull request titles, and issue titles share one grammar. It exists so that an expert can tell at a glance whether a change touches their niche, and so release notes can be assembled from the history without anyone re-reading every diff. + +## Subject line + +``` +Area[.Submodule] - TAG[!] - Imperative summary +``` + +Used verbatim for commit subjects, PR titles, and (without the TAG) issue titles. The separator is a space-hyphen-space; the Area is a single token; the TAG is a single word. + +## TAG + +Two blocks. Decide first whether the change is noteworthy, then what kind it is. + +Release-note tags — each names a section of the release notes: + +| TAG | Section | Use when | +|---|---|---| +| `FEATURE` | New capabilities | A user can do something they could not before | +| `BUGFIX` | Bug fixes | Something was wrong and now is not | +| `PERF` | Performance | Same answers, less time or memory | +| `API` | Interface & format changes | A config key, output dataset, or exported name changed | +| `DEPRECATION` | Deprecations | Something still works but should not be relied on | +| `DOCS` | Documentation | Documentation only | + +Excluded tags — never appear in release notes: + +| TAG | Use when | +|---|---| +| `MINOR` | You judge the change not worth reporting: work in progress, trivia, tidying | +| `REFACTOR` | Deliberate restructuring that preserves behavior | +| `TEST` | Tests only | + +`MINOR` is an author's judgement that a commit can be skipped, not a category of work. That judgement is the useful signal — keep making it. + +## The `!` mark + +Append `!` to the TAG when the change is visible to a user: results move, an output dataset changes, or a config key changes meaning. + +``` +Equilibrium - BUGFIX! - Correct bp0 edge quadrature +PerturbedEquilibrium - API! - Rename Clebsch datasets to xi_clebsch_* +ForceFreeStates - REFACTOR! - Reassociate Riccati sums +``` + +**`!` promotes a change into the release notes whatever its tag.** Every marked change is listed under `Changed results & breaking changes` at the top of the release, ahead of the per-tag sections. This is why `REFACTOR!` is worth having: a restructuring that genuinely perturbs numbers is exactly what a user needs told, and the honest label should exist. + +`MINOR!`, `TEST!`, and `DOCS!` are rejected — none of those can move a result. Reaching for one means the change is really a `BUGFIX`, `API`, or `FEATURE`. + +## Area + +Modules, with the abbreviation to use in prose: + +| Area | Prose | Source | +|---|---|---| +| `Analysis` | — | `src/Analysis/` | +| `Deck` | — | `src/Deck.jl` | +| `Equilibrium` | EQUIL | `src/Equilibrium/` | +| `ErrorFields` | EF | `src/ErrorFields/` | +| `ForceFreeStates` | FFS | `src/ForceFreeStates/` | +| `ForcingTerms` | FT | `src/ForcingTerms/` | +| `HDF5Schema` | — | `src/Utilities/HDF5Schema.jl` | +| `InnerLayer` | IL | `src/InnerLayer/` | +| `KineticForces` | KF | `src/KineticForces/` | +| `LocalStability` | LS | `src/LocalStability/` | +| `PerturbedEquilibrium` | PE | `src/PerturbedEquilibrium/` | +| `Precompile` | — | `src/Precompile.jl` | +| `Rerun` | — | `src/Rerun.jl` | +| `Tearing` | — | `src/Tearing/` | +| `Utilities` | — | `src/Utilities/` | +| `Vacuum` | VAC | `src/Vacuum/` | + +`EQUIL`, `VAC`, `FFS`, and `PE` were already in common use; the rest are new. Short module names take no abbreviation. + +A submodule may be named where it helps triage: `ForceFreeStates.Galerkin`, `InnerLayer.GGJ`, `InnerLayer.SLAYER`, `Tearing.Dispersion`. Only directories holding Julia sources qualify, so data directories are not Areas. + +Areas outside `src/`: `Benchmarks`, `Build`, `CI`, `Docs`, `Examples`, `Regression`, `Repo`, `Test`. + +A change spanning exactly two areas may name both, `Equilibrium/Vacuum`. Three or more is `Repo`. + +## Abbreviations in prose + +Titles always carry the full name. In the body of an issue, PR, or comment, expand on first use and abbreviate after: + +> The ForceFreeStates (FFS) solver disagreed with PerturbedEquilibrium (PE) at the q=2 surface. FFS now matches PE to 1e-10. + +This keeps a title searchable and a thread readable without assuming the reader has this table memorized. + +## The release-note block + +Every PR body carries one: + +```markdown +## Release note +- **Audience:** users | developers +- **Numerical impact:** none | _(harness @ )_ +- **Migration:** none | + +<1-3 sentences in user-facing language.> +``` + +These three fields are the things a reader of the diff cannot work out for themselves, which is why they are asked for rather than inferred. + +**Numerical impact** comes from the regression harness, which every PR must run anyway (see [`regression-harness.md`](regression-harness.md)). Record the commit it ran at. Review changes code, and a report from five commits ago may no longer be true — so CI re-checks on every push and fails if anything under `src/` changed after the stamped commit. Commits touching only docs, tests, or examples never trip it. A PR stacked on another PR's branch is checked more narrowly — only its own `src/` changes must match those at the stamp — and strictly again once it targets `develop` (see [`git-workflow.md`](git-workflow.md#stacked-pull-requests)). + +**Migration** must say something real when the title carries `!`. + +## Issue titles + +`Area - Short description`, with no TAG — an issue is a request, not a change, and GitHub labels carry its type. Using the same Area vocabulary means one scan covers issues, PRs, and history alike. + +## Pull request metadata + +Labels are applied by CI from the TAG in the title, so there is nothing to do by hand. + +An assignee and at least one human reviewer are the author's job, and a pull request without them should be opened as a draft. CI comments when either is missing but does not block the merge. [`contributors.md`](contributors.md) lists lead developers and their handles as a starting point for who to ask; it is a suggestion list, not the full set of people who can review. + +## Enforcement + +`ci/conventions/check_subject.py` implements all of the above. What it blocks: + +- the `pr-conventions` CI workflow checks the **pull request title**, the release-note block, and the freshness of the harness stamp; +- the `naming-table-in-sync` pre-commit hook checks the Area table above against `src/`. + +**Individual commit subjects are not enforced.** They follow the same grammar, and should, but the release notes are compiled from pull requests rather than from `git log` — so a rejected commit mid-flow would cost more than the consistency is worth. Merge commits carry the pull request title, which means `git log --merges` reads correctly whatever happens inside a branch. + +Valid Areas are derived from `src/` on disk rather than listed in the script, so adding or removing a module updates the vocabulary on its own. Every rejection prints the replacement to use, including for the spellings this convention replaced (`EQUIL`, `VACUUM`, `IMPROVEMENT`, `CLEANUP`, and the rest). + +To check a subject by hand, or to wire it into your own clone as a `commit-msg` hook: + +```bash +python3 ci/conventions/check_subject.py --title "Equilibrium - BUGFIX! - Correct bp0 edge quadrature" + +# The script carries PEP 723 metadata, so it also runs with no Python environment set up: +uv run ci/conventions/check_subject.py --title "Equilibrium - BUGFIX! - Correct bp0 edge quadrature" +``` diff --git a/docs/development/plotting.md b/docs/development/plotting.md new file mode 100644 index 0000000000..ef7becc43e --- /dev/null +++ b/docs/development/plotting.md @@ -0,0 +1,21 @@ +# Plotting Conventions + +## Spectrum plots + +Any plot with discrete mode numbers m or n on the x-axis must use `seriestype=:steppre` with a `step_series` helper that pads zeros on both ends. Pattern from `benchmarks/benchmark_coil_ForcingTerms_against_fortran.jl`: + +```julia +function step_series(m_vals, amps) + m_ext = [m_vals[1] - 1; m_vals; m_vals[end] + 1] + amp_ext = [0.0; amps; 0.0] + return m_ext, amp_ext +end +# Usage: +m_ext, a_ext = step_series(m_vals, amplitudes) +plot!(p, m_ext, a_ext; seriestype=:steppre, lw=2, label="...") +``` + +## Figures and plots + +- Always print the full absolute path of any figure or plot file you save, so the user can open it directly without searching the filesystem. +- Always check that axis labels are not clipped. In Plots.jl there is no `tight_layout()` equivalent; use explicit margins instead: `left_margin=12Plots.mm`, `bottom_margin=4Plots.mm`, etc. When in doubt, add a generous `left_margin` to prevent y-axis label cutoff. diff --git a/docs/development/references.md b/docs/development/references.md new file mode 100644 index 0000000000..9dbb3c4112 --- /dev/null +++ b/docs/development/references.md @@ -0,0 +1,149 @@ +# Key References + +The papers in `docs/resources/` provide the theoretical foundation for GPEC's algorithms and +should be referenced to understand what the code is doing. **Citing equations from these papers +in code comments and annotations is strongly encouraged** to maintain traceability between theory +and implementation. + +## Vacuum Module + +The Vacuum module implements the methods described in: + +- **Chance et al. (1997)**: "Vacuum calculations in azimuthally symmetric geometry" + - Location: `docs/resources/1997-Chance-Vacuum_calculations_in_azimuthally_symmetric_geometry.pdf` + - Published: Physics of Plasmas **4**, 2161 (1997) + - Link: https://pubs.aip.org/aip/pop/article-abstract/4/6/2161/263192 + - Describes: Fundamental vacuum response calculation method for tokamak geometry + +- **Chance et al. (2007)**: "Calculation of the vacuum Green's function valid even for high toroidal mode numbers in tokamaks" + - Location: `docs/resources/2007-Chance-Calculation of the vacuum Greens function valid even for high toroidal mode numbers in tokamaks.pdf` + - Published: Physics of Plasmas **14**, 052506 (2007) + - Describes: Improved Green's function calculation for high-n modes + +## ForceFreeStates Module + +The ForceFreeStates module (ideal MHD stability analysis) implements methods from: + +- **Glasser (2016)**: "The direct criterion of Newcomb for the ideal MHD stability of an axisymmetric toroidal plasma" + - Location: `docs/resources/2016-Glasser-The_direct_criterion_of_Newcomb_for_the_ideal_MHD_stability_of_an_axisymmetric_toroidal_plasma.pdf` + - Published: Physics of Plasmas **23**, 072505 (2016) + - **Describes: FUNDAMENTAL PAPER - Newcomb's criterion for ideal MHD stability (current implementation)** + +- **Glasser (2018)**: "A Riccati solution for the ideal MHD plasma response with applications to real-time stability control" + - Location: `docs/resources/2018-Glasser-A Riccati solution for the ideal MHD plasma response with applications to real-time stability control.pdf` + - Published: Physics of Plasmas **25**, 032507 (2018) + - Describes: Riccati method for ideal MHD eigenvalue problem + +## PerturbedEquilibrium Module + +The PerturbedEquilibrium module implements GPEC-style perturbed equilibrium calculations from: + +- **Park et al. (2007a)**: "Computation of three-dimensional tokamak and spherical torus equilibria" + - Location: `docs/resources/2007-Park-Computation_of_three-dimensional_tokamak_and_spherical_torus_equilibria-compressed.pdf` + - Published: Physics of Plasmas **14**, 052110 (2007) + - Describes: 3D equilibrium perturbations in toroidal geometry + +- **Park et al. (2007b)**: "Control of Asymmetric Magnetic Perturbations in Tokamaks" + - Location: `docs/resources/2007-Park-Control_of_Asymmetric_Magnetic_Perturbations_in_Tokamaks.pdf` + - Published: Physical Review Letters **99**, 195003 (2007) + - Describes: Plasma response to resonant magnetic perturbations (RMP) + +- **Park et al. (2008)**: "Spectral asymmetry due to magnetic coordinates" + - Location: `docs/resources/2008-Park-Spectral_asymmetry_due_to_magnetic_coordinates.pdf` + - Published: Physics of Plasmas **15**, 064501 (2008) + - Describes: Coordinate-dependence of the perturbed-field Fourier spectrum, motivating area-normalization of the resonant harmonic to obtain the coordinate-invariant resonant field + +- **Park et al. (2009)**: "Importance of plasma response to nonaxisymmetric perturbations in tokamaks" + - Location: `docs/resources/2009-Park-Importance_of_plasma_response_to_nonaxisymmetric_perturbations_in_tokamaks-compressed.pdf` + - Published: Physics of Plasmas **16**, 056115 (2009) + - Describes: Self-consistent plasma response calculation + +- **Park et al. (2011)**: "Kinetic energy principle and neoclassical toroidal torque in tokamaks" + - Location: `docs/resources/2011-Park-Physics_of_Plasmas_Kinetic_energy_principle_and_neoclassical_toroidal_torque_in_tokamaks.pdf` + - Published: Physics of Plasmas **18**, 110702 (2011) + - Describes: Energy principle for perturbed equilibria + +- **Park et al. (2017)**: "Self-consistent perturbed equilibrium with neoclassical toroidal torque in tokamaks" + - Location: `docs/resources/2017-Park-Self_consistent_perturbed_equilibrium_with_neoclassical_toroidal_torque_in_toka.pdf` + - Published: Physics of Plasmas **24**, 032505 (2017) + - Describes: Self-consistent coupling with neoclassical effects + +## InnerLayer Module (SLAYER) + +The SLAYER slab inner-layer solver and the resistive-layer width diagnostics are based on: + +- **Burgess et al. (2026)**: "Tearing Stability Prediction Combining Toroidal Calculations With a Two-Fluid Slab Layer" + - Location: `docs/resources/2026-Burgess-Tearing Stability Prediction Combining Toroidal Calculations With a Two-Fluid Slab Layer.pdf` + - Published: Plasma Physics and Controlled Fusion **68**, 065017 (2026), doi 10.1088/1361-6587/ae7156 + - Describes: The two-fluid slab layer, its four regimes, and the `S^(1/3)` outer-inner matching + +- **Fitzpatrick (2022)**: "Influence of anomalous perpendicular transport on linear tearing mode dynamics in tokamak plasmas" + - Location: `docs/resources/2022-Fitzpatrick-Influence of anomalous perpendicular transport on linear tearing mode dynamics in tokamak plasmas.pdf` + - Published: Physics of Plasmas **29**, 032507 (2022) + - Describes: The perpendicular particle diffusivity D_⊥ built from the electron and ion energy diffusivities (Eqs. 4-6, 16), used by `P_perp_model = :D_perp` + +- **Fitzpatrick (2023)**: "Error-field penetration thresholds in ohmically heated ITER and SPARC plasmas" + - Location: `docs/resources/2023-Fitzpatrick-Error-field penetration thresholds in ohmically heated ITER and SPARC plasmas.pdf` + - Published: Physics of Plasmas **30**, 092512 (2023) + - Describes: P_⊥ = P_φ = τ_R/τ_E with the local τ_E = r²/χ_E (Eqs. 81-83, 131, 143) and the whole-plasma τ̄_E = κ_e a²/χ_E (Eqs. 37-38), used by `P_perp_model = :P_phi` and `:tau_E` + +- **Fitzpatrick (2025)**: "Response of a magnetically diverted tokamak plasma to a resonant magnetic perturbation" + - Location: `docs/resources/2025-Fitzpatrick-Response of a magnetically diverted tokamak plasma to a resonant magnetic perturbation.pdf` + - Published: Nuclear Fusion (2025), doi 10.1088/1741-4326/ae4fdd (open access: arXiv:2511.07666) + - Describes: Diffusive-resistive layer width (Eq. 100) and the resistive-layer-overlap truncation `0 < Ψ < 1 − ε_c` (Sect. 5.9) + +- **Cole & Fitzpatrick (2006)**: "Drift-magnetohydrodynamical model of error-field penetration in tokamak plasmas" + - Location: not in `docs/resources/` + - Published: Physics of Plasmas 13, 032503 (2006), doi 10.1063/1.2178167 + - Describes: Steady-state torque balance at a rational surface (Eq. 61) and the critical resonant field for error-field penetration (Eq. 62), used by `Tearing.CriticalResonantField` +- **Fitzpatrick (1993)**: "Interaction of tearing modes with external structures in cylindrical geometry" + - Location: not in `docs/resources/` + - Published: Nuclear Fusion 33, 1049 (1993) + - Describes: Bifurcated rotating states and the fold at which an error field penetrates (Sec. 7.4), the threshold definition used by `Tearing.CriticalResonantField` + +## Resistive MHD Stability Analysis (Future Work) + +GPEC will eventually implement resistive MHD stability analysis based on: + +- **Glasser (2016)**: "Computation of resistive instabilities by matched asymptotic expansions" + - Location: `docs/resources/2016-Glasser-Computation_of_resistive_instabilities_by_matched_asymptotic_expansions-compressed.pdf` + - Published: Physics of Plasmas **23**, 112506 (2016) + - Describes: Resistive stability analysis and Δ' calculation via matched asymptotic expansions + +- **Glasser (2018)**: "A robust solution for the resistive MHD toroidal Δ′ matrix in near real-time" + - Location: `docs/resources/2018-Glasser-A robust solution for the resistive MHD toroidal Delta-prime matrix in near real-time.pdf` + - Published: Physics of Plasmas **25**, 032501 (2018) + - Describes: Fast computation of Δ' matrix for resistive stability + +- **Wang et al. (2020)**: "Modeling of resistive plasma response in toroidal geometry using an asymptotic matching approach" + - Location: `docs/resources/2020-Wang-Modeling of resistive plasma response in toroidal geometry using an asymptotic matching approach.pdf` + - Published: Physics of Plasmas **27**, 122509 (2020) + - Describes: Asymptotic matching for resistive plasma response + +## KineticForces Module (NTV) + +The KineticForces module (formerly PENTRC) implements neoclassical toroidal viscosity calculations. Based on: + +- **Logan & Park (2013)**: "Neoclassical toroidal viscosity in perturbed equilibria with general tokamak geometry" + - Location: `docs/resources/2013-Logan-Neoclassical_toroidal_viscosity_in_perturbed_equilibria_with_general_tokamak_geometry.pdf` + - Published: Physics of Plasmas **20**, 122507 (2013) + - Describes: Neoclassical toroidal viscosity (NTV) in perturbed equilibria + +- **Logan (2015)**: "Electromagnetic Torque in Tokamaks with Toroidal Asymmetries" + - Location: `docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries-compressed.pdf` + - Published: PhD Thesis, Princeton University (2015) + - Describes: Complete NTV theory and implementation. **Chapter 7** details the hybrid drift-kinetic MHD eigenfunction calculation: 6 kinetic matrices Ak,Bk,Ck,Dk,Ek,Hk (Eqs 7.30-7.35) as energy-space integrals of perturbed action operators WX,WY,WZ; hybrid Euler-Lagrange equations; resonance splitting/suppression where Fh=(Q-P†)F̄(Q-P)+... shifts singularities away from rational surfaces (Eq 7.46); convergence to ideal limit. **Appendix C** derives the DCON matrix form of the perturbed action (Eqs C.1-C.11) used to compute the kinetic coefficient matrices. **Appendix D** details numerical treatment of integrable singularities in bounce averages. + +## Additional References + +- **Park et al. (2009)**: "Nonambipolar Transport by Trapped Particles in Tokamaks" + - Location: `docs/resources/2009-Park-Nonambipolar_Transport_by_Trapped_Particles_in_Tokamaks.pdf` + - Published: Physical Review Letters **102**, 065002 (2009) + - Link: https://doi.org/10.1103/PhysRevLett.102.065002 + - Describes: Trapped-particle nonambipolar transport theory underpinning the NTV calculation + +- **Connor et al. (2015)**: "The role of thermal conduction in tearing mode theory" + - Location: not in `docs/resources/`; preprint at https://arxiv.org/abs/1410.7240 + - Published: Plasma Physics and Controlled Fusion **57**, 065001 (2015) + - Link: https://doi.org/10.1088/0741-3335/57/6/065001 + - Describes: Toroidal critical Δ′ from finite parallel thermal conduction at a rational surface. Eq. 59 gives the geometric factor (`toroidal_dgeo`) and Eq. 20 the parallel-wavenumber gradient (`toroidal_kpar`) used by `dc_type = :toroidal` in `InnerLayer.SLAYER`; Eq. 61 is the large-aspect-ratio form of Eq. 59, and Eq. 65 gives the corresponding critical island width of Fitzpatrick (1995) diff --git a/docs/development/regression-harness.md b/docs/development/regression-harness.md new file mode 100644 index 0000000000..32613aa13c --- /dev/null +++ b/docs/development/regression-harness.md @@ -0,0 +1,246 @@ +# Regression Harness + +***This should be used at least once every single pull request before merging into develop. This test harness is what tracks values as they evolve across changes to the code, and must be both kept up to date and used consistently. Do not forget this and make sure to suggest any new regression cases or updates to existing ones as needed. Remind the user of its existence and report back the output regression report you get when modifying the code significantly. This is extremely important, do not forget this tidbit.*** + +Set up an alias for convenience (optional): +```bash +alias regress='julia --project=regression-harness regression-harness/regress.jl' +``` + +**List available cases:** +```bash +regress --list-cases +``` +``` +Available regression cases: +---------------------------------------------------------------- + diiid_n1 DIII-D-like equilibrium, n=1, ideal + perturbed equilibrium + dir: examples/DIIID-like_ideal_example (24 quantities) + solovev_multi_n Solovev analytical equilibrium, multi-n, ideal stability + dir: examples/Solovev_ideal_example_multi_n (12 quantities) + solovev_n1 Solovev analytical equilibrium, n=1, ideal stability + dir: examples/Solovev_ideal_example (18 quantities) +``` + +**Compare two branches/commits:** +```bash +regress --cases diiid_n1 --refs develop,feature/kinetic-damping +``` + +**Build mode.** Every case runs with GPEC's PrecompileTools workload disabled (the harness sets the `precompile_workload` preference per subprocess), so results compare code compiled on first use. `diiid_n1_riccati_precompiled` sets `precompile_workload = true` in its `[case]` table to track the precompiled build users get by default; the two builds can differ at the percent level in Δ′, so compare that case only against itself. That case is a variant: its file sets `base = "diiid_n1_riccati"` and supplies only `name`, `description` and `precompile_workload`, inheriting the base's `example_dir`, `[quantities.*]` and `[overrides]`. Any `[case]` key, `[quantities.*]` block or `[overrides]` entry a variant sets replaces the base's same-named one. The base is read from the sibling `.toml`; a missing base, or a base that itself has a base, is a load error. To check a variant, `regress --list-cases` should show it with the same quantity count as its base. Each run records the build mode it actually ran (`aot` or `jit`) in its environment fingerprint; the report flags two compared runs whose modes differ, and a run whose mode differs from its case's (e.g. the precompiled case at a commit that predates the workload) logs a warning. + +All cases requested in a single invocation share one git worktree (and one `Pkg.instantiate`/precompile) per commit, so `--cases a,b,c` in one command is substantially faster than three separate runs. +``` +================================================================ +Case: diiid_n1 — DIII-D-like equilibrium, n=1, ideal + perturbed equilibrium +================================================================ +[ Info: Cached: diiid_n1 @ 0a905a7d (2026-04-06T23:41:50+09:00) +[ Info: Cached: diiid_n1 @ 44b2494f (2026-04-08T18:30:46+09:00) + +Regression Report: diiid_n1 +================================================================================================================== +Ref 1: develop @ 0a905a7d (2026-04-06) +Ref 2: feature/kinetic-damping @ 44b2494f (2026-04-08) +------------------------------------------------------------------------------------------------------------------ +Quantity develop feature/kinetic-damping Diff Status +------------------------------------------------------------------------------------------------------------------ +beta_n -1.376214e+00 -1.376214e+00 0.0e+00 OK +beta_t 1.322850e-02 1.322850e-02 0.0e+00 OK +Chirikov parameter [4 elements] [4 elements] 0.0e+00 OK +delta prime [4 elements] [4 elements] 0.0e+00 OK +plasma energy Re(ep[1]) -8.809610e-01 -8.809610e-01 0.0e+00 OK +total energy Im(et[1]) 6.175834e-05 6.175834e-05 0.0e+00 OK +total energy Re(et[1]) 1.199597e+00 1.199597e+00 0.0e+00 OK +vacuum energy Re(ev[1]) 2.080558e+00 2.080558e+00 0.0e+00 OK +island half-widths [4 elements] [4 elements] 0.0e+00 OK +mpert 34 34 0.0e+00 OK +# singular surfaces 4 4 0.0e+00 OK +npert 1 1 0.0e+00 OK +ODE steps (saved) 740 740 0.0e+00 OK +ODE steps (total) 1348 1348 0.0e+00 OK +PE plasma energy 0.000000e+00 0.000000e+00 0.0e+00 OK +PE total energy 0.000000e+00 0.000000e+00 0.0e+00 OK +pressure profile (checksum) 657ad2329d7b... 657ad2329d7b... identical OK +q0 1.209710e+00 1.209710e+00 0.0e+00 OK +q95 4.505007e+00 4.505007e+00 0.0e+00 OK +q profile (checksum) 75912afcc351... 75912afcc351... identical OK +||resonant flux|| 4.523707e+02 4.523707e+02 0.0e+00 OK +Runtime (s) 50.9s 52.0s -- +singular psi locations [4 elements] [4 elements] 0.0e+00 OK +singular q values [4 elements] [4 elements] 0.0e+00 OK +================================================================================================================== +Summary: 23 unchanged, 3 missing/N/A +``` + +Each row's status is `OK` (within the case's noise threshold), `** CHANGED **` (moved past it), +`** CHANGED (missing) **` (on the earlier ref, gone from the later one), `** CHANGED (type) **` (stored +with a different shape or type), `NEW` (only on the later ref), `N/A` (on neither ref) or `FAILED`. +Every `CHANGED` status counts toward `--fail-on-change`; `NEW` and `N/A` do not. + +**Compare your uncommitted working tree against develop:** +```bash +regress --cases solovev_n1 --refs develop,local +``` + +**Track a specific quantity across cached commits:** +```bash +regress --show et_real --case solovev_n1 +``` +``` +History: et_real — solovev_n1 +================================================================================ +Commit Date Value Δ from prev Status +-------------------------------------------------------------------------------- +edff6e86 2026-04-02 -4.624928e-01 -- -- +0a905a7d 2026-04-06 -4.624928e-01 0.0e+00 OK +================================================================================ +``` + +**Scan across a range of commits (git-bisect style):** +```bash +regress --cases solovev_n1 --ref-range develop~10..develop +``` + +**Other useful flags:** +- `--force` — re-run even if cached +- `--verbose` — print GPEC subprocess output +- `--no-instantiate` — skip `Pkg.instantiate()` (faster if deps are already resolved) +- `--no-pin-manifest` — let each ref resolve its own package set (see below) +- `--allow-env-mismatch` — reuse cached results produced in a different environment +- `--fail-on-change` — exit non-zero when any tracked quantity changed, disappeared or changed storage type + +GPEC subprocesses run with `-t auto` (all cores) so GPEC's threaded kernels are active; set `GPEC_REGRESS_THREADS=1` to force single-threaded runs. Tracked quantities are thread-count independent, and the count each run actually used is recorded in its environment fingerprint (shown in the report's `env:` lines). Thread count is deliberately not part of the cache key, so `Runtime (s)` rows cached from single-threaded runs are not comparable to threaded ones — re-baseline with `--force` if runtime tracking matters. + +On a machine you share with other people or with your own parallel sessions, `-t auto` is antisocial: set `GPEC_REGRESS_THREADS` to a bounded count, and `JULIA_NUM_PRECOMPILE_TASKS` alongside it, since `Pkg.instantiate()` otherwise fans out to `Sys.CPU_THREADS + 1` precompile workers at the start of every ref. If the machine has a batch scheduler, submit the run through it and pin both variables to the allocation. + +## Three things named "regression" + +They share the word and nothing else, which is a reliable source of confusion: + +- **This harness** (`regression-harness/`) tracks numerical quantities across commits. Its cases + live in `regression-harness/cases/*.toml` and point at decks under `examples/`. This is what the + pull-request checklist means by "the regression harness". +- **`test/test_data/regression_*/`** are *input decks for the unit tests* — a `gpec.toml`, plus a + `kinetic.dat` where the kinetic profiles are tabulated. They are not harness cases and the + harness never reads them. +- **`test/runtests_*.jl`** are the unit and golden-value tests. Their expected numbers live in the + test files themselves, so moving one is a hand edit that has to be justified in review. + +## Run isolation + +Every git ref in a comparison is checked out into its own detached worktree, so a harness run is +insulated from whatever you do to the working tree while it runs. The `local` ref is the exception: +it runs GPEC in the live checkout, as a **fresh `julia` subprocess per case** that loads `src/` from +disk when it starts. An edit landing between two cases of a `--refs develop,local` run yields a +single report whose rows were produced by different code — with nothing in the output to say so. + +For anything you intend to cite — a pull-request report, a bisect, a number you will act on — +commit the work to the feature branch and compare branch refs: + +```bash +git fetch && git checkout develop && git merge --ff-only origin/develop +regress --cases diiid_n1 --refs develop,my-feature-branch +``` + +`develop` resolves to your **local** branch, and the harness never fetches, so the first line is +what keeps the baseline current (or pass `origin/develop` directly). After a fetch, a local pointer +that still lags its remote prints a `!! STALE BASELINE` banner; without one the banner is silent. + +A pull request stacked on another one compares against the branch it targets, not `develop`, so +its report shows only its own changes: `regress --cases diiid_n1 --refs ,my-feature-branch` +(see [Stacked pull requests](git-workflow.md#stacked-pull-requests)). + +`local` stays the right tool for a quick spot check on uncommitted work, provided you leave the tree +alone until it finishes. + +Two related hazards outside the harness, for the same reason: + +- **`test/runtests.jl` loads GPEC once, in-process, then `include`s its test files in sequence.** + Editing `src/` mid-run changes nothing the run sees, so it reports green for code no longer on + disk; editing a `test/runtests_*.jl` file *is* picked up when the run reaches it, so the report + mixes old and new. Neither failure is loud. +- **Concurrent runs in one checkout collide over output paths.** `runtests_fullruns.jl` writes and + then deletes `test/test_data/regression_*/gpec.h5`; direct example runs and the harness `local` + ref write `examples//gpec.h5`. Two runs in the same clone delete each other's output. Give + the second one its own worktree (`git worktree add --detach HEAD`, copying `Manifest.toml` + across so both resolve the same package set). + +## Making source code the only variable + +`Manifest.toml` is untracked, so a worktree checked out at an old commit used to resolve whatever +package versions were newest at run time. Machine-epsilon differences in library math then get +amplified by the adaptive ODE step controller and by ill-conditioned near-resonant diagnostics +into double-digit-percent "regressions" that no source change caused. + +Two mechanisms prevent that: + +**The working tree's Manifest is pinned into every worktree** before `Pkg.instantiate()`, so all +refs in a comparison run against one package set. `--no-pin-manifest` opts out (and says so +loudly). If a commit declares a direct dependency the pinned Manifest lacks, `Pkg.instantiate()` +refuses to run: that ref is recorded as a failed run whose error suggests `--no-pin-manifest` to +let it resolve its own package set. + +**Every run records the environment that produced it** — Julia version, host, resolved Manifest +hash, Julia and BLAS thread counts. The cache still holds a single result per +`(commit, case)`, so a re-run replaces the stored one rather than keeping a result per +environment; what the fingerprint adds is that a cached result whose environment differs from +the current one is re-run instead of silently reused. `--allow-env-mismatch` skips that check +and reuses whatever is cached, whatever produced it. Every report prints the environment of +each ref: + +``` +Ref 1: develop @ a0cad260 (2026-08-12) + env: julia 1.11.6, arm64-apple-darwin24.0.0, manifest 7e5c34ad (pinned), 1 thread/8 BLAS +``` + +When two compared runs did not share an environment, the report says so before the table rather +than leaving you to infer it from the numbers. + +Results cached before environment fingerprinting existed carry no environment and are therefore +re-run once — those are exactly the entries whose provenance cannot be established. + +If the two refs in a comparison ran under different thread counts, the report flags it. + +## Exit status + +- `0` — every run completed (and, with `--fail-on-change`, nothing changed) +- `1` — a run failed, or a quantity changed under `--fail-on-change` + +## Pinned Δ′ and grid convergence + +The ideal Δ′ diagonal pinned in `test/runtests_parallel_integration.jl` and tracked by the harness is a +single-grid snapshot that catches unintended changes, not a converged value. A `psi_accuracy` scan of the +DIII-D-like SLAYER deck on the automatic grid (`implied` is the knot count `implied_knot_count` asks for +after the refined solve): + +| psi_accuracy | knots | implied | dpm[1,1] | dpm[2,2] | dpm[3,3] | gamma 2/1 (Hz) | +|---|---|---|---|---|---|---| +| 2.0e-3 | 172 | – | 6.850 | -5.783 | -16.308 | 165.3 | +| 1.0e-3 | 205 | – | 6.387 | -5.873 | -16.867 | 154.3 | +| 5.0e-4 | 252 | – | 8.003 | -6.105 | -16.413 | 192.9 | +| 2.5e-4 | 307 | 522 | 8.567 | -6.235 | -16.529 | 206.2 | +| 1.25e-4 | 372 | 666 | 8.146 | -6.285 | -16.649 | 196.2 | +| 6.25e-5 | 457 | 916 | 8.911 | -6.477 | -16.510 | 214.4 | +| 3.125e-5 | 559 | 1226 | 9.514 | -6.260 | -16.485 | 228.7 | + +The same deck on a fixed `core_edge_packed` grid: + +| mpsi | dpm[1,1] | dpm[2,2] | dpm[3,3] | gamma 2/1 (Hz) | +|---|---|---|---|---| +| 128 | 5.208 | -8.666 | -19.157 | 126.2 | +| 256 | 7.097 | -5.187 | -15.729 | 171.2 | +| 512 | 8.489 | -6.217 | -16.517 | 204.4 | +| 1024 | 8.932 | -6.298 | -16.529 | 214.9 | +| 2048 | 8.914 | -6.385 | -16.468 | 214.5 | + +- Convergence is per surface: the q = 4 `dpm[3,3]` settles near -16.5 on both grid families, while the q = 2 + `dpm[1,1]` converges to about 8.91 only on the fixed grid and keeps moving on the automatic one. +- The SLAYER growth rate is linear in Δ′ here (`gamma/dpm[1,1]` ≈ 24.1 within 0.5%), so a converged Δ′ gives a + converged growth rate. +- The automatic grid stops after its second pass, and the knot count it implies grows faster than the grid in + use as `psi_accuracy` tightens, so tightening moves it further from self-consistency. + +A plateau criterion (scan `psi_accuracy` and the truncation controls, pin the stationary value per surface) +would be a real convergence statement; it needs either the fixed grid or knot refinement iterated to a fixed +point, and is not implemented. Until then, read a pinned diagonal as a sign and order-of-magnitude signal and +re-pin it when the equilibrium grid changes. diff --git a/docs/development/toml-conventions.md b/docs/development/toml-conventions.md new file mode 100644 index 0000000000..dfab01507f --- /dev/null +++ b/docs/development/toml-conventions.md @@ -0,0 +1,58 @@ +# TOML Annotation Conventions + +Config-style TOML files (`examples/*/gpec.toml`, `examples/*/sol.toml`, the +`test/test_data/*` fixtures, and `regression-harness/cases/*.toml`) follow one shared +annotation style so they stay consistent and self-documenting for users who copy and +modify them. **Do not invent a new convention** — match what the existing example files do. + +1. **File header (2–10 lines).** Begin each config-style TOML with a plain `#` comment block + stating the example's purpose/context and calling out key settings (wall choice, forcing, + n-range, ideal vs kinetic). No ASCII-art separators or decorative dividers. +2. **Inline annotation on every variable line.** Every `key = value` gets a trailing + `# description`. Source the wording from the matching config struct's docstring + (`EquilibriumConfig`, `WallSettings`, `ForceFreeStatesControl`, `ForcingTermsControl`, + the `plasma_response`/`perturbed_equilibrium` docstrings, `SolovevConfig`, `TJAnalyticConfig`, `KineticForcesControl`) + and keep it to one terse line. **Use the same description for the same variable across all + files** — `examples/DIIID-like_ideal_example/gpec.toml` is the canonical reference for the + common sections. Exception: in `regression-harness/cases/*.toml` the repeated + `[quantities.*]` schema keys (`h5path`, `type`, `extract`, `label`, `noise_threshold`, + `order`) are developer metadata and do **not** need inline comments — a case file needs + the header plus an informative block comment per quantity group instead. +3. **Section comments only when informative.** No comment is required above a section. Keep a + block comment only when it carries real information (e.g. the Solovev `[Wall]` note on why a + conformal wall is needed). Never add decorative section dividers. +4. **No Fortran references — annotations must be self-contained and Julia-centric.** The Julia + code is the production workhorse; a new user must not need to know the legacy Fortran GPEC + code to read an example. Headers explain the *scenario and its unique aspect*; inline comments + explain the *variable*. Do **not** cite Fortran namelist files (`dcon.in`/`equil.in`/`pentrc.in`), + Fortran flag names (`kin_flag`, `sas_flag`, `electron_flag`, …), or legacy code names + (`DCON`/`STRIDE`/`PENTRC`) as things the reader must know. Non-Fortran external-model citations + (e.g. the analytic TJ model behind `tj_analytic`) are acceptable provenance. (This applies to + the user-facing `examples/*` and `test/test_data/*` TOMLs; `regression-harness/cases/*` may + retain algorithm names as developer metadata.) +5. **Only active, meaningful variables in examples.** Do not set deprecated or "not yet + implemented" variables just to mirror defaults — they add clutter and imply false relevance + (e.g. the `truncate_at_dW_peak` edge-peak truncation, `thmax0`). Omit them so examples show + only knobs that do something on that run. +6. **`Project.toml`-family files are exempt.** `Project.toml`, `docs/Project.toml`, + `regression-harness/Project.toml`, and `.JuliaFormatter.toml` are machine-managed; do not + inline-annotate dependency UUIDs or `[compat]` entries. + +## Enforcement + +Four `pygrep` hooks in `.pre-commit-config.yaml` (no external scripts — a pygrep hook +fails when its pattern matches a violation) lint-check the covered TOMLs on commit: + +- `toml-header-block` — the file must open with a `#` header block (all covered files). +- `toml-no-decorative-dividers` — no `# ----`-style divider lines (all covered files). +- `toml-inline-annotations` — every `key = value` line carries a trailing `# description` + (`examples/*` and `test/test_data/*` only, per the rule-2 exception above). +- `toml-no-deprecated-keys` — no deprecated config keys; its key list mirrors the + `_DEPRECATED_*_KEYS` and `_MOVED_KEYS` lists in `src/Deck.jl`, which stop any run that + sets one, so extend the hook's pattern whenever a key is added there. + +Run them manually with: + +```bash +pre-commit run --all-files +``` diff --git a/docs/figure_tools.jl b/docs/figure_tools.jl new file mode 100644 index 0000000000..13b91d08f3 --- /dev/null +++ b/docs/figure_tools.jl @@ -0,0 +1,169 @@ +# figure_tools.jl +# +# Shared helpers for GPEC documentation figures. Every figure generator +# (`docs/src/figures//make_.jl`) `include`s this file and calls +# `save_doc_figure` to stamp, save, and register its output. See +# `docs/DOC_STANDARD.md` for the figure-organization and provenance policy. +# +# This file lives OUTSIDE `docs/src/` so Documenter does not publish it. The +# `make_.jl` scripts do live under `docs/src/figures/` (co-located with +# their PNGs) and are published alongside the figures for reproducibility. +# +# Figure scripts run MANUALLY in the root project, never at docs-build time: +# julia --project=. docs/src/figures//make_.jl +# so the Documenter build stays fast and Plots-free. + +using Plots +using Dates +using TOML + +const _DOCS_DIR = @__DIR__ # .../docs +const _REPO_ROOT = normpath(joinpath(_DOCS_DIR, "..")) # repo root +const FIGURES_ROOT = joinpath(_DOCS_DIR, "src", "figures") # docs/src/figures +const MANIFEST_PATH = joinpath(FIGURES_ROOT, "manifest.toml") + +""" + figure_provenance() -> (; commit, date, dirty) + +Short git commit and ISO date stamped onto every figure. `commit` is the +`git rev-parse --short HEAD` hash (reusing the idiom from +`benchmarks/benchmark_git_branches.jl`), with a `-dirty` suffix when the +working tree has uncommitted changes so a figure never claims a cleaner +provenance than it has. Generated figures and the manifest do not count as changes, so +several figures can be regenerated in turn on one clean commit. Falls back to `"unknown"` +outside a git checkout. +""" +function figure_provenance() + date = string(Dates.today()) + try + h = String(strip(read(`git -C $(_REPO_ROOT) rev-parse --short HEAD`, String))) + outputs = [":(exclude,glob)docs/src/figures/**/*.png", ":(exclude)docs/src/figures/manifest.toml"] + dirty = !isempty(strip(read(`git -C $(_REPO_ROOT) status --porcelain -- . $outputs`, String))) + return (; commit=(dirty ? "$h-dirty" : h), date=date, dirty=dirty) + catch + return (; commit="unknown", date=date, dirty=false) + end +end + +""" + stamp!(p; commit, date, subplot=length(p.subplots)) + +Annotate a small `GPEC · ` provenance mark in the bottom-right +corner of subplot `subplot` (default: the last panel), so the figure's age is +visible when browsing the rendered docs. Works on linear or log axes +(positions in data coordinates). Never throws: a plotting hiccup warns and +leaves the figure unstamped rather than losing the plot. +""" +function stamp!(p::Plots.Plot; commit::AbstractString, date::AbstractString, + subplot::Int=length(p.subplots)) + try + sp = p[subplot] + xl = Plots.xlims(sp) + yl = Plots.ylims(sp) + xpos = xl[2] # right edge (data coords) + ypos = yl[1] # bottom edge + txt = Plots.text("GPEC $(commit) · $(date)", 6, RGBA(0.5, 0.5, 0.5, 0.9), + :right, :bottom) + annotate!(sp, xpos, ypos, txt) + catch err + @warn "stamp! failed; saving figure without provenance mark" exception = err + end + return p +end + +""" + save_doc_figure(p, mod, name; script, depends, npx...) -> String + +Stamp `p` with the current git provenance, save it to +`docs/src/figures//.png`, and upsert its `manifest.toml` entry +(`script`, `commit`, `date`, `depends`). `depends` lists the repo-relative +source files whose numbers the figure visualizes — the regeneration policy in +`docs/DOC_STANDARD.md` keys off them. Returns the absolute PNG path (printed +so it can be opened directly). Extra keyword args are forwarded to `savefig`. +""" +function save_doc_figure(p::Plots.Plot, mod::AbstractString, name::AbstractString; + script::AbstractString, depends::AbstractVector{<:AbstractString}=String[]) + prov = figure_provenance() + stamp!(p; commit=prov.commit, date=prov.date) + + outdir = joinpath(FIGURES_ROOT, mod) + mkpath(outdir) + outpath = joinpath(outdir, name * ".png") + savefig(p, outpath) + + _update_manifest!(mod, name; script=String(script), commit=prov.commit, + date=prov.date, depends=String.(depends)) + + println("Saved doc figure: $(abspath(outpath))") + return abspath(outpath) +end + +# Load / merge-write the figures manifest, keyed [.]. Kept sorted and +# human-diffable; one entry per committed figure. +function _load_manifest() + return isfile(MANIFEST_PATH) ? TOML.parsefile(MANIFEST_PATH) : Dict{String,Any}() +end + +function _write_manifest(manifest) + open(MANIFEST_PATH, "w") do io + println(io, "# GPEC documentation-figure provenance manifest.") + println(io, "# One [.
] entry per committed PNG under docs/src/figures/.") + println(io, "# Regenerate a figure (and this entry) only when a file in its `depends`") + println(io, "# list changes the numbers it shows — see docs/DOC_STANDARD.md.") + println(io) + TOML.print(io, manifest; sorted=true) + end + return manifest +end + +function _update_manifest!(mod::AbstractString, name::AbstractString; + script::String, commit::String, date::String, depends::Vector{String}) + manifest = _load_manifest() + modtbl = get!(manifest, mod, Dict{String,Any}()) + modtbl[name] = Dict{String,Any}( + "script" => script, + "commit" => commit, + "date" => date, + "depends" => depends + ) + return _write_manifest(manifest) +end + +""" + register_legacy_figure(mod, name; note, depends=String[]) + +Record a manifest entry for a figure that predates this provenance system and +whose original generator script was not preserved (`commit = "legacy"`). Use +this only for migrated figures that cannot be faithfully reproduced from the +current code — never to skip writing a generator for a new figure. `note` +states why the figure is legacy (e.g. "compared the since-removed Mercier.jl"). +""" +function register_legacy_figure(mod::AbstractString, name::AbstractString; + note::AbstractString, depends::AbstractVector{<:AbstractString}=String[]) + manifest = _load_manifest() + modtbl = get!(manifest, mod, Dict{String,Any}()) + modtbl[name] = Dict{String,Any}( + "script" => "(not preserved)", + "commit" => "legacy", + "date" => "unknown", + "depends" => String.(depends), + "note" => String(note) + ) + _write_manifest(manifest) + println("Registered legacy figure: $(mod).$(name)") + return manifest +end + +""" + step_series(m_vals, amps) -> (m_ext, amp_ext) + +Canonical spectrum-plot helper (CLAUDE.md plotting convention): pad a zero on +each end of a discrete-mode series so a `seriestype=:steppre` plot draws clean +boxes that fall to zero at the edges. Centralized here so doc figures and +benchmarks share one definition. +""" +function step_series(m_vals, amps) + m_ext = [m_vals[1] - 1; m_vals; m_vals[end] + 1] + amp_ext = [0.0; amps; 0.0] + return m_ext, amp_ext +end diff --git a/docs/make.jl b/docs/make.jl index 556d7f21c5..1bd8818aa2 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,11 +1,11 @@ using Documenter -# Try to load JPEC package +# Try to load GeneralizedPerturbedEquilibrium package try - using JPEC - @info "Successfully loaded JPEC package" + using GeneralizedPerturbedEquilibrium + @info "Successfully loaded GeneralizedPerturbedEquilibrium package" catch e - @error "Failed to load JPEC package" exception = e + @error "Failed to load GeneralizedPerturbedEquilibrium package" exception = e # Try to provide helpful debugging info using Pkg @info "Current project:" Pkg.project().path @@ -14,31 +14,46 @@ catch e end makedocs(; - sitename="JPEC.jl", + sitename="GPEC.jl", format=Documenter.HTML(; prettyurls=get(ENV, "CI", nothing) == "true", - canonical="https://OpenFUSIONToolkit.github.io/JPEC/" + canonical="https://OpenFUSIONToolkit.github.io/GPEC/", + size_threshold=409_600, # 400 KiB — stability.md is a large single-page module reference + size_threshold_warn=204_800 # 200 KiB ), - modules=[JPEC], + modules=[GeneralizedPerturbedEquilibrium], pages=[ "Home" => "index.md", - "API Reference" => [ - "Splines" => "splines.md", + "Setup" => "set_up.md", + "Workflow" => "workflow.md", + "Scripting API" => "api.md", + "Conventions Reference" => "conventions.md", + "Modules" => [ + "Equilibrium" => "equilibrium.md", "Vacuum" => "vacuum.md", - "Equilibrium" => "equilibrium.md" + "Stability" => "stability.md", + "Galerkin Solver" => "galerkin.md", + "Local Stability" => "ballooning.md", + "Forcing Terms" => "forcing_terms.md", + "Perturbed Equilibrium" => "perturbed_equilibrium.md", + "Kinetic Forces" => "kinetic_forces.md", + "Error Fields" => "error_fields.md", + "Tearing" => "inner_layer.md", + "Analysis" => "analysis.md", + "Utilities" => "utilities.md" ], - "Examples" => [ - "Spline Examples" => "examples/splines.md", - "Vacuum Examples" => "examples/vacuum.md", - "Equilibrium Examples" => "examples/equilibrium.md" - ] + "Citations" => "citations.md", + "Developer Notes" => "developer_notes.md" ], checkdocs=:exports ) -deploydocs(; - repo="github.com/OpenFUSIONToolkit/JPEC.git", - branch="gh-pages", - devbranch="main", - push_preview=true -) +# Queue candidates validate the build; previews and published docs come from PRs and pushes. +if get(ENV, "GITHUB_EVENT_NAME", "") != "merge_group" + deploydocs(; + repo="github.com/OpenFUSIONToolkit/GPEC.git", + branch="gh-pages", + devbranch="develop", + push_preview=true + ) +end diff --git a/docs/resources/1997-Chance-Vacuum_calculations_in_azimuthally_symmetric_geometry.pdf b/docs/resources/1997-Chance-Vacuum_calculations_in_azimuthally_symmetric_geometry.pdf new file mode 100644 index 0000000000..40dff285a9 Binary files /dev/null and b/docs/resources/1997-Chance-Vacuum_calculations_in_azimuthally_symmetric_geometry.pdf differ diff --git a/docs/resources/2007-Chance-Calculation of the vacuum Greens function valid even for high toroidal mode numbers in tokamaks.pdf b/docs/resources/2007-Chance-Calculation of the vacuum Greens function valid even for high toroidal mode numbers in tokamaks.pdf new file mode 100644 index 0000000000..c5bdbdfdfd Binary files /dev/null and b/docs/resources/2007-Chance-Calculation of the vacuum Greens function valid even for high toroidal mode numbers in tokamaks.pdf differ diff --git a/docs/resources/2007-Park-Computation_of_three-dimensional_tokamak_and_spherical_torus_equilibria-compressed.pdf b/docs/resources/2007-Park-Computation_of_three-dimensional_tokamak_and_spherical_torus_equilibria-compressed.pdf new file mode 100644 index 0000000000..a47f657a68 Binary files /dev/null and b/docs/resources/2007-Park-Computation_of_three-dimensional_tokamak_and_spherical_torus_equilibria-compressed.pdf differ diff --git a/docs/resources/2007-Park-Control_of_Asymmetric_Magnetic_Perturbations_in_Tokamaks.pdf b/docs/resources/2007-Park-Control_of_Asymmetric_Magnetic_Perturbations_in_Tokamaks.pdf new file mode 100644 index 0000000000..fdaf63b10a Binary files /dev/null and b/docs/resources/2007-Park-Control_of_Asymmetric_Magnetic_Perturbations_in_Tokamaks.pdf differ diff --git a/docs/resources/2008-Park-Spectral_asymmetry_due_to_magnetic_coordinates.pdf b/docs/resources/2008-Park-Spectral_asymmetry_due_to_magnetic_coordinates.pdf new file mode 100644 index 0000000000..1351fb0148 Binary files /dev/null and b/docs/resources/2008-Park-Spectral_asymmetry_due_to_magnetic_coordinates.pdf differ diff --git a/docs/resources/2009-Park-Importance_of_plasma_response_to_nonaxisymmetric_perturbations_in_tokamaks-compressed.pdf b/docs/resources/2009-Park-Importance_of_plasma_response_to_nonaxisymmetric_perturbations_in_tokamaks-compressed.pdf new file mode 100644 index 0000000000..746be98963 Binary files /dev/null and b/docs/resources/2009-Park-Importance_of_plasma_response_to_nonaxisymmetric_perturbations_in_tokamaks-compressed.pdf differ diff --git a/docs/resources/2009-Park-Nonambipolar_Transport_by_Trapped_Particles_in_Tokamaks.pdf b/docs/resources/2009-Park-Nonambipolar_Transport_by_Trapped_Particles_in_Tokamaks.pdf new file mode 100644 index 0000000000..022dab1585 Binary files /dev/null and b/docs/resources/2009-Park-Nonambipolar_Transport_by_Trapped_Particles_in_Tokamaks.pdf differ diff --git a/docs/resources/2011-Park-Physics_of_Plasmas_Kinetic_energy_principle_and_neoclassical_toroidal_torque_in_tokamaks.pdf b/docs/resources/2011-Park-Physics_of_Plasmas_Kinetic_energy_principle_and_neoclassical_toroidal_torque_in_tokamaks.pdf new file mode 100644 index 0000000000..82b7c2becd Binary files /dev/null and b/docs/resources/2011-Park-Physics_of_Plasmas_Kinetic_energy_principle_and_neoclassical_toroidal_torque_in_tokamaks.pdf differ diff --git a/docs/resources/2013-Logan-Neoclassical_toroidal_viscosity_in_perturbed_equilibria_with_general_tokamak_geometry.pdf b/docs/resources/2013-Logan-Neoclassical_toroidal_viscosity_in_perturbed_equilibria_with_general_tokamak_geometry.pdf new file mode 100644 index 0000000000..7675cb44b0 Binary files /dev/null and b/docs/resources/2013-Logan-Neoclassical_toroidal_viscosity_in_perturbed_equilibria_with_general_tokamak_geometry.pdf differ diff --git a/docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries-compressed.pdf b/docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries-compressed.pdf new file mode 100644 index 0000000000..849acaae6a Binary files /dev/null and b/docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries-compressed.pdf differ diff --git a/notebooks/soloviev_ideal/fortran_rzphi_knots.dat b/docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries.pdf similarity index 50% rename from notebooks/soloviev_ideal/fortran_rzphi_knots.dat rename to docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries.pdf index 2952ce44fc..33425bc999 100644 Binary files a/notebooks/soloviev_ideal/fortran_rzphi_knots.dat and b/docs/resources/2015-Logan-Electromagnetic_Torque_in_Tokamaks_with_Toroidal_Asymmetries.pdf differ diff --git a/docs/resources/2016-Glasser-Computation_of_resistive_instabilities_by_matched_asymptotic_expansions-compressed.pdf b/docs/resources/2016-Glasser-Computation_of_resistive_instabilities_by_matched_asymptotic_expansions-compressed.pdf new file mode 100644 index 0000000000..7107cd34d9 Binary files /dev/null and b/docs/resources/2016-Glasser-Computation_of_resistive_instabilities_by_matched_asymptotic_expansions-compressed.pdf differ diff --git a/docs/resources/2016-Glasser-The_direct_criterion_of_Newcomb_for_the_ideal_MHD_stability_of_an_axisymmetric_toroidal_plasma.pdf b/docs/resources/2016-Glasser-The_direct_criterion_of_Newcomb_for_the_ideal_MHD_stability_of_an_axisymmetric_toroidal_plasma.pdf new file mode 100644 index 0000000000..391877cc72 Binary files /dev/null and b/docs/resources/2016-Glasser-The_direct_criterion_of_Newcomb_for_the_ideal_MHD_stability_of_an_axisymmetric_toroidal_plasma.pdf differ diff --git a/docs/resources/2017-Park-Self_consistent_perturbed_equilibrium_with_neoclassical_toroidal_torque_in_toka.pdf b/docs/resources/2017-Park-Self_consistent_perturbed_equilibrium_with_neoclassical_toroidal_torque_in_toka.pdf new file mode 100644 index 0000000000..a44ba87388 Binary files /dev/null and b/docs/resources/2017-Park-Self_consistent_perturbed_equilibrium_with_neoclassical_toroidal_torque_in_toka.pdf differ diff --git a/docs/resources/2018-Glasser-A Riccati solution for the ideal MHD plasma response with applications to real-time stability control.pdf b/docs/resources/2018-Glasser-A Riccati solution for the ideal MHD plasma response with applications to real-time stability control.pdf new file mode 100644 index 0000000000..31a768c131 Binary files /dev/null and b/docs/resources/2018-Glasser-A Riccati solution for the ideal MHD plasma response with applications to real-time stability control.pdf differ diff --git a/docs/resources/2018-Glasser-A robust solution for the resistive MHD toroidal Delta-prime matrix in near real-time.pdf b/docs/resources/2018-Glasser-A robust solution for the resistive MHD toroidal Delta-prime matrix in near real-time.pdf new file mode 100644 index 0000000000..fe8411bc6f Binary files /dev/null and b/docs/resources/2018-Glasser-A robust solution for the resistive MHD toroidal Delta-prime matrix in near real-time.pdf differ diff --git a/docs/resources/2020 - Glasser - Asymptotic solutions and convergence studies of the resistive inner region equations.pdf b/docs/resources/2020 - Glasser - Asymptotic solutions and convergence studies of the resistive inner region equations.pdf new file mode 100644 index 0000000000..3b386d44b0 Binary files /dev/null and b/docs/resources/2020 - Glasser - Asymptotic solutions and convergence studies of the resistive inner region equations.pdf differ diff --git a/docs/resources/2020-Wang-Modeling of resistive plasma response in toroidal geometry using an asymptotic matching approach.pdf b/docs/resources/2020-Wang-Modeling of resistive plasma response in toroidal geometry using an asymptotic matching approach.pdf new file mode 100644 index 0000000000..d180c34776 Binary files /dev/null and b/docs/resources/2020-Wang-Modeling of resistive plasma response in toroidal geometry using an asymptotic matching approach.pdf differ diff --git a/docs/resources/2022-Fitzpatrick-Influence of anomalous perpendicular transport on linear tearing mode dynamics in tokamak plasmas.pdf b/docs/resources/2022-Fitzpatrick-Influence of anomalous perpendicular transport on linear tearing mode dynamics in tokamak plasmas.pdf new file mode 100644 index 0000000000..184539270b Binary files /dev/null and b/docs/resources/2022-Fitzpatrick-Influence of anomalous perpendicular transport on linear tearing mode dynamics in tokamak plasmas.pdf differ diff --git a/docs/resources/2022-Park-Parametric dependencies of resonant layer responses across linear, two-fluid, drift-MHD regimes.pdf b/docs/resources/2022-Park-Parametric dependencies of resonant layer responses across linear, two-fluid, drift-MHD regimes.pdf new file mode 100644 index 0000000000..8490e3d117 Binary files /dev/null and b/docs/resources/2022-Park-Parametric dependencies of resonant layer responses across linear, two-fluid, drift-MHD regimes.pdf differ diff --git a/docs/resources/2023-Fitzpatrick-Error-field penetration thresholds in ohmically heated ITER and SPARC plasmas.pdf b/docs/resources/2023-Fitzpatrick-Error-field penetration thresholds in ohmically heated ITER and SPARC plasmas.pdf new file mode 100644 index 0000000000..014a7212ce Binary files /dev/null and b/docs/resources/2023-Fitzpatrick-Error-field penetration thresholds in ohmically heated ITER and SPARC plasmas.pdf differ diff --git a/docs/resources/2025-Fitzpatrick-Response of a magnetically diverted tokamak plasma to a resonant magnetic perturbation.pdf b/docs/resources/2025-Fitzpatrick-Response of a magnetically diverted tokamak plasma to a resonant magnetic perturbation.pdf new file mode 100644 index 0000000000..b12990f322 Binary files /dev/null and b/docs/resources/2025-Fitzpatrick-Response of a magnetically diverted tokamak plasma to a resonant magnetic perturbation.pdf differ diff --git a/docs/resources/2026-Burgess-Tearing Stability Prediction Combining Toroidal Calculations With a Two-Fluid Slab Layer.pdf b/docs/resources/2026-Burgess-Tearing Stability Prediction Combining Toroidal Calculations With a Two-Fluid Slab Layer.pdf new file mode 100644 index 0000000000..27449b26d2 Binary files /dev/null and b/docs/resources/2026-Burgess-Tearing Stability Prediction Combining Toroidal Calculations With a Two-Fluid Slab Layer.pdf differ diff --git a/docs/resources/2026-Pharr-Coordinate-invariant_flux-surface-Fourier-analysis-in-tokamaks.pdf b/docs/resources/2026-Pharr-Coordinate-invariant_flux-surface-Fourier-analysis-in-tokamaks.pdf new file mode 100644 index 0000000000..fccfff0b14 Binary files /dev/null and b/docs/resources/2026-Pharr-Coordinate-invariant_flux-surface-Fourier-analysis-in-tokamaks.pdf differ diff --git a/docs/src/analysis.md b/docs/src/analysis.md new file mode 100644 index 0000000000..6034f2caf2 --- /dev/null +++ b/docs/src/analysis.md @@ -0,0 +1,55 @@ +# Analysis + +`Analysis` holds the plotting and post-processing functions for GPEC results, grouped by the stage they read; each works on the in-memory result or on `gpec.h5`. + +## Submodules + +- `ForceFreeStates`: the eigenmode energies, the `crit` scan and the displacement profiles of a stability solve +- `Equilibrium`: flux surfaces, profiles and the radial grid of an equilibrium +- `CoilForcing`: coil geometry and the normal-field spectra it drives +- `PerturbedEquilibrium`: the plasma response, the resonant fields, island widths and Chirikov parameter against ψ_N or q +- `PerturbedEquilibriumModes`: helpers that convert the modal output to (ψ, θ) and (ψ, θ, φ) grids for real-space plots +- `ErrorFields`: coil sensitivities, tolerance distributions, locking risk and correction limits (see [Error Fields](error_fields.md)) + +`examples/DIIID-like_ideal_example/analyze_example.jl`, `examples/Solovev_ideal_example/analyze_example.jl` and `examples/DIIID-like_error_field_example/analyze_example.jl` are worked examples of each; the s–α maps of local stability use `salpha_reference`, `compute_ballooning_stability!` and `scan_delta_prime_map`. +Figure conventions for spectrum plots are in `docs/development/plotting.md` of the repository, and `write_imas` on the [Utilities](utilities.md) page exports results to an IMAS data dictionary. + +```@docs +GeneralizedPerturbedEquilibrium.Analysis +``` + +## ForceFreeStates + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Analysis.ForceFreeStates] +``` + +## Equilibrium + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Analysis.Equilibrium] +``` + +## CoilForcing + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Analysis.CoilForcing] +``` + +## PerturbedEquilibrium + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Analysis.PerturbedEquilibrium] +``` + +## PerturbedEquilibriumModes + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Analysis.PerturbedEquilibriumModes] +``` + +## ErrorFields + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Analysis.ErrorFields] +``` diff --git a/docs/src/api.md b/docs/src/api.md new file mode 100644 index 0000000000..c520cf44b5 --- /dev/null +++ b/docs/src/api.md @@ -0,0 +1,146 @@ +# Scripting API + +A deck runs through `GeneralizedPerturbedEquilibrium.main`; the scripting API exposes the same stages as Julia functions, so a run can be built, parameterized and looped over without a deck, and a deck can never take a path a script cannot. +Every stage is a `solve` of a problem type with an algorithm, or a function on the previous stage's result; nothing is written until `save_to_hdf5` is called. + +```julia +using GeneralizedPerturbedEquilibrium + +eq = PlasmaEquilibrium("input.geqdsk"; jac_type="hamada", kinetic_file="kinetic.h5") +ffs = solve(eq, Forward(); nn=1, delta_mlow=8, delta_mhigh=8, vac_flag=true) # [ForceFreeStates]: Forward, no wall, for the response below +ls = solve(LocalStabilityProblem(eq; alpha_boundary_scan=true), Ballooning()) # [LocalStability] +resp = plasma_response(ffs; compute_singular_coupling=true) # [PerturbedEquilibrium] +pe = perturbed_equilibrium(resp, RMPField("coils.dat")) # with the [ForcingTerms] drive +kf = kinetic_torque(pe, KineticForcesControl()) # [KineticForces] +ef = error_fields(resp, pe.coil_sets, ForcingTerms.CoilConfig(ft_ctrl); # [ErrorFields]; ft_ctrl: the [ForcingTerms] control + tolerances=ErrorFields.read_tolerance_toml("tolerances.toml")) +tear = solve(TearingProblem(ffs), SLAYERModel(); config=TearingConfig()) # [Tearing] + +save_to_hdf5("gpec.h5", ffs; local_stability=ls) +foreach(r -> save_to_hdf5("gpec.h5", r), (pe, kf, ef, tear)) +``` + +`solve(eq, alg; kwargs...)` is sugar for the problem form `solve(ForceBalanceProblem(eq; kwargs...), alg)`: the problem names what is solved (the force-balance equation on this equilibrium, with this mode range, wall and closure) and the algorithm names how. +`main` translates each deck section into the problem, algorithm or control it names, calls these functions for every section present, saves each result into `[Run] output_file`, and returns `(; equil, local_stability, ffs, match, resp, pe, kinetic, error_fields, tearing)`, each `nothing` when its section is absent. + +| Stage | Deck section | Call | Result | +|---|---|---|---| +| Equilibrium | `[Equilibrium]` (or `[Solovev]`, `[LargeAspectRatio]`, `[TJAnalytic]`) | `PlasmaEquilibrium(file; kwargs...)`, `PlasmaEquilibrium(config)`, `PlasmaEquilibrium(dd)` | `PlasmaEquilibrium` | +| ForceFreeStates | `[ForceFreeStates]` + `[Forward]`, `[Riccati]` or `[Galerkin]` | `solve(ForceBalanceProblem(eq; ...), Forward() \| Riccati() \| Galerkin())` | `ForceFreeStatesResult` | +| LocalStability | `[LocalStability]` + `[Ballooning]` | `solve(LocalStabilityProblem(eq; ...), Ballooning(; ...))` | `LocalStabilityResult` | +| Match | `[Match]` + `[GGJ]` | `solve(MatchProblem(ffs; ...), GGJModel())` | `ForceFreeStatesResult` with `closure == :matched` | +| Drive | `[ForcingTerms]` | `RMPField(ft_ctrl)` (a `ForcingTermsControl`), `RMPField(file)`, `RMPField(modes)` | `RMPField` | +| PerturbedEquilibrium | `[PerturbedEquilibrium]` | `plasma_response(ffs; ...)`, then `perturbed_equilibrium(resp, rmp; ...)` | `PlasmaResponse`, `PerturbedEquilibriumState` | +| KineticForces | `[KineticForces]` | `kinetic_torque(pe, KineticForcesControl(; ...))` | `KineticForcesResult` | +| ErrorFields | `[ErrorFields]` | `error_fields(resp, pe.coil_sets, CoilConfig(...); ...)` | `ErrorFieldsResult` | +| Tearing | `[Tearing]` + `[SLAYER]` or `[GGJ]` | `solve(TearingProblem(ffs; ...), SLAYERModel() \| GGJModel(); config=TearingConfig(...))` | `TearingResult` | + +The [Workflow](workflow.md) page describes what each stage computes; the module pages document the keywords, which are the deck keys of the matching section. + +## Saving and rerunning + +`save_to_hdf5(target, result)` saves any stage result; `target` is a path, which opens or creates the file, or an open HDF5 file or group, which stays owned by the caller. +Each save records in `Input/gpec_toml_raw` the deck sections that state the problem, algorithm and controls the result was computed with, so a script run's file replays like a deck run's: + +```bash +./gpec gpec.h5 # main(["gpec.h5"]) in Julia +./gpec gpec.h5 --coil-source coils +``` + +A coil run is replayed either from the stored deck coil configuration (`--coil-source forcing-modes`, the default, which needs the original geometry files) or from the coil geometry stored under `Input/RawInputs/Coils/` (`--coil-source coils`, which needs no original files and allows the equilibrium to be overridden). +A forcing file is replayed from its raw contents under `Input/RawInputs/ForcingTerms/`, and a forcing no deck can state (a weighted field, a spectrum or a sum) from the control-surface modes the save records. + +## Equilibrium and kinetic profiles + +Analytic equilibria take their parameters from a config object, with the `eq_type` implied, and IMAS equilibria from a data dictionary: + +```julia +eq = PlasmaEquilibrium(Equilibrium.SolovevConfig(; elongation=1.5); mpsi=0) +eq = PlasmaEquilibrium(dd; imas_cocos=11) +``` + +Every equilibrium remembers its source, so with `mpsi=0` `solve` re-forms it on the refined two-pass grid the deck uses; `Equilibrium.reform(eq, psi_nodes)` re-forms it on any grid. +The `kinetic_file` (and, for HDF5, `kinetic_group`) keyword reads the kinetic profiles once into `equil.kinetic`, from which every kinetic stage derives its view; [`Equilibrium.attach_kinetic_profiles!`](@ref) attaches a file or an in-memory `KineticProfileData` to an equilibrium built without one. + +## Combining forcing sources + +`RMPField`s form a vector space: `+`, `-` and scalar `*` build lazy linear combinations that compute nothing until `perturbed_equilibrium` materializes them, and because the response is linear in the forcing, driving with a combination equals combining the materialized fields; a complex scalar phase-rotates a source. + +```julia +nominal = RMPField("nominal_efc.dat") +weld_field = RMPField("weld_fields.h5") +pe = perturbed_equilibrium(resp, 2.0 * nominal + 0.5 * weld_field) +``` + +This is the substrate for error-field workflows: build per-unit sources (a coil set per ampere, a displacement field per millimetre), then assemble physical cases by weighting and summing, with one `plasma_response` serving every combination. +`RMPField(modes)` wraps a control-surface spectrum computed elsewhere, in the unit-norm ``\Phi_x`` convention, as a leaf. +The weights are linear-combination coefficients, not physical amplitudes: a coil set with a failed conductor is `nominal - failed_coil`, not `0.9 * nominal`. + +## Choosing an integrator + +The second argument of `solve` picks the formalism and its fields are that formalism's numerical settings; physical inputs belong on `ForceBalanceProblem`: + +```julia +ffs = solve(eq, Forward(; eulerlagrange_tolerance=1e-8); nn=1, vac_flag=true) # dense ξ profiles: the only input plasma_response accepts; kinetic solves +ffs = solve(eq, Riccati(; nchunks=40); nn=1, vac_flag=true) # chunked propagators, Δ′ matrix (default) +ffs = solve(eq, Galerkin(; nx=512); nn=1) # finite-element outer-region Δ′ +``` + +A result carries `nothing` in the fields its integrator does not produce, and consumers warn and skip rather than erroring; `plasma_response` in particular needs a `Forward()` solve with `wall=nothing` (it errors on a wall or a Galerkin result and skips with a warning on a Riccati one), see [Stability](stability.md). +A kinetic solve takes `Forward()` with the problem's `kinetic` keyword: `kinetic=KineticForcesControl(; ...)` evaluates the drift-kinetic matrices on the equilibrium's kinetic profiles, `kinetic=TestKineticMatrices(; scale)` uses synthetic matrices, `kinetic_factor` scales either, and `km = kinetic_matrices(prob)` evaluates the source once for reuse in later solves ([`KineticMatrices`](@ref GeneralizedPerturbedEquilibrium.ForceFreeStates.KineticMatrices)). + +## Inner-layer matching + +A `MatchProblem` holds the outer Δ′ a solve published plus per-surface layer parameters, and the inner-layer model passed to `solve` computes the layer response at the prescribed rotation; the result is a new `ForceFreeStatesResult` with the closure changed from `:ideal` to `:matched` and the eigenfunctions replaced, so layer-parameter scans cost one cheap match per point: + +```julia +ffs = solve(eq, Galerkin(; rpec_flag=true, cut_solution=true); nn=1) +matched = solve(MatchProblem(ffs; eta=1e-6, rho=1e-7, rotation=0.0), GGJModel()) +bpens = [solve(MatchProblem(ffs; eta=1e-6, rho=1e-7, rotation=f), GGJModel()).bpen for f in 0.0:50.0:500.0] +``` + +A scalar applies to every surface and a vector gives one value per entry of `ffs.surfaces`, core to edge. +The problem needs a Δ′ payload with coil-response columns, so it accepts Galerkin (`rpec_flag=true`) and Riccati (`vac_flag=true`) results; a Riccati-fed match fills `bpen` and the resonant data but keeps `solution === nothing`. +`GGJModel()` is the closure-capable model; `SLAYERModel()` is slab-only and drives the tearing solve instead. + +## Tearing stability + +A `TearingProblem` holds the outer solution and the surface coupling, the inner-layer model its own closures, and `TearingConfig` the scan and root-search settings: + +```julia +tear = solve(TearingProblem(ffs), SLAYERModel(; P_perp_model=:chi_perp_e); + config=TearingConfig(; Q_re_range=(-2.0, 2.0), Q_im_range=(-0.5, 3.0))) +ggj = solve(TearingProblem(ffs; coupling_mode=:coupled), GGJModel(; solver=:ray); + config=TearingConfig(; gamma_range=(1.0, 1e4), omega_range=(-1e4, 1e4))) +``` + +Both models report lab-frame frequencies (`omega` in rad/s, `gamma` in 1/s, positive for instability, with `frequency_frame` recorded on the roots group); SLAYER searches a normalized Q box and GGJ a physical (γ, ω) box. + +## Local stability + +```julia +problem = LocalStabilityProblem(eq; theta_k=0.0, alpha_boundary_scan=true) +ls = solve(problem, Ballooning(; n_scan=64); verbose=false) +ls.psi, ls.D_I, ls.D_R, ls.ballooning_Delta_prime # profiles on the equilibrium ψ grid +ls.ballooning_psi, ls.alpha, ls.alpha_critical # the ballooning boundary, NaN outside its scan window +``` + +## Entry points + +```@docs +GeneralizedPerturbedEquilibrium.ForceBalanceProblem +GeneralizedPerturbedEquilibrium.solve +GeneralizedPerturbedEquilibrium.kinetic_matrices +GeneralizedPerturbedEquilibrium.perturbed_equilibrium +``` + +[`plasma_response`](@ref GeneralizedPerturbedEquilibrium.PerturbedEquilibrium.plasma_response), which builds the +[`PlasmaResponse`](@ref GeneralizedPerturbedEquilibrium.PerturbedEquilibrium.PlasmaResponse) that `perturbed_equilibrium` +applies, is documented with the [Perturbed Equilibrium](perturbed_equilibrium.md) module. + +## Integrator selectors + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.ForceFreeStates] +Pages = ["Integrators.jl"] +``` diff --git a/docs/src/assets/crf_fortran_benchmark.png b/docs/src/assets/crf_fortran_benchmark.png new file mode 100644 index 0000000000..80ce504eb9 Binary files /dev/null and b/docs/src/assets/crf_fortran_benchmark.png differ diff --git a/docs/src/assets/grid_knot_placement.png b/docs/src/assets/grid_knot_placement.png new file mode 100644 index 0000000000..584b705e0d Binary files /dev/null and b/docs/src/assets/grid_knot_placement.png differ diff --git a/docs/src/assets/logo.svg b/docs/src/assets/logo.svg new file mode 100644 index 0000000000..a4f1f010e6 --- /dev/null +++ b/docs/src/assets/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/docs/src/ballooning.md b/docs/src/ballooning.md new file mode 100644 index 0000000000..0adcb01fe5 --- /dev/null +++ b/docs/src/ballooning.md @@ -0,0 +1,78 @@ +# Local Stability + +`LocalStability` evaluates the local, high-``n`` ideal and resistive criteria on every flux surface of the equilibrium: the Mercier interchange index ``D_I``, the Glasser–Greene–Johnson resistive-interchange index ``D_R`` and the ballooning stability index, with an optional scan for the critical pressure gradient. +It runs as its own stage, after `ForceFreeStates` and on the same equilibrium, and is the local counterpart of the global analysis on the [Stability](stability.md) page. + +## What it computes + +- **Mercier criterion** ``D_I``: the ideal interchange index of each surface. A surface is Mercier unstable where ``D_I > 0``; the same number sets the exponents ``-1/2 \pm \sqrt{-D_I}`` of the large and small solutions that the global integration matches at every rational surface. +- **Resistive interchange** ``D_R = D_I + (H - 1/2)^2``, with ``H`` from the flux-surface averages of [Glasser, Greene and Johnson (1975)](citations.md#LocalStability). ``D_R > 0`` indicates resistive interchange instability. +- **Ballooning stability** ``\Delta'``: the high-``n`` ballooning index, obtained by integrating the marginal ballooning equation along the field line and matching the solution to its asymptotic forms at both ends ([Connor, Hastie and Taylor (1978)](citations.md#LocalStability)). It is distinct from the tearing Δ′ of `SingularSurfaces/`; ``\Delta' = 0`` is the marginal ballooning boundary. +- **Critical pressure gradient**: with `alpha_boundary_scan = true` the pressure gradient of each surface is scaled until the ballooning boundary is reached, giving the normalized gradient ``\alpha`` and its critical value ``\alpha_\mathrm{crit}``, the s–α diagram collapsed to a profile. + +## Governing equations + +The stage integrates the marginal ballooning equation of [Connor, Hastie and Taylor (1978)](citations.md#LocalStability) for the perturbation ``\hat{\varphi}`` along the field line on the covering space ``-\infty < \theta < \infty``, where the shear ``q'`` enters through ``|\nabla\beta|^2`` of the Clebsch angle ``\beta`` and the projected curvature ``\kappa_w = \kappa_n - q'\theta\,\kappa_s`` splits into a non-secular part and a secular part driven by the shear (``\chi``, ``P'`` and ``\mathcal{J}`` below are the poloidal flux, the pressure gradient and the Jacobian). +The code integrates it as the first-order system ``\partial_\theta \mathbf{u} = \mathbf{L}\mathbf{u}`` in ``\mathbf{u} = (\hat{\varphi},\ |\nabla\beta|^2 \chi' \partial_\theta\hat{\varphi}/(B^2\mathcal{J}))``. + +As ``\theta \to \infty`` the coefficients diverge like ``q'^2\theta^2|\nabla\psi|^2``. +The canonical transformation ``\mathbf{R} = \operatorname{diag}(\theta^{-1/2}, \theta^{1/2})`` followed by a shear ``\mathbf{S}`` with ``\sigma = -2\pi f P' q'/(\chi'^2 B^2)`` makes the leading-order matrix ``\mathbf{N}_0`` finite, and the period average of that matrix fixes the asymptotic exponent: + +```math +\det[\langle\mathbf{N}_0\rangle - \alpha\mathbf{I}] = 0, \qquad \alpha = \pm\sqrt{-D_I} . +``` + +This is the Mercier index: ``D_I = -\alpha^2`` evaluated from ``\langle\mathbf{N}_0\rangle`` equals the classical surface-average Mercier expression, and ``D_I > 0`` (complex exponents) is interchange instability. +The large and small solutions behave as ``\theta^{\alpha - 1/2}`` and ``\theta^{-\alpha - 1/2}``; the boundary condition at the finite integration limit ``\theta_\max`` includes the first-order correction of the asymptotic series, and the ballooning index is the jump in the logarithmic derivative of the solution between the two ends. + +The s–α diagram scans the local stability of one surface without recomputing the equilibrium: a thin-layer ordering promotes local perturbations of the pressure gradient ``P'_s`` and shear ``q'_s`` to order unity in the derivatives, and the integrated shear ``I_\mathrm{tot} = q'(\theta - \theta_k) + I_\mathrm{per}`` carries them into ``|\nabla\beta|^2`` and ``\kappa_w``. +The cross term and the quadratic term in the perturbed shear are kept in the matrix assembly, and the ballooning angle ``\theta_k`` is the gauge where the total accumulated shear vanishes, ``I_\mathrm{tot}(\theta_k) = 0``. + +## Running it + +A `[LocalStability]` section runs the stage; it needs `[ForceFreeStates]` in the same deck. +Its keys are the problem settings, and the optional `[Ballooning]` section holds the [`Ballooning`](@ref) numerical settings, as in `examples/DIIID-like_ideal_example/gpec.toml`: + +```toml +[LocalStability] +theta_k = 0.0 # ballooning angle +alpha_boundary_scan = true # also find the critical alpha of each surface + +[Ballooning] +ode_tol = 1e-8 +n_scan = 24 # alpha scan points +max_alpha_scale = 8.0 +alpha_tol = 1e-3 +psi_window = [0.1, 0.99] # surfaces scanned for the alpha boundary +``` + +A script runs the same stage with `solve(LocalStabilityProblem(eq; theta_k=0.0, alpha_boundary_scan=true), Ballooning(); verbose=false)`; see the [Scripting API](api.md). +`examples/DIIID-like_ideal_example/analyze_example.jl` plots the ``s`` and ``\alpha`` profiles, the ``D_I`` and ballooning ``\Delta'`` profiles, and the two-dimensional s–α maps with their zero contours, using `salpha_reference`, `compute_ballooning_stability!` and `scan_delta_prime_map`. + +## Outputs + +| Dataset | Meaning | +|---|---| +| `LocalStability/psi` | the flux surfaces, ψ_N | +| `LocalStability/D_I` | Mercier index; ``D_I > 0`` is interchange unstable | +| `LocalStability/D_R` | resistive interchange index; ``D_R > 0`` is unstable | +| `LocalStability/ballooning_Delta_prime` | ballooning stability index along `psi`; ``\Delta' = 0`` is marginal | +| `LocalStability/ballooning_psi`, `alpha`, `alpha_critical` | with `alpha_boundary_scan = true`: the scanned surfaces, their normalized pressure gradient and its critical value | + +`SingularSurfaces/D_I` repeats the Mercier index at the rational surfaces of the global solve. + +## Numerical notes + +- The non-secular curvature ``\kappa_n`` is evaluated from the geometric expression in ``\mathcal{J}B`` and its ``\psi`` and ``\theta`` derivatives rather than from Grad–Shafranov identities, which amplify equilibrium errors near the edge. +- Background quantities are differentiated once, in a first loop; the s–α perturbations are then added algebraically, so noisy geometric derivatives are never repeated. +- The integration limit is set where the quadratic term of the wave-number factor dominates, ``\theta_\max = \min(16.5,\ 10\max_\theta\sqrt{|c/a_2|})``, and the ballooning index is taken from a solution forced to vanish at ``\pm\theta_\max`` rather than from the coefficient of the small solution, which is numerically fragile: + +![Comparison of the numerical stability of the methods for extracting the ballooning index.](figures/ballooning/delta_prime_matching.png) + +- The periodic shear functions are shifted to vanish at ``\theta_k`` so the gauge ``I_\mathrm{tot}(\theta_k) = 0`` holds exactly. + +## API reference + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.LocalStability] +``` diff --git a/docs/src/citations.md b/docs/src/citations.md new file mode 100644 index 0000000000..014a258393 --- /dev/null +++ b/docs/src/citations.md @@ -0,0 +1,298 @@ +# Citations + +The papers below provide the theoretical foundations for GPEC's algorithms; refer to them for derivations, equation numbering and physical context. PDFs of most of them are in `docs/resources/` of the repository. + +--- + +## Vacuum + +> M. S. Chance, "Vacuum calculations in azimuthally symmetric geometry," +> *Physics of Plasmas* **4**, 2161 (1997). +> DOI: [10.1063/1.872380](https://doi.org/10.1063/1.872380) + +Describes the fundamental vacuum response calculation for tokamak geometry. Derives the vacuum Green's function matrices and their relation to the plasma boundary, including the singular factor (m - nq)(m' - nq) scaling of the response matrix. This is the primary reference for the `Vacuum` module's core algorithm. + +--- + +> M. S. Chance, A. D. Turnbull, and P. B. Snyder, "Calculation of the vacuum Green's function valid even for high toroidal mode numbers in tokamaks," +> *Journal of Computational Physics* **221**, 330–348 (2007). +> DOI: [10.1016/j.jcp.2006.06.025](https://doi.org/10.1016/j.jcp.2006.06.025) + +Extends the 1997 method to high toroidal mode numbers by using 32-point Gaussian quadrature for Legendre function evaluation when nρ̂ ≥ 0.1. Replaces the polynomial approximations that break down at large n. + +--- + +### 3D vacuum singular correction + +The 3D vacuum singular correction (`src/Vacuum/Kernel3D.jl`) is translated from BIEST and licensed under Apache-2.0 (see `NOTICE`). If you use the 3D vacuum calculation, please cite the three references below. + +> D. Malhotra, "BIEST: Boundary Integral Equation Solver for Taylor states," +> [https://github.com/dmalhotra/BIEST](https://github.com/dmalhotra/BIEST). +> DOI: [10.5281/zenodo.23511944](https://doi.org/10.5281/zenodo.23511944) + +The C++ library from which the 3D singular-correction quadrature was translated. + +--- + +> D. Malhotra, A. Cerfon, L.-M. Imbert-Gérard, and M. O'Neil, "Taylor states in stellarators: A fast high-order boundary integral solver," +> *Journal of Computational Physics* **397**, 108791 (2019). +> DOI: [10.1016/j.jcp.2019.06.067](https://doi.org/10.1016/j.jcp.2019.06.067) + +Sec. 3.2 defines the polar singular quadrature: partition-of-unity split, polar Gauss–Legendre/trapezoidal rule, and Lagrange interpolation from the collocation grid to the polar nodes. + +--- + +> D. Malhotra, A. J. Cerfon, M. O'Neil, and E. Toler, "Efficient high-order singular quadrature schemes in magnetic fusion," +> *Plasma Physics and Controlled Fusion* **62**, 024004 (2020). +> DOI: [10.1088/1361-6587/ab57f4](https://doi.org/10.1088/1361-6587/ab57f4) + +Applies the scheme to the singular surface integrals of magnetic fusion (exterior vacuum fields, virtual casing) on non-axisymmetric toroidal surfaces, and shows its high-order convergence where singularity subtraction is low order. + +--- + +## ForceFreeStates + +> A. H. Glasser, "The direct criterion of Newcomb for the ideal MHD stability of an axisymmetric toroidal plasma," +> *Physics of Plasmas* **23**, 072505 (2016). +> DOI: [10.1063/1.4958328](https://doi.org/10.1063/1.4958328) + +The primary reference for the `ForceFreeStates` module. Derives the Euler-Lagrange equations for the DCON ideal MHD stability problem, establishes Newcomb's direct criterion for identifying instability via sign changes, and specifies the jump conditions for crossing singular surfaces (where q = m/n). Equations in this paper are cited directly in `src/ForceFreeStates/`. + +--- + +> A. S. Glasser, E. Kolemen, and A. H. Glasser, "A Riccati solution for the ideal MHD plasma response with applications to real-time stability control," +> *Physics of Plasmas* **25**, 032507 (2018). +> DOI: [10.1063/1.5007042](https://doi.org/10.1063/1.5007042) + +Reformulates the DCON eigenvalue problem as a Riccati matrix ODE, enabling parallel integration across singular surfaces and faster computation. Implemented in `src/ForceFreeStates/Riccati/` and enabled via `integrator = "Riccati"` in `[ForceFreeStates]`. + +--- + +## PerturbedEquilibrium + +> J.-K. Park, A. H. Boozer, and A. H. Glasser, "Computation of three-dimensional tokamak and spherical torus equilibria," +> *Physics of Plasmas* **14**, 052110 (2007). +> DOI: [10.1063/1.2732170](https://doi.org/10.1063/1.2732170) + +Describes the 3D equilibrium perturbation formalism in toroidal geometry that forms the basis of GPEC's perturbed equilibrium approach. Establishes the mode-space representation of displacement and field perturbations. + +--- + +> J.-K. Park, M. J. Schaffer, J. E. Menard, and A. H. Boozer, "Control of Asymmetric Magnetic Perturbations in Tokamaks," +> *Physical Review Letters* **99**, 195003 (2007). +> DOI: [10.1103/PhysRevLett.99.195003](https://doi.org/10.1103/PhysRevLett.99.195003) + +Demonstrates the GPEC framework for computing plasma response to resonant magnetic perturbations (RMPs) and its application to error field correction and ELM suppression. Introduces the decomposition of the response into resonant and non-resonant components. + +--- + +> J.-K. Park, A. H. Boozer, J. E. Menard, A. M. Garofalo, M. J. Schaffer, R. J. Hawryluk, S. M. Kaye, S. P. Gerhardt, S. A. Sabbagh, and the NSTX Team, "Importance of plasma response to nonaxisymmetric perturbations in tokamaks," +> *Physics of Plasmas* **16**, 056115 (2009). +> DOI: [10.1063/1.3122862](https://doi.org/10.1063/1.3122862) + +Establishes the self-consistent plasma response calculation. Derives the permeability matrix formalism linking external fields to the internal displacement field, and connects the response to island half-widths and singular coupling diagnostics — the theoretical foundation for the `PerturbedEquilibrium` module. + +--- + +> J.-K. Park, A. H. Boozer, and J. E. Menard, "Spectral asymmetry due to magnetic coordinates," +> *Physics of Plasmas* **15**, 064501 (2008). +> DOI: [10.1063/1.2932110](https://doi.org/10.1063/1.2932110) + +Shows that the Fourier spectrum of a perturbed field on a flux surface is asymmetric and depends on the magnetic working coordinate, so the raw spectrum is not by itself a physical measure of the field. Motivates normalizing the resonant harmonic by the scalar surface area to obtain the coordinate-independent resonant field — see the [Conventions Reference](conventions.md). + +--- + +> M. Pharr, E. Bursch, N. C. Logan, P. Lunia, J.-K. Park, and C. Paz-Soldan, "Coordinate-invariant flux-surface Fourier analysis in tokamaks," +> Preprint (2026). +> [arXiv:2606.02901](https://arxiv.org/abs/2606.02901) + +Establishes the area weightings that make flux-surface Fourier spectra of perturbed fields coordinate-invariant. Shows that normalizing the resonant flux by the scalar surface area (the resonant field) and the square-root-area-weighted normal field by the scalar square-root area (the power-normalized field) leaves both quantities in tesla and independent of the magnetic working coordinate. The reference for the field-amplitude conventions reported by the `PerturbedEquilibrium` module — see the [Conventions Reference](conventions.md). + +--- + +## LocalStability + +> A. H. Glasser, J. M. Greene, and J. L. Johnson, "Resistive instabilities in general toroidal plasma configurations," +> *Physics of Fluids* **18**, 875 (1975). + +The resistive interchange criterion ``D_R`` and the layer coefficients E, F, G, H, K, M of each rational surface, evaluated by `LocalStability` and stored under `SingularSurfaces/`. + +--- + +> J. W. Connor, R. J. Hastie, and J. B. Taylor, "Shear, periodicity, and plasma ballooning modes," +> *Physical Review Letters* **40**, 396 (1978). + +The ballooning representation on the covering space and the marginal ballooning equation that `LocalStability` integrates along the field line. + +--- + +## Tearing + +> A. H. Glasser, Z. R. Wang, and J.-K. Park, "Computation of resistive instabilities by matched asymptotic expansions," +> *Physics of Plasmas* **23**, 112506 (2016). +> DOI: [10.1063/1.4967862](https://doi.org/10.1063/1.4967862) + +Derives the resistive MHD stability analysis and Δ' calculation using matched asymptotic expansions in the singular layer. + +--- + +> A. S. Glasser and E. Kolemen, "A robust solution for the resistive MHD toroidal Δ′ matrix in near real-time," +> *Physics of Plasmas* **25**, 082502 (2018). +> DOI: [10.1063/1.5029477](https://doi.org/10.1063/1.5029477) + +Provides an efficient algorithm for computing the full Δ' matrix (coupling between all singular surfaces) suitable for near-real-time control applications. + +--- + +> A. H. Glasser and Z. R. Wang, "Asymptotic solutions and convergence studies of the resistive inner region equations," +> *Physics of Plasmas* **27**, 012506 (2020). +> DOI: [10.1063/1.5134999](https://doi.org/10.1063/1.5134999) + +Constructs the Wasow large-``x`` asymptotic basis (the `inps` kernel) that supplies the far-field boundary condition for every GGJ InnerLayer backend — the traditional Galerkin solver and the rotated-contour (`:ray`) solver alike. + +--- + +> Z. Wang, A. H. Glasser, D. Brennan, Y. Liu, and J.-K. Park, "Modeling of resistive plasma response in toroidal geometry using an asymptotic matching approach," +> *Physics of Plasmas* **27**, 122503 (2020). +> DOI: [10.1063/5.0020010](https://doi.org/10.1063/5.0020010) + +Demonstrates asymptotic matching for resistive plasma response in realistic toroidal geometry, bridging the outer ideal MHD solution to the inner resistive layer solution. + +--- + +> J.-K. Park, "Parametric dependencies of resonant layer responses across linear, two-fluid, drift-MHD regimes," +> *Physics of Plasmas* **29**, 072506 (2022). +> DOI: [10.1063/5.0093079](https://doi.org/10.1063/5.0093079) + +The SLAYER solver implemented in `InnerLayer.SLAYER`: the two-fluid slab layer equations of Cole and Fitzpatrick (2006) with a few terms dropped, solved for the inner-layer response of each rational surface. + +--- + +> A. Cole and R. Fitzpatrick, "Drift-magnetohydrodynamical model of error-field penetration in tokamak plasmas," +> *Physics of Plasmas* **13**, 032503 (2006). +> DOI: [10.1063/1.2178167](https://doi.org/10.1063/1.2178167) + +The two-fluid drift-MHD layer equations that SLAYER solves in reduced form, and the torque-balance error-field penetration threshold (Eqs. 61–62) behind the critical resonant field computed in `Tearing.CriticalResonantField`. + +--- + +> D. A. Burgess, N. C. Logan, J.-K. Park, C. J. Hansen, and C. Paz-Soldan, "Tearing stability prediction combining toroidal calculations with a two-fluid slab layer," +> *Plasma Physics and Controlled Fusion* **68**, 065017 (2026). +> DOI: [10.1088/1361-6587/ae7156](https://doi.org/10.1088/1361-6587/ae7156) + +Combines the toroidal outer-region calculation with the two-fluid slab layer model to predict tearing stability, extending the matched-asymptotic approach used in the `InnerLayer` module. + +--- + +> R. Fitzpatrick, "Response of a magnetically diverted tokamak plasma to a resonant magnetic perturbation," +> *Nuclear Fusion* **66**, 046025 (2026). +> DOI: [10.1088/1741-4326/ae4fdd](https://doi.org/10.1088/1741-4326/ae4fdd) (open access: arXiv:2511.07666) + +Gives the diffusive-resistive layer width (Eq. 100) and, in Sect. 5.9, restricts the response calculation to the region inside the point where adjacent resonant layers first overlap. Both are implemented by the layer-overlap scan in `src/Tearing/LayerOverlap.jl`, which feeds the `psilim_from_layer_overlap` cap described in [Integration domain](@ref). + +--- + +> R. Fitzpatrick, "Investigation of tearing mode stability near ideal stability boundaries via asymptotic matching techniques," +> *Physics of Plasmas* **32**, 062509 (2025). +> DOI: [10.1063/5.0273568](https://doi.org/10.1063/5.0273568) (arXiv:2503.24184) + +The critical island width `W_d` and its short/long-mean-free-path parallel conductivity blend, used for the critical-Δ offset in `src/InnerLayer/SLAYER/LayerParameters.jl`. + +--- + +> J. W. Connor, C. J. Ham, R. J. Hastie, and Y. Q. Liu, "The role of thermal conduction in tearing mode theory," +> *Plasma Physics and Controlled Fusion* **57**, 065001 (2015). arXiv:1410.7240 + +The toroidal (Eq. 59) and large-aspect-ratio (Eq. 61) critical-Δ offsets from finite parallel thermal conduction, building on H. Lütjens, J.-F. Luciani, and X. Garbet, *Physics of Plasmas* **8**, 4267 (2001). + +--- + +## ErrorFields + +> N. C. Logan et al., "Robustness of the tokamak error field correction tolerance scaling," +> *Plasma Physics and Controlled Fusion* **62**, 084001 (2020). + +The n = 1 ITPA error-field penetration-threshold fits on the ohmic and L-mode (`"O,L"`) and +ohmic, L- and H-mode (`"O,L,H"`) databases, each by OLS, downsampled OLS and weighted least +squares. Used by `ErrorFields.Risk` (`ITPA_THRESHOLD_SCALINGS`). + +--- + +> N. C. Logan et al., "Empirical scaling of the n = 2 error field penetration threshold in tokamaks," +> *Nuclear Fusion* **60**, 086010 (2020). + +The n = 2 penetration-threshold fits used by `ErrorFields.Risk`. + +--- + +> E. M. Bursch, J.-K. Park, N. C. Logan, F. Mao, N. Wang, C. F. B. Zimmermann, R. J. Buttery, C. Paz-Soldan, M. Pharr, L. Piron, G. Szepesi, H. Wang, S. M. Yang, JET Contributors, and the EUROfusion Tokamak Exploitation Team, +> "Improved n=1 empirical error field penetration threshold scaling with Ohmic and L-mode conventional tokamak plasma discharges," +> *Plasma Physics and Controlled Fusion* (2026). +> DOI: [10.1088/1361-6587/aea7d6](https://doi.org/10.1088/1361-6587/aea7d6) + +The 2026 n = 1 penetration-threshold scalings on the core dominant-mode overlap δ, fitted to +ohmic and L-mode discharges of conventional tokamaks only (C-Mod, DIII-D, EAST, JET, J-TEXT, +KSTAR) and adding a plasma-current term: Eq. 7 (OLS) and Eq. 8 (KDE-weighted WLS), +`δ = 10^α_c (β_n/l_i)^α_β |I_p|^α_I R_0^α_R n_e^α_n |B_T|^α_B`. These are the `year = 2026` +fits of `ErrorFields.Risk`. The paper's overlap is the same dominant-mode metric the `ErrorFields` +module computes (Eqs. 1–5). + +--- + +> N. C. Logan et al., "SPARC tokamak error field expectations and physics-based correction coil design," +> *Nuclear Fusion* **66**, 066003 (2026). +> DOI: [10.1088/1741-4326/ae6086](https://doi.org/10.1088/1741-4326/ae6086) + +The NTV-limited error-field correction model of `ErrorFields.NTV`: the correction current is capped by the torque budget the non-resonant field of the correction array spends. + +--- + +> N. Leuthold et al., "ARC physics basis–magnetohydrodynamics," +> *Journal of Plasma Physics* **92**, E49 (2026). +> DOI: [10.1017/S0022377826101421](https://doi.org/10.1017/S0022377826101421) + +The same model applied to ARC, with the zero-rotation `torque_only` limit reported beside the residual-torque one. + +--- + +## KineticForces + +The following papers develop the kinetic-force and neoclassical toroidal viscosity (NTV) theory underpinning GPEC's kinetic analysis path — the energy principle with kinetic effects, the self-consistent coupling of the perturbed equilibrium to NTV, and the PENTRC (Perturbed Equilibrium Neoclassical TRansport Code) formalism. + +> J.-K. Park, A. H. Boozer, and J. E. Menard, "Nonambipolar Transport by Trapped Particles in Tokamaks," +> *Physical Review Letters* **102**, 065002 (2009). +> DOI: [10.1103/PhysRevLett.102.065002](https://doi.org/10.1103/PhysRevLett.102.065002) + +Grounds the NTV calculation in the trapped-particle nonambipolar transport theory used downstream by Logan & Park (2013) and Logan (2015) — establishes the kinetic transport channel through which the perturbed equilibrium drives a toroidal torque. + +--- + +> J.-K. Park, "Kinetic energy principle and neoclassical toroidal torque in tokamaks," +> *Physics of Plasmas* **18**, 110702 (2011). +> DOI: [10.1063/1.3662039](https://doi.org/10.1063/1.3662039) + +Extends the energy principle to account for kinetic effects and derives the relationship between the perturbed equilibrium response and neoclassical toroidal torque. + +--- + +> N. C. Logan, J.-K. Park, K. Kim, Z. Wang, and J. W. Berkery, "Neoclassical toroidal viscosity in perturbed equilibria with general tokamak geometry," +> *Physics of Plasmas* **20**, 122507 (2013). +> DOI: [10.1063/1.4849395](https://doi.org/10.1063/1.4849395) + +Derives the neoclassical toroidal viscosity (NTV) torque in perturbed equilibria for general tokamak geometry, providing the theoretical basis for the use of KineticForces in ForceFreeStates. + +--- + +> N. C. Logan, "Electromagnetic Torque in Tokamaks with Toroidal Asymmetries," +> PhD Thesis, Princeton University (2015). +> Link: [www.proquest.com/docview/1658203153](https://www.proquest.com/docview/1658203153) + +Provides the complete theory and implementation details, including NTV in the presence of finite collisionality and general magnetic geometry. + +--- + +> J.-K. Park and N. C. Logan, "Self-consistent perturbed equilibrium with neoclassical toroidal torque in tokamaks," +> *Physics of Plasmas* **24**, 032505 (2017). +> DOI: [10.1063/1.4977898](https://doi.org/10.1063/1.4977898) + +Describes the fully self-consistent coupling between the perturbed equilibrium and neoclassical toroidal viscosity (NTV), providing the theoretical foundation for the KineticForces functionality. diff --git a/docs/src/conventions.md b/docs/src/conventions.md new file mode 100644 index 0000000000..6a95d70114 --- /dev/null +++ b/docs/src/conventions.md @@ -0,0 +1,217 @@ +# Conventions Reference + +The sign, coordinate and field-amplitude conventions used throughout GPEC, for reading its output and interfacing it with other codes. +They are inherited from the Fortran GPEC suite and predate the COCOS standard (see [COCOS compatibility](@ref)); each section names the source that establishes the convention. + +## Quick reference + +| Quantity | Convention | Forced? | +|----------|-----------|---------| +| ``\psi`` (poloidal flux) | Normalized 0 (axis) to 1 (edge), no ``2\pi`` | Yes, positive | +| ``\theta`` (poloidal angle) | Upward outboard, one circuit = 1 | Fixed | +| ``\phi`` (toroidal angle) | CCW for LH, CW for RH | By helicity | +| ``F = R B_\phi`` | Always positive | Yes, `abs` | +| ``q`` (safety factor) | Computed (direct) / input (inverse), positive | Effectively positive | +| ``n`` (toroidal mode) | Always positive (``\ge 1``) | By convention | +| Resonant ``m`` | Always positive (since ``n>0``, ``q>0``) | By construction | +| helicity | +1 RH, ``-1`` LH | Computed | +| ``\omega_E`` | Positive = direction of ``\zeta`` | No | +| Field amplitudes | ``b``, ``\bar b``, ``\tilde b``, all in tesla | By area weighting | + +## Coordinate system + +GPEC uses right-handed magnetic coordinates ``(\psi, \theta, \zeta)`` with the Fourier kernel + +```math +\exp\!\big(i(m\theta - n\zeta)\big), +``` + +matching the Fortran ``\exp(im\theta - in\phi)`` convention, with the angles in radians in the kernel (``2\pi`` times the unit-period ``\theta`` and ``\zeta`` below). +The analysis transform that produces the mode coefficients ``f_m`` of a profile ``f(\theta)`` uses the conjugate kernel ``\exp(-im\theta)``, and the synthesis reconstructs with ``\exp(+im\theta)`` (`Utilities.FourierTransforms`). + +### Poloidal flux ``\psi`` + +- Normalized from 0 (magnetic axis) to 1 (plasma boundary); ``\psi_0 = |\psi_\mathrm{bry} - \psi_\mathrm{axis}|`` is forced positive on read, and an EQDSK with ``\psi_\mathrm{bry} < \psi_\mathrm{axis}`` has its flux array sign-flipped so that ``\psi`` increases outward (`Equilibrium.Sources.GFile`). +- The internal ``\psi`` carries no factor of ``2\pi`` (poloidal flux per radian); IMAS inputs, which use the full flux, are divided by ``2\pi`` on read. +- ``\rho = \sqrt{\psi}`` serves as a radius-like variable where one is needed. + +### Poloidal angle ``\theta`` + +- Upward outboard, always: ``\theta = 0`` on the outboard midplane, increasing upward. +- Normalized to increase by 1, not ``2\pi``, over one poloidal circuit, whatever the helicity or working coordinate. + +### Toroidal coordinate ``\zeta`` and ``\phi`` + +- The magnetic toroidal angle is ``\zeta = (\phi - \nu(\psi,\theta))/(2\pi)``, with ``\phi`` the internal toroidal angle and ``\nu`` the single-valued straight-field-line offset of the working coordinate (``\nu = 0`` in PEST coordinates). +- The physical (laboratory) toroidal angle is ``\phi_\mathrm{lab} = -\,\mathrm{helicity}\,(2\pi\zeta + \nu)`` (`ForcingTerms`), a different angle from the internal ``\phi`` above, so ``\phi_\mathrm{lab}`` runs counter-clockwise viewed from above for left-handed configurations and clockwise for right-handed ones. + +### Working coordinate options + +`[Equilibrium] jac_type` selects the Jacobian ``J \propto B_p^{p_{bp}}\, B^{p_b}\, R^{-p_r}\, r_c^{-p_{rc}}``, with ``B_p`` the poloidal field, ``B`` the total field, ``R`` the major radius and ``r_c`` the distance from the magnetic axis: + +| Name | `power_bp` | `power_b` | `power_r` | `power_rc` | +|------|-----------|-----------|-----------|------------| +| Hamada (default) | 0 | 0 | 0 | 0 | +| PEST | 0 | 0 | 2 | 0 | +| Boozer | 0 | 2 | 0 | 0 | +| Equal-arc | 1 | 0 | 0 | 0 | +| Park | 0 | 1 | 0 | 0 | + +`jac_type = "custom"` sets the four exponents by hand through `jac_custom_power_bp`, `jac_custom_power_b`, `jac_custom_power_r` and `jac_custom_power_rc`. + +## Helicity and handedness + +Helicity is the product of the toroidal-field and plasma-current directions, `helicity = bt_sign * sign(crnt)` (`ForcingTerms`): +1 is right-handed (``B_t`` and ``I_p`` parallel), ``-1`` left-handed (opposed). +It sets the direction of the laboratory toroidal grid, ``\phi_{\mathrm{lab},j} = -\mathrm{helicity}\times 2\pi j/n_\zeta``, and the output sign flips of [Spectrum output sign conventions](@ref); for DIII-D (``B_t < 0``, ``I_p > 0``, helicity ``-1``) ``\phi_\mathrm{lab}`` increases with ``j``. + +## ``F = R B_\phi`` + +The poloidal-current function is forced positive on read, ``F = |F|`` (`Equilibrium.Sources.GFile`); the sign of ``B_t`` is carried separately by `fpol_sign` and the helicity inputs. + +## Safety factor ``q`` + +The direct solver computes ``q`` by field-line integration and overwrites the file profile (`Equilibrium.Axisymmetric.Direct`), + +```math +q = \frac{F}{2\pi}\oint \frac{J}{B_p}\,dl_p , +``` + +which is positive because ``J > 0`` and ``F = |F|``; a warning marks an on-axis extrapolation with ``q_0 \le 0`` as a spline artifact. +Inverse equilibria use the ``q`` profile supplied with the input (`Equilibrium.Axisymmetric.Inverse`), the convention of the Fortran GPEC CHEASE reader. + +## Mode numbers ``m`` and ``n`` + +### Toroidal mode number ``n`` + +`[ForceFreeStates] nn` is a positive integer or a list of consecutive integers. +``n < 1`` is not supported: a run whose modes are all below 1 is an error, and a lowest mode below 1 is clamped to 1 with a warning (`ForceFreeStates.Run`). +Results for several ``n`` can be superposed. + +### Poloidal mode range + +The poloidal spectrum spans `mlow` to `mhigh` (`ForceFreeStates.ForceBalance`): + +```math +m_\mathrm{low} = \min(\lfloor n_\mathrm{low}\,q_\mathrm{min}\rfloor,\,0) - 4 - \Delta m_\mathrm{low}, \qquad +m_\mathrm{high} = \lfloor n_\mathrm{high}\,q_\mathrm{max}\rfloor + 2\,\Delta m_\mathrm{high}. +``` + +`delta_mlow` and `delta_mhigh` widen the range beyond the resonant modes; `delta_mhigh` is doubled before use, the Fortran DCON convention, so it adds twice as many modes on the high side as `delta_mlow` does on the low side. + +### Why positive ``m`` is always resonant + +Resonant surfaces are the flux surfaces where ``m = n\,q(\psi)`` (`ForceFreeStates.Surfaces`). +Since ``n > 0`` by convention and ``q > 0`` for a standard tokamak, the resonant ``m`` is always positive, ``m_\mathrm{res} = n\,q > 0``, and negative-``m`` modes are never resonant: the ``m = 2`` displacement resonates at ``q = 2``. + +## Spectrum output sign conventions + +For the real-space representation GPEC takes the complex conjugate of the mode amplitudes for right-handed configurations (`Analysis.PerturbedEquilibriumModes`), after the straight-field-line offset has entered as ``\exp(i\,n\,\nu)``. +For the full Fourier representation ``\exp(i(m\theta - n\zeta))`` the conjugation flips both the toroidal direction and the up/down sense. + +### Interfacing with SURFMN + +SURFMN expands in ``\exp(-im\theta - in\phi)`` with CCW ``\phi``; to convert from GPEC, + +```python +m_surfmn = helicity * m_gpec +b_surfmn = real(b_m) - 1j * helicity * imag(b_m) +``` + +so that for LH configurations only the sign of ``m`` is flipped and for RH the complex conjugate is taken. + +### Interfacing with Vacuum + +The `Vacuum` module works in the same angles as the rest of GPEC, ``\theta`` upward-outboard and the analysis basis ``\exp(-i(m\theta - n\zeta))`` in radians, unlike the Fortran VACUUM code whose ``\theta`` and ``\phi`` both run the other way; a clockwise plasma contour is rejected and a clockwise wall file is reversed with a warning. +The ``\nu`` passed to it is GPEC's ``\nu = \phi - 2\pi\zeta``, which Chance (1997, Eq. 97) writes as ``\zeta = \phi + \nu``. + +## Field amplitudes and units + +GPEC reports perturbed fields as Fourier spectra on flux surfaces. +A raw spectrum depends on the working coordinate ([Park, Boozer and Menard (2008)](citations.md#PerturbedEquilibrium)), but the resonant component on a rational surface and the 2-norm of the spectrum on any surface become coordinate-invariant with the right area weighting in the Fourier integrand ([Pharr et al. (2026)](citations.md#PerturbedEquilibrium)). +GPEC therefore works with three decompositions of the normal field, all in tesla, distinguished only by that weight: + +| Pharr symbol | Integrand weight | Name | HDF5 token | +|---|---|---|---| +| ``b`` (bare) | ``1`` | normal field | `b_n` | +| ``\bar b`` (bar) | ``\mathcal{J}\,\lvert\nabla\psi\rvert`` | area-weighted field | `area` | +| ``\tilde b`` (tilde) | ``\sqrt{\mathcal{J}\,\lvert\nabla\psi\rvert}`` | root-area-weighted field | `root_area` | + +```math +b_m = \oint (\mathbf{b}\cdot\hat{\mathbf n})\, e^{-i m\theta}\, d\theta, \qquad +\bar b_m = \frac{1}{A}\oint \mathcal{J}\lvert\nabla\psi\rvert\,(\mathbf{b}\cdot\hat{\mathbf n})\, e^{-i m\theta}\, d\theta, \qquad +\tilde b_m = \frac{1}{\sqrt{A}}\oint \sqrt{\mathcal{J}\lvert\nabla\psi\rvert}\,(\mathbf{b}\cdot\hat{\mathbf n})\, e^{-i m\theta}\, d\theta , +``` + +with ``A`` the scalar surface area. +The root-area weighting is the unique one under which the spectrum transforms unitarily between working coordinates, so ``\sum_m |\tilde b_m|^2 = \langle (\mathbf{b}\cdot\hat{\mathbf n})^2 \rangle`` (Parseval) and the quantities derived from ``\tilde b``, such as the singular values of the resonant coupling matrix, are coordinate-invariant; "power-normalized field" in early GPEC literature names this property. +The area weighting is coordinate-invariant for the pitch-resonant ``m = nq`` harmonic on a rational surface: the **resonant field** ``\bar b^{\,r} = \Phi^r/A^r``, the resonant flux divided by the surface area, stored as `PerturbedEquilibrium/SingularCoupling/resonant_area_weighted_field`. +In an ideal perturbed equilibrium the surface is perfectly shielded and the stored value is the effective resonant field, the resonant flux the shielding current cancels; resistive and kinetic equilibria have finite resonant fields as well. + +With ``\Sigma`` the mode-space square-root-weight convolution (`sqrtamat`) and ``A`` the surface area (`jarea`), the three fields and the flux are related by (`Equilibrium.CoordinateInvariant`) + +```math +\tilde b = \Sigma\, b, \qquad +\bar b = (\Sigma/\sqrt{A})\,\tilde b, \qquad +\Phi = A\,\bar b = \Sigma\sqrt{A}\;\tilde b , +``` + +where `rootarea_to_area_weight` ``= \Sigma/\sqrt{A}`` maps ``\tilde b \to \bar b``, `area_to_rootarea_weight` is its inverse, and ``R = \Sigma\sqrt{A}`` is the internal flux-conform operator. +Flux ``\Phi`` is never stored: it appears only when a user supplies flux-valued forcing and is recovered on demand as ``\Phi = A\,\bar b``. + +### Field representations in GPEC output + +- `PerturbedEquilibrium/forcing_b`, `forcing_b_root_area`, `forcing_b_area` and the `response_*` triplet: the control-surface spectra in the bare, root-area-weighted and area-weighted representations. +- `PerturbedEquilibrium/Response/b_n` and `b_psi_area_weighted`: the bare normal field and the area-weighted radial field profiles. +- `PerturbedEquilibrium/SingularCoupling/resonant_area_weighted_field`, `C_resonant_area_weighted_field` and `penetrated_area_weighted_field`: the resonant area-weighted field, its coupling matrix and the penetrated field. +- `PerturbedEquilibrium/ResponseMatrices/`: the response matrices in root-area-weighted space, with `rootarea_to_area_weight_operator` ``S`` (``L_{\bar b} = S\,\tilde L\,S^\dagger``) and `surface_area` ``A`` (``\Phi = A\,\bar b``) to recover the other forms. +- `ForceFreeStates/FreeBoundaryStability/eigenmode_energies`: energies per unit surface-averaged squared displacement, a normalization described on the [Stability](stability.md) page. + +## KineticForces rotation conventions + +The rotation profiles enter the `KineticForces` integrals as follows. + +### ``\omega_E`` (E × B rotation) + +``\omega_E`` is the input E × B toroidal rotation profile, positive in the direction of the toroidal coordinate ``\zeta``. +In the laboratory frame, with ``\phi`` counter-clockwise viewed from above, ``\phi = -h\,(2\pi\zeta + \nu)`` for helicity ``h`` ([Helicity and handedness](@ref)), so positive ``\omega_E`` is counter-clockwise with sign ``-h``, and its sign relative to the plasma current is ``-h\,s_I = -s_B`` with ``s_I``, ``s_B`` the signs of ``I_p`` and ``B_t`` (`ip_sign`, `bt_sign`): positive ``\omega_E`` is co-current exactly when ``B_t`` is clockwise (``s_B = -1``), whatever the helicity. + +### Diamagnetic frequencies + +Computed at evaluation time from the kinetic-profile gradients (Logan et al. (2013), Eq. 7): + +```math +\omega_{*n} = -\frac{2\pi\, T_i}{e\, Z_i\, \chi_1\, n_i}\frac{dn_i}{d\psi_n}, \qquad +\omega_{*T} = -\frac{2\pi}{e\, Z_i\, \chi_1}\frac{dT_i}{d\psi_n}, +``` + +with ``\chi_1 = 2\pi\psi_0``, so that an outward-decreasing density or temperature gives a positive diamagnetic frequency; the total toroidal rotation is ``\omega_\phi = \omega_E + \omega_{*n} + \omega_{*T}``. + +### Energy integral resonance + +The resonance denominator of the energy integral is ``i\,\Omega(x) - \nu`` with + +```math +\Omega(x) = n\,\omega_E + \ell_\mathrm{eff}\,\omega_b\sqrt{x} + n\,\omega_D\,x , +\qquad +\ell_\mathrm{eff} = \begin{cases} \ell + n q & \text{passing particles},\\ \ell & \text{trapped particles}, \end{cases} +``` + +where ``x = E/T`` is the normalized energy, ``\nu`` the energy-dependent effective collisionality, and ``\omega_b``, ``\omega_D`` the bounce and precession frequencies at thermal energy (``\omega_b \propto v_\mathrm{th}``, ``\omega_D \propto v_\mathrm{th}^2``; `BounceAveraging.jl`). +The sign of ``\omega_E`` shifts the resonance in velocity space. + +## COCOS compatibility + +The conventions above are GPEC-native and predate the COCOS standard of [Sauter and Medvedev (2013)](https://doi.org/10.1016/j.cpc.2012.09.010): right-handed ``(\psi, \theta, \zeta)``, ``\psi`` without the ``2\pi`` and increasing outward, ``q > 0``, ``n > 0`` and ``F = |F|``. +The code labels this internal convention COCOS 2 (`Equilibrium.Sources.IMAS`) and accepts IMAS input in two forms: + +```julia +imas_cocos = 11 # default: IMAS standard, ψ divided by 2π on read +imas_cocos = 2 # data already in the internal convention, no conversion +``` + +The conversion from COCOS 11 divides ``\psi`` by ``2\pi`` and relies on the forced sign conventions above (positive ``\psi``, ``F`` and ``q``) for the rest; no other COCOS index is converted. + +## See also + +- [Workflow](workflow.md), where each convention enters the pipeline +- [Citations](citations.md), the references behind the conventions diff --git a/docs/src/developer_notes.md b/docs/src/developer_notes.md new file mode 100644 index 0000000000..e84e98d68d --- /dev/null +++ b/docs/src/developer_notes.md @@ -0,0 +1,191 @@ +# Developer Notes + +## Git workflow + +This project uses [GitFlow](http://nvie.com/posts/a-successful-git-branching-model): + +- Two permanent branches: `main` and `develop` +- `main` is updated only at release-ready stages +- Feature branches should come off `develop` and merge back with `--no-ff` + +## Commit message format + +``` +Area[.Submodule] - TAG[!] - Imperative summary +``` + +`Area` is a module name such as `Equilibrium` or `ForceFreeStates`, or a repository area such as `Docs` or `CI`. `TAG` is one of `FEATURE`, `BUGFIX`, `PERF`, `API`, `DEPRECATION`, `DOCS` (which appear in release notes) or `MINOR`, `REFACTOR`, `TEST` (which do not). Append `!` when results move or an interface changes, for example `Equilibrium - BUGFIX! - Correct bp0 edge quadrature`. + +Both vocabularies are closed — do not invent new ones. Pull request titles use the same grammar and are checked in CI; commit subjects follow it by convention rather than enforcement. Pull request bodies carry a release-note block, which is what the changelog is compiled from. The full grammar, the abbreviations to use in prose, and the release-note block are documented in +[`docs/development/naming.md`](https://github.com/OpenFUSIONToolkit/GPEC/blob/develop/docs/development/naming.md). + +## Documentation standard + +Module reference pages follow one skeleton (*What it computes* → *Governing equations* → +*Running it*, with a deck snippet from a shipped example and the matching script call → *Outputs*, +a table of `gpec.h5` datasets with units → *Numerical notes*, including validation figures → +*API reference*), and every documentation figure is committed together with the script that made +it and a provenance stamp. The full policy — page template, figure +organization under `docs/src/figures//`, provenance, and the +regenerate-only-when-`depends`-change rule — is in +[`docs/DOC_STANDARD.md`](https://github.com/OpenFUSIONToolkit/GPEC/blob/develop/docs/DOC_STANDARD.md). +The `Stability` and `Local Stability` pages are the reference exemplars. + +## Deck and API parity + +Every capability must be reachable both from a `gpec.toml` deck (through `main`) and from the +scripting API (`solve` and the problem and result types). A feature that exists on only one path +is not finished. A new feature, deck key, or API keyword also adds a case to the `"deck and API parity"` +testset in `test/runtests_solve_api.jl`. That case runs the workflow once from a deck and once +from the same inputs through the API, and checks that the two results agree. A known drift +between the paths is marked `@test_broken`, not dropped, so the change that fixes it shows up +as an unexpected pass. + +## Regression testing + +The regression harness **must be run on every pull request before merging into `develop`**. It is the project's primary safeguard for tracking how numerical results evolve across changes, so it is only useful if every PR exercises it. When you open a PR, paste the regression report into the PR thread so reviewers can see what moved (and what did not). If your change touches a quantity that is not yet tracked, add a new regression case — or extend an existing one — in the same PR. + +### Pinned Δ′ values + +The Δ′ values pinned in the tests and tracked by the harness are single-grid snapshots, not converged values: the extraction is grid-sensitive, and on the DIII-D-like deck the q = 2 diagonal converges only on a fixed `core_edge_packed` grid, while the automatic grid leaves it moving by several percent between refinements. +Treat a pinned diagonal as a sign and order-of-magnitude regression signal, expect to re-pin it when the equilibrium grid changes, and see `docs/development/regression-harness.md` for the convergence scans behind this. + +### Regression harness: quick usage guide + +Set up an alias for convenience (optional): +```bash +alias regress='julia --project=regression-harness regression-harness/regress.jl' +``` + +**List available cases:** +```bash +regress --list-cases +``` +**Compare two branches/commits:** +```bash +regress --cases diiid_n1 --refs develop,feature/kinetic-damping +``` +The report lists every tracked quantity of the case for each ref with its difference and an `OK`/changed status. + +**Compare your uncommitted working tree against develop:** +```bash +regress --cases solovev_n1 --refs develop,local +``` + +**Track a specific quantity across cached commits:** +```bash +regress --show et_real --case solovev_n1 +``` +**Scan across a range of commits (git-bisect style):** +```bash +regress --cases solovev_n1 --ref-range develop~10..develop +``` + +**Other useful flags:** +- `--force` — re-run even if cached +- `--verbose` — print GPEC subprocess output +- `--no-instantiate` — skip `Pkg.instantiate()` (faster if deps are already resolved) + +The full flag list, the environment-pinning behaviour that keeps source code the only variable in +a comparison, and the exit-status contract are documented in +[`docs/development/regression-harness.md`](https://github.com/OpenFUSIONToolkit/GPEC/blob/develop/docs/development/regression-harness.md). + +## Developer tips + +### Precompile workload + +Building the package runs a small Solovev case through the solver so that a fresh Julia process +skips most first-call compilation, at the cost of a longer one-time build. Code compiled into the +package image can round differently in the last bit from code compiled on first use, and +near-marginal quantities such as Δ′ amplify that, so results are reproducible only within one +build mode. The regression harness runs with the workload disabled. To disable it yourself (for a +faster build, or to match harness numbers), set the preference by UUID from the project, without +loading GPEC (which would first build it with the workload on). `Preferences` must be installed, +e.g. in your global environment: + +```julia +using Preferences, UUIDs +set_preferences!(UUID("462872dd-e066-4d2e-b993-6468b5239634"), "precompile_workload" => false; force=true) +``` + +This writes `LocalPreferences.toml` next to the active `Project.toml` (the file is gitignored); +delete the entry to re-enable. The regression harness overrides this file for its own runs. + +The workload runs only when Julia rebuilds the package image, not at the start of each run. That +happens after any change under `src/` (a pull, a branch switch or a local edit) and after a change +of dependency versions, Julia version or this preference. Repeated runs on an unchanged checkout +reuse the cached image. Julia keeps a separate image per set of compile flags, so the first +`Pkg.test()` after such a change (which checks array bounds) runs the workload once more. If you edit `src/` and restart Julia often, disabling the workload keeps +each rebuild short; within one Revise session nothing is rebuilt. + +### Revise.jl + +When iterating on code, use [Revise.jl](https://timholy.github.io/Revise.jl/stable/) to avoid full recompilation on every change. It tracks source file modifications and recompiles only the affected code. + +Install Revise in your **global** Julia environment (not in this project's `Project.toml`): + +```julia +# In the Julia REPL (no --project flag) +using Pkg +Pkg.add("Revise") +``` + +Then load it before `GeneralizedPerturbedEquilibrium` in any session: + +```julia +using Revise +using GeneralizedPerturbedEquilibrium +``` + +For it to load automatically, add `using Revise` to your [Julia startup file](https://timholy.github.io/Revise.jl/stable/config/#Using-Revise-by-default): + +```julia +# ~/.julia/config/startup.jl +try + using Revise +catch e + @warn "Could not load Revise" exception=e +end +``` + +### Pre-commit hooks + +The repository uses pre-commit hooks to maintain code quality. They run automatically on `git commit`, and +the same hooks run in CI on every pull request (the `pre-commit` check), which fails if they would change a file. +They: +- Format Julia code according to `.JuliaFormatter.toml` settings +- Remove trailing whitespace and fix line endings +- Validate YAML/TOML syntax +- Prevent accidentally committing large files (>5MB) +- Check config-style TOML annotation conventions (header block, inline `# description` + comments, no deprecated keys — see `docs/development/toml-conventions.md`) + +**Installation:** + +```bash +# Install pre-commit (requires Python/pip) +pip install pre-commit + +# Install JuliaFormatter 1.0.62 in the default environment: the hook runs whatever version is there, +# and CI pins this one, so another version formats differently and fails the check +julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter", version="1.0.62"))' + +# Install the git hooks in your local repository +cd /path/to/GPEC +pre-commit install +``` + +You can also run the hooks manually without committing: + +```bash +pre-commit run --all-files # run on all files +pre-commit run # run on staged files only +``` + +In rare cases where you need to bypass the hooks: + +```bash +git commit --no-verify +``` + +This is discouraged as it may introduce formatting inconsistencies. diff --git a/docs/src/equilibrium.md b/docs/src/equilibrium.md index ab4b486ceb..f58f644318 100644 --- a/docs/src/equilibrium.md +++ b/docs/src/equilibrium.md @@ -1,116 +1,102 @@ -# Equilibrium Module +# Equilibrium -The Equilibrium module provides tools to read, construct, and analyze -magnetohydrodynamic (MHD) plasma equilibria. It wraps a variety of -equilibrium file formats (EFIT, CHEASE, SOL, LAR, and others), runs the -appropriate direct or inverse solver, and returns a processed -`PlasmaEquilibrium` object ready for downstream calculations. +`Equilibrium` reads or constructs the axisymmetric MHD equilibrium, maps it to the straight-field-line coordinates every later stage works in, and returns a `PlasmaEquilibrium` holding the flux-surface splines, the profiles and the global parameters of the plasma. +It is the first stage of every run and the only one that reads physics input files: the equilibrium itself and, optionally, the kinetic profiles. -## Overview +## What it computes -Key responsibilities of the module: +From the poloidal flux ``\psi(R, Z)`` and the profiles ``F = R B_\phi`` and ``p`` of a Grad–Shafranov solution, the module traces the closed flux surfaces, finds the magnetic axis and the separatrix, integrates the safety factor ``q(\psi)`` and the surface quantities ``dV/d\psi``, and builds bicubic splines of the surface geometry ``R(\psi, \theta)``, ``Z(\psi, \theta)`` and of the toroidal-angle offset ``\nu(\psi, \theta)`` in the coordinates selected by `jac_type` (`"hamada"`, `"pest"`, `"boozer"`, `"equal_arc"`, …; see the [Conventions Reference](conventions.md) page). +The normalized flux ``\psi_N \in [0, 1]`` runs from the axis to the boundary, and the rational surfaces q = m/n of the run are located on it. -- Read equilibrium input files and TOML configuration (see `EquilibriumConfig`). -- Provide convenient constructors for analytic / model equilibria (Large - Aspect Ratio, Solev'ev). -- Build spline representations used throughout the code (1D cubic and - 2D bicubic splines). -- Run the direct or inverse equilibrium solver and post-process results - (global parameters, q-profile finding, separatrix finding, GSE checks). +## Inputs -The module exposes a small public API that covers setup, configuration, -and common analyses used by other JPEC components (e.g. `DCON`, vacuum -interfaces). +`[Equilibrium] eq_type` selects the file format of `eq_filename`: -## API Reference +| `eq_type` | Format | Notes | +|---|---|---| +| `"efit"` | EFIT g-file | flux surfaces traced by a field-line ODE in the geometric angle; the default | +| `"efit_arclength"` | EFIT g-file | the same traced in arc length, which stays regular near the separatrix and reaches higher `psihigh` | +| `"efit_by_inversion"` | EFIT g-file | level sets traced by marching squares and fed to the inverse solver; the most accurate geometry | +| `"chease"` (`"chease_binary"`) | CHEASE binary output | inverse solver | +| `"chease2"` (`"chease_ascii"`) | CHEASE ASCII output | inverse solver | +| `"ifile"` | inverse i-file, as TokaMaker writes | inverse solver | +| `"imas"` | an `IMASdd.dd` passed from a script | `PlasmaEquilibrium(dd; kwargs...)`; see `examples/DIIID-like_ideal_example_IMAS` | -```@autodocs -Modules = [JPEC.Equilibrium] -``` +An analytic equilibrium is selected by its own section instead of `eq_type`: `[Solovev]` (the Solov'ev solution), `[LargeAspectRatio]` (a large-aspect-ratio model with prescribed profiles) or `[TJAnalytic]` (the profile family of Fitzpatrick's TJ code, `solver = "inverse" | "direct"`); their keys are the fields of `SolovevConfig`, `LargeAspectRatioConfig` and `TJAnalyticConfig`. -## Important types - -- `EquilibriumConfig` — top-level configuration container parsed from a - TOML file (outer constructor `EquilibriumConfig(path::String)` is - provided). -- `EquilibriumControl` — low-level control parameters (grid, jacobian - type, tolerances, etc.). -- `EquilibriumOutput` — options controlling what output is written. -- `PlasmaEquilibrium` — the runtime structure containing spline fields, - geometry, profiles, and computed diagnostics (q-profile, separatrix, - etc.). -- `LargeAspectRatioConfig`, `SolevevConfig` — convenience structs to - construct analytic/model equilibria when using `eq_type = "lar"` or - `eq_type = "sol"`. - -## Key functions - -- `setup_equilibrium(path::String = "equil.toml")` - — main entry point that reads configuration, builds the equilibrium, - runs the solver, and returns a `PlasmaEquilibrium` instance. -- `equilibrium_separatrix_find!(pe::PlasmaEquilibrium)` — locate - separatrix and related boundary geometry in-place. -- `equilibrium_global_parameters!(pe::PlasmaEquilibrium)` — populate - common global parameters (major radius, magnetic axis, volumes, etc.). -- `equilibrium_qfind!(pe::PlasmaEquilibrium)` — compute safety factor - (q) information across the grid. -- `equilibrium_gse!(pe::PlasmaEquilibrium)` — diagnostics on the - Grad–Shafranov solution. - -## Example usage - -Basic example: read a TOML config and build an equilibrium +### Kinetic profiles -```julia -using JPEC +`[Equilibrium] kinetic_file` attaches the density, temperature, rotation and transport profiles that the kinetic solve, [Kinetic Forces](kinetic_forces.md) and the `SLAYER` layer of [Tearing](inner_layer.md) read, so every kinetic consumer sees one plasma. +Two formats are read, by extension: -# Build from a TOML file (searches relative paths if needed) -pe = JPEC.Equilibrium.setup_equilibrium("docs/examples/dcon.toml") +- **HDF5** (`.h5`, `.hdf5`), the GPEC kinetic schema: datasets at the file root, each with a `units` attribute, on the normalized flux `psi`: required `n_e`, `T_i`, `T_e`, `omega_E` (and `n_i`, defaulting to `n_e`); optional `omega_tor`, `chi_e`, `chi_i`, `chi_phi` (perpendicular heat and momentum diffusivities), `zeff`, and the supplied collision rates `lnLambda`, `nu_i` and `nu_e` (see [Kinetic Forces](kinetic_forces.md)); further `n_*` datasets are named per-species densities. Densities in m⁻³, temperatures in eV, frequencies in rad/s, collision rates in 1/s, diffusivities in m²/s. `write_kinetic_h5` writes the format. +- **ASCII** (`.gpeckf`, `.kin`, `.dat`): the six-column table `psi_n n_i n_e T_i T_e omega_E`, or with `kinetic_format = "rates-v1"` nine columns that append `lnLambda nu_i nu_e` (the Fortran PENTRC rates-v1 table); the column count must match `kinetic_format`, and header rows are skipped. -println("Magnetic axis: ", pe.params.r0, ", ", pe.params.z0) -println("q(0) = ", pe.params.q0) +The ion composition is part of the same data: `[[Equilibrium.ion_species]]` lists the main ions, each with `z`, `m` and either `fraction` (its share of the file's total `n_i`) or `density` (a named `n_*` dataset), and `impurity_charge` and `impurity_mass` describe the impurity that closes quasineutrality; the default is one deuterium ion carrying all of `n_i` with a carbon impurity. +``Z_\mathrm{eff}(\psi)`` is taken from the `zeff` dataset when present, else from `n_i/n_e` with the impurity charge. +In a script the same data is attached with `attach_kinetic_profiles!(equil, "kinetic.h5"; ion_species=[IonSpecies(; z=1, m=2, fraction=0.5), IonSpecies(; z=1, m=3, fraction=0.5)], impurity_charge=6, impurity_mass=12)`. -# Find separatrix (in-place) and inspect results -JPEC.Equilibrium.equilibrium_separatrix_find!(pe) -println("rsep = ", pe.params.rsep) -``` +### Radial grid -Analytic / testing example: construct a large-aspect-ratio model +With `grid_type = "auto"` and `mpsi = 0` (the defaults) the radial knots are placed by a two-pass refinement driven by one accuracy knob, `psi_accuracy`: the equilibrium is formed on a coarse grid, the curvature of the profiles, of the surface geometry and of the kinetic profiles is measured on it, and the knot density that keeps the cubic-spline derivative error below the tolerance is equidistributed, with geometric packing toward the axis and the separatrix and a mandatory knot at every rational surface of the run. +Every region's knot count scales as `psi_accuracy^(-1/3)`, so tightening it refines the core, the pedestal and the edge together; the rational-surface knots are what converge the Δ′ boundary-value problem. +The fixed layouts `grid_type = "core_edge_packed"`, `"pow1"` and `"uniform"` with an explicit `mpsi` remain available. -```julia -using JPEC +![Radial knot packing on the DIII-D-like example: the automatic two-pass grid against a fixed core_edge_packed grid.](assets/grid_knot_placement.png) + +## Running it -# Create a LAR config from a small TOML fragment or file -larcfg = JPEC.Equilibrium.LargeAspectRatioConfig(lar_r0=10.0, lar_a=1.0, beta0=1e-3) -pe = JPEC.Equilibrium.setup_equilibrium(JPEC.Equilibrium.EquilibriumConfig(control=Dict("eq_filename"=>"unused","eq_type"=>"lar")), larcfg) +From `examples/DIIID-like_ideal_example/gpec.toml`: -println("Built LAR equilibrium with a = ", lorcfg.lar_a) +```toml +[Equilibrium] +eq_type = "efit" +eq_filename = "TkMkr_D3Dlike_Hmode.geqdsk" +kinetic_file = "TkMkr_D3Dlike_Hmode_kinetic.h5" +jac_type = "hamada" +grid_type = "auto" +psilow = 1e-4 # innermost flux surface +psihigh = 0.995 # outermost flux surface +mpsi = 0 # 0: automatic radial grid +psi_accuracy = 0.001 # spline accuracy that sizes the automatic grid +mtheta = 256 # poloidal resolution +impurity_charge = 6 +impurity_mass = 12 + +[[Equilibrium.ion_species]] +z = 1 +m = 2 +fraction = 1.0 ``` -Notes: +The same from a script, with the deck keys as keywords: -- The `EquilibriumConfig(path::String)` constructor parses TOML and - expects `[EQUIL_CONTROL]` to contain at minimum `eq_filename` and - `eq_type` fields. Paths that are not absolute are resolved relative - to the TOML file location. -- When `eq_type == "inverse_testing"` a small example inverse run is - constructed (useful in tests and examples). +```julia +using GeneralizedPerturbedEquilibrium -## Notes and Caveats +eq = PlasmaEquilibrium("examples/DIIID-like_ideal_example/TkMkr_D3Dlike_Hmode.geqdsk"; jac_type="hamada", psihigh=0.995) +eq = PlasmaEquilibrium(Equilibrium.LargeAspectRatioConfig(; r0=10.0, a=1.0, beta0=1e-3)) # an analytic equilibrium +``` -- Many routines rely on spline representations; the `Splines` module is - used heavily and should be initialized where appropriate. -- The Equilibrium module contains several reader routines for external - formats (EFIT/CHEASE) and also interfaces to older Fortran helpers — - ensure required data files are present for those backends. -- For programmatic usage, prefer constructing `EquilibriumConfig` from a - TOML file to ensure all path resolution and defaults are handled. +Relative paths in the deck are resolved against the deck directory. +A `PlasmaEquilibrium` built with `mpsi = 0` keeps the coarse first-pass grid until a `solve` re-forms it; `Equilibrium.reform` with the nodes of `refined_psi_grid` does the same by hand. -## See also +## Outputs -- `docs/src/splines.md` — spline helpers used by equilibrium routines -- `docs/src/vacuum.md` — coupling between equilibrium and vacuum solvers +The group is written once `[ForceFreeStates]` has run, on the grid of the solve. -``` +| Dataset | Meaning | Units | +|---|---|---| +| `Equilibrium/R_axis`, `Z_axis`, `B_T_axis`, `R_mean`, `a_mean`, `aspect_ratio`, `kappa`, `delta_upper`, `delta_lower` | axis position and field, mean radii and shape parameters | m, T | +| `Equilibrium/I_p`, `volume` | plasma current and volume | A, m³ | +| `Equilibrium/beta_N`, `beta_t`, `beta_p_1`, `l_i_1`, … | normalized, toroidal and poloidal beta and internal inductance | | +| `Equilibrium/q_axis`, `q_95`, `q_edge`, `q_min`, `q_max`, `q_extrema_psi` | the safety factor at the axis, the 95% surface and the edge, and its extrema | | +| `Equilibrium/psi_boundary`, `psi_total`, `I_p_sign`, `B_T_sign` | the flux normalization and the sign conventions of the file | | +| `Equilibrium/Profiles/psi`, `q`, `2piF`, `mu0p`, `dVdpsi` | the 1-D profiles on ψ_N: q, 2πF, μ₀p and dV/dψ_N | T·m (`2piF`), T² (`mu0p`), m³ (`dVdpsi`) | +| `Equilibrium/Geometry/psi`, `theta`, `rcoords`, `offset`, `nu`, `jac` | the 2-D surface geometry on (ψ_N, θ): the radial coordinate, the angle offset, the toroidal offset ν and the Jacobian | | +## API reference +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Equilibrium] +``` diff --git a/docs/src/error_fields.md b/docs/src/error_fields.md new file mode 100644 index 0000000000..c108780fd6 --- /dev/null +++ b/docs/src/error_fields.md @@ -0,0 +1,183 @@ +# Error Fields + +`ErrorFields` turns a coil-driven perturbed equilibrium into an error-field tolerance assessment: how much resonant field each coil set drives when it is misaligned, how likely the assembled machine is to lock for given manufacturing tolerances, and how much correction current an error-field correction array may and must carry. +Once one plasma solve exists the resonant drive is linear in each coil's motion, so the module linearizes it once and everything downstream is linear algebra on a small table. + +## What it computes + +1. **Coil sensitivities** (`[ErrorFields]`, `ErrorFields/CoilSensitivities/`): the resonant spectrum of each coil set as built and its derivatives with respect to rigid shifts and tilts. +2. **Tolerances** (`tolerance_file`): the manufacturing tolerances of each coil set, in their own TOML file. +3. **Monte Carlo** (`[ErrorFields.MonteCarlo]`, `ErrorFields/MonteCarlo/`): the distribution of the dominant-mode overlap ``|\delta|`` over sampled misalignments, intrinsic and with error-field correction. +4. **Locking risk** (`[ErrorFields.scenario]`, `[ErrorFields.Risk]`, `ErrorFields/Risk/`): the overlap distribution against the empirical penetration-threshold scaling, giving the locking probability and its scan against tolerance scale, from which the allowable tolerance follows. +5. **Correction limits** (`[ErrorFields.NTV]`, `ErrorFields/NTV/`): how much correction current each array may carry before the NTV torque of its non-resonant field spends the rotation budget, and how much it needs. + +For every named coil set of a coil-forced run the stage evaluates the root-area-weighted control-surface spectrum ``\tilde b`` of the set as built and its central-difference derivatives with respect to the six rigid-body degrees of freedom, the shifts ``(\Delta x, \Delta y, \Delta z)`` in metres and the rotations ``(\theta_x, \theta_y, \theta_z)`` about the machine axes in degrees, placed on the run's (m, n) ordering so that they are exactly the vectors the resonant coupling matrix and its singular vectors act on (see [Dominant resonant-coupling mode](perturbed_equilibrium.md#Dominant-resonant-coupling-mode)). +The stored primitive is the linearization of the spectrum, not a scalar, so the resonant surfaces retained, the singular mode and the normalization are post-hoc choices, and every later step can be re-evaluated from `gpec.h5` without a new plasma solve. +In the snippets below `EF` is `GeneralizedPerturbedEquilibrium.ErrorFields` and `AEF` is `GeneralizedPerturbedEquilibrium.Analysis.ErrorFields`. + +## Governing equations + +The overlap of a coil set with singular mode ``k`` of the coupling matrix, normalized by the axis toroidal field, is the dimensionless + +```math +\delta = \frac{V_k^{\mathrm{H}}\,\tilde{b}}{B_{T0}}, +``` + +and because the projection is linear, a rigid shift ``(\Delta x, \Delta y)`` moves it by ``S_x \Delta x + S_y \Delta y`` with the complex sensitivities ``S_x = \partial\delta/\partial\Delta x``, ``S_y = \partial\delta/\partial\Delta y``, exact for any coil shape; for an axisymmetric coil ``S_y = \pm i S_x`` and the response reduces to one magnitude with a free phase. +Over sampled misalignments the overlap of the machine is the coherent sum over coil sets of ``S\,(\Delta + u) + T\,(\theta + v)`` for each coil's draw ``(\Delta, \theta)`` and placement uncertainty ``(u, v)``, plus one shared draw per coherent group and a random-direction unattributed budget; no field is recomputed, so a million samples take about a second. + +An overlap distribution becomes a locking risk through the empirical ITPA penetration-threshold scalings of [Logan et al. (2020)](citations.md#ErrorFields) for n = 1 and n = 2 and [Bursch et al. (2026)](citations.md#ErrorFields) for n = 1, + +```math +\delta_\mathrm{thresh} = 10^{\alpha_c}\, n_e^{\alpha_n}\, B_T^{\alpha_B}\, R_0^{\alpha_R}\, (\beta_N/l_i)^{\alpha_\beta}\, I_p^{\alpha_I}, +``` + +with ``n_e`` in 10¹⁹ m⁻³, ``B_T`` in T, ``R_0`` in m and ``I_p`` in MA (the current term only in the 2026 fits). +Sampling the fitted exponents within their standard errors turns the threshold into a distribution whose cumulative distribution is the probability ``P(\mathrm{lock}\,|\,\delta)`` that an overlap locks, and the locking probability of the assembled machine is ``100 \int \mathrm{pdf}(\delta)\, P(\mathrm{lock}\,|\,\delta)\, d\delta`` over the Monte Carlo bins, per batch. + +A correction array cancels the dominant-mode overlap at ``C_c`` per kilo-ampere-turn, but the non-resonant remainder of its field drives an NTV torque ``T I^2`` that a perfect correction does not remove. +With a torque budget ``T_0`` and the threshold taken to fall in proportion to the torque spent, the current that corrects an intrinsic overlap ``\delta_\mathrm{EF}`` solves + +```math +\delta_\mathrm{EF} - C_c I = s\,\delta_\mathrm{thresh}\left(1 - \frac{T_\mathrm{residual} I^2}{T_0}\right), +``` + +the model of [Logan et al. (2026)](citations.md#ErrorFields) for SPARC and [Leuthold et al. (2026)](citations.md#ErrorFields) for ARC. +It holds only while the plasma still rotates, so the current is capped at ``I_\max = \sqrt{T_0/T_\mathrm{residual}}``, and the largest correctable overlap is the peak of the quadratic or the overlap cancelled at ``I_\max``, whichever comes first; the two zero-rotation limits ``C_c\sqrt{T_0/T_\mathrm{residual}}`` and ``C_c\sqrt{T_0/T_\mathrm{full}}`` are reported beside it. + +## Running it + +The stage needs a `[PerturbedEquilibrium]` run with `forcing_data_format = "coil"` and the singular coupling computed; `examples/DIIID-like_error_field_example` is the complete case, with the DIII-D C-coil as the n = 1 source and the eighteen F coils as single-filament hoops whose error-field content is entirely their sensitivity to misalignment. +From its deck: + +```toml +[ErrorFields] +fd_step_shift_m = 1e-3 # central-difference step of the rigid shifts [m] +fd_step_tilt_deg = 0.1 # central-difference step of the rigid tilts [degrees] +rotation_center = "conductor" # tilt pivot: each conductor's own centre, or "set" for the whole set's +exclude_coils = [] # coil sets that are not error-field sources (the NTV efc_coils are excluded automatically) +tolerance_file = "tolerances.toml" # manufacturing tolerances, relative to the deck directory + +[ErrorFields.MonteCarlo] +nsample = 200000 # samples per batch +nbatch = 10 # independent batches; their spread is the statistical error bar +seed = 1 +nbins = 300 # histogram bins on [0, delta_max]; delta_max = 0 is 1.5 × the worst-case bound + +[ErrorFields.scenario] +n_e = 5.0 # electron density of the threshold scaling [1e19 m^-3]; B_T, R_0, beta_N, l_i and I_p default from the equilibrium + +[ErrorFields.Risk] +year = 2020 # threshold fit: 2020 (n = 1 and 2) or 2026 (n = 1) +dataset = "O,L" # ITPA dataset of the fit ("O,L", "O,L,H", ...) +fit = "WLS" # "OLS", "DSOLS" or "WLS" +nsample_threshold = 1000000 +scan_scales = [0.05, 0.1, 0.25, 0.5, 1.0, 2.0, 4.0, 8.0, 16.0] # tolerance multipliers of the allowable-tolerance scan + +[ErrorFields.NTV] +efc_coils = ["d3d_c"] # the correction arrays; never error-field sources +method = "fgar" # KineticForces torque method, enabled in [KineticForces] +rotation_scan = true # tabulate the torques against a rigid E×B rotation shift +rotation_scan_points = 5 +rotation_scan_max_points = 9 +rotation_scan_tolerance = 0.05 +rotation_span_factor = 1.0 +rotation_offset_factor = 2.0 +``` + +`[ErrorFields.MonteCarlo]` also takes `tolerance_scale` (multiplies every tolerance, for scans; `scale_subset` and `scale_map` restrict it) and `coil_subset` (the coil sets sampled; empty samples all), and `[ErrorFields.Risk]` takes `distribution`, how the fit exponents are sampled (`"normal"`, `"flat"`, `"normal_truncated"`). + +The tilt pivot matters for multi-filament winding packs: `"conductor"` rotates each filament about its own centre, `"set"` rotates the pack rigidly about its common centre, which is what an engineering axis-line tolerance constrains. +A coil set that is not an error-field source (a correction array or anything in `exclude_coils`) is swept like the others but left out of the as-designed field, the Monte Carlo and the scans; a tolerance that names it is an error. +The NTV limits need a `[KineticForces]` section, since each array's torque per kAt² is one kinetic torque evaluation of its unit-current spectrum, with and without the dominant mode projected out. + +The tolerances are engineering data, reviewed independently of any run, so they live in their own file, echoed into `Input/RawInputs/ErrorFields/tolerance_toml_raw` for replay; every key is checked against the schema. +Its tables are `[ErrorFields.defaults]` (fallbacks for every coil block), one `[[ErrorFields.coil]]` per coil set that moves on its own, `[[ErrorFields.coherent_group]]` for coil sets that move together, `[ErrorFields.correctability]` and `[ErrorFields.other_field]`: + +```toml +[ErrorFields.defaults] +shift_sigma_mm = 0.0 # Gaussian placement uncertainty added to the sampled shift [mm] +tilt_sigma = 0.0 # the same for the tilt, in tilt_units +tilt_units = "deg" # "deg", or "m" for a rim displacement converted through the coil's major radius +radial_shape = "hollow" # radial sampling density on the disk: flat, uniform_area, hollow or ring +tolerance_model = "additive" # "additive" (independent shift and tilt) or "cylinder" (correlated axis line) +current_factor = 1.0 # current as built relative to the run's; 0 switches a coil off + +[[ErrorFields.coil]] +name = "PF1U" # must match a [[ForcingTerms.coil_set]] of the run +shift_tol_mm = 0.5 # radius of the disk the coil centre may lie in [mm] +tilt_tol = 0.019 # tilt tolerance, in tilt_units +tolerance_model = "cylinder" +cylinder_half_height_m = 1.5 # sets the tilt reachable at the given radius + +[[ErrorFields.coherent_group]] +name = "upper_pf_brace" +members = ["PF1U", "PF2U"] # one shared draw per sample +shift_tol_mm = 2.0 +tilt_tol = 0.0 +phase_group = "braces" # groups naming the same phase_group share the random direction +rotation_center_z_m = 0.0 # pivot of the group's rigid rotation on the machine axis [m] + +[ErrorFields.correctability] +uncorrectable_coils = ["EFCC_U"] # coil sets the error-field correction cannot reduce +efc_factor = 2.0 # divisor applied to correctable contributions under correction + +[ErrorFields.other_field] +magnitude = 8.2e-6 # overlap budget of sources not attributed to any coil +sigma = 0.0 +radial_shape = "ring" +``` + +A coherent group's tilt is a rigid rotation of its members about the pivot, so a member at height ``z`` also shifts by ``(z - z_\mathrm{pivot})\,\theta``; coil sets without a tolerance block contribute their as-designed overlap only; a coil's `current_factor` scales its overlap and sensitivities together, the knob for a risk against the current in an uncorrectable coil. + +From a script the stage is one call on the plasma response and the coil sets, and every product is re-evaluated post hoc from the file for any surface window, mode, fit, tolerance scale or coil subset: + +```julia +ef = error_fields(resp, pe.coil_sets, ForcingTerms.CoilConfig(ft_ctrl); # ft_ctrl: the [ForcingTerms] control + tolerances=EF.read_tolerance_toml("tolerances.toml"), + scenario=EF.ScenarioParameters(ffs.equil; n_e=5.0)) +save_to_hdf5("gpec.h5", ef) + +table = EF.sensitivity_table("gpec.h5"; psi_low=0.5) # overlaps and sensitivities per coil set, edge window +mc = EF.run_monte_carlo("gpec.h5"; psi_low=0.5, tolerance_scale=2.0) # |δ| distribution, intrinsic and corrected +risk = EF.locking_risk("gpec.h5"; n_e=5.0, risk_ctrl=EF.RiskControl(; year=2026, fit="OLS")) +EF.allowable_tolerance(EF.ToleranceScan("gpec.h5"), 1.0; corrected=true) # tolerance multiplier at 1% locking risk +EF.max_correctable_overlap(EF.read_efc_couplings("gpec.h5")[1]; delta_threshold=1.4e-4, torque_budget=4.0) +AEF.plot_error_field_summary("gpec.h5"; save_path="error_field_summary.png") +``` + +`correction_requirement` gives the current each array needs, by the dominant mode or jointly over every rational surface in the least-squares sense, and `uncorrectable_probability` the fraction of the Monte Carlo overlap beyond what the NTV-limited array can correct; `phasing_map` evaluates the dominant-mode field per kilo-ampere-turn over the relative phases of several arrays; `ResonantDriveContext` and `coil_overlaps` assess a new coil design against the stored run at one Biot–Savart pass per coil set, with `combine_overlaps` applying a design current pattern afterwards. +`Analysis.ErrorFields` plots each product, and every plotting function takes `label => path` pairs so coil-design revisions overplot on one axis. + +## Outputs + +| Dataset | Meaning | +|---|---| +| `ErrorFields/CoilSensitivities/field_as_designed`, `shift_sensitivity_per_m`, `tilt_sensitivity_per_deg` | the control-surface spectrum of each coil set as built and its derivatives with respect to the rigid shifts and tilts, with the current pattern they were evaluated at and the finite-difference linearity residual per tap | +| `ErrorFields/CoilSensitivities/DominantMode/delta_as_designed`, … | the overlaps and sensitivities projected onto the run's full-window dominant mode, their direction-averaged in-plane magnitudes, and the shift and tilt that would cancel the as-designed overlap | +| `ErrorFields/MonteCarlo/abs_delta_pdf`, `abs_delta_efc_pdf`, `abs_delta_bin_edges` | the intrinsic and corrected ``\lvert\delta\rvert`` histograms over the sampled tolerances, per batch and averaged | +| `ErrorFields/MonteCarlo/abs_delta_total_as_designed`, `abs_delta_worst_case` | the overlap of the design as built and the worst-case bound with every term at its tolerance edge and in phase | +| `ErrorFields/Risk/locking_probability_given_delta`, threshold density | ``P(\mathrm{lock}\,|\,\delta)`` and the threshold distribution on the Monte Carlo grid | +| `ErrorFields/Risk/locking_probability_percent`, `locking_probability_efc_percent`, `*_batches_percent`, `locking_probability_as_designed_percent`, `locking_probability_fit_threshold_percent` | the locking probability of the intrinsic and corrected distributions with per-batch values, of the design as built, and at the sharp fitted threshold | +| `ErrorFields/Risk/ToleranceScan/` | the same against `tolerance_scale`; the allowable tolerance at a target risk is its inversion | +| `ErrorFields/NTV/` | each correction array's dominant-mode coupling ``C_c``, resonant fraction and NTV torque per kAt² for its full field and with the dominant mode projected out | + +Every dataset name follows one grammar, `[_][_efc][_][_]`: `delta` is the overlap and `abs_delta` its magnitude; `_as_designed`, `_sampled`, `_worst_case` name the geometry; `_efc` marks error-field correction applied; a probability stored as a percentage carries `_percent`. + +## Numerical notes + +- The sensitivities assume the overlap is linear in the misalignments out to the tolerance edge; the run checks it only at the finite-difference step, and `linearity_check` displaces each coil set by `±scale × tolerance` and recomputes its overlap to report the relative error of the linear model at the largest scale a tolerance scan will use (one Biot–Savart pass per row). +- Monte Carlo batches are seeded individually, so results are bit-identical for any thread count; `risk_convergence` repeats the Monte Carlo over sample and bin counts, and the batch spread must fall as ``1/\sqrt{n_\mathrm{sample}}`` while the value must not move with the bin count by more than the spread. +- A threshold scaling is fitted for one toroidal mode number, so the risk refuses a run that spans several; project onto one n's coupling first. +- A coil set whose field at the run's toroidal mode is round-off (an axisymmetric hoop at n = 1) has no resonant fraction and reports `NaN` rather than the ratio of two noise vectors. +- The NTV torques are stored with their sign and the limits consume the budget with their magnitude; the torque budget is the plasma's intrinsic torque and an analysis choice. + +## API reference + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.ErrorFields] +``` + +```@docs +GeneralizedPerturbedEquilibrium.equilibrium_from_h5 +``` diff --git a/docs/src/examples/equilibrium.md b/docs/src/examples/equilibrium.md deleted file mode 100644 index 7653866c55..0000000000 --- a/docs/src/examples/equilibrium.md +++ /dev/null @@ -1,7 +0,0 @@ -# Equilibrium Development Examples - -This page demonstrates equilibrium development and analysis using JPEC, based on the Solov'ev analytical equilibrium. - -## Equilibrium Setup - -### Basic Data Structures diff --git a/docs/src/examples/splines.md b/docs/src/examples/splines.md deleted file mode 100644 index 7401039da5..0000000000 --- a/docs/src/examples/splines.md +++ /dev/null @@ -1,152 +0,0 @@ -# Spline Examples - -This page demonstrates the usage of the Splines module with practical examples. - -## 1D Cubic Spline Interpolation - -### Basic Usage - -```julia -using JPEC, Plots - -# Create sample data -xs = collect(range(0.0, stop=2π, length=21)) -fs = sin.(xs) -fc = cos.(xs) - -# Combine into matrix (each column is a different quantity) -fs_matrix = hcat(fs, fc) - -# Set up spline for 2 quantities -spline = JPEC.SplinesMod.CubicSpline(xs, fs_matrix, 2) - -# Evaluate on fine grid -xs_fine = collect(range(0.0, stop=2π, length=100)) -fs_fine = JPEC.SplinesMod.spline_eval(spline, xs_fine) - -# Plot results -plot(xs_fine, fs_fine[:, 1], label="sin(x) spline", legend=:topright) -plot!(xs_fine, fs_fine[:, 2], label="cos(x) spline") -scatter!(xs, fs, label="sin(x) data") -scatter!(xs, fc, label="cos(x) data") -``` - -### Complex-valued Splines - -```julia -# Create complex exponential data -xs = collect(range(0.0, stop=2π, length=20)) -fm = exp.(-im .* xs) # e^(-ix) -fp = exp.(im .* xs) # e^(ix) - -# Combine complex data -fs_matrix = hcat(fm, fp) - -# Set up complex spline -spline = JPEC.SplinesMod.CubicSpline(xs, fs_matrix, 2) - -# Evaluate -xs_fine = collect(range(0.0, stop=2π, length=100)) -fs_fine = JPEC.SplinesMod.spline_eval(spline, xs_fine) - -# Plot real and imaginary parts -plot(xs_fine, real.(fs_fine[:, 1]), label="Re(e^(-ix))") -plot!(xs_fine, imag.(fs_fine[:, 1]), label="Im(e^(-ix))") -``` - -## 2D Bicubic Spline Interpolation - -### Basic 2D Function - -```julia -using JPEC, Plots - -# Create 2D grid -xs = collect(range(0.0, stop=2π, length=20)) -ys = collect(range(0.0, stop=2π, length=20)) - -# Create 2D function data -fs1 = sin.(xs') .* cos.(ys) .+ 1.0 -fs2 = cos.(xs') .* sin.(ys) .+ 1.0 - -# Prepare data array (nx × ny × nquantities) -fs = zeros(20, 20, 2) -fs[:, :, 1] = fs1 -fs[:, :, 2] = fs2 - -# Set up bicubic spline -bcspline = JPEC.SplinesMod.bicube_setup(xs, ys, fs, 2, 2) - -# Evaluate on fine grid -xs_fine = collect(range(0.0, stop=2π, length=100)) -ys_fine = collect(range(0.0, stop=2π, length=100)) -fs_fine = JPEC.SplinesMod.bicube_eval(bcspline, xs_fine, ys_fine) - -# Create contour plot -contourf(xs_fine, ys_fine, fs_fine[:, :, 1]', - title="Bicubic Spline: sin(x)cos(y) + 1") -``` - -### With Derivatives - -```julia -# Evaluate with first derivatives -fs_fine, fsx_fine, fsy_fine = JPEC.SplinesMod.bicube_eval(bcspline, xs_fine, ys_fine, 1) - -# Plot function and derivatives -p1 = contourf(xs_fine, ys_fine, fs_fine[:, :, 1]', title="f(x,y)") -p2 = contourf(xs_fine, ys_fine, fsx_fine[:, :, 1]', title="∂f/∂x") -p3 = contourf(xs_fine, ys_fine, fsy_fine[:, :, 1]', title="∂f/∂y") - -plot(p1, p2, p3, layout=(1,3), size=(1200, 400)) -``` - -### Equilibrium Example - -This example shows spline usage with the Solov'ev equilibrium: - -```julia -# Create equilibrium parameters -kappa = 1.8 # elongation -a = 1.0 # minor radius -r0 = 3.5 # major radius -q0 = 1.25 # safety factor - -# Create spatial grid -mr, mz = 40, 43 -rmin, rmax = r0 - 1.5*a, r0 + 1.5*a -zmin, zmax = -1.5*kappa*a, 1.5*kappa*a - -rs = collect(range(rmin, stop=rmax, length=mr)) -zs = collect(range(zmin, stop=zmax, length=mz)) - -# Create Solov'ev equilibrium psi field -f0 = r0 * 1.0 # b0fac = 1.0 -psio = kappa * f0 * a^2 / (2 * q0 * r0) -psifac = psio / (a*r0)^2 -efac = 1/kappa^2 - -psifs = zeros(mr, mz, 1) -for i in 1:mr, j in 1:mz - psifs[i, j, 1] = psio - psifac * (efac * (rs[i] * zs[j])^2 + (rs[i]^2-r0^2)^2/4) -end - -# Set up bicubic spline for psi -psi_spline = JPEC.SplinesMod.bicube_setup(rs, zs, psifs, 3, 3) - -# Evaluate on fine grid for plotting -rs_fine = collect(range(rmin, stop=rmax, length=110)) -zs_fine = collect(range(zmin, stop=zmax, length=100)) -psi_fine = JPEC.SplinesMod.bicube_eval(psi_spline, rs_fine, zs_fine) - -# Create contour plot -contourf(rs_fine, zs_fine, psi_fine[:, :, 1]', - title="Ψ: Solov'ev Equilibrium", - xlabel="R", ylabel="Z", - aspect_ratio=:equal) -``` - -## Performance Tips - -1. **Batch evaluations**: Evaluate multiple points simultaneously for better performance -2. **Memory management**: Splines should not be re-created frequently; reuse existing spline objects diff --git a/docs/src/examples/vacuum.md b/docs/src/examples/vacuum.md deleted file mode 100644 index 0ed3babb11..0000000000 --- a/docs/src/examples/vacuum.md +++ /dev/null @@ -1,173 +0,0 @@ -# Vacuum Examples - -This page demonstrates the usage of the Vacuum module for magnetostatic calculations. - -## Basic Vacuum Field Calculation - -### Setting up DCON Parameters - -The vacuum module requires initialization with DCON (Displacement CONtinuum) parameters: - -```julia -using JPEC - -# Define DCON parameters -mthin = Int32(4) # Number of theta points -lmin = Int32(1) # Minimum poloidal mode number -lmax = Int32(4) # Maximum poloidal mode number -nnin = Int32(2) # Toroidal mode number -qa1in = 1.23 # Safety factor parameter - -# Create geometry arrays -n_modes = lmax - lmin + 1 -xin = rand(Float64, n_modes) # Radial coordinates -zin = rand(Float64, n_modes) # Vertical coordinates -deltain = rand(Float64, n_modes) # Displacement data - -# Initialize DCON interface -JPEC.VacuumMod.set_dcon_params(mthin, lmin, lmax, nnin, qa1in, xin, zin, deltain) -``` - -### Vacuum Matrix Calculation - -```julia -# Set up vacuum calculation parameters -mpert = 5 # Number of perturbation modes -mtheta = 256 # Number of theta points for plasma -mthvac = 256 # Number of theta points for vacuum - -# Initialize result matrix -wv = zeros(ComplexF64, mpert, mpert) - -# Calculation flags -complex_flag = true # Use complex arithmetic -kernelsignin = -1.0 # Kernel sign -wall_flag = false # Include wall effects -farwal_flag = true # Far wall approximation - -# Geometry and source data -grrio = rand(Float64, 2*(mthvac+5), mpert*2) # Geometry data -xzptso = rand(Float64, mthvac+5, 4) # Source points - -# Perform vacuum matrix calculation -JPEC.VacuumMod.mscvac( - wv, mpert, mtheta, mthvac, - complex_flag, kernelsignin, - wall_flag, farwal_flag, - grrio, xzptso -) - -println("Vacuum matrix calculation completed") -println("Result matrix dimensions: ", size(wv)) -``` - -### Analyzing Results - -```julia -using Plots - -# Plot magnitude of vacuum matrix elements -heatmap(abs.(wv), - title="Vacuum Matrix |W| Elements", - xlabel="Mode j", - ylabel="Mode i", - color=:plasma) - -# Plot phase information -heatmap(angle.(wv), - title="Vacuum Matrix Phase", - xlabel="Mode j", - ylabel="Mode i", - color=:phase) -``` - -## Advanced Usage - -### Including Wall Effects - -```julia -# Enable wall calculations -wall_flag = true -farwal_flag = false # Do not use far wall approximation - -# Additional wall parameters might be needed -# (specific implementation depends on geometry) - -JPEC.VacuumMod.mscvac( - wv, mpert, mtheta, mthvac, - complex_flag, kernelsignin, - wall_flag, farwal_flag, - grrio, xzptso -) -``` - -### Multi-mode Analysis - -```julia -# Analyze eigenvalues of vacuum matrix -using LinearAlgebra - -eigenvals = eigvals(wv) -eigenvecs = eigvecs(wv) - -# Plot eigenvalue spectrum -scatter(real.(eigenvals), imag.(eigenvals), - title="Vacuum Matrix Eigenvalues", - xlabel="Real part", - ylabel="Imaginary part", - legend=false) - -# Identify most unstable mode -max_growth_idx = argmax(imag.(eigenvals)) -println("Most unstable eigenvalue: ", eigenvals[max_growth_idx]) -``` - -### Coupling with Equilibrium Data - -```julia -# This example shows how vacuum calculations integrate with equilibrium - -# 1. Set up equilibrium (using spline example from equilibrium page) -# ... equilibrium setup code ... - -# 2. Extract boundary data for vacuum calculation -# boundary_data = extract_boundary(psi_spline, flux_surface) - -# 3. Convert to DCON format -# xin, zin, deltain = process_boundary_data(boundary_data) - -# 4. Perform vacuum calculation -# JPEC.VacuumMod.set_dcon_params(mthin, lmin, lmax, nnin, qa1in, xin, zin, deltain) -# ... vacuum calculation ... - -# 5. Analyze stability -# stability_analysis(wv, eigenvals) -``` - -## Troubleshooting - -### Common Issues - -1. **Initialization Error**: Ensure `set_dcon_params` is called before `mscvac` -2. **Memory Issues**: Large `mtheta`/`mthvac` values require significant memory -3. **Convergence**: Check that geometry arrays are properly normalized -4. **Complex Arithmetic**: Ensure `complex_flag=true` for stability analysis - -### Performance Optimization - -```julia -# For large problems, consider: -# - Reducing mtheta/mthvac if possible -# - Using real arithmetic (complex_flag=false) when appropriate -# - Parallelization (if available in Fortran backend) - -# Monitor memory usage -using Pkg -Pkg.add("BenchmarkTools") -using BenchmarkTools - -@time JPEC.VacuumMod.mscvac(wv, mpert, mtheta, mthvac, - complex_flag, kernelsignin, - wall_flag, farwal_flag, - grrio, xzptso) -``` diff --git a/docs/src/figures/ballooning/delta_prime_matching.png b/docs/src/figures/ballooning/delta_prime_matching.png new file mode 100644 index 0000000000..a22404e2ba Binary files /dev/null and b/docs/src/figures/ballooning/delta_prime_matching.png differ diff --git a/docs/src/figures/inner_layer/backend_regime_map.png b/docs/src/figures/inner_layer/backend_regime_map.png new file mode 100644 index 0000000000..d86221bcdf Binary files /dev/null and b/docs/src/figures/inner_layer/backend_regime_map.png differ diff --git a/docs/src/figures/inner_layer/make_backend_regime_map.jl b/docs/src/figures/inner_layer/make_backend_regime_map.jl new file mode 100644 index 0000000000..f0978ab303 --- /dev/null +++ b/docs/src/figures/inner_layer/make_backend_regime_map.jl @@ -0,0 +1,65 @@ +# make_backend_regime_map.jl +# +# Figure: accuracy of the three GGJ backends along the imaginary-Q axis. The +# :ray backend is the reference (its own error stays below ~1e-5 out to +# |Q| = 500 — see the convergence figure). The relative error of :shooting and +# :galerkin against it is plotted versus |Q|: :shooting holds to |Q| ~ 1, +# :galerkin to |Q| ~ 4, and both then lose all accuracy, while :ray continues. +# The crossovers of each curve with the 1% line are the practical reach of each +# method — this is what motivates the :ray backend. A few tens of solves; ~1 min. +# +# Run manually: julia --project=. docs/src/figures/inner_layer/make_backend_regime_map.jl + +include(joinpath(@__DIR__, "..", "..", "..", "figure_tools.jl")) + +using GeneralizedPerturbedEquilibrium +using LinearAlgebra +using Printf + +const IL = GeneralizedPerturbedEquilibrium.InnerLayer +const GGJ = IL.GGJ + +p = IL.GGJ.q4_surface_benchmark() +γ(aq) = im * aq * GGJ.q0(p) # imaginary axis: Q = i|Q| + +# Worst-case relative error over the two parity components vs the :ray reference. +relerr(Δ, Δref) = maximum(abs.(Δ .- Δref) ./ abs.(Δref)) + +absQ = 10 .^ range(-1, log10(500); length=22) +ref = [IL.solve_inner(IL.GGJModel(; solver=:ray), p, γ(aq)) for aq in absQ] + +gal_x, gal_e = Float64[], Float64[] +shoot_x, shoot_e = Float64[], Float64[] +for (k, aq) in enumerate(absQ) + try + Δ = IL.solve_inner(IL.GGJModel(; solver=:galerkin), p, γ(aq)) + all(isfinite, Δ) && (push!(gal_x, aq); push!(gal_e, max(relerr(Δ, ref[k]), 1e-16))) + catch + end + aq > 10 && continue # :shooting is meaningless past its regime + try + Δ = IL.solve_inner(IL.GGJModel(; solver=:shooting), p, γ(aq)) + all(isfinite, Δ) && (push!(shoot_x, aq); push!(shoot_e, max(relerr(Δ, ref[k]), 1e-16))) + catch + end +end + +@printf("computed %d :ray refs, %d :galerkin, %d :shooting points\n", + length(absQ), length(gal_x), length(shoot_x)) + +plt = plot(; size=(820, 560), legend=:bottomright, xscale=:log10, yscale=:log10, + xlabel="|Q| (imaginary axis, Q = i|Q|)", + ylabel="relative error vs :ray reference", + title="Backend accuracy along the imaginary-Q axis (q=4 surface)", + ylims=(1e-7, 3e0), left_margin=9Plots.mm, bottom_margin=5Plots.mm) + +plot!(plt, shoot_x, shoot_e; lw=2, marker=:diamond, ms=5, color=:seagreen, label=":shooting") +plot!(plt, gal_x, gal_e; lw=2, marker=:circle, ms=5, color=:orange, label=":galerkin") +hline!(plt, [1e-2]; color=:red, ls=:dash, lw=1.5, label="1% accuracy") +annotate!(plt, 8.0, 3e-6, + Plots.text(":ray reference\n(error < 1e-5 to |Q| = 500)", 8, :center, RGB(0.2, 0.3, 0.6))) + +save_doc_figure(plt, "inner_layer", "backend_regime_map"; + script=basename(@__FILE__), + depends=["src/InnerLayer/GGJ/Ray.jl", "src/InnerLayer/GGJ/Galerkin.jl", + "src/InnerLayer/GGJ/Shooting.jl"]) diff --git a/docs/src/figures/manifest.toml b/docs/src/figures/manifest.toml new file mode 100644 index 0000000000..fd45fad1e4 --- /dev/null +++ b/docs/src/figures/manifest.toml @@ -0,0 +1,18 @@ +# GPEC documentation-figure provenance manifest. +# One [.
] entry per committed PNG under docs/src/figures/. +# Regenerate a figure (and this entry) only when a file in its `depends` +# list changes the numbers it shows — see docs/DOC_STANDARD.md. + + +[ballooning.delta_prime_matching] +commit = "legacy" +date = "unknown" +depends = ["src/LocalStability/Ballooning.jl"] +note = "Predates the provenance system; compared methods for extracting the ballooning Delta_prime and c_a1. Original generator script not preserved." +script = "(not preserved)" + +[inner_layer.backend_regime_map] +commit = "3a0837e3-dirty" +date = "2026-07-08" +depends = ["src/InnerLayer/GGJ/Ray.jl", "src/InnerLayer/GGJ/Galerkin.jl", "src/InnerLayer/GGJ/Shooting.jl"] +script = "make_backend_regime_map.jl" diff --git a/docs/src/forcing_terms.md b/docs/src/forcing_terms.md new file mode 100644 index 0000000000..8ad9ae5a0f --- /dev/null +++ b/docs/src/forcing_terms.md @@ -0,0 +1,122 @@ +# Forcing Terms + +`ForcingTerms` specifies the external magnetic perturbation that drives the perturbed equilibrium: the spectrum of the normal field on the control surface at `psilim`, given either as a file of (m, n) amplitudes or computed by Biot–Savart from coil geometry. +The `[ForcingTerms]` section is read when `[PerturbedEquilibrium]` runs; its result is the `RMPField` that `perturbed_equilibrium` applies to the plasma response. + +## Normalization + +Internally every forcing is the unit-norm flux spectrum ``\Phi_x``, the Fourier amplitude of ``2\pi R\,(B_R\,\partial Z/\partial\theta - B_Z\,\partial R/\partial\theta)`` over the unit-norm angles ``\theta, \zeta \in [0, 1]``, the quantity the Fortran GPEC calls `Phi_x`. +A forcing file declares its own normalization by a tag; a coil set needs none, because the Biot–Savart pipeline produces ``\Phi_x`` directly. + +| Tag | Quantity | Units | On load | +|---|---|---|---| +| `"normal_field_T"` (default) | Fourier modes of ``B\cdot\hat n`` over ``\theta \in [0, 2\pi]`` | T | weighted by the area element ``J\lvert\nabla\psi\rvert`` and projected onto every harmonic of the run; a poloidally varying weight mixes modes, so the file must cover its sidebands (a warning asks for a wider m range when an edge harmonic carries more than 10% of the flux) | +| `"sfl_flux_Wb"` | the straight-field-line flux ``R\,(B_R\,\partial Z/\partial\theta - B_Z\,\partial R/\partial\theta)`` over ``\theta \in [0, 2\pi]`` | T·m² | multiplied by ``(2\pi)^2`` | +| `"unit_flux"` | ``\Phi_x`` itself, as a run records its forcing for replay | T·m² | loaded as is | + +## File formats + +ASCII (`forcing_data_format = "ascii"`): an optional `# normalization: ` line, comment lines starting with `#`, and one `n m real(amplitude) imag(amplitude)` row per mode; all four columns are required: + +``` +# normalization: normal_field_T +1 6 1.0e-4 0.0 +1 7 5.0e-5 2.0e-5 +``` + +HDF5 (`forcing_data_format = "hdf5"`): datasets `n`, `m`, `amplitude_real` and `amplitude_imag`, all required, with the optional root attribute `normalization`: + +```julia +using HDF5 +h5open("forcing.h5", "w") do f + f["n"] = [1, 1, 1] + f["m"] = [5, 6, 7] + f["amplitude_real"] = [1e-4, 2e-4, 1e-4] + f["amplitude_imag"] = [0.0, 0.0, 0.0] + HDF5.attrs(f)["normalization"] = "normal_field_T" +end +``` + +## Coils + +`forcing_data_format = "coil"` computes the field from coil geometry: the plasma boundary is sampled on an `mtheta_coil × nzeta_coil` grid, the Biot–Savart field of every coil set is evaluated there, projected onto the normal flux and Fourier decomposed. +The toroidal angle of each observation point includes the straight-field-line offset ``\nu(\psi, \theta)`` and the helicity ``\mathrm{sign}(B_T)\,\mathrm{sign}(I_p)`` of the equilibrium, without which the decomposition would be wrong. +Each `[[ForcingTerms.coil_set]]` block supplies one coil set, selected by `source`; shifts and tilts (`shiftx/y/z`, `tiltx/y/z`, `n_tilt`, …) apply to every source, which is what the [Error Fields](error_fields.md) stage sweeps. +From `examples/DIIID-like_ideal_example/gpec.toml` (the first block) and the other sources: + +```toml +[ForcingTerms] +forcing_data_format = "coil" +machine = "d3d" +mtheta_coil = 480 +nzeta_coil = 32 + +# ASCII .dat file, resolved to {dat_dir}/{machine}_{name}.dat +[[ForcingTerms.coil_set]] +name = "c" +currents = [1000.0, 500.0, -500.0, -1000.0, -500.0, 500.0] + +# HDF5 file; `name` selects one set of a multi-set file +[[ForcingTerms.coil_set]] +source = "file" +h5_file = "/path/to/my_coils.h5" +name = "il" +currents = [1e4, 1e4, 1e4, 1e4, 1e4, 1e4] + +# Analytic PF hoop; a tilt breaks axisymmetry to give n ≠ 0 forcing +[[ForcingTerms.coil_set]] +source = "pf_hoop" +radius = 1.8 +height = 0.9 +currents = [5e3] +tiltx = [2.0] + +# Analytic window-pane array, by two opposite [R, Z] corners ... +[[ForcingTerms.coil_set]] +source = "window_pane" +n_coils = 8 +rz_corners = [[2.2, -0.6], [2.2, 0.6]] +gap_fraction = 0.15 +currents = [1e3, -1e3, 1e3, -1e3, 1e3, -1e3, 1e3, -1e3] + +# ... or by standoff off the plasma surface at a poloidal angle (degrees) +[[ForcingTerms.coil_set]] +source = "window_pane" +n_coils = 2 +standoff = 0.25 +poloidal_angle = 0.0 +poloidal_length = 1.2 +poloidal_tilt = 0.0 +gap_fraction = 0.15 +currents = [1e4, -1e4] + +# Analytic helical array on a circular torus, pitch m_hel/n_hel +[[ForcingTerms.coil_set]] +source = "helical" +R0 = 1.7 +a = 0.6 +m_hel = 4 +n_hel = 1 +n_coils = 4 +currents = [1e3, 1e3, 1e3, 1e3] +``` + +A coil HDF5 file stores one subgroup per coil set under a parent group (default `coils`): `//x`, `y`, `z` as `ncoil × s × nsec` Cartesian metres, an optional `currents` dataset, and the winding multiplier as the attribute `nw`; `convert_coil_dat_to_h5` and `convert_coil_h5_to_dat` translate the ASCII format, `read_coil_h5` and `write_coil_h5` read and write it. + +The field is linear in the coils, so a `CoilForcingGrid` built once per equilibrium and n evaluates coil sets one at a time, for comparing coil variants against one plasma without re-sampling the boundary: + +```julia +forcing_grid = CoilForcingGrid(equil, cfg, n; psi=ffs.psilim) +spectra = [coil_forcing_modes(cs, forcing_grid, n, m_low, m_high) for cs in coil_sets] +``` + +## Outputs + +The applied spectrum is written with the response: `PerturbedEquilibrium/ForcingModes/` (`m`, `n`, `amplitude`) and the control-surface spectra `PerturbedEquilibrium/forcing_b`, `forcing_b_root_area` and `forcing_b_area` (see [Perturbed Equilibrium](perturbed_equilibrium.md)). +A forcing file is captured raw with its tag under `Input/RawInputs/ForcingTerms/`, and the coil geometry as used, after shifts and tilts, under `Input/RawInputs/Coils/`, so that `main(["gpec.h5"])` can replay the run without the original files; see the [Scripting API](api.md). + +## API reference + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.ForcingTerms] +``` diff --git a/docs/src/galerkin.md b/docs/src/galerkin.md new file mode 100644 index 0000000000..b3e59a745b --- /dev/null +++ b/docs/src/galerkin.md @@ -0,0 +1,57 @@ +# Galerkin Solver + +The Galerkin solver is the third `[ForceFreeStates] integrator`: the singular Galerkin method of the Fortran RDCON for the outer-region Δ′ of resistive stability ([Glasser, Wang and Park (2016)](citations.md#Tearing)). + +## What it computes + +Instead of integrating the Euler–Lagrange system radially, it discretizes the displacement on a packed radial grid of Hermite-cubic finite elements and solves one global banded system. +The cells adjacent to each rational surface carry the large and small power-law asymptotic solutions analytically, so the singular behavior is built into the basis rather than resolved numerically, and the solve yields the Δ′ matrix and the PEST-3 matching blocks of every surface at once. + +## Governing equations + +The ideal coefficient is reconstructed as ``F = (QL)(QL)^\dagger`` from the interpolated Cholesky factor ``L`` with the resonance factor ``Q = \operatorname{diag}(m - nq)``, the same representation the Euler–Lagrange integration and the singular-surface expansions use, so ``F`` stays Hermitian positive semidefinite between spline knots. +The weak form of the same system as on the [Stability](stability.md) page is assembled on the Hermite basis, with the resonant and extension cells next to each rational surface carrying the asymptotic solutions ``|\psi - \psi_s|^{-1/2 \pm \sqrt{-D_I}}``. + +## Running it + +Select it in `[ForceFreeStates]` and set its options in `[Galerkin]`, as in `examples/DIIID-like_gal_resistive_example` (abridged): + +```toml +[ForceFreeStates] +integrator = "Galerkin" + +[Galerkin] +nx = 256 # Hermite-cubic elements per interval between singular surfaces +pfac = 0.03 # grid packing ratio toward the singular surfaces +rpec_flag = true # edge coil-response columns (Delta_coil), needed by [Match] and [Tearing] +``` + +`cut_solution = true` (default `false`) adds the cut solution that composite inner-region profiles need. + +It replaces the radial integration rather than supplementing it: the run computes its own vacuum response at the control surface (with `vac_flag`) but produces no free-boundary energies, no ODE trace and no `crit` scan, it does not support the kinetic solve, and its result cannot drive `[PerturbedEquilibrium]`, which needs the free-boundary δW of a `"Forward"` solve. +A `[Match]` section matches the inner layer to its Δ′ and gives a driven ``\xi`` solution; the composite inner-region profiles need `cut_solution = true` (`Galerkin(; cut_solution=true)` followed by `solve(MatchProblem(ffs))` in a script). + +## Outputs + +| Dataset | Meaning | +|---|---| +| `SingularSurfaces/Delta_prime_matrix` | the Δ′ matrix in the PEST-3 convention, on the same path as the Riccati result, so downstream stages never branch on which formalism ran | +| `SingularSurfaces/Delta_prime_raw`, `Delta_coil` | the side-major matching matrix and the coil-response columns (`rpec_flag = true`) | +| `SingularSurfaces/pest3_A`, `pest3_B`, `pest3_Gamma` | the PEST-3 interchange and mixed blocks ``A'``, ``B'``, ``\Gamma'`` | +| `ForceFreeStates/Solutions/GalerkinIntegration/` | the closed ``\xi`` profiles on the Galerkin grid, the surface list, the factorization used (`solver`) and, with `basis_output = true`, the `Basis/` group | + +Nothing is written under `ForceFreeStates/FreeBoundaryStability/`. + +## Numerical notes + +- The radial grid is packed toward the rational surfaces and the edge, and the banded system is factorized by a Hermitian Cholesky decomposition, falling back to a Jacobi-scaled LU when the band is not Hermitian positive definite; the factorization used is recorded as `solver`. +- The singular cells make the solution insensitive to the resolution at the rational surfaces, so convergence is set by the smooth part of the grid (`nx`). + +## API reference + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.ForceFreeStates] +Pages = ["Galerkin/GalerkinStructs.jl", "Galerkin/GalerkinGrid.jl", "Galerkin/GalerkinAssembly.jl", "Galerkin/GalerkinSolution.jl", "Galerkin/GalerkinSolve.jl"] +``` + +See [Stability](stability.md) for the module this solver belongs to and [Tearing](inner_layer.md) for the inner-layer models matched to its Δ′. diff --git a/docs/src/index.md b/docs/src/index.md index 4cbd24e7bf..aa6f53553e 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -1,56 +1,64 @@ -# JPEC.jl - -*A Julia implementation of the Generalized Perturbed Equilibrium Code suite* - -JPEC.jl is a work-in-progress Julia port of the [Generalized Perturbed Equilibrium Code (GPEC)](https://github.com/PrincetonUniversity/GPEC) suite, providing tools for magnetohydrodynamic (MHD) equilibrium and stability analysis in fusion plasmas. - -## Overview - -JPEC provides functionality for: - -- **some stuff** Fill this in later - -## Installation - -```julia -using Pkg -Pkg.add("JPEC") +```@meta +CurrentModule = GeneralizedPerturbedEquilibrium ``` -## Quick Start - -```julia -using JPEC - -# Great question +```@eval +using Markdown +using GeneralizedPerturbedEquilibrium +readme = read(joinpath(pkgdir(GeneralizedPerturbedEquilibrium), "README.md"), String) +# The "Full documentation" self-link belongs in the GitHub README, not on the doc site itself. +# If that line is ever removed from README.md this `replace` silently no-ops, which is fine — +# the strip is defensive, not load-bearing. +readme = replace(readme, r"^Full documentation:.*$"m => "") +Markdown.parse(readme) ``` -## Modules +## What GPEC computes -```@contents -Pages = ["splines.md", "vacuum.md", "equilibrium.md"] -Depth = 1 -``` +One deck, `gpec.toml`, runs the stages below in order, each when its section is present, into one output file, `gpec.h5`. +The [Workflow](workflow.md) page maps every stage to its physics, deck sections and datasets. -## Examples +| Stage | Deck section | Main output | +|---|---|---| +| `Equilibrium` — the axisymmetric equilibrium, its flux-surface coordinates and rational surfaces | `[Equilibrium]` | `Equilibrium/` | +| `ForceFreeStates` — ideal (or drift-kinetic) MHD stability by the Newcomb criterion, with the `Vacuum` response of the `[Wall]` (a wall case is a separate deck from a response case) | `[ForceFreeStates]` | `ForceFreeStates/FreeBoundaryStability/`, `SingularSurfaces/Delta_prime_matrix` | +| `LocalStability` — Mercier, resistive-interchange and ballooning criteria | `[LocalStability]` | `LocalStability/` | +| `Match` — the resistive inner layer matched to the outer-region Δ′ | `[Match]` | `SingularSurfaces/Match/` | +| `PerturbedEquilibrium` — the plasma response to the field of `[ForcingTerms]`: resonant fields, islands, overlap; needs a `"Forward"` solve without a wall | `[PerturbedEquilibrium]` | `PerturbedEquilibrium/SingularCoupling/` | +| `KineticForces` — the neoclassical toroidal viscosity torque of that response | `[KineticForces]` | `KineticForces/` | +| `ErrorFields` — coil tolerances, locking risk and correction limits | `[ErrorFields]` | `ErrorFields/Risk/` | +| `Tearing` — tearing growth rates and critical resonant fields from an inner-layer model | `[Tearing]` | `Tearing/Roots/` | -The package includes several Jupyter notebook examples: +### Coming from the Fortran codes -- `example.ipynb`: Fill this in later +| Fortran | Here | +|---|---| +| DCON | `[ForceFreeStates]` with `integrator = "Forward"` | +| STRIDE | `integrator = "Riccati"`, with Δ′ in `SingularSurfaces/Delta_prime_matrix` | +| RDCON and its `rmatch` | `integrator = "Galerkin"` and `[Match]` | +| VACUUM | the `Vacuum` module, with the wall in `[Wall]` | +| GPEC | `[ForcingTerms]` and `[PerturbedEquilibrium]` | +| PENTRC | `[KineticForces]` | +| SLAYER | `[Tearing]` with `inner_model = "SLAYER"`, options in `[SLAYER]` | +| `dcon.in`, `vac.in`, `gpec.in`, `pentrc.in`, … | one `gpec.toml`, and one `gpec.h5` for every result | -## Developer Notes +The [Conventions Reference](conventions.md) states the coordinates, signs and normalizations, including where they differ from the Fortran outputs. -### Commit Messages +## Documentation -To assist with release note compilation, please follow the commit message format: -``` -CODE - TAG - Detailed message -``` -where CODE is EQUIL, DCON, VAC, etc. and TAGs are descriptors like WIP, MINOR, IMPROVEMENT, BUG FIX, NEW FEATURE, etc. +- [**Setup**](set_up.md) — Installation on Linux, macOS and Windows (WSL), and a first run +- [**Workflow**](workflow.md) — The pipeline: physics of each stage, deck sections, examples and the output file +- [**Scripting API**](api.md) — The same pipeline from Julia scripts +- [**Conventions Reference**](conventions.md) — Coordinates, signs, normalizations and field amplitudes +- [**Modules**](equilibrium.md) — Governing equations, deck keys, outputs and function reference per module +- [**Citations**](citations.md) — Papers underpinning GPEC's algorithms +- [**Developer Notes**](developer_notes.md) — Git workflow, commit conventions and the regression-testing harness +- [**Introduction slides**](https://docs.google.com/presentation/d/1Iey7bsvpDG1WDRow24OD0NOWyKYOXMs0U7QLc7Y-wj4/edit?slide=id.g2798d4bd85b_0_15#slide=id.g2798d4bd85b_0_15) — An overview of GPEC and its physics + +## Contact -Additionally, please see [this](https://docs.google.com/document/d/1XAOTz1IV8ErZAAk-iSuEuddNOLB5XcoVZsAbPKRUUuA) google doc for more details on using the GitHub. +GPEC is developed and maintained by **Nikolas Logan** ([ncl2128@columbia.edu](mailto:ncl2128@columbia.edu)) and **Jong-Kyu Park** ([jkpark@snu.ac.kr](mailto:jkpark@snu.ac.kr)) as lead developers. -## Links +New users and developers are warmly encouraged to reach out — whether you have questions about running GPEC, want to discuss the physics, or are interested in contributing or collaborating, please get in touch. -- [Source Repository](https://github.com/OpenFUSIONToolkit/JPEC) -- [Original GPEC](https://github.com/PrincetonUniversity/GPEC) +For bug reports and feature requests, please open an issue on the [GitHub repository](https://github.com/OpenFUSIONToolkit/GPEC/issues). diff --git a/docs/src/inner_layer.md b/docs/src/inner_layer.md new file mode 100644 index 0000000000..e67b984ffd --- /dev/null +++ b/docs/src/inner_layer.md @@ -0,0 +1,177 @@ +# Tearing + +`Tearing` solves the resistive tearing stability of each rational surface by matching the outer-region Δ′ of the force-free states to the response of a resistive inner layer. +The outer region, ideal MHD everywhere except at the rational surfaces, gives the tearing drive Δ′; the inner layer, where resistivity, two-fluid effects and rotation act, gives the response Δ(Q) as a function of the complex mode frequency; the roots of Δ′ = Δ(Q) are the tearing modes, with their growth rates and real frequencies. + +## What it computes + +Two layer models are available, selected by `[Tearing] inner_model`: + +- `"SLAYER"`, the two-fluid drift-MHD slab layer equations of Cole and Fitzpatrick (2006), in the form Park's SLAYER solver takes them with a few terms dropped ([Park (2022)](citations.md#Tearing), [Burgess et al. (2026)](citations.md#Tearing)), with resistivity, perpendicular and toroidal transport, diamagnetic drifts and E×B rotation taken from the kinetic profiles of the equilibrium; its options are in `[SLAYER]`. +- `"GGJ"`, the toroidal resistive-MHD layer of Glasser, Greene and Johnson ([Glasser, Wang and Park (2016)](citations.md#Tearing), [Glasser and Wang (2020)](citations.md#Tearing)), whose coefficients E, F, G, H, K are the flux-surface averages stored under `SingularSurfaces/`; its solver options are in `[GGJ]`. + +For each surface the `Dispersion` search scans the complex frequency plane, extracts the roots of the dispersion relation and classifies them, and `[Tearing.search]` sets the scan. +With `[Tearing.CriticalResonantField]` the stage also computes, for the `SLAYER` layer, the resonant field at which a static error field overcomes the layer's shielding: the error-field penetration threshold. + +The same GGJ layer serves the `[Match]` stage of the [Workflow](workflow.md), which does the complementary job: instead of finding the free modes it imposes the layer response on the driven force-free states and writes the matched solution to `SingularSurfaces/Match/`. + +## Governing equations + +The GGJ layer equations couple the perturbed flux ``\Psi`` and the layer variables ``\Xi``, ``\Upsilon`` in the scaled distance ``x`` from the rational surface (Glasser, Wang and Park (2016), Eq. 11). +A physical growth rate ``\gamma`` maps to the scaled frequency ``Q`` by [`inner_Q`](@ref InnerLayer.inner_Q), the model requires Mercier stability ``D_I < 0`` ([`mercier_di`](@ref InnerLayer.mercier_di)), and at large ``x`` the solutions contain the two power laws of the outer region. +The matching datum ``\Delta`` is the ratio of the large to the small power-law coefficient; imposing the two parities at ``x = 0`` gives the pair ``(\Delta_+, \Delta_-)``: ``\Delta_+`` even in the displacement ``\Xi``, the **interchange** channel, ``\Delta_-`` odd, the **tearing** channel. +Positional pairs are always ordered ``(\Delta_+, \Delta_-)``, and `InnerLayerResponse` names them `interchange` and `tearing`. +The dispersion relation of a surface is then ``\Delta'_s = \Delta_-(Q)`` with ``\Delta'_s`` the surface's diagonal of `SingularSurfaces/Delta_prime_matrix` converted to the layer's normalization (`PerSurface/Delta_prime_norm`); `coupling_mode = "coupled"` solves the determinant of the full matrix over up to `msing_max` surfaces instead. + +The SLAYER layer is a slab with the Lundquist number ``S``, the temperature ratio ``\tau = T_i/T_e``, the compressibility ``c_\beta``, the perpendicular and toroidal Prandtl numbers ``P_\perp``, ``P_\mathrm{tor}`` and the diamagnetic frequencies ``Q_e``, ``Q_i`` of each surface (all stored under `Tearing/PerSurface/`), solved by a Riccati integration across the layer; its layer thickness ``\delta_s`` is written to `Tearing/LayerWidths/` and feeds the layer-overlap cap of [Stability](stability.md). + +### Critical resonant field + +With `[Tearing.CriticalResonantField]` each `SLAYER` surface gets the critical resonant field for error-field penetration, from the steady-state torque balance of Cole and Fitzpatrick (2006): the electromagnetic torque on the layer balances the viscous restoring torque when + +```math +\frac{\operatorname{Im}\hat\Delta(Q)}{|\alpha + \hat\Delta(Q)|^2} + = \frac{2P\,(Q_0 - Q)}{S\hat\kappa\,(b_r/B_\phi)^2}, +\qquad +\hat\kappa = \left[\frac{2}{s}\right]^2 \int_{r_s}^{a} \frac{\mu(r_s)}{\mu(r)}\,\frac{dr}{r}, +``` + +and balance is lost above + +```math +\left(\frac{b_r}{B_\phi}\right)^2_\mathrm{crit} + = \max_Q \frac{2P\,(Q_0 - Q)}{S\hat\kappa\,\operatorname{Im}[-1/(\alpha + \hat\Delta(Q))]} . +``` + +``P`` is the layer's `P_tor`, ``S`` its Lundquist number, ``Q_0 = \tau_k n \omega_E`` the natural E×B rotation of the mode (from the kinetic file's `omega_E` or the `omega_E_kHz` override, a `[Tearing]` table keyed by surface such as `omega_E_kHz = { "2/1" = 3.0 }`; a surface without one is skipped), and the viscosity integral is taken as 1/2, so ``\hat\kappa = 2/s^2``. +Each surface uses its own stability parameter ``\alpha = S^{-1/3}(\Delta_\mathrm{crit} - r_s\Delta')``, so the torque balance shares its poles with the free modes; the threshold needs a tearing-stable surface, and one with ``\alpha \le 0`` is skipped with a warning. +Penetration is the first resolved saddle-node bifurcation on the path from ``Q_0`` toward the electron fluid at ``Q_e``, above which the shielded branch no longer exists; small numerical extrema and the unshielded zone next to ``Q_e`` are excluded, and a surface without a resolved bifurcation reports NaN. +`br_crit` is normalized by the toroidal field, and `br_crit_tesla = br_crit·|B0|` is the quantity to compare with `PerturbedEquilibrium/SingularCoupling/resonant_area_weighted_field`, the total resonant field of the ideal response; the comparison is approximate, since the theory's ``b_r`` is a cylindrical field at the layer. + +## Running it + +From `examples/DIIID-like_SLAYER_example/gpec.toml`, after a `[ForceFreeStates]` solve with `integrator = "Riccati"` and `vac_flag = true` (or `"Galerkin"` with `rpec_flag = true`), which provides the Δ′ matrix, and with a kinetic file on the equilibrium: + +```toml +[Tearing] +inner_model = "SLAYER" # or "GGJ" +coupling_mode = "uncoupled" # one dispersion relation per surface; "coupled" uses the full Δ′ matrix + +[Tearing.search] +scan_mode = "amr" # adaptively refined scan of the complex Q plane +Q_re_range = [-2.0, 2.0] +Q_im_range = [-0.5, 3.0] +nre = 41 +nim = 31 +pole_threshold_adaptive = true +filter_above_poles = true +filter_outside_re = true +polish_roots = true +store_scan = false # true: write Tearing/Scan/Surface_/ + +[Tearing.CriticalResonantField] +n = 2000 # Q samples of the torque balance +store_scan = false + +[SLAYER] +D_c_type = "none" # critical-Δ offset from parallel transport ("none": zero) +P_perp_model = "D_perp" # how the perpendicular Prandtl number is built (below) +``` + +In a script, `solve(TearingProblem(ffs; critical_resonant_field=CriticalResonantFieldControl()), SLAYERModel(); config=TearingConfig(...))` runs the same stage, and `solve_inner(GGJModel(), p, γ)` evaluates one GGJ layer at a growth rate ``\gamma``. +GGJ's η and ρ default from the kinetic data (η by the resistivity closure SLAYER uses, ρ = μ·m_p·n_e); pass `eta` and `rho` to `TearingProblem` to override them. + +### SLAYER inputs: perpendicular diffusivities + +The layer widths and the critical island width of `SLAYER` take perpendicular diffusivities from the kinetic file (`chi_e`, `chi_i`, `chi_phi` profiles in m²/s). +Two formulas want different quantities: the layer width goes through `P_perp`, the diffusion of the total pressure, while the critical island width ``W_d \propto (\chi_\perp/\chi_\parallel)^{1/4}`` is set by the electron heat transport that flattens the electron temperature across the island. +`[SLAYER] P_perp_model` selects how `P_perp` is built: from the perpendicular particle diffusivity ``D_\perp`` that weights the electron and ion channels by their share of the pressure gradient (`"D_perp"`, the default), from `chi_e` alone (`"chi_perp_e"`, the single-χ convention of Fitzpatrick's TJ code), from `chi_i`, from the toroidal Prandtl number, from its ``c_\beta^2`` floor, or from a whole-plasma confinement time (`"tau_E"`, for a plasma without χ profiles). +`[SLAYER] chi_perp_Dc` sets the electron diffusivity of the critical island width alone, one value for every surface, for a run in which one χ⊥,e cannot serve both formulas; where an interpretive power balance cannot separate the species, treat it as a modeling choice and scan it. +Since the widths scale as ``\chi^{1/4}``, a factor-two change in χ moves them by about 19%, which can be enough to move the resistive-layer overlap cut of [Stability](stability.md) by one surface. +The DIII-D-like kinetic file of the examples carries analytic χ profiles of the order of power-balance values for DIII-D H-modes; they are not fits to a measured discharge, do not resolve the pedestal transport barrier, and `chi_e` is raised by a factor of two so that the default deck exercises the layer-overlap cut. + +## Outputs + +| Dataset | Meaning | +|---|---| +| `Tearing/PerSurface/rational_psi`, `rational_q`, `m`, `n` | the analyzed surfaces | +| `Tearing/PerSurface/S`, `tau`, `c_beta`, `P_perp`, `P_tor`, `Q_e`, `Q_i`, `tau_k`, `tau_R`, `eta`, `rs`, … | the layer parameters of each surface (SLAYER); `E`, `F`, `G`, `H`, `K`, `M`, `tau_A` and `tau_R` for GGJ | +| `Tearing/PerSurface/Delta_prime_raw` and `Delta_prime_matrix_rs` (SLAYER) or `Delta_prime_matrix` (GGJ) | the outer matrix each layer was matched to | +| `Tearing/PerSurface/Delta_prime_norm`, `delta_prime_conversion` | the Δ′ normalization of the SLAYER layer | +| `Tearing/Roots/Q_root`, `omega`, `gamma`, `no_root` | the dispersion root of each surface in the lab frame (`frequency_frame` attribute), its real frequency and growth rate, and the flag of a surface with no usable root | +| `Tearing/LayerWidths/delta_s`, `delta_s_abs`, `d_beta` | the complex resistive layer thickness, its magnitude in metres, and the ion drift scale | +| `Tearing/Diagnostics/ValidRoots`, `Poles`, `FilteredRoots` | the root classification of the scan | +| `Tearing/Scan/Surface_/` | the scanned Δ(Q) with `store_scan = true` | +| `Tearing/CriticalResonantField/br_crit`, `br_crit_tesla`, `Q_peak`, `Q0` | the penetration threshold as ``b_r/B_\phi`` and in tesla, the Q of the balance maximum and the natural rotation | + +## Numerical notes + +The `GGJ` model exposes three solvers through the `solver` type parameter of [`GGJModel`](@ref InnerLayer.GGJModel), all returning ``\Delta`` in the same convention: + +| backend | method | robust regime | +|:--------|:-------|:--------------| +| `:shooting` | backward stable shoot from ``X_\mathrm{max}\to 0`` | ``\lvert Q\rvert \ll 1`` | +| `:galerkin` | Hermite-cubic finite-element solve on the real axis | surface- and ``\arg Q``-dependent; see [Choosing a backend](@ref) | +| `:ray` (default) | rotated contour: inward-marched far-field condition and a Hermite-cubic BVP | ``\lvert Q\rvert \sim 500`` on or near the imaginary axis | + +`:ray` continues the layer equations analytically onto the ray ``x = e^{i\theta}s`` with ``\theta = \arg(Q)/4``, which makes the exponent of the outer solutions real and lifts the pseudo-resonance at ``x^2 = -Q^2(G + KF)`` off the contour; it takes the far-field data from the asymptotic series where its residual is below tolerance ([`pick_smax`](@ref InnerLayer.pick_smax)), marches the power pair inward by an implicit Radau scheme that damps the growing exponential directions, and solves the Hermite-cubic boundary-value problem with the parity conditions at ``s = 0``. +``\Delta`` does not depend on the contour angle, so [`delta_convergence`](@ref InnerLayer.delta_convergence) re-solves with each numerical knob perturbed and reports the spread as an error bar. + +![Relative error of the :shooting and :galerkin backends against the :ray reference along the imaginary-Q axis, on the q = 4 DIII-D-like benchmark surface; each curve's crossing of the 1% line marks that method's practical reach.](figures/inner_layer/backend_regime_map.png) + +Validation: at the real operating point ``Q = 0.1234`` of Glasser and Wang (2020) the `:galerkin` backend reproduces the Fortran `rmatch` solver to ``\sim 10^{-8}`` and `:ray` agrees to ``4\times10^{-7}`` in ``\Delta_-`` (``1.3\times10^{-4}`` in ``\Delta_+``, which sits near a pole); on the ``q = 4`` benchmark surface ([`q4_surface_benchmark`](@ref InnerLayer.GGJ.q4_surface_benchmark), ``S \approx 4.6\times10^6``, ``D_I \approx -0.31``) `:ray` is pinned at ``Q = 500i`` to ``\Delta_+ = 2.4720 + 13.3540\,i``, ``\Delta_- = 0.13750 + 0.74275\,i``, with a knob-perturbation error bar of ``\sim 5\times10^{-6}`` and ``6\times10^{-7}``. +The critical-field torque balance reproduces the Fortran SLAYER's balance to all printed digits when fed the same Δ(Q) table; the thresholds differ through the layer model (Park's reduced form of the layer equations against Cole and Fitzpatrick's full ones) and the selection rule above. + +![Fortran and Julia inner-layer Δ(Q) and torque balance on the DIII-D-like example.](assets/crf_fortran_benchmark.png) + +### Choosing a backend + +`:ray` is the default and the recommended backend for every ``Q``: it resolves the local WKB rate in every cell and holds ``\sim 2\times10^{-6}`` for ``|Q|`` from 0.01 to 1 on every surface tested. +`:galerkin` takes a few milliseconds per solve, 3–60 times less than `:ray`, but its accuracy depends on the surface and on ``\arg Q`` even at small ``|Q|``, erring by order unity near ``\arg Q = 157.5^\circ``. +The backends take disjoint numerical keywords: pass `GGJModel(solver=:galerkin)` explicitly when supplying its `nx`/`xfac` knobs. + +```julia +using GeneralizedPerturbedEquilibrium.InnerLayer + +p = GGJ.q4_surface_benchmark() # GGJParameters for the q = 4 benchmark surface +γ = 500im * GGJ.q0(p) # physical rate placing Q on the imaginary axis at 500i +Δ = solve_inner(GGJModel(), p, γ) # Δ.interchange, Δ.tearing with the default :ray backend +``` + +## API reference + +### Tearing + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Tearing] +``` + +### InnerLayer + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.InnerLayer] +``` + +### GGJ + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.InnerLayer.GGJ] +``` + +### SLAYER + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.InnerLayer.SLAYER] +``` + +### Dispersion + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Tearing.Dispersion] +``` + +### CriticalResonantField + +```@autodocs +Modules = [GeneralizedPerturbedEquilibrium.Tearing.CriticalResonantField] +``` diff --git a/docs/src/kinetic_forces.md b/docs/src/kinetic_forces.md new file mode 100644 index 0000000000..d88681a6b2 --- /dev/null +++ b/docs/src/kinetic_forces.md @@ -0,0 +1,107 @@ +# Kinetic Forces + +`KineticForces` computes the neoclassical toroidal viscosity (NTV) of a perturbed equilibrium: the toroidal torque and the kinetic energy that a non-axisymmetric field exerts on the plasma through the drift-kinetic response of its particles. +It implements the PENTRC formulation of [Park et al. (2009)](citations.md#KineticForces), [Logan et al. (2013)](citations.md#KineticForces) and [Park and Logan (2017)](citations.md#KineticForces). + +## What it computes + +A non-axisymmetric perturbation ``\delta B`` breaks the toroidal symmetry that conserves the canonical angular momentum of each particle. +The resulting non-ambipolar radial transport, dominated by trapped particles whose bounce and precession motion resonates with the E×B rotation, carries toroidal momentum out of the plasma: the NTV torque ``T_\phi``, which brakes or spins up the rotation, together with a kinetic contribution ``\delta W_k`` to the perturbed energy that stabilizes or destabilizes the response. +Both are halves of one complex quantity, ``T = T_\phi + 2in\,\delta W_k``, and the module integrates it over the plasma for one or more torque `method`s. + +The torque enters the pipeline in two ways: + +| Workflow | Deck | What it gives | +|---|---|---| +| Torque of a perturbed equilibrium | `[KineticForces]` after `[PerturbedEquilibrium]` | ``T(\psi)`` integrated over the stored response; the stage of the [Workflow](workflow.md) | +| Self-consistent kinetic solve | `[ForceFreeStates] kinetic = "KineticForces"` | the drift-kinetic energy matrices replace the ideal ones inside the stability solve, so the response itself carries the torque (see [Stability](stability.md) and `TorqueResponse/` on the [Perturbed Equilibrium](perturbed_equilibrium.md) page) | + +Both need the kinetic profiles of `[Equilibrium] kinetic_file` (n_e, T_i, T_e, ω_E, and n_i when it differs from n_e) and the ion composition of `[[Equilibrium.ion_species]]`; the file schema is on the [Equilibrium](equilibrium.md) page. + +## Governing equations + +The torque is a sum over species of three nested integrals, over flux ``\psi``, over the pitch variable ``\Lambda`` separating trapped from passing particles and over the normalized energy ``x = \varepsilon/T``, with a sum over the bounce harmonics ``\ell`` up to `bounce_harmonic_max`. +The integrand is the bounce-averaged perturbation of the field strength ``\langle \delta B_\parallel / B \rangle_\ell``, evaluated from the displacement ``\xi`` of the perturbed equilibrium, weighted by the Maxwellian and by the resonance factor ``1/(i\Omega(x) - \nu)``: the energy integral is Eq. 8 of [Logan et al. (2013)](citations.md#KineticForces) and the torque and its normalization Eq. 19, ``\operatorname{Im} T = 2n\,\delta W_k``. +The resonance frequency ``\Omega(x)``, and the diamagnetic frequencies that relate the E×B rotation ``\omega_E`` of the kinetic file to the toroidal rotation, are given with their sign conventions under [KineticForces rotation conventions](conventions.md#KineticForces-rotation-conventions). +The collision operator is selected by `nutype`: `"harmonic"` (the default), `"krook"`, `"small"` or `"zero"` (the collisionless limit), with the collision frequencies computed from the density and temperature profiles; `f0type` selects the equilibrium distribution, `"maxwellian"` (the default), `"park"` or `"cgl"`. +The kinetic file may also supply the Coulomb logarithm and collision rates `lnLambda`, `nu_i` and `nu_e`, which then replace the collision model of the NTV calculation (Logan et al. (2013), Eq. 6) that otherwise computes them from ``n`` and ``T``. +Each is optional and must be positive; they are interpolated, and extended past the table's ψ range, in log space, so supply them over the whole range. +A supplied `nu_i` is the first main ion's rate, and the other ions and the impurity keep the model's ``(Z_s/Z_1)^2\sqrt{m_1/m_s}`` ratio to it. +They feed only the NTV calculation (the tearing layers' resistivity keeps its `lnLambda_form` closure), the density and temperature scaling factors do not rescale them, and `collisionality_factor` still multiplies them. + +### Torque methods + +Each `method` is one evaluation of the same integral under a different model, switched on by its `_flag` in `[KineticForces]`; the first letter selects the particle population, `f` full, `t` trapped only, `p` passing only. + +| Method | Model | +|---|---| +| `fgar` (default), `tgar`, `pgar` | general-aspect-ratio calculation of the torque and energy | +| `fcgl` | fluid Chew–Goldberger–Low limit | +| `fwmm`, `twmm`, `pwmm` | energy from the Euler–Lagrange kinetic matrix | +| `ftmm`, `ttmm`, `ptmm` | torque from the Euler–Lagrange kinetic matrix | +| `fkmm`, `tkmm`, `pkmm` | norm of the kinetic energy matrix | +| `frmm`, `trmm`, `prmm` | norm of the kinetic torque matrix | + +## Running it + +From `examples/DIIID-like_ideal_example/gpec.toml`, after the `[PerturbedEquilibrium]` section (`examples/TJ_kinetic_NTV_example` is the same stage on an analytic equilibrium): + +```toml +[KineticForces] +bounce_harmonic_max = 6 # bounce harmonics summed over ℓ = -max:max +electron = false # add the electron NTV to the ion species +nutype = "harmonic" # collision operator: "zero", "small", "krook", "harmonic" +f0type = "maxwellian" # distribution function: "maxwellian", "park", "cgl" +atol_pitch = 1e-9 # absolute tolerance of the pitch integration +axis_validity_suppression = true # suppress the near-axis terms where the model is not valid +``` + +In a script the stage is one call on the perturbed equilibrium: + +```julia +using GeneralizedPerturbedEquilibrium + +kinetic = kinetic_torque(pe, KineticForcesControl(; bounce_harmonic_max=6, nutype="harmonic")) +``` + +The torque is three nested adaptive Gauss–Kronrod integrals, and each inner tolerance is derived from the outer one unless set: `rtol_psi` (1e-2, the primary knob; two significant figures match the model's validity), `rtol_pitch` (`psi_tolerance_margin × rtol_psi`) and `rtol_x` (`nested_tolerance_margin × rtol_pitch`). +Tightening `rtol_psi` tightens the whole ladder; tightening an inner level alone only multiplies evaluations. +The kinetic matrix build of a `kinetic = "KineticForces"` solve has no ψ quadrature and uses `rtol_pitch` (1e-3 when unset). + +### Profile scaling + +Seven factors on `KineticForcesControl` scale the kinetic inputs for sensitivity studies. +`density_factor`, `temperature_factor`, `ExB_rotation_factor` and `toroidal_rotation_factor` act on the profiles before any physics evaluation: the diamagnetic frequencies are formed from the unscaled profiles, ``\omega_E`` is rebuilt from the scaled total rotation ``\omega_\phi`` minus the scaled diamagnetic terms and then multiplied by `ExB_rotation_factor`, and the collision frequencies are recomputed from the scaled density and temperature. +`magnetic_drift_factor`, `collisionality_factor` and `divxi_factor` multiply the magnetic drift, the collisionality and ``\nabla\cdot\xi_\perp`` at evaluation time; use `collisionality_factor` to scale the collisionality alone. +The four profile factors are not supported together with a multi-ion `ion_species` list; the three evaluation-time factors are. + +### Multi-ion runs + +With several main-ion species the NTV is computed per species under one shared ``Z_\mathrm{eff}`` and summed over the main ions, the quasineutrality-closing impurity and, with `electron = true`, the electrons (`electron` always means in addition to the ions). +The sum is written to `KineticForces//` as in a single-ion run and each species to `KineticForces/PerSpecies/