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_min meters,vel_mpsin m/s,mass_kgin kilograms,charge_cin Coulombs, andradius_min meters.The simulation space is a rectangular torus of size
(WORLD_WIDTH_M, WORLD_HEIGHT_M)derived fromPIXELS_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_EPSare 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 whenNUMBA_PARALLEL_ACCEL = True)._compute_accelerations_numba_serialas the fallback serial kernel.
Fixed, neutral, or zero-mass particles are skipped to avoid unnecessary work. If
UNIFORM_FIELD_ACTIVEis true, the uniform field vector fromUNIFORM_FIELD_VECTOR_NCis applied as an additional constant acceleration term.
Time Integration Pipeline#
electrosim.simulation.engine.Simulation.step_frame()determines the number of substeps asSUBSTEPS_BASE_PER_FRAME × SPEED_MULTIPLIERS[speed_index].For each substep,
electrosim.simulation.physics.rk4_integrate()advances mobile particles using classical RK4. Intermediate evaluations temporarily perturb particle state while respecting world wrapping.After each RK4 stage, positions are wrapped with
electrosim.simulation.physics.wrap_position_in_place()to keep them inside the torus.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
idvalues are re-assigned whenever a particle is removed to keep the list contiguous.
See math/elastic_collision_impulse for the impulse derivation.
Boundary Handling#
Positions are wrapped after every integration substep and post-collision via
electrosim.simulation.physics.wrap_position_in_place().The same minimum-image logic is reused for selection hit-testing and tooltip distance calculations within the UI layer.
Energy Accounting#
Kinetic energy comes from
electrosim.simulation.physics.total_kinetic_energy(), summing \(E_k = \tfrac{1}{2} m v^2\) for mobile particles only.Potential energy uses
electrosim.simulation.physics.total_potential_energy(): $\(E_p = \sum_{i<j} k \frac{q_i q_j}{\max(r_{ij}, 10^{-6})}\)\( The \)10^{-6}$ m clamp avoids singularities without introducing additional softening. Refer to math/potential_energy_modeling for the rationale.Energies are recomputed once per frame and exposed via the overlay as
E_kin,E_pot, andE_tot.
Field Evaluation and Visualization#
electrosim.simulation.physics.electric_field_at_point()computes the electric field at an arbitrary point with per-source softening \(\epsilon_j = f_\text{soft} r_j\).electrosim.rendering.field_sampler.ElectricFieldSamplercaches these values on a regular grid whenFIELD_SAMPLER_ENABLEDis true, allowing the renderer to reuse the grid for arrow drawing.Display modes (
brightnessvslength) map field magnitude to arrow length/alpha as described in math/field_visualization_mapping.
Validation Hooks#
electrosim.simulation.engine.Simulation.start_uniform_field_validation()clears the scene and spawns the analytical uniform field scenario described in developer_guide/validation.During validation, accelerations gain a constant \((q/m)\mathbf{E}\) term and the overlay reports position/velocity error metrics each frame.
electrosim.simulation.engine.Simulation.stop_validation()restores configuration defaults and clears the validation state (validation_*fields).