- Ruff for format and lint (
pyproject.toml); import order follows Ruff isort rules. - Type hints on all public functions and methods.
- NumPy-style docstrings where non-trivial:
Parameters,Returns,Raisesas needed. - Size targets (ARCHITECTURE): soft cap ~400 lines per module / ~80 per public function; hard caps ~600 / 150. Split modules when approaching limits.
See ARCHITECTURE.md §2.8. In short:
- Pydantic v2 only in
core/io.pyat the YAML boundary (load_run_config/save_run_config). - Dataclasses for runtime objects (
Geometry,SimParams,SimState, …) so JAX pytrees stay simple and hot loops avoid per-step validation.
- Add
def my_stress(*, L: float, n: int, sigma_0: float, ...) -> tuple[Array, Array, Array]:incore/stress.py(same return layout asnone). STRESS_BUILDERS["my_stress"] = my_stress.- Append
"my_stress"toStressSpec.mode’sLiteral[...]incore/io.py. - Add or copy a template YAML with
stress: { mode: my_stress, ... }. - Add a small unit test under
tests/unit/if the mode has non-trivial kwargs.
- Add
def my_masks(*, L, R, n, ...) -> dict[str, Array | float | int]:incore/masks.py(same keys ascircular_cavity_masks). Reuse or extendcore/_geometry_helpers.pyfor shared distance / grid utilities when useful. MASK_BUILDERS["my_geometry"] = my_masks(key must matchgeometry.type).- Extend
GeometrySpec.typeand_geometry_required_fieldsincore/io.py. - Template YAML under
experiments/canonical/; document the shape briefly indocs/PHYSICS.md§6.3 if it is a new family.
- Extend
PhasesSpec/ YAML underphysics.phases(optional keys mirroralpha_quartz/impurity). - Add matching defaults on
SimParams(phi_q_potential,phi_imp_potential) incore/imex.pyif the new slot needs non-trivial defaults. - Ensure
models.*.build_sim_paramsmaps YAML →PhasePotentialParams(phase_potential_params_from_specpattern). - If the phase participates in ψ-stress, set
psi_sign; useactive: falsefor placeholders. - Update
save_final_state_npz/ snapshot writers if the field must appear in artifacts.
- Add
def my_potential_prime(phi: Array, *, …) -> Arrayincore/potentials.py(same contract as existing*_primehelpers: returns∂f/∂φ, shape/dtype matchphi). - Register it:
POTENTIAL_BUILDERS["my_potential"] = my_potential_prime. - Extend
PhaseSpec.potential’sLiteral[...]incore/io.pyand handle any new kwargs inmodels/cavity_reactive.pyphase_potential_params_from_spec(mappotential_kwargsintoPhasePotentialParamsfields) if the potential needs parameters beyond the existing slots.
- Stage I (cavity / rim):
core/diagnostics_stage1.py— NumPy (and SciPy) only, no JAX. - Stage II (bulk):
core/diagnostics_stage2.py— same rule. - Prefer keys that do not collide with the other stage’s primary
summary.jsonheadlines (ARCHITECTURE §2.3).
- New module under
src/continuous_patterns/models/, thin composition ofcore/+imex_step+ the right diagnostics module. - Register
simulateinexperiments/run.pyMODEL_DISPATCH. - Add
experiment.modeltoExperimentSpec.modelLiteralincore/io.py. - One integration smoke test (
tests/integration/) with smallnand shortT.
- Unit:
tests/unit/— fast, CPU, isolated modules (target a few seconds total for the default suite). - Integration: short end-to-end runs per model driver and CLI paths.
- Regression: reserved for Phase 4 (bitwise or metric comparisons to archived results); not CI-gated yet.
Progress / logs in tests: pass show_progress=False to simulate / run_one so tqdm stays off. run_one with write_artifacts=True writes run.log under the run root (ResultPaths.log_file) — assert on file contents or attach a logging handler to the continuous_patterns logger if you need in-memory capture (the package logger uses propagate=False once configured by run_one).
RunConfigValidated.physics: still mostly adict[str, Any]; the nestedphysics.phasesblock is validated viaPhasesSpec/PhaseSpec. A full typedPhysicsSpecperexperiment.modelis deferred until validation pain justifies it.RunConfigValidated.initial: optional IC block, interpreted by eachmodels.*.build_initial_state.
- Cavity CH update
(1−χ)φ + χ·φ_newis not a mass-conserving projection on the full torus; cavity-weighted silica integrals are the intended accounting (PHYSICS §4, ARCHITECTURE §3.4). - Hard clip on
φis a numerical guardrail; rim flux diagnostics (Option B) monitor consistency with diffusion, not variational exactness of the clip. - IMEX split: biharmonic part implicit, nonlinear + reaction + stress explicit; stability is empirical for the calibrated σ ranges (PHYSICS §9).