Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ce6d0a6
ForceFreeStates - NEW FEATURE - Dual Riccati reformulation of EL ODE …
logan-nc Feb 28, 2026
0385e7f
ForceFreeStates - NEW FEATURE - Parallel FM integration + Δ' output
logan-nc Feb 28, 2026
1d2a863
ForceFreeStates - IMPROVEMENT - Fix Δ' computation and add Riccati/pa…
logan-nc Mar 1, 2026
11f394b
ForceFreeStates - CLEANUP - Correct explanation for why standard path…
logan-nc Mar 1, 2026
0ca20e2
ForceFreeStates - IMPROVEMENT - Off-diagonal Δ' column + parallel FM …
logan-nc Mar 1, 2026
5a7b756
ForceFreeStates - NEW FEATURE - STRIDE global BVP inter-surface Δ' ma…
logan-nc Mar 1, 2026
af7f359
ForceFreeStates - NEW FEATURE - Bidirectional parallel FM integration…
logan-nc Mar 2, 2026
9961fbd
ForceFreeStates - NEW FEATURE - Thread-scaling benchmark script
logan-nc Mar 8, 2026
7bb3942
ForceFreeStates - BUG FIX - Address Claude code review of perf/riccati
logan-nc Mar 8, 2026
88448fc
ForceFreeStates - NEW FEATURE - Sanity-check benchmarks for riccati_d…
logan-nc Mar 8, 2026
86b60a2
ForceFreeStates - CLEANUP - Clarify Riccati integration strategy docs…
logan-nc Mar 9, 2026
cb4c2bf
ForceFreeStates - IMPROVEMENT - Refactor runtests_riccati.jl: shared …
logan-nc Mar 9, 2026
c7dfa41
ForceFreeStates - IMPROVEMENT - Remove dead parallel_threads field; a…
logan-nc Mar 9, 2026
2eb3c26
ForceFreeStates - MERGE - Merge origin/develop (GPEC rename) into per…
logan-nc Mar 9, 2026
2f494c9
ForceFreeStates - CLEANUP - Update JPEC→GeneralizedPerturbedEquilibri…
logan-nc Mar 9, 2026
142a79c
ForceFreeStates - NEW FEATURE - Add stability analysis documentation …
logan-nc Mar 9, 2026
1515591
ForceFreeStates - BUG FIX - Fix CI failures (Random stdlib + docs mar…
logan-nc Mar 9, 2026
725a527
ForceFreeStates - IMPROVEMENT - Thread safety, psilim guard, consiste…
logan-nc Mar 9, 2026
a63dc69
Merge develop into perf/riccati
matt-pharr Apr 8, 2026
6431392
TESTING - FIX - fixed failing tests post merge
matt-pharr Apr 8, 2026
0d72584
TESTING - FIX - Re-add comments to gpec.toml accidentally removed in …
matt-pharr Apr 8, 2026
dc2b44b
BENCH - NEW - integration paths benchmark script
matt-pharr Apr 8, 2026
8dc4dc4
Merge remote-tracking branch 'origin/develop' into perf/riccati
matt-pharr Apr 8, 2026
290cfc5
EQUIL - NEW FEATURE - TJ analytic model (tj_run inverse + tj_run_direct)
d-burg Apr 18, 2026
5be4c98
ForceFreeStates - NEW FEATURE - Parallel FM Δ' BVP with inter-surface…
d-burg Apr 18, 2026
97a6826
BENCH - NEW - TJ pole-approach scans, regression case, and unit tests
d-burg Apr 18, 2026
d67cabd
CLEANUP - Drop unused deps, fix stale comments and docs
d-burg Apr 18, 2026
b9c177e
CLEANUP - Drop brittle Fortran/TJ source line-number citations from c…
d-burg Apr 18, 2026
57db4e7
Merge origin/develop into perf/riccati
d-burg Apr 18, 2026
5d5b8ee
ForceFreeStates - NEW FEATURE - Decouple edge-dW scan from integratio…
d-burg Apr 18, 2026
685a92a
EXAMPLES - BUG FIX - Remove stale kin_flag/con_flag from LAR/TJ scan …
d-burg Apr 18, 2026
defcec8
CI - BUG FIX - Restore Random stdlib dep and refresh test regression …
d-burg Apr 19, 2026
1dfc3ae
CI - BUG FIX - Loosen Solovev Δ'(surface 2) regression test
d-burg Apr 19, 2026
c6c845f
CI - BUG FIX - Handle maxthreadid() and decouple DIIID cross-path test
d-burg Apr 19, 2026
54d12fe
ForceFreeStates - NEW FEATURE - Port set_psilim_via_dmlim + default t…
d-burg Apr 20, 2026
4845ec8
ForceFreeStates - IMPROVEMENT - Default use_parallel=true, singfac_mi…
d-burg Apr 20, 2026
db7c490
ForceFreeStates - BUG FIX - Wire ctrl.parallel_threads into BVP path;…
d-burg Apr 28, 2026
7ac87c8
ForceFreeStates - PERFORMANCE - parallel_threads default 1 → 2 (≈20% …
d-burg Apr 28, 2026
3c8130d
ForceFreeStates - BUG FIX + EXAMPLES - truncate_at_dW_peak self-consi…
d-burg May 13, 2026
5acf147
ForceFreeStates - NEW FEATURE - Dense ξ in parallel BVP path + bit-id…
d-burg May 14, 2026
6d07c07
EQUIL - REFACTOR - Rename TJ → TJ-like with Fitzpatrick citation ever…
d-burg May 14, 2026
085de13
EXAMPLES - CLEANUP - LAR scans: single-file gpec.toml, per-line annot…
d-burg May 14, 2026
5a4c2c2
EXAMPLES - CLEANUP - Remove TJ_epsilon_pole_example and its regressio…
d-burg May 14, 2026
cc2affe
EQUIL - REFACTOR - Rename tj_like → tj_analytic (cleaner, less hedge-y)
d-burg May 14, 2026
8073c12
ForceFreeStates - BUG FIX - Preserve Riccati-gauge ca_l/ca_r across d…
d-burg May 14, 2026
3653a15
DOCS - CLEANUP - Move stride dev notes out of repo to CTM-processing
d-burg May 21, 2026
8bbe836
ForceFreeStates - CLEANUP - Address pre-merge review items
d-burg May 21, 2026
7efb15d
ForceFreeStates - TEST - Tighten Solovev kinetic multi-n rtol (drift …
d-burg May 21, 2026
c6c379c
ForceFreeStates - REFACTOR - Address PR 178 review: flag surfacing + …
d-burg May 21, 2026
0296956
ForceFreeStates / PerturbedEquilibrium - REFACTOR - De-emphasize per-…
d-burg May 21, 2026
a89bd5d
ForceFreeStates - CLEANUP - Pre-merge audit response (H1-H5, D1-D3, V…
d-burg May 22, 2026
ace5e21
Merge remote-tracking branch 'origin/develop' into perf/riccati
d-burg May 22, 2026
4fbd882
ForceFreeStates - CLEANUP - Pre-merge audit fixes (B1 thread-safety, …
d-burg May 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
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"
Expand Down Expand Up @@ -38,6 +39,7 @@ Contour = "0.6.3"
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"
Expand All @@ -60,3 +62,9 @@ Statistics = "1"
TOML = "1"
Test = "1"
julia = "1.11"

[extras]
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[targets]
test = ["Random"]
95 changes: 95 additions & 0 deletions benchmarks/benchmark_delta_prime_methods.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Sanity check: compute_delta_prime_from_ca! vs inline Δ' from riccati_cross_ideal_singular_surf!
#
# riccati_cross_ideal_singular_surf! computes Δ' inline at each singular surface crossing
# using the diagonal formula (no Gaussian reduction permutation):
# Δ'[s] = (ca_r[ipert_res, ipert_res, 2, s] - ca_l[ipert_res, ipert_res, 2, s]) / (4π²·ψ₀)
#
# compute_delta_prime_from_ca! applies the identical formula post-hoc from the stored
# ca_l/ca_r arrays. Since both operate on the same data with the same formula, results
# should match to floating-point precision (not just approximately — exactly).
#
# This verifies that compute_delta_prime_from_ca! is a correct standalone implementation
# of the Δ' formula that can be used for testing or alternative integration drivers.
#
# Usage (from JPEC_main root):
# julia --project=. benchmarks/benchmark_delta_prime_methods.jl

using LinearAlgebra, Printf, TOML
using GeneralizedPerturbedEquilibrium

const FFS = GeneralizedPerturbedEquilibrium.ForceFreeStates

function setup_and_run_solovev()
ex = joinpath(@__DIR__, "..", "test", "test_data", "regression_solovev_ideal_example")
inputs = TOML.parsefile(joinpath(ex, "gpec.toml"))
inputs["ForceFreeStates"]["verbose"] = false
inputs["ForceFreeStates"]["use_riccati"] = true
intr = FFS.ForceFreeStatesInternal(; dir_path=ex)
ctrl = FFS.ForceFreeStatesControl(;
(Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...)
eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(inputs["Equilibrium"], ex)
equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config)
intr.wall_settings = GeneralizedPerturbedEquilibrium.Vacuum.WallShapeSettings(;
(Symbol(k) => v for (k, v) in inputs["Wall"])...)
FFS.sing_lim!(intr, ctrl, equil)
intr.nlow = ctrl.nn_low; intr.nhigh = ctrl.nn_high; intr.npert = 1
FFS.sing_find!(intr, equil)
intr.mlow = min(intr.nlow * equil.params.qmin, 0) - 4 - ctrl.delta_mlow
intr.mhigh = trunc(Int, intr.nhigh * equil.params.qmax) + ctrl.delta_mhigh
intr.mpert = intr.mhigh - intr.mlow + 1
intr.mband = intr.mpert - 1
intr.numpert_total = intr.mpert * intr.npert
metric = FFS.make_metric(equil; mband=intr.mband, fft_flag=ctrl.fft_flag)
ffit = FFS.make_matrix(equil, intr, metric)
odet = FFS.riccati_eulerlagrange_integration(ctrl, equil, ffit, intr)
return ctrl, equil, ffit, intr, odet
end

println("\n=== compute_delta_prime_from_ca! consistency check ===")
println("Verifies the standalone Δ' formula matches the inline Riccati crossing computation.")
println("Expected error: exactly zero (same formula, same data).\n")

ctrl, equil, ffit, intr, odet = setup_and_run_solovev()
msing = intr.msing

# Capture Δ' values set inline by riccati_cross_ideal_singular_surf! during integration
delta_prime_inline = [copy(intr.sing[s].delta_prime) for s in 1:msing]

# Now call compute_delta_prime_from_ca! — it reads the same ca_l/ca_r arrays and
# overwrites intr.sing[s].delta_prime using the identical diagonal formula
FFS.compute_delta_prime_from_ca!(odet, intr, equil)

println(" N=$(intr.numpert_total) modes, $msing singular surfaces\n")
@printf(" %6s %4s %4s %22s %22s %12s\n",
"Surf", "m", "n", "Δ' (inline)", "Δ' (from_ca)", "abs diff")
println(" " * "-"^76)

max_absdiff = let max_absdiff = 0.0
for s in 1:msing
sing = intr.sing[s]
dp_from_ca = intr.sing[s].delta_prime
for i in eachindex(delta_prime_inline[s])
dp_il = delta_prime_inline[s][i]
dp_fc = dp_from_ca[i]
absdiff = abs(dp_fc - dp_il)
max_absdiff = max(max_absdiff, absdiff)
@printf(" %6d %4d %4d %22.6f%+.6fi %22.6f%+.6fi %12.4e\n",
s, sing.m[i], sing.n[i],
real(dp_il), imag(dp_il),
real(dp_fc), imag(dp_fc),
absdiff)
end
end
max_absdiff
end

println()
if max_absdiff == 0.0
println("PASSED — Δ' values are bit-for-bit identical (max abs diff = 0.0)")
elseif max_absdiff < 1e-14
@printf("PASSED — max abs diff = %.2e (floating-point rounding only)\n", max_absdiff)
else
@printf("FAILED — max abs diff = %.2e (expected exact agreement)\n", max_absdiff)
exit(1)
end
println()
131 changes: 131 additions & 0 deletions benchmarks/benchmark_riccati_der.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# 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 JPEC_main root):
# julia --project=. benchmarks/benchmark_riccati_der.jl

using LinearAlgebra, Random, Printf, TOML
using GeneralizedPerturbedEquilibrium

const FFS = GeneralizedPerturbedEquilibrium.ForceFreeStates

function setup_solovev()
ex = joinpath(@__DIR__, "..", "test", "test_data", "regression_solovev_ideal_example")
inputs = TOML.parsefile(joinpath(ex, "gpec.toml"))
inputs["ForceFreeStates"]["verbose"] = false
intr = FFS.ForceFreeStatesInternal(; dir_path=ex)
ctrl = FFS.ForceFreeStatesControl(;
(Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...)
eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(inputs["Equilibrium"], ex)
equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config)
intr.wall_settings = GeneralizedPerturbedEquilibrium.Vacuum.WallShapeSettings(;
(Symbol(k) => v for (k, v) in inputs["Wall"])...)
FFS.sing_lim!(intr, ctrl, equil)
intr.nlow = ctrl.nn_low; intr.nhigh = ctrl.nn_high; intr.npert = 1
FFS.sing_find!(intr, equil)
intr.mlow = min(intr.nlow * equil.params.qmin, 0) - 4 - ctrl.delta_mlow
intr.mhigh = trunc(Int, intr.nhigh * equil.params.qmax) + ctrl.delta_mhigh
intr.mpert = intr.mhigh - intr.mlow + 1
intr.mband = intr.mpert - 1
intr.numpert_total = intr.mpert * intr.npert
metric = FFS.make_metric(equil; mband=intr.mband, fft_flag=ctrl.fft_flag)
ffit = FFS.make_matrix(equil, intr, metric)
return ctrl, equil, ffit, intr
end

# Evaluate the Riccati RHS explicitly from splines: dS = w†·F̄⁻¹·w - S·Ḡ·S
function riccati_rhs_manual(S, psi, equil, ffit, intr)
N = intr.numpert_total
L = zeros(ComplexF64, N, N)
Kmat = zeros(ComplexF64, N, N)
Gmat = zeros(ComplexF64, N, N)
ffit.fmats_lower(vec(L), psi; hint=ffit._hint)
ffit.kmats(vec(Kmat), psi; hint=ffit._hint)
ffit.gmats(vec(Gmat), psi; hint=ffit._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, ffit, intr = setup_solovev()
N = intr.numpert_total

odet = FFS.OdeState(N, ctrl.numsteps_init, ctrl.numunorms_init, intr.msing)
FFS.initialize_el_at_axis!(odet, ctrl, 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, ffit, 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, ffit, 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()
76 changes: 76 additions & 0 deletions benchmarks/benchmark_threads.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Thread-scaling benchmark for the bidirectional parallel FM integration.
# Runs the Solovev (N=8) and DIIID-like (N=26) examples with use_parallel=true
# across 1, 2, 4, 8 threads and compares against the serial Riccati path.
#
# Usage (from JPEC_main root):
# for t in 1 2 4 8; do julia -t $t --project=. benchmarks/benchmark_threads.jl; done

using GeneralizedPerturbedEquilibrium, TOML, Printf, Statistics

function run_ffs(ex; use_parallel, use_riccati=false)
inputs = TOML.parsefile(joinpath(ex, "gpec.toml"))
inputs["ForceFreeStates"]["verbose"] = false
inputs["ForceFreeStates"]["use_parallel"] = use_parallel
inputs["ForceFreeStates"]["use_riccati"] = use_riccati
inputs["ForceFreeStates"]["write_outputs_to_HDF5"] = false
intr = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesInternal(; dir_path=ex)
ctrl = GeneralizedPerturbedEquilibrium.ForceFreeStates.ForceFreeStatesControl(;
(Symbol(k) => v for (k, v) in inputs["ForceFreeStates"])...)
eq_config = GeneralizedPerturbedEquilibrium.Equilibrium.EquilibriumConfig(inputs["Equilibrium"], ex)
equil = GeneralizedPerturbedEquilibrium.Equilibrium.setup_equilibrium(eq_config)
intr.wall_settings = GeneralizedPerturbedEquilibrium.Vacuum.WallShapeSettings(;
(Symbol(k) => v for (k, v) in inputs["Wall"])...)
GeneralizedPerturbedEquilibrium.ForceFreeStates.sing_lim!(intr, ctrl, equil)
intr.nlow = ctrl.nn_low; intr.nhigh = ctrl.nn_high; intr.npert = 1
GeneralizedPerturbedEquilibrium.ForceFreeStates.sing_find!(intr, equil)
intr.mlow = min(intr.nlow * equil.params.qmin, 0) - 4 - ctrl.delta_mlow
intr.mhigh = trunc(Int, intr.nhigh * equil.params.qmax) + ctrl.delta_mhigh
intr.mpert = intr.mhigh - intr.mlow + 1
intr.mband = intr.mpert - 1
intr.numpert_total = intr.mpert * intr.npert
metric = GeneralizedPerturbedEquilibrium.ForceFreeStates.make_metric(equil; mband=intr.mband, fft_flag=ctrl.fft_flag)
ffit = GeneralizedPerturbedEquilibrium.ForceFreeStates.make_matrix(equil, intr, metric)
odet, _, _, _ = GeneralizedPerturbedEquilibrium.ForceFreeStates.eulerlagrange_integration(ctrl, equil, ffit, intr)
vac = GeneralizedPerturbedEquilibrium.ForceFreeStates.free_run!(odet, ctrl, equil, ffit, intr)
return real(vac.et[1]), intr.numpert_total
end

function timed_run(ex; use_parallel, use_riccati=false, nwarm=1, nrep=2)
# Warmup
for _ in 1:nwarm
run_ffs(ex; use_parallel, use_riccati)
end
# Timed runs
times = Float64[]
local et1, N
for _ in 1:nrep
t0 = time()
et1, N = run_ffs(ex; use_parallel, use_riccati)
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_std, et_std, N = timed_run(ex; use_parallel=false, use_riccati=false)
t_ric, et_ric, _ = timed_run(ex; use_parallel=false, use_riccati=true)
t_par, et_par, _ = timed_run(ex; use_parallel=true, use_riccati=false)

err_ric = abs(et_ric - et_std) / abs(et_std) * 100
err_par = abs(et_par - et_std) / abs(et_std) * 100

println("$label (N=$N, nthreads=$nthreads)")
@printf(" standard et[1]=%.5f t=%.2fs speedup=1.00×\n", et_std, t_std)
@printf(" riccati et[1]=%.5f t=%.2fs speedup=%.2f× err=%.4f%%\n",
et_ric, t_ric, t_std/t_ric, err_ric)
@printf(" parallel et[1]=%.5f t=%.2fs speedup=%.2f× err=%.4f%%\n",
et_par, t_par, t_std/t_par, err_par)
println()
end
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ makedocs(;
"API Reference" => [
"Vacuum" => "vacuum.md",
"Equilibrium" => "equilibrium.md",
"Stability Analysis" => "stability.md",
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about all these other md files outside of docs/src (just in docs)? What was the intent here?

"Utilities" => "utilities.md",
"Forcing Terms" => "forcing_terms.md",
"Perturbed Equilibrium" => "perturbed_equilibrium.md",
Expand Down
2 changes: 1 addition & 1 deletion docs/src/equilibrium.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,4 @@ println("Built LAR equilibrium with a = ", lorcfg.lar_a)

## See also

- `docs/src/vacuum.md` — coupling between equilibrium and vacuum solvers
- `docs/src/stability.md` — ideal MHD stability analysis built on top of the equilibrium
Loading