Overdamped particle in a tilted periodic potential
Overdamped particles in the tilted periodic potential V(x) = U0 cos(2 pi x / L) - f x.
- Name:
tilted_periodic_potential - Field:
physics/soft_matter - Formalism: stochastic differential equations
- References: Bertin 2021; Risken 1996; Reimann 2002
Factory
@register_model(
field='physics/soft_matter',
tags=('brownian motion', 'sde', 'nonequilibrium steady state', 'current'),
references=('Bertin 2021', 'Risken 1996', 'Reimann 2002'),
defaults={'until': 50.0, 'dt': 0.25, 'solver': EulerMaruyamaSolver(dt=0.005)},
)
def tilted_periodic_potential(
n_particles: int = 500,
*,
f: float = 1.0,
U0: float = 0.5,
L: float = 1.0,
gamma: float = 1.0,
kT: float = 0.5,
) -> System:
"""Return overdamped particles in the tilted periodic potential `V(x) = U0 cos(2 pi x / L) - f x`.
The force `f` breaks the left and right symmetry, so the particles reach a nonequilibrium steady state with a
uniform probability current `J`. Their mean velocity is
`J L = L (1 - exp(-f L / kT)) / int_0^L dx int_0^L dy exp((V(x) - V(x - y)) / kT) kT / gamma`, which is
`f / gamma` without the potential and exponentially small in the barrier for a weak force. The particles start
at the origin.
Args:
n_particles:
The number of independent particles.
f:
The constant force that tilts the potential.
U0:
The amplitude of the periodic potential.
L:
The period of the potential.
gamma:
The friction coefficient.
kT:
The thermal energy.
Returns:
system:
The model, with the positions `particles.x`.
Examples:
>>> from skcomplex.models.physics.soft_matter import tilted_periodic_potential
>>> tilted_periodic_potential(10, f=2.0).interactions[0].f
2.0
"""
particles = GroupElement(
'particles',
element=BrownianParticleElement,
size=n_particles,
values={'x': np.zeros((n_particles, 1)), 'gamma': gamma, 'temperature': kT},
)
washboard = TiltedWashboardInteraction('washboard', target='particles', U0=U0, L=L, f=f)
bath = ThermalNoiseInteraction('bath', target='particles')
return System(elements=[particles], interactions=[washboard, bath], space=ContinuousSpace(n_dim=1))
Components
class BrownianParticleElement(Element):
"""Overdamped Brownian particle at the position `x`, with the friction `gamma` and the thermal energy `temperature`.
The particle moves by `dx = F / gamma dt + sqrt(2 temperature / gamma) dW`, with the thermal noise of
`ThermalNoiseInteraction` and the forces of other interactions, which divide by `gamma`.
Args:
label:
The label of the particle or the group.
visual:
How the element is drawn, or `None` for `default_visual_`.
x:
The initial position.
gamma:
The friction coefficient.
temperature:
The thermal energy.
Examples:
>>> from skcomplex.models.physics.soft_matter import BrownianParticleElement
>>> BrownianParticleElement('bead', temperature=2.0).temperature
2.0
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = POINTS,
*,
x: Annotated[npt.ArrayLike, StateSpec(vector=True, role='position')] = 0.0,
gamma: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
temperature: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
) -> None:
super().__init__(label, visual=visual)
self.x = x
self.gamma = gamma
self.temperature = temperature
class TiltedWashboardInteraction(GradientInteraction):
"""Tilted periodic potential `U(x) - f x`, with `U(x) = U0 cos(2 pi x / L)`, on overdamped particles on a line.
The force divides by the friction `gamma` of the target.
Args:
label:
The label of the interaction.
target:
The path of the particle or the group.
U0:
The amplitude of the periodic potential, half its barrier height.
L:
The period of the potential.
f:
The constant non-conservative force that tilts the potential.
Examples:
>>> from skcomplex.models.physics.soft_matter import TiltedWashboardInteraction
>>> TiltedWashboardInteraction('washboard', target='beads', f=0.5).f
0.5
"""
def __init__(
self,
label: str,
*,
target: Annotated[str, RoleSpec()],
U0: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
L: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
f: Annotated[float, ParameterSpec()] = 0.0,
) -> None:
super().__init__(label, target=target, states='x', divisor='gamma')
self.U0 = U0
self.L = L
self.f = f
def _get_potential(self, s: SimpleNamespace) -> Expression:
"""Return the tilted potential `U0 cos(2 pi x / L) - f x`.
Args:
s:
The namespace of the symbols the potential can use.
Returns:
potential:
The potential.
"""
position = s.target.x[0]
return s.U0 * cos(2 * math.pi * position / s.L) - s.f * position
class ThermalNoiseInteraction(RateInteraction):
"""Thermal noise of overdamped particles, the random force `sqrt(2 gamma temperature) dW` divided by `gamma`.
The friction `gamma` and the thermal energy `temperature` are parameters of the target, so every component of the
position diffuses with the coefficient `temperature / gamma`.
Args:
label:
The label of the interaction.
target:
The path of the particle or the group.
state:
The name of the state that holds the positions.
Examples:
>>> from skcomplex.models.physics.soft_matter import ThermalNoiseInteraction
>>> ThermalNoiseInteraction('bath', target='beads').divisor
'gamma'
"""
def __init__(self, label: str, *, target: Annotated[str, RoleSpec()], state: str = 'x') -> None:
super().__init__(label, target=target, state=state, divisor='gamma')
def _get_noise(self, namespace: SimpleNamespace) -> object:
"""Return the amplitude `sqrt(2 gamma temperature)` of the random force.
Args:
namespace:
The namespace of the symbols the amplitude can use.
Returns:
amplitude:
The noise amplitude, before the division by the friction.
"""
target = namespace.target
return sqrt(2 * target.gamma * target.temperature)
Equations
particles.x[i, 0]: diff(x_particles_0(t), t) == (f_washboard + 6.283185307179586*U0_washboard*sin(6.283185307179586*x_particles_0(t)/L_washboard)/L_washboard)/gamma_particles