Skip to content

Commit

Permalink
Add formulas to README (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaslink authored Nov 15, 2023
2 parents bf3afd7 + eec7822 commit 41eeafb
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 27 deletions.
55 changes: 37 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

# FrequenPy
<h1 align="center" style="border-bottom: none;"> FrequenPy </h1>

![coverage_badge](https://codecov.io/gh/tomaslink/frequenpy/branch/master/graph/badge.svg)

_FrequenPy_ is a high-precision physics engine dedicated to the study of standing waves and visualization of its normal modes.
<p align="center">
<a>
<img alt="Coverage" src="https://codecov.io/gh/tomaslink/frequenpy/branch/master/graph/badge.svg">
</a>
</p>

This package has educational purposes.
**frequenpy** is a high-precision physics engine dedicated to the study and visualization of standing waves.

## Wave theory

Expand All @@ -21,21 +23,38 @@ This results have been (and can be) demonstrated experimentally.

<div align="justify">

According to wave theory, any arbitrary movement of the string
can be decomposed into a superposition of natural modes of oscillation.
In each natural mode **m**,
all masses in the system oscilate at the same frequency ***f(m)***
and pass through the equilibrium position at the same time.
This natural modes of oscillation are called **normal modes**.

A flexible elastic string with tension **T** is loaded with **N** identical particles,
each of mass **m**, equally spaced a distance **a** apart.
Let us hold the string fixed at two points,
one at a distance **a** to the left of the first particle
and the other at a distance **a** to the right of the Nth particle.

According to the theory, the movement of each of the masses in the vertical direction
can be decomposed into a superposition of **N** **normal modes** modes of oscillation.
That way, the $y$ position of the particle **n** as a function of time is
```math
y_n(t) = \sum_{p=1}^N A_p \sin(k_p n a) \cos(\omega_p t + \theta_p).
```
Where $A_p$ and $\theta_p$ depend on the initial conditions,
$k_p$ will depend on the boundary conditions and $\omega_p$ will have the form
```math
\omega_p = 2 \omega_0 \sin\left(\frac{p \pi}{2(N + 1)}\right)
\qquad,
\qquad
\omega_0 = \sqrt{\frac{T}{ma}}.
```

There are as many normal modes as there are degrees of freedom (masses) in the system.
So, a string loaded with **N** masses, will have **N** **normal modes**.
The first will corresponde to the lowest frequency (called fundamental)
and each next will be higher than the previous one, until we reach the last and highest frequency.
Any movement, as strange as it may be, can be expressed as a superposition of those **N** normal modes
In each natural mode **p**,
all masses in the system oscilate at the same frequency $\omega_p$
and pass through the equilibrium position at the same time.
The first mode, **p=1**, corresponds to the lowest frequency (called fundamental)
and each subsequent mode will have a frequency higher than the previous one.
Any movement of the string, as strange as it may be,
can be expressed as a superposition of those **N** normal modes
(some will contribute more than others to the final movement).

As the number of masses gets higher and highter (***N*** ---> ******),
As the number of masses gets higher and highter ($N \rightarrow \infty$),
we approximate to the continuous system (a vibrating string - no discrete masses).
In this simulation, you can use **N = 30** to see a continuous effect.

Expand Down Expand Up @@ -98,7 +117,7 @@ optional arguments:
Example: frequenpy loaded_string --masses 3 --modes 1 2 3 --speed 0.1 --boundary 0
```
Remember that for system of **N** masses there are N normal modes.
Remember that for system of **N** masses there are **N** normal modes.
You can pass only one of them or a combination of several, e.g. "2 6 3".
The order doesn't matter.
Expand Down
13 changes: 7 additions & 6 deletions frequenpy/loaded_string/animation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
logger = logging.getLogger(__name__)


LINE_WIDTH = 1
LINE_WIDTH = 0.5
LINE_MARKERTYPE = 'o'
LINE_MARKERSIZE = 8
LINE_MARKERFACECOLOR = 'None'
LINE_MARKERFACECOLOR = 'gray'
LINE_COLOR = 'white'

BACKGROUND_COLOR = 'black'
Expand All @@ -22,7 +22,7 @@
WALL_WIDTH = 0.5
WALL_COLOR = 'white'

FIG_SIZE = (7, 2)
FIG_SIZE = (7, 4)
FIG_DPI = 100
FIG_X_LIMIT = (-0.5, 0.5)
FIG_Y_LIMIT = (-1, 1)
Expand Down Expand Up @@ -93,11 +93,10 @@ def _build_line_without_markers(self):

def _build_frames(self):
self._loaded_string.apply_speed(self._speed)
frames = range(0, self._n_frames)

return [
self._loaded_string.position_at_time_t(t)
for t in frames
for t in range(0, self._n_frames)
]

def _build_figure(self):
Expand All @@ -117,6 +116,8 @@ def _build_figure(self):
ax = plt.axes(xlim=FIG_X_LIMIT, ylim=FIG_Y_LIMIT, frameon=False)
ax.set_yticks([])
ax.set_xticks([])
ax.set_xlim(-0.5, 0.51)
ax.set_ylim(-0.5, 0.51)

ax.add_line(self._left_support(support_distance_from_origin))
ax.add_line(self._right_support(support_distance_from_origin))
Expand Down Expand Up @@ -151,7 +152,7 @@ def _build_animation(self):
self._figure,
self._update,
frames=self._n_frames,
interval=5,
interval=10,
blit=True,
repeat=True)

Expand Down
8 changes: 5 additions & 3 deletions frequenpy/loaded_string/loaded_string.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@


LONGITUDE = 1
TENSION = 0.5
TENSION = 1
MASS = 0.1

AMPLITUDE = 0.5
AMPLITUDE = 0.2
THETA = 0


Expand All @@ -18,6 +18,8 @@ def __init__(self, N, modes):
self._modes = modes
self._masses = range(0, N + 2)
self._a = LONGITUDE / (N + 1)

self._omega0 = np.sqrt(TENSION / (MASS * self._a))
self._omega = self._omega()

self.rest_position = self._get_rest_position()
Expand Down Expand Up @@ -57,7 +59,7 @@ def modes(self):

def _omega(self):
return {
p: 2 * np.sqrt(TENSION / MASS * self._a) * np.sin(self._wavenumber(p) * self._a / 2)
p: 2 * self._omega0 * np.sin((self._wavenumber(p) * self._a) / 2)
for p in self._modes
}

Expand Down

0 comments on commit 41eeafb

Please sign in to comment.