Force simulations

detroit.force_simulation(nodes: list[SimulationNode] | None = None) ForceSimulation

A force simulation implements a velocity Verlet numerical integrator for simulating physical forces on particles (nodes). The simulation assumes a constant unit time step \(\Delta t = 1\) for each step and a constant unit mass \(m = 1\) for all particles. As a result, a force \(F\) acting on a particle is equivalent to a constant acceleration a over the time interval \(\Delta t\), and can be simulated simply by adding to the particle’s velocity, which is then added to the particle’s position.

Parameters:

nodes (list[SimulationNode] | None) – List of nodes

Returns:

Simulation object

Return type:

ForceSimulation

class detroit.force.simulation.ForceSimulation(nodes: list[SimulationNode])
find(x: float, y: float, radius: float | None = None) SimulationNode

Returns the node closest to the position \((x,y)\) with the given search radius.

Parameters:
  • x (float) – x-coordinate value of the position

  • y (float) – y-coordinate value of the position

  • radius (float | None) – Radius value

Returns:

Closest node

Return type:

SimulationNode

tick(iterations: float | None = None) ForceSimulation

Manually steps the simulation by the specified number of iterations, and returns the simulation. If iterations is not specified, it defaults to 1 (single step).

For each iteration, it increments the current alpha by (alpha_target - alpha) * alpha_decay; then invokes each registered force, passing the new alpha; then decrements each node’s velocity by velocity * velocity_decay; lastly increments each node’s position by velocity.

This method does not dispatch events; events are only dispatched by the internal timer when the simulation is started automatically upon creation or by calling simulation.restart. The natural number of ticks when the simulation is started is \(\lceil \log(\alpha_{\text{min}}) / \log(1 - \alpha_{\text{decay}}) \rceil\); by default, this is 300.

This method can be used in conjunction with simulation.stop to compute a static force layout. For large graphs, static layouts should be computed in a web worker to avoid freezing the user interface.

Parameters:

iterations (float | None) – Number of iterations

Returns:

Itself

Return type:

ForceSimulation

set_alpha(alpha: float) ForceSimulation

It decreases over time as the simulation “cools down”.

Parameters:

alpha (float) – Alpha value

Returns:

Itself

Return type:

ForceSimulation

set_alpha_decay(alpha_decay: float) ForceSimulation

Sets the alpha decay rate to the specified number in the range \([0,1]\) and returns this simulation.

The alpha decay rate determines how quickly the current alpha interpolates towards the desired target alpha; since the default target alpha is zero, by default this controls how quickly the simulation cools. Higher decay rates cause the simulation to stabilize more quickly, but risk getting stuck in a local minimum; lower values cause the simulation to take longer to run, but typically converge on a better layout. To have the simulation run forever at the current alpha, set the decay rate to zero; alternatively, set a target alpha greater than the minimum alpha.

Parameters:

alpha_decay (float) – Alpha decay value

Returns:

Itself

Return type:

ForceSimulation

set_alpha_min(alpha_min: float) ForceSimulation

Sets the minimum alpha to the specified number in the range \([0,1]\) and returns this simulation.

The simulation’s internal timer stops when the current alpha is less than the minimum alpha. The default alpha decay rate of ~0.0228 corresponds to 300 iterations.

Parameters:

alpha_min (float) – Alpha minimum value

Returns:

Itself

Return type:

ForceSimulation

set_alpha_target(alpha_target: float) ForceSimulation

Sets the current target alpha to the specified number in the range \([0,1]\) and returns this simulation.

Parameters:

alpha_target (float) – Alpha target value

Returns:

Itself

Return type:

ForceSimulation

set_nodes(nodes: list[SimulationNode]) ForceSimulation

Sets the simulation’s nodes to the specified array of objects, initializing their positions and velocities if necessary, and then re-initializes any bound forces; returns the simulation.

Each node must be an dictionary (dict). The following properties are assigned by the simulation:

  • index (int) - the node’s zero-based index into nodes

  • x (float) - the node’s current x-position

  • y (float) - the node’s current y-position

  • vx (float) - the node’s current x-velocity

  • vy (float) - the node’s current y-velocity

  • fx (float) - the node’s fixed x-position

  • fy (float) - the node’s fixed y-position

The position \((x,y)\) and velocity \((v_x,v_y)\) may be subsequently modified by forces and by the simulation. If either vx or vy is nan, the velocity is initialized to \((0,0)\). If either x or y is nan, the position is initialized in a phyllotaxis arrangement, so chosen to ensure a deterministic, uniform distribution.

At the end of each tick, after the application of any forces, a node with a defined node["fx"] has node["x"] reset to this value and node["vx"] set to zero; likewise, a node with a defined node["fy"] has node["y"] reset to this value and node["vy"] set to zero. To unfix a node that was previously fixed, set node["fx"] and node["fy"] to None, or delete these properties.

If the specified array of nodes is modified, such as when nodes are added to or removed from the simulation, this method must be called again with the new (or changed) array to notify the simulation and bound forces of the change; the simulation does not make a defensive copy of the specified array.

Parameters:

nodes (list[SimulationNode]) – Array of nodes

Returns:

Itself

Return type:

ForceSimulation

set_random_source(random_source: Callable[[None], float]) ForceSimulation

Sets the function used to generate random numbers; this should be a function that returns a number between 0 (inclusive) and 1 (exclusive). If source is not specified, returns this simulation’s current random source which defaults to a fixed-seed linear congruential generator.

Parameters:

random_source (Callable[[None], float]) – Random source function

Returns:

Itself

Return type:

ForceSimulation

set_velocity_decay(velocity_decay: float) ForceSimulation

Sets the velocity decay factor to the specified number in the range \([0,1]\) and returns this simulation.

The decay factor is akin to atmospheric friction; after the application of any forces during a tick, each node’s velocity is multiplied by 1 - decay. As with lowering the alpha decay rate, less velocity decay may converge on a better solution, but risks numerical instabilities and oscillation.

Parameters:

velocity_decay (float) – Velocity decay value

Returns:

Itself

Return type:

ForceSimulation