Spring chains and guitar string
Masses joined in a row by springs of zero rest length between two anchors, a discrete string.
- Name:
spring_chain - Field:
physics/waves - Formalism: ordinary differential equations
- References: Argun et al. 2021
Factory
@register_model(
field='physics/waves',
tags=('waves', 'ode', 'springs', 'normal modes', 'mechanics'),
references=('Argun et al. 2021',),
defaults={'until': 20.0, 'dt': 0.05, 'solver': VelocityVerletSolver(dt=0.01)},
)
def spring_chain(
n_masses: int = 15,
*,
length: float = 1.0,
k: float = 1.0,
mass: float = 0.01,
pluck: float = 0.05,
pluck_at: float = 0.5,
amplitude: float = 0.0,
omega: float = 2.0,
) -> System:
"""Return masses joined in a row by springs of zero rest length between two anchors, a discrete string.
The masses start evenly spaced between the anchors at `0` and `length`, stretched, so the chain carries tension.
With `pluck`, the masses start on a triangle of that height peaking at the fraction `pluck_at` of the length, the
pluck of a guitar string. With `amplitude`, the left anchor moves transversally as `amplitude sin(omega t)` and
sends a transverse wave along the chain. Springs of zero rest length are linear, so the transverse modes are
independent, with frequencies `2 sqrt(k / m) sin(n pi / (2 (N + 1)))`.
Args:
n_masses:
The number of masses between the anchors.
length:
The distance between the anchors.
k:
The stiffness of every spring.
mass:
The mass of every mass.
pluck:
The initial transverse displacement of the plucked point.
pluck_at:
The plucked point, as a fraction of the length.
amplitude:
The amplitude of the transverse motion of the left anchor.
omega:
The angular frequency of the left anchor.
Returns:
system:
The model, with the masses `mass_<i>` and their positions `mass_<i>.x` in two dimensions.
Examples:
>>> from skcomplex.models.physics.waves import spring_chain
>>> len(spring_chain(4).interactions)
5
"""
spacing = length / (n_masses + 1)
positions = spacing * np.arange(1, n_masses + 1)
heights = _pluck(positions, length, pluck, pluck_at) if pluck else np.zeros(n_masses)
masses = [
MassPointElement(f'mass_{index}', x=[float(positions[index]), float(heights[index])], x_d1=[0.0, 0.0], m=mass)
for index in range(n_masses)
]
springs = [
SpringInteraction(f'spring_{index}', first=f'mass_{index}', second=f'mass_{index + 1}', k=k)
for index in range(n_masses - 1)
]
anchors = [
DrivenAnchorInteraction('left_end', target='mass_0', k=k, center=[0.0, 0.0], amplitude=amplitude, omega=omega),
DrivenAnchorInteraction('right_end', target=f'mass_{n_masses - 1}', k=k, center=[length, 0.0]),
]
return System(elements=masses, interactions=[*anchors, *springs], space=ContinuousSpace(n_dim=2))
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
class DrivenAnchorInteraction(HarmonicPotentialInteraction):
"""Spring that ties a particle to an anchor point, which oscillates transversally as `amplitude sin(omega t)`.
The energy is `k |x - center - (0, amplitude sin(omega t))|² / 2`, and a zero amplitude gives a fixed anchor.
Args:
label:
The label of the interaction.
target:
The path of the particle.
k:
The stiffness of the spring.
center:
The rest position of the anchor.
amplitude:
The amplitude of the transverse motion of the anchor.
omega:
The angular frequency of the anchor.
Examples:
>>> from skcomplex.models.physics.waves import DrivenAnchorInteraction
>>> DrivenAnchorInteraction('end', target='mass_0', center=[0.0, 0.0], amplitude=0.1).amplitude
0.1
"""
def __init__(
self,
label: str,
*,
target: Annotated[str, RoleSpec()],
k: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
center: Annotated[npt.ArrayLike, ParameterSpec(vector=True)] = 0.0,
amplitude: Annotated[float, ParameterSpec()] = 0.0,
omega: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
) -> None:
super().__init__(label, target=target, k=k, center=center)
self.amplitude = amplitude
self.omega = omega
def _get_center(self, s: SimpleNamespace) -> Expression:
"""Return the position `center + (0, amplitude sin(omega t))` of the anchor."""
return s.center + vector(SR, [0, s.amplitude * sin(s.omega * s.t)])
class SpringInteraction(PotentialEnergyInteraction):
"""Spring between two particles.
The energy is `k * (|x_first - x_second| - length)**2 / 2`, and its gradient moves both particles.
Args:
label:
The label of the interaction.
first:
The path of the first particle.
second:
The path of the second particle.
k:
The stiffness.
length:
The rest length.
states:
The name of the vector state that holds the positions of the particles.
divisor:
The name of the state or parameter that holds the masses of the particles.
Examples:
>>> from skcomplex.models.physics.common.particles import SpringInteraction
>>> SpringInteraction('spring', first='a', second='b', k=3.0, length=1.0).length
1.0
"""
def __init__(
self,
label: str,
*,
first: Annotated[str, RoleSpec()],
second: Annotated[str, RoleSpec()],
k: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
length: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.0,
states: str = 'x',
divisor: str = 'm',
) -> None:
super().__init__(label, target=first, states=states, divisor=divisor)
self.first = first
self.second = second
self.k = k
self.length = length
def _get_potential(self, s: SimpleNamespace) -> Expression:
"""Return `k * (|x_first - x_second| - length)**2 / 2`.
Args:
s:
The namespace of the symbols the energy can use.
Returns:
energy:
The potential energy as a symbolic expression.
"""
gap = getattr(s.first, self._get_names()[0]) - getattr(s.second, self._get_names()[0])
return s.k * (sqrt(gap.dot_product(gap)) - s.length) ** 2 / 2
Equations
mass_0.x[0]: diff(x_mass_0_0(t), t, t) == k_spring_0*(length_spring_0 - sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2))*(x_mass_0_0(t) - x_mass_1_0(t))/(sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2)*m_mass_0) + (center_left_end_0 - x_mass_0_0(t))*k_left_end/m_mass_0
mass_0.x[1]: diff(x_mass_0_1(t), t, t) == k_spring_0*(length_spring_0 - sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2))*(x_mass_0_1(t) - x_mass_1_1(t))/(sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2)*m_mass_0) + (amplitude_left_end*sin(omega_left_end*t) + center_left_end_1 - x_mass_0_1(t))*k_left_end/m_mass_0
mass_1.x[0]: diff(x_mass_1_0(t), t, t) == -k_spring_0*(length_spring_0 - sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2))*(x_mass_0_0(t) - x_mass_1_0(t))/(sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2)*m_mass_1) + k_spring_1*(length_spring_1 - sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2))*(x_mass_1_0(t) - x_mass_2_0(t))/(sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2)*m_mass_1)
mass_1.x[1]: diff(x_mass_1_1(t), t, t) == -k_spring_0*(length_spring_0 - sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2))*(x_mass_0_1(t) - x_mass_1_1(t))/(sqrt((x_mass_0_0(t) - x_mass_1_0(t))^2 + (x_mass_0_1(t) - x_mass_1_1(t))^2)*m_mass_1) + k_spring_1*(length_spring_1 - sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2))*(x_mass_1_1(t) - x_mass_2_1(t))/(sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2)*m_mass_1)
mass_2.x[0]: diff(x_mass_2_0(t), t, t) == -k_spring_1*(length_spring_1 - sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2))*(x_mass_1_0(t) - x_mass_2_0(t))/(sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2)*m_mass_2) + k_spring_2*(length_spring_2 - sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2))*(x_mass_2_0(t) - x_mass_3_0(t))/(sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2)*m_mass_2)
mass_2.x[1]: diff(x_mass_2_1(t), t, t) == -k_spring_1*(length_spring_1 - sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2))*(x_mass_1_1(t) - x_mass_2_1(t))/(sqrt((x_mass_1_0(t) - x_mass_2_0(t))^2 + (x_mass_1_1(t) - x_mass_2_1(t))^2)*m_mass_2) + k_spring_2*(length_spring_2 - sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2))*(x_mass_2_1(t) - x_mass_3_1(t))/(sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2)*m_mass_2)
mass_3.x[0]: diff(x_mass_3_0(t), t, t) == -k_spring_2*(length_spring_2 - sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2))*(x_mass_2_0(t) - x_mass_3_0(t))/(sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2)*m_mass_3) + k_spring_3*(length_spring_3 - sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2))*(x_mass_3_0(t) - x_mass_4_0(t))/(sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2)*m_mass_3)
mass_3.x[1]: diff(x_mass_3_1(t), t, t) == -k_spring_2*(length_spring_2 - sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2))*(x_mass_2_1(t) - x_mass_3_1(t))/(sqrt((x_mass_2_0(t) - x_mass_3_0(t))^2 + (x_mass_2_1(t) - x_mass_3_1(t))^2)*m_mass_3) + k_spring_3*(length_spring_3 - sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2))*(x_mass_3_1(t) - x_mass_4_1(t))/(sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2)*m_mass_3)
mass_4.x[0]: diff(x_mass_4_0(t), t, t) == -k_spring_3*(length_spring_3 - sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2))*(x_mass_3_0(t) - x_mass_4_0(t))/(sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2)*m_mass_4) + k_spring_4*(length_spring_4 - sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2))*(x_mass_4_0(t) - x_mass_5_0(t))/(sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2)*m_mass_4)
mass_4.x[1]: diff(x_mass_4_1(t), t, t) == -k_spring_3*(length_spring_3 - sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2))*(x_mass_3_1(t) - x_mass_4_1(t))/(sqrt((x_mass_3_0(t) - x_mass_4_0(t))^2 + (x_mass_3_1(t) - x_mass_4_1(t))^2)*m_mass_4) + k_spring_4*(length_spring_4 - sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2))*(x_mass_4_1(t) - x_mass_5_1(t))/(sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2)*m_mass_4)
mass_5.x[0]: diff(x_mass_5_0(t), t, t) == -k_spring_4*(length_spring_4 - sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2))*(x_mass_4_0(t) - x_mass_5_0(t))/(sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2)*m_mass_5) + k_spring_5*(length_spring_5 - sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2))*(x_mass_5_0(t) - x_mass_6_0(t))/(sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2)*m_mass_5)
mass_5.x[1]: diff(x_mass_5_1(t), t, t) == -k_spring_4*(length_spring_4 - sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2))*(x_mass_4_1(t) - x_mass_5_1(t))/(sqrt((x_mass_4_0(t) - x_mass_5_0(t))^2 + (x_mass_4_1(t) - x_mass_5_1(t))^2)*m_mass_5) + k_spring_5*(length_spring_5 - sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2))*(x_mass_5_1(t) - x_mass_6_1(t))/(sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2)*m_mass_5)
mass_6.x[0]: diff(x_mass_6_0(t), t, t) == -k_spring_5*(length_spring_5 - sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2))*(x_mass_5_0(t) - x_mass_6_0(t))/(sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2)*m_mass_6) + k_spring_6*(length_spring_6 - sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2))*(x_mass_6_0(t) - x_mass_7_0(t))/(sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2)*m_mass_6)
mass_6.x[1]: diff(x_mass_6_1(t), t, t) == -k_spring_5*(length_spring_5 - sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2))*(x_mass_5_1(t) - x_mass_6_1(t))/(sqrt((x_mass_5_0(t) - x_mass_6_0(t))^2 + (x_mass_5_1(t) - x_mass_6_1(t))^2)*m_mass_6) + k_spring_6*(length_spring_6 - sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2))*(x_mass_6_1(t) - x_mass_7_1(t))/(sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2)*m_mass_6)
mass_7.x[0]: diff(x_mass_7_0(t), t, t) == -k_spring_6*(length_spring_6 - sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2))*(x_mass_6_0(t) - x_mass_7_0(t))/(sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2)*m_mass_7) + k_spring_7*(length_spring_7 - sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2))*(x_mass_7_0(t) - x_mass_8_0(t))/(sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2)*m_mass_7)
mass_7.x[1]: diff(x_mass_7_1(t), t, t) == -k_spring_6*(length_spring_6 - sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2))*(x_mass_6_1(t) - x_mass_7_1(t))/(sqrt((x_mass_6_0(t) - x_mass_7_0(t))^2 + (x_mass_6_1(t) - x_mass_7_1(t))^2)*m_mass_7) + k_spring_7*(length_spring_7 - sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2))*(x_mass_7_1(t) - x_mass_8_1(t))/(sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2)*m_mass_7)
mass_8.x[0]: diff(x_mass_8_0(t), t, t) == -k_spring_7*(length_spring_7 - sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2))*(x_mass_7_0(t) - x_mass_8_0(t))/(sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2)*m_mass_8) + k_spring_8*(length_spring_8 - sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2))*(x_mass_8_0(t) - x_mass_9_0(t))/(sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2)*m_mass_8)
mass_8.x[1]: diff(x_mass_8_1(t), t, t) == -k_spring_7*(length_spring_7 - sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2))*(x_mass_7_1(t) - x_mass_8_1(t))/(sqrt((x_mass_7_0(t) - x_mass_8_0(t))^2 + (x_mass_7_1(t) - x_mass_8_1(t))^2)*m_mass_8) + k_spring_8*(length_spring_8 - sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2))*(x_mass_8_1(t) - x_mass_9_1(t))/(sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2)*m_mass_8)
mass_9.x[0]: diff(x_mass_9_0(t), t, t) == -k_spring_9*(length_spring_9 - sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2))*(x_mass_10_0(t) - x_mass_9_0(t))/(sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2)*m_mass_9) - k_spring_8*(length_spring_8 - sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2))*(x_mass_8_0(t) - x_mass_9_0(t))/(sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2)*m_mass_9)
mass_9.x[1]: diff(x_mass_9_1(t), t, t) == -k_spring_9*(length_spring_9 - sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2))*(x_mass_10_1(t) - x_mass_9_1(t))/(sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2)*m_mass_9) - k_spring_8*(length_spring_8 - sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2))*(x_mass_8_1(t) - x_mass_9_1(t))/(sqrt((x_mass_8_0(t) - x_mass_9_0(t))^2 + (x_mass_8_1(t) - x_mass_9_1(t))^2)*m_mass_9)
mass_10.x[0]: diff(x_mass_10_0(t), t, t) == k_spring_10*(length_spring_10 - sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2))*(x_mass_10_0(t) - x_mass_11_0(t))/(sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2)*m_mass_10) + k_spring_9*(length_spring_9 - sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2))*(x_mass_10_0(t) - x_mass_9_0(t))/(sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2)*m_mass_10)
mass_10.x[1]: diff(x_mass_10_1(t), t, t) == k_spring_10*(length_spring_10 - sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2))*(x_mass_10_1(t) - x_mass_11_1(t))/(sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2)*m_mass_10) + k_spring_9*(length_spring_9 - sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2))*(x_mass_10_1(t) - x_mass_9_1(t))/(sqrt((x_mass_10_0(t) - x_mass_9_0(t))^2 + (x_mass_10_1(t) - x_mass_9_1(t))^2)*m_mass_10)
mass_11.x[0]: diff(x_mass_11_0(t), t, t) == -k_spring_10*(length_spring_10 - sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2))*(x_mass_10_0(t) - x_mass_11_0(t))/(sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2)*m_mass_11) + k_spring_11*(length_spring_11 - sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2))*(x_mass_11_0(t) - x_mass_12_0(t))/(sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2)*m_mass_11)
mass_11.x[1]: diff(x_mass_11_1(t), t, t) == -k_spring_10*(length_spring_10 - sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2))*(x_mass_10_1(t) - x_mass_11_1(t))/(sqrt((x_mass_10_0(t) - x_mass_11_0(t))^2 + (x_mass_10_1(t) - x_mass_11_1(t))^2)*m_mass_11) + k_spring_11*(length_spring_11 - sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2))*(x_mass_11_1(t) - x_mass_12_1(t))/(sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2)*m_mass_11)
mass_12.x[0]: diff(x_mass_12_0(t), t, t) == -k_spring_11*(length_spring_11 - sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2))*(x_mass_11_0(t) - x_mass_12_0(t))/(sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2)*m_mass_12) + k_spring_12*(length_spring_12 - sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2))*(x_mass_12_0(t) - x_mass_13_0(t))/(sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2)*m_mass_12)
mass_12.x[1]: diff(x_mass_12_1(t), t, t) == -k_spring_11*(length_spring_11 - sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2))*(x_mass_11_1(t) - x_mass_12_1(t))/(sqrt((x_mass_11_0(t) - x_mass_12_0(t))^2 + (x_mass_11_1(t) - x_mass_12_1(t))^2)*m_mass_12) + k_spring_12*(length_spring_12 - sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2))*(x_mass_12_1(t) - x_mass_13_1(t))/(sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2)*m_mass_12)
mass_13.x[0]: diff(x_mass_13_0(t), t, t) == -k_spring_12*(length_spring_12 - sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2))*(x_mass_12_0(t) - x_mass_13_0(t))/(sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2)*m_mass_13) + k_spring_13*(length_spring_13 - sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2))*(x_mass_13_0(t) - x_mass_14_0(t))/(sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2)*m_mass_13)
mass_13.x[1]: diff(x_mass_13_1(t), t, t) == -k_spring_12*(length_spring_12 - sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2))*(x_mass_12_1(t) - x_mass_13_1(t))/(sqrt((x_mass_12_0(t) - x_mass_13_0(t))^2 + (x_mass_12_1(t) - x_mass_13_1(t))^2)*m_mass_13) + k_spring_13*(length_spring_13 - sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2))*(x_mass_13_1(t) - x_mass_14_1(t))/(sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2)*m_mass_13)
mass_14.x[0]: diff(x_mass_14_0(t), t, t) == -k_spring_13*(length_spring_13 - sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2))*(x_mass_13_0(t) - x_mass_14_0(t))/(sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2)*m_mass_14) + (center_right_end_0 - x_mass_14_0(t))*k_right_end/m_mass_14
mass_14.x[1]: diff(x_mass_14_1(t), t, t) == -k_spring_13*(length_spring_13 - sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2))*(x_mass_13_1(t) - x_mass_14_1(t))/(sqrt((x_mass_13_0(t) - x_mass_14_0(t))^2 + (x_mass_13_1(t) - x_mass_14_1(t))^2)*m_mass_14) + (amplitude_right_end*sin(omega_right_end*t) + center_right_end_1 - x_mass_14_1(t))*k_right_end/m_mass_14