Regularization

Regularization is an optional, advanced feature. The core integrator runs unregularized by default; pass regularization = RegularizationOptions(enabled = true) to opt in.

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 NaN values 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 = WeberProblem(
    sys, tspan, q0, p0;
    masses  = [1.0, 1.0],
    charges = [1.0, -1.0],
    c       = 10.0,
    dt      = 0.01,
    regularization = RegularizationOptions(enabled = true),   # opt in
)

Backends

BackendSymbolDimensionsMethod
Levi-Civita (default):lifted_pair2D onlyLifts the pair to ℝ⁴ fictitious time
Adaptive Cartesian:adaptive_cartesian2D and 3DCartesian 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).

3D KS constraint

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
prob = WeberProblem(sys, tspan, q0, p0; ...
    regularization = RegularizationOptions(
        enabled = true,
        backend = :adaptive_cartesian,
    ),
)

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:

prob = WeberProblem(sys, tspan, q0, p0; ...
    regularization = RegularizationOptions(
        enabled = true,
        r_on    = 0.05,
        r_off   = 0.10,
    ),
)

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 RegularizationOptions(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).

prob = WeberProblem(sys, tspan, q0, p0; ...
    regularization = RegularizationOptions(collision_bounce_radius = 0.02),  # reflect at r < 0.02
)

Collision bounce works best without Levi-Civita regularization (the unregularized symplectic integrator keeps energy bounded across the bounce). See CollisionBounceRegularization for details.

Diagnostics

Every WeberSolution 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:

FieldMeaningWorth investigating if…
activation_countHow many times regularization switched onVery high → roff is too close to ron
min_encounter_distanceClosest particle approach over the runNear 0 → singularity risk; consider bounce radius
max_constraint_violationPeak KS constraint residual (3D only)> 1e-8 → reduce dt or max_substeps
backend_fallback_stepsSteps that used the fallback backend> 0 when :lifted_pair was requested on a 3D problem
total_substepsTotal regularization micro-steps takenVery large → consider wider ron/roff thresholds

API reference

WeberElectrodynamics.RegularizationOptionsType
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. If nothing, computed as r_on_factor × min_initial_separation.
  • r_off=nothing: Absolute deactivation distance. If nothing, computed as r_off_factor × min_initial_separation. Must be greater than r_on.
  • r_on_factor=0.15: Scale factor for automatic r_on (positive).
  • r_off_factor=0.25: Scale factor for automatic r_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_pair uses Levi-Civita/KS (2D only; auto-falls back to :adaptive_cartesian for 3D). :adaptive_cartesian works 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.

source
WeberElectrodynamics.RegularizationDiagnosticsType
RegularizationDiagnostics

Diagnostics collected during a solve! call describing regularization usage.

Returned as WeberSolution.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 in RegularizationOptions.
  • 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).
source