electrosim.simulation.physics#

Functions

compute_accelerations(particles, ...)

Compute accelerations for all particles from Coulomb forces.

electric_field_at_point(point_m, particles, ...)

Compute electric field vector at a world point from all particles.

electric_force_pair(p_i, q_i, r_i, p_j, q_j, ...)

Compute softened Coulomb force on particle i due to j.

minimum_image_displacement(p_i, p_j, ...)

Compute displacement from i to j using the minimum distance convention in a 2D torus.

resolve_collisions(particles, world_size_m)

Resolve merges (opposite charges) and elastic collisions.

rk4_integrate(particles, world_size_m, dt_s, ...)

Advance non-fixed particles using classical RK4.

total_kinetic_energy(particles)

Compute total kinetic energy for mobile particles.

total_potential_energy(particles, world_size_m)

Compute pairwise Coulomb potential with minimum-image and singularity guard.

wrap_position_in_place(pos_m, world_size_m)

Wrap a position into the periodic domain in-place.

minimum_image_displacement(p_i, p_j, world_size_m)[source]#

Compute displacement from i to j using the minimum distance convention in a 2D torus.

Parameters:
  • p_i (numpy.ndarray shape (2,)) – Positions in meters.

  • p_j (numpy.ndarray shape (2,)) – Positions in meters.

  • world_size_m (numpy.ndarray shape (2,)) – World size in meters (width, height).

Returns:

numpy.ndarray shape (2,) – Displacement vector from i to j under minimum-image on the torus.

Return type:

ndarray

wrap_position_in_place(pos_m, world_size_m)[source]#

Wrap a position into the periodic domain in-place.

Parameters:
  • pos_m (numpy.ndarray shape (2,)) – Position in meters to be wrapped. Modified in-place.

  • world_size_m (numpy.ndarray shape (2,)) – World size in meters (width, height).

Return type:

None

electric_force_pair(p_i, q_i, r_i, p_j, q_j, r_j, world_size_m, softening_fraction)[source]#

Compute softened Coulomb force on particle i due to j.

Uses minimum-image displacement on a 2D torus and Plummer-like softening with epsilon = softening_fraction * (r_i + r_j).

Parameters:
  • p_i (numpy.ndarray shape (2,)) – Positions (m) of particles i and j.

  • p_j (numpy.ndarray shape (2,)) – Positions (m) of particles i and j.

  • q_i (float) – Charges (C).

  • q_j (float) – Charges (C).

  • r_i (float) – Contact radii (m).

  • r_j (float) – Contact radii (m).

  • world_size_m (numpy.ndarray) – World size (m) as (Lx, Ly).

  • softening_fraction (float) – Softening fraction applied to contact radius.

Returns:

numpy.ndarray shape (2,) – Force vector on i due to j (N).

Return type:

ndarray

compute_accelerations(particles, world_size_m, softening_fraction)[source]#

Compute accelerations for all particles from Coulomb forces.

Fixed, massless or neutral particles get zero acceleration. Uses Numba when available; falls back to pure Python otherwise.

Parameters:
  • particles (list[Particle]) – Particle list with positions (m), velocities (m/s), masses (kg), charges (C), radii (m).

  • world_size_m (numpy.ndarray shape (2,)) – World size (m) as (Lx, Ly).

  • softening_fraction (float) – Softening fraction applied to contact radius in pairwise force.

Returns:

numpy.ndarray shape (N, 2) – Accelerations (m/s^2) for each particle.

Return type:

np.ndarray

total_kinetic_energy(particles)[source]#

Compute total kinetic energy for mobile particles.

Parameters:

particles (list[Particle]) – Particles.

Returns:

float – Total kinetic energy (J) excluding fixed particles.

Return type:

float

total_potential_energy(particles, world_size_m)[source]#

Compute pairwise Coulomb potential with minimum-image and singularity guard.

Note: No softening is applied to the potential; instead, a small-distance clamp max(r, 1e-6) is used. See documentation for rationale.

Parameters:
  • particles (list[Particle]) – Particles.

  • world_size_m (numpy.ndarray shape (2,)) – World size (m).

Returns:

float – Total potential energy (J).

Return type:

float

electric_field_at_point(point_m, particles, world_size_m, softening_fraction)[source]#

Compute electric field vector at a world point from all particles.

Softened per-source radius using epsilon_j = softening_fraction * r_j.

Parameters:
  • point_m (numpy.ndarray shape (2,)) – Observation point (m).

  • particles (list[Particle]) – Particles.

  • world_size_m (numpy.ndarray shape (2,)) – World size (m).

  • softening_fraction (float) – Softening fraction per source radius.

Returns:

numpy.ndarray shape (2,) – Electric field (N/C) at the observation point.

Return type:

np.ndarray

rk4_integrate(particles, world_size_m, dt_s, softening_fraction)[source]#

Advance non-fixed particles using classical RK4.

Accelerations derive from current positions at each stage; state is temporarily updated during stage evaluations and restored before final write. Only non-fixed particles are updated.

Parameters:
  • particles (list[Particle]) – Particles to integrate.

  • world_size_m (numpy.ndarray shape (2,)) – World size (m).

  • dt_s (float) – Time step per substep (s).

  • softening_fraction (float) – Softening fraction passed to acceleration computation.

Return type:

None

resolve_collisions(particles, world_size_m)[source]#

Resolve merges (opposite charges) and elastic collisions.

Two-phase handling: 1) Merge phase for overlapping opposite-charge pairs: conserve mass, charge,

and momentum (if not fixed); area-equivalent radius; history merge; id reassign.

  1. Elastic phase for remaining overlaps: positional correction along normal, then 1D normal impulse with restitution e=1. Fixed treated as infinite mass.

Parameters:
  • particles (list[Particle]) – Mutable particle list.

  • world_size_m (numpy.ndarray shape (2,)) – World size (m) for displacement and wrapping.

Return type:

None