Physics Overview#

ElectroSim models 2D Coulomb dynamics on a periodic domain with a fixed timestep integrator. This page summarises the numerical model implemented in electrosim.simulation.physics and how it is orchestrated by electrosim.simulation.engine.Simulation.

Units and Domain#

  • All quantities use SI units: pos_m in meters, vel_mps in m/s, mass_kg in kilograms, charge_c in Coulombs, and radius_m in meters.

  • The simulation space is a rectangular torus of size (WORLD_WIDTH_M, WORLD_HEIGHT_M) derived from PIXELS_PER_METER. Particles that leave one edge re-enter on the opposite side.

  • Distances use the minimum-image convention via electrosim.simulation.physics.minimum_image_displacement(), ensuring the shortest wrap-around separation is always chosen.

  • Charges with |q| below NEUTRAL_CHARGE_EPS are treated as neutral for rendering and force skipping.

Force Model#

  • Pairwise forces implement softened Coulomb interaction in electrosim.simulation.physics.electric_force_pair().

  • The softening radius per pair is $\(\epsilon_{ij} = f_\text{soft} (r_i + r_j)\)$ where f_soft = SOFTENING_FRACTION. This keeps forces finite when particles overlap while maintaining the far-field Coulomb behaviour. See math/plummer_softening for derivation.

  • electrosim.simulation.physics.compute_accelerations() builds the acceleration array used by the integrator. When Numba is available it JIT-compiles two kernels:

    • _compute_accelerations_numba_parallel (enabled when NUMBA_PARALLEL_ACCEL = True).

    • _compute_accelerations_numba_serial as the fallback serial kernel.

  • Fixed, neutral, or zero-mass particles are skipped to avoid unnecessary work. If UNIFORM_FIELD_ACTIVE is true, the uniform field vector from UNIFORM_FIELD_VECTOR_NC is applied as an additional constant acceleration term.

Time Integration Pipeline#

  1. electrosim.simulation.engine.Simulation.step_frame() determines the number of substeps as SUBSTEPS_BASE_PER_FRAME × SPEED_MULTIPLIERS[speed_index].

  2. For each substep, electrosim.simulation.physics.rk4_integrate() advances mobile particles using classical RK4. Intermediate evaluations temporarily perturb particle state while respecting world wrapping.

  3. After each RK4 stage, positions are wrapped with electrosim.simulation.physics.wrap_position_in_place() to keep them inside the torus.

  4. Once all substeps are complete, the simulation updates trails, recomputes energies, and (optionally) caches forces for visualization.

The fixed timestep (DT_S) keeps the integrator deterministic and simplifies validation. See math/rk4_derivation for details.

Collisions and Merging#

  • Overlap detection occurs when \(\|\mathbf{r}_{ij}\| < r_i + r_j\) using the minimum-image displacement.

  • Opposite-signed charges merge in an inelastic pass (electrosim.simulation.physics.resolve_collisions()). The merged particle conserves total charge and, if both were mobile, linear momentum. The new radius satisfies area conservation: \(r_\text{new} = \sqrt{r_i^2 + r_j^2}\).

  • Remaining overlaps (same-sign or neutral participants) go through an elastic impulse solver with restitution \(e = 1\). The impulse is applied along the contact normal and positions are separated to remove penetration.

  • Fixed particles behave as infinite-mass anchors during collision resolution.

  • Particle id values are re-assigned whenever a particle is removed to keep the list contiguous.

See math/elastic_collision_impulse for the impulse derivation.

Boundary Handling#

Energy Accounting#

Field Evaluation and Visualization#

Validation Hooks#