Ideal gas
An ideal gas of non-interacting molecules in a reflecting box.
- Name:
ideal_gas - Field:
physics/statistical_mechanics - Formalism: ordinary differential equations
- References: Maxwell 1860
Factory
@register_model(
field='physics/statistical_mechanics',
tags=('gas', 'ode', 'boundary'),
references=('Maxwell 1860',),
defaults={'until': 2.0, 'dt': 0.01},
)
def ideal_gas(
n_molecules: int = 500,
*,
kT: float = 1.0,
size: float = 1.0,
n_dim: int = 2,
mass: float = 1.0,
random_state: int | np.random.Generator | None = 0,
label: str = 'gas',
) -> System:
"""Return an ideal gas of non-interacting molecules in a reflecting box.
Positions are uniform in the box and velocity components are normal with variance `kT / mass`, with the mean
velocity removed. The molecules are drawn as points colored by speed.
Args:
n_molecules:
The number of molecules.
kT:
The temperature in energy units.
size:
The side of the box.
n_dim:
The number of dimensions.
mass:
The mass of every molecule.
random_state:
The seed or generator of the initial positions and velocities.
label:
The label of the molecule group. The box is labelled `box`.
Returns:
system:
The gas.
Examples:
>>> from skcomplex.models.physics.statistical_mechanics import ideal_gas
>>> group = ideal_gas(1000, random_state=0).elements[0]
>>> group.label, group.element.__name__, group.size
('gas', 'MassPointElement', 1000)
"""
rng = np.random.default_rng(random_state)
velocities = rng.normal(0.0, np.sqrt(kT / mass), size=(n_molecules, n_dim))
velocities -= velocities.mean(axis=0)
visual = VisualSpec('points', {'position': 'x', 'radius': size / 200, 'color_by': 'x_d1', 'states': ()})
molecules = GroupElement(
label,
element=MassPointElement,
size=n_molecules,
values={'x': rng.uniform(0.0, size, size=(n_molecules, n_dim)), 'x_d1': velocities, 'm': mass},
visual=visual,
)
return System(
elements=[molecules],
space=ContinuousSpace(extent=[size] * n_dim, boundary='reflecting'),
)
Components
class MassPointElement(PointElement):
"""Point mass, a point whose position `x` has order 2, with its velocity `x_d1` and a mass `m`.
The forces on the point divide by the mass through their divisor `'m'`.
Args:
label:
The label of the point.
x:
The initial position. A number applies to every component.
x_d1:
The initial velocity. A number applies to every component.
visual:
How the point is drawn, or `None` for `default_visual_`.
m:
The mass.
Attributes:
default_visual_:
Points at the positions.
Examples:
>>> from skcomplex.models.physics.common.particles import MassPointElement
>>> MassPointElement('ball', x=[0.0, 1.0], m=2.0).m
2.0
"""
def __init__(
self,
label: str,
x: npt.ArrayLike = 0.0,
x_d1: npt.ArrayLike = 0.0,
visual: VisualSpec | None = None,
*,
m: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
) -> None:
super().__init__(label, x=x, x_d1=x_d1, visual=visual)
self.m = m
Equations
gas.x[i, 0]: diff(x_gas_0(t), t, t) == 0
gas.x[i, 1]: diff(x_gas_1(t), t, t) == 0