From eec78222087f0bad231f0523ab83077f3372f79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Link?= Date: Tue, 14 Nov 2023 20:45:36 -0300 Subject: [PATCH] Add formulas to README --- README.md | 55 ++++++++++++++++-------- frequenpy/loaded_string/animation.py | 13 +++--- frequenpy/loaded_string/loaded_string.py | 8 ++-- 3 files changed, 49 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index d46f43c..60f944d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ -# FrequenPy +

FrequenPy

-![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. +

+ + Coverage + +

-This package has educational purposes. +**frequenpy** is a high-precision physics engine dedicated to the study and visualization of standing waves. ## Wave theory @@ -21,21 +23,38 @@ This results have been (and can be) demonstrated experimentally.
- 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. @@ -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. diff --git a/frequenpy/loaded_string/animation.py b/frequenpy/loaded_string/animation.py index fa75085..744ba6d 100644 --- a/frequenpy/loaded_string/animation.py +++ b/frequenpy/loaded_string/animation.py @@ -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' @@ -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) @@ -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): @@ -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)) @@ -151,7 +152,7 @@ def _build_animation(self): self._figure, self._update, frames=self._n_frames, - interval=5, + interval=10, blit=True, repeat=True) diff --git a/frequenpy/loaded_string/loaded_string.py b/frequenpy/loaded_string/loaded_string.py index 4a283ec..a1274ba 100644 --- a/frequenpy/loaded_string/loaded_string.py +++ b/frequenpy/loaded_string/loaded_string.py @@ -3,10 +3,10 @@ LONGITUDE = 1 -TENSION = 0.5 +TENSION = 1 MASS = 0.1 -AMPLITUDE = 0.5 +AMPLITUDE = 0.2 THETA = 0 @@ -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() @@ -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 }