Contributing
MILEVA is small enough that one person can hold all of it, and it stays that way if every change leaves the code with one way to do each thing and a test that shows the thing is done right. The conventions below keep it that way.
The path of a change. The suite is run three times because a solver with a non-deterministic iteration can pass by luck; any red or flaky result, or a new test that also passes on the old code, sends the change back.
One live path, no cruft
Keep one path live at a time. When an alternative is tried and loses (a smoother, a coupling scheme, a solver variant), delete it rather than leaving it behind a flag or as a half-wired scaffold. A few research options remain available behind flags: the bicubic Yin-Yang sampler (
_USE_BICUBIC_YY[]), the φ-buffer (MILEVA_PHI_BUFFER=1), the minimum-overlap grid (cfg.useMinOverlapGrid), theMILEVA_NO_MDPItoggle and the alternative operator forms (cfg.useDeviatoricStress = false,cfg.useCurvatureTerms = false); see Yin-Yang grid and Discretization. If the result of an attempt that was dropped is worth keeping, record it in the commit message or the comment of the code that was kept, with the numbers.No unused configuration. A
SimConfigfield that nothing reads misleads the user who sets it. If a feature is declared but not implemented,validate!refuses it with a message that says so (as it does forcfg.useAMRandcfg.useDruckerPrager), andtests/test_config_matrix.jlchecks that refusal.No silent no-ops. A function that cannot do its job on a given branch throws, naming what to use instead.
New physics defaults to off.
tests/test_config_matrix.jlasserts that no major physics option is on by default, so a new flag does not change every existing run.Project-specific physics goes through the hooks, not new fields in
SimConfig:initialMarkerHook,markerStepHookandrestartHooklet a driver paint an initial composition, keep per-parcel bookkeeping or branch from a restored state without the generic loop knowing about it.
Every operator change keeps the identities green
Any change to solvers/stokes_assembly.jl, solvers/stokes_assembly_3d.jl, solvers/diffuse_temperature.jl, utils/compute_strain_rate.jl, utils/compute_diagnostics.jl or the marker transport must keep these tests passing, unchanged:
| Test | What it guards |
|---|---|
work equals dissipation, tests/test_annulus_operator.jl and tests/test_stokes_3d_variable_eta.jl | the momentum operator is |
rigid rotation is an exact null mode, tests/test_annulus_operator.jl | the curvature terms of the spherical operator |
Nu = 1 on a conducting state and Nu_top == Nu_bot, tests/test_nusselt_conduction.jl | the wall flux is evaluated where the boundary condition is held |
discrete energy budget closes, tests/test_nusselt_conduction.jl | the Dirichlet walls neither add nor remove heat outside the reported fluxes |
one marker step conserves energy, tests/test_marker_energy.jl | the marker hand-back delivers exactly the energy the grid solver computed |
Do not loosen a tolerance in these files to make a change pass. If a change moves one of them, find out why before going further.
More rules for operators:
Use the local cell width. On a stretched radial grid
grid.dris the smallest cell width; usegrid.drVec[i]andgrid.drFVec[i].Keep the 3-D assemblies in step.
solvers/stokes_assembly_3d.jlandsolvers/stokes_assembly_3d_local.jlare structural mirrors; a term added to one goes into the other, andtests/test_assembly_3d_mirror.jlmust stay green.Terms that must cancel are switched together. The adiabatic sink and the viscous-dissipation source of the compressible energy equation are both gated by
cfg.useShearHeating; carrying one without the other injects or drains energy the model does not have. The same logic applies to any pair of terms that are only consistent jointly.
Check that a new test can fail
A new test should be shown to fail on the code it protects against: revert the change locally, run the test, and see it fail. See Testing for how to choose a property that responds to what you want to check.
Geometry, resolution and solvers
2-D means the spherical annulus. Examples, tests of production physics and benchmarks use
cfg.geometry = "spherical_annulus"(the default). The quantitative references for the 2-D path are spherical-annulus ones: Hernlund and Tackley (2008) and the annulus column of Fleury et al. (2024).Science resolution starts at Nr = 64. Coarser grids are for smoke tests and identity tests, and are labelled as such wherever their output appears.
3-D Stokes is multigrid.
cfg.solverType = "multigrid"andcfg.mgLevels ≥ 2, withNrandNtthat halve cleanly; a 3-D direct LU needs more memory than a laptop has.Changing a default changes results. Switching a solver default, a pressure pin or an advection scheme can shift answers by several per cent (the dense and point pressure pins differed by 7 per cent in velocity on a production field). Make it a deliberate, measured change with the effect recorded.
Reporting results
Report what was measured, with its evidence: solver residuals, Picard sweep counts (
picard_n,picard_rel),budget_resid, Nu_top against Nu_bot. A step whose nonlinear iteration hit its cap is not a converged solution.Comparisons with Hernlund and Tackley (2008) are verification against published results, whose values come from one 32 × 256 grid. State the seeded planform (
cfg.m_wavenumber) with any V_rms, because several steady planforms exist and they differ in V_rms.Do not extrapolate a quantity whose convergence order you have not measured.
Interfaces that others depend on
diagnostics.csv: the column order inDIAG_CSV_COLUMNS(utils/diagnostics_csv.jl) is fixed and post-processing keys on the header names. Append new columns at the end; writeNaNfor an undefined value, never 0.Markers: every per-marker vector is listed in
MARKER_VECTOR_FIELDS. A new marker field goes into the struct, that tuple, the positional constructors,init_markers, reseeding (with an inheritance rule) and the checkpoint, or the marker set desynchronises.Checkpoints: new persistent state must be saved by
save_checkpointand restored in the resume block ofmain_SphCon.jl, and a resumed run must reproduce a continuous one.
Code style
Julia only.
Comments explain why, and carry the measurement that justified a choice (the number, the grid, the date). Cite the method source where an algorithm is taken from the literature (for example Kageyama and Sato 2004 for the Yin-Yang grid, Tackley 2008 for the Yin-Yang Stokes solver, Trottenberg et al. for multigrid).
Keep the include order of
include/mileva.jlacyclic: a file may use only what is defined above it.Build the documentation with
bin/mileva docsand update the page that describes any behaviour you change.