Skip to content

Mechanics

Mechanics realizations shared by the physics packages: pendulums and reflections off walls.

PendulumElement(label, visual=None, *, theta=0.5, theta_d1=0.0, m=1.0, g=9.81, l=1.0)

Bases: Element

Pendulum, a bob of mass m on a rigid rod of length l that swings by the angle theta under gravity g.

The angle has order 2 and its first derivative theta_d1 is the angular velocity. The motion comes from an interaction such as PendulumInteraction.

Parameters:

Name Type Description Default
label str

The label of the pendulum.

required
visual VisualSpec | None

How the pendulum is drawn, or None for its class visual.

None
theta Annotated[float, StateSpec(period=2 * pi, order=2)]

The initial angle from the vertical, in radians.

0.5
theta_d1 float

The initial angular velocity.

0.0
m Annotated[float, ParameterSpec(domain=open(0, oo))]

The mass of the bob.

1.0
g Annotated[float, ParameterSpec(domain=open(0, oo))]

The gravitational acceleration.

9.81
l Annotated[float, ParameterSpec(domain=open(0, oo))]

The length of the rod.

1.0

Examples:

>>> from skcomplex.systems.physics.common.mechanics import PendulumElement
>>> PendulumElement('bob', theta=0.2, l=2.0).l
2.0
Source code in src/skcomplex/systems/physics/common/mechanics/_pendulum.py
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
def __init__(
    self,
    label: str,
    visual: VisualSpec | None = None,
    *,
    theta: Annotated[float, StateSpec(period=2 * pi, order=2)] = 0.5,
    theta_d1: float = 0.0,
    m: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
    g: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 9.81,
    l: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
) -> None:
    super().__init__(label, visual=visual)
    self.theta = theta
    self.theta_d1 = theta_d1
    self.m = m
    self.g = g
    self.l = l

PendulumInteraction(label, *, target)

Bases: EulerLagrangeInteraction

Motion of a pendulum, the Euler-Lagrange equation of L = m l² θ'² / 2 + m g l cos θ.

The equation is Newton's law m l² θ'' = -m g l sin θ for the angle θ of the target, whose mass m, length l, and gravity g are parameters of the target.

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
target Annotated[str, RoleSpec()]

The path of the pendulum.

required

Examples:

>>> from skcomplex.systems.physics.common.mechanics import PendulumInteraction
>>> PendulumInteraction('swing', target='bob').states
'theta'
Source code in src/skcomplex/systems/physics/common/mechanics/_pendulum_interaction.py
35
36
def __init__(self, label: str, *, target: Annotated[str, RoleSpec()]) -> None:
    super().__init__(label, target=target, states='theta')

ReflectionInteraction(label, *, target, guard, positions=('x',), velocities=('x_d1',), direction=0, restitution=1.0, region=None)

Bases: CrossingInteraction

Reflection of a moving point off a wall g(x) = 0, with a coefficient of restitution e.

When the guard g crosses zero, the velocity v of the point becomes v - (1 + e) (v . n) n / |n|**2, where n is the gradient of g with respect to the position. The normal velocity is reversed and scaled by e, and the tangential velocity is kept, so e = 1 is a specular, elastic reflection. The position and the velocity are given by the names of their states, scalar or vector, whose components pair up in order. A region restricts the wall to the part of the surface where it holds, and the point crosses the rest of the surface freely.

Parameters:

Name Type Description Default
label str

The label of the interaction.

required
target Annotated[str, RoleSpec()]

The path of the element that reflects.

required
guard Callable[[SimpleNamespace], object]

The function guard(namespace) of the wall, an expression of the position that is zero on the wall.

required
positions Sequence[str]

The names of the states that hold the position.

('x',)
velocities Sequence[str]

The names of the states that hold the velocity, the first derivatives of the position.

('x_d1',)
direction int

1 for crossings from negative to positive, -1 for the opposite, and 0 for both.

0
restitution Annotated[float, ParameterSpec(domain=closed(0, 1))]

The coefficient of restitution, the ratio of the normal speeds after and before the reflection.

1.0
region Callable[[SimpleNamespace], object] | None

The function region(namespace) of the condition that holds on the part of the surface that is a wall, or None for a wall on the whole surface.

None

Raises:

Type Description
ValidationError

The positions and the velocities have different numbers of names, or a function is not callable.

Examples:

>>> from skcomplex.core.components.elements import PointElement
>>> from skcomplex.core.components.spaces import ContinuousSpace
>>> from skcomplex.core.components.system import System
>>> from skcomplex.core.engine.runtime import Simulation
>>> from skcomplex.systems.physics.common.mechanics import ReflectionInteraction
>>> wall = ReflectionInteraction('wall', target='ball', guard=lambda s: s.target.x[0] - 1, direction=1)
>>> ball = PointElement('ball', x=[0.0, 0.0], x_d1=[1.0, 1.0])
>>> system = System(elements=[ball], interactions=[wall], space=ContinuousSpace(n_dim=2))
>>> trajectory = Simulation(system, dt=0.5).prepare().run(until=2.0).trajectory_
>>> trajectory.values('ball.x')[-1].tolist()
[0.0, 2.0]
Source code in src/skcomplex/systems/physics/common/mechanics/_reflection.py
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
def __init__(
    self,
    label: str,
    *,
    target: Annotated[str, RoleSpec()],
    guard: Callable[[SimpleNamespace], object],
    positions: Sequence[str] = ('x',),
    velocities: Sequence[str] = ('x_d1',),
    direction: int = 0,
    restitution: Annotated[float, ParameterSpec(domain=RealSet.closed(0, 1))] = 1.0,
    region: Callable[[SimpleNamespace], object] | None = None,
) -> None:
    super().__init__(label, target=target, guard=guard, direction=direction)
    self.positions = positions
    self.velocities = velocities
    self.restitution = restitution
    self.region = region