Regularization
Regularization is an optional, advanced feature. The core integrator runs unregularized by default; opt in by wrapping the base algorithm in a RegularizedIntegrator and passing it to solve.
When to use it
The Weber potential contains a Coulomb singularity at zero separation. For most simulations with well-separated particles this is not an issue. Enable regularization when:
- Two or more particles are expected to have close encounters (separation approaching zero).
- You observe energy drift or
NaNvalues near close passages without it.
Regularization handles the Coulomb/Kepler singularity only. Weber's velocity-dependent correction is not regularized — the LC and KS transforms apply to the conservative part of the force.
Enabling regularization
prob = HamiltonianProblem(
sys, tspan, q0, p0;
masses = [1.0, 1.0],
charges = [1.0, -1.0],
c = 10.0,
dt = 0.01,
)
alg = RegularizedIntegrator(SymmetricProjectionIntegrator()) # opt in
sol = solve(prob, alg)All keyword arguments below go on the RegularizedIntegrator(...) constructor itself. Its kwargs mirror RegularizationOptions (with enabled = true set implicitly).
Backends
| Backend | Symbol | Dimensions | Method |
|---|---|---|---|
| Levi-Civita (default) | :lifted_pair | 2D only | Lifts the pair to ℝ⁴ fictitious time |
| Adaptive Cartesian | :adaptive_cartesian | 2D and 3D | Cartesian sub-stepping with KS constraint |
For 3D problems, :lifted_pair automatically falls back to :adaptive_cartesian (with an optional warning controlled by warn_on_fallback).
The :adaptive_cartesian backend applies a one-pass KS constraint projection at each close-encounter lift, then runs a Cartesian sub-step. The constraint is not iterated to convergence within the sub-step; max_constraint_violation in the diagnostics tracks the residual. For current 2D-heavy use cases this is sufficient. True iterative 3D KS stepping is deferred to a future release.
# Explicit 3D choice
alg = RegularizedIntegrator(
SymmetricProjectionIntegrator();
backend = :adaptive_cartesian,
)
sol = solve(prob, alg)Activation hysteresis
Regularization switches on when any pair separation drops below r_on and switches off once all separations in the active component exceed r_off. The thresholds are computed from the initial minimum separation by default:
r_on = r_on_factor × min_initial_separation (default factor: 0.15)
r_off = r_off_factor × min_initial_separation (default factor: 0.25)You can override them directly:
alg = RegularizedIntegrator(
SymmetricProjectionIntegrator();
r_on = 0.05,
r_off = 0.10,
)
sol = solve(prob, alg)Chain mode
When three or more particles form a connected close-encounter cluster, regularization falls back to chain mode (adaptive Cartesian substeps for the whole cluster). Chain mode is enabled by default; disable with RegularizedIntegrator(...; chain_enabled = false).
Collision bounce
For head-on (ℓ = 0) collisions between like-charge pairs, a reflection boundary can be applied before each macro-step. This avoids the non-regularizable ℓ ≠ 0 singularity (where particles reach r = 0 at infinite speed).
Collision bounce is a CollisionBounce callback; pass it through the callbacks kwarg of solve (or init):
sol = solve(prob, SymmetricProjectionIntegrator();
callbacks = CollisionBounce(0.02)) # reflect at r < 0.02As a convenience, RegularizedIntegrator also accepts a collision_bounce_radius kwarg and synthesises a matching callback automatically:
alg = RegularizedIntegrator(SymmetricProjectionIntegrator();
collision_bounce_radius = 0.02)
sol = solve(prob, alg)Collision bounce works best without Levi-Civita regularization (the unregularized symplectic integrator keeps energy bounded across the bounce).
Diagnostics
Every HamiltonianSolution carries a regularization::RegularizationDiagnostics field with step counts, backend used, minimum encounter distance, and more.
sol = solve(prob, SymmetricProjectionIntegrator())
d = sol.regularization
println("Backend used: ", d.used_backend)
println("Activation count: ", d.activation_count)
println("Min encounter distance: ", d.min_encounter_distance)Key fields and what they mean:
| Field | Meaning | Worth investigating if… |
|---|---|---|
activation_count | How many times regularization switched on | Very high → roff is too close to ron |
min_encounter_distance | Closest particle approach over the run | Near 0 → singularity risk; consider bounce radius |
max_constraint_violation | Peak KS constraint residual (3D only) | > 1e-8 → reduce dt or max_substeps |
backend_fallback_steps | Steps that used the fallback backend | > 0 when :lifted_pair was requested on a 3D problem |
total_substeps | Total regularization micro-steps taken | Very large → consider wider ron/roff thresholds |
API reference
WeberElectrodynamics.RegularizationOptions — Type
RegularizationOptions(; kwargs...)Configuration for close-encounter regularization.
When two particles approach within r_on, the integrator switches from Cartesian coordinates to a regularized representation (Levi-Civita/KS or adaptive Cartesian substeps) and back once the separation exceeds r_off.
Keywords
enabled=false: Enable regularization globally.r_on=nothing: Absolute activation distance. Ifnothing, computed asr_on_factor × min_initial_separation.r_off=nothing: Absolute deactivation distance. Ifnothing, computed asr_off_factor × min_initial_separation. Must be greater thanr_on.r_on_factor=0.15: Scale factor for automaticr_on(positive).r_off_factor=0.25: Scale factor for automaticr_off(positive).max_substeps=512: Maximum regularized substeps per macro-step.constraint_tolerance=1e-12: Convergence threshold for the projection constraint.g_floor=1e-12: Minimum regularization scale to prevent division by zero.chain_enabled=true: Allow chain regularization for multi-particle close encounters.backend=:lifted_pair: Regularization backend.:lifted_pairuses Levi-Civita/KS (2D only; auto-falls back to:adaptive_cartesianfor 3D).:adaptive_cartesianworks for all dimensions.warn_on_fallback=true: Emit a warning when the backend is automatically changed.collision_bounce_radius=0.0: Reflect pairs that come closer than this distance before each macro-step (0.0 = disabled). Intended for head-on (ℓ=0) collisions.
Fields
See keyword documentation above; each keyword maps directly to a stored field.
WeberElectrodynamics.RegularizationDiagnostics — Type
RegularizationDiagnosticsDiagnostics collected during a solve! call describing regularization usage.
Returned as HamiltonianSolution.regularization. All step counts refer to macro-steps of the outer SymmetricProjectionIntegrator.
Fields
enabled::Bool: Whether regularization was active for this solve.requested_backend::Symbol: Backend requested inRegularizationOptions.used_backend::Symbol: Backend actually used (may differ due to fallback).activation_count::Int: Number of times regularization was switched on.deactivation_count::Int: Number of times regularization was switched off.active_steps::Int: Steps taken while any regularization was active.pair_steps::Int: Steps handled by the pair regularization path.adaptive_pair_steps::Int: Steps handled by:adaptive_cartesian.lifted_pair_steps::Int: Steps handled by:lifted_pair(Levi-Civita/KS).chain_steps::Int: Steps handled by the chain (multi-pair) path.unregularized_steps::Int: Steps taken in plain Cartesian coordinates.backend_fallback_steps::Int: Steps where a fallback backend was used.total_substeps::Int: Total regularized substeps across all macro-steps.max_substeps_used::Int: Largest substep count used in any single macro-step.max_constraint_violation::Float64: Maximum projection constraint residual observed.min_encounter_distance::Float64: Minimum pairwise separation seen during the solve.mode_history::Vector{UInt8}: Per-step regularization mode (0=none, 1=pair, 2=chain).