Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 3 additions & 1 deletion docs/src/developer_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ a comparison, and the exit-status contract are documented in

### Precompile workload

Building the package runs a small Solovev case through the solver so that a fresh Julia process
Building the package runs two small cases through every stage of a full run (a Solovev case through
the Riccati, forward and Galerkin stability solves, and a TJ-analytic case with kinetic profiles and
coil forcing through the perturbed equilibrium and the kinetic torque) 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
Expand Down
71 changes: 55 additions & 16 deletions src/Precompile.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
# Precompile workload: run a small Solovev case through the Riccati and forward formalisms at
# build time so a fresh process skips most first-call compilation. Disable with the
# PrecompileTools preference `precompile_workload = false` (see docs/src/set_up.md).
# Precompile workload: run small cases through each stage at build time so a fresh process skips most
# first-call compilation. A Solovev case covers the Riccati, forward and Galerkin formalisms; a TJ-analytic
# case with kinetic profiles and coil forcing covers the perturbed equilibrium and the kinetic torque.
# Disable with the PrecompileTools preference `precompile_workload = false` (see docs/src/set_up.md).
using PrecompileTools: @setup_workload, @compile_workload
import Logging

@setup_workload begin
# Run one deck silently in a scratch directory; `files` are written beside it first.
function run_workload(inputs; files=())
mktempdir() do workdir
foreach(((name, text),) -> write(joinpath(workdir, name), text), files)
open(io -> TOML.print(io, inputs), joinpath(workdir, "gpec.toml"), "w")
Logging.with_logger(Logging.NullLogger()) do
redirect_stdio(; stdout=devnull) do
return main([workdir])
end
end
end
end

# examples/Solovev_ideal_example, coarsened in mpsi and mtheta_vacuum to keep the build short.
inputs = Dict{String,Any}(
stability = Dict{String,Any}(
"Equilibrium" => Dict{String,Any}(
"jac_type" => "pest", "grid_type" => "core_edge_packed", "psilow" => 1e-4, "psihigh" => 0.9995,
"mpsi" => 16, "mtheta" => 256, "newq0" => 0, "flux_surface_rtol" => 1e-7
Expand All @@ -18,20 +32,45 @@ import Logging
),
"Solovev" => Dict{String,Any}("mr" => 128, "mz" => 128, "ma" => 128, "elongation" => 1.6, "a" => 0.33, "r0" => 1.0, "q0" => 1.9)
)

# examples/TJ_kinetic_NTV_example, coarsened, at a DIII-D-like size so the DIII-D C-coil forcing applies, and on the
# direct solver, so the inverse-solver path stays compiled on first use as before.
kinetic_table = join(("$(x) 1.2e19 1.2e19 $(20 + 180 * (1 - x)) $(20 + 180 * (1 - x)) $(2e4 * (1 - x))" for x in range(0, 1; length=12)), "\n")
response = Dict{String,Any}(
"Equilibrium" => Dict{String,Any}(
"kinetic_file" => "kinetic.dat", "jac_type" => "hamada", "grid_type" => "core_edge_packed",
"psilow" => 0.01, "psihigh" => 0.99, "mpsi" => 16, "mtheta" => 64
),
"TJAnalytic" => Dict{String,Any}(
"r0" => 1.7, "a" => 0.17, "q0" => 1.6, "qa" => 3.2, "p0" => 0.1, "pressure_peaking" => 2.0, "B0" => 1.0,
"ma" => 32, "mtheta" => 64, "solver" => "direct"
),
"ForceFreeStates" => Dict{String,Any}(
"vac_flag" => true, "qlow" => 1.02, "qhigh" => 1e3, "nn" => 1, "delta_mlow" => 4, "delta_mhigh" => 4,
"mtheta_vacuum" => 64, "singfac_min" => 1e-4, "integrator" => "Forward", "truncate_at_rational_offset" => false
),
"Forward" => Dict{String,Any}("eulerlagrange_tolerance" => 1e-6),
"ForcingTerms" => Dict{String,Any}(
"forcing_data_format" => "coil", "machine" => "d3d", "mtheta_coil" => 32, "nzeta_coil" => 8,
"coil_set" => [Dict{String,Any}("name" => "c", "currents" => [1000.0, 500.0, -500.0, -1000.0, -500.0, 500.0])]
),
"PerturbedEquilibrium" => Dict{String,Any}("compute_singular_coupling" => true, "regularization_width" => 0.05),
"KineticForces" => Dict{String,Any}(
"bounce_harmonic_max" => 1, "electron" => false, "nutype" => "harmonic", "f0type" => "maxwellian",
"atol_pitch" => 1e-4, "axis_validity_suppression" => true
)
)

@compile_workload begin
for formalism in ("Riccati", "Forward")
inputs["ForceFreeStates"]["integrator"] = formalism
foreach(name -> delete!(inputs, name), ("Riccati", "Forward"))
inputs[formalism] = Dict{String,Any}("renorm_threshold" => 1e3, "eulerlagrange_tolerance" => 1e-7)
mktempdir() do workdir
open(io -> TOML.print(io, inputs), joinpath(workdir, "gpec.toml"), "w")
Logging.with_logger(Logging.NullLogger()) do
redirect_stdio(; stdout=devnull) do
return main([workdir])
end
end
end
for formalism in ("Riccati", "Forward", "Galerkin")
stability["ForceFreeStates"]["integrator"] = formalism
foreach(name -> delete!(stability, name), ("Riccati", "Forward", "Galerkin"))
stability[formalism] =
formalism == "Galerkin" ? Dict{String,Any}("nx" => 32, "rpec_flag" => true) :
Dict{String,Any}("renorm_threshold" => 1e3, "eulerlagrange_tolerance" => 1e-7)
run_workload(stability)
end
run_workload(response; files=("kinetic.dat" => kinetic_table,))
end
# Start each session with empty caches, as a JIT build does, instead of the workload's run state.
Vacuum.reset_caches!()
Expand Down
Loading