125
+126
+127
+128
129
130
131
@@ -1341,10 +1378,7 @@ 136
137
138
-139
-140
-141
-142 | def __call__(self, t: "Sequence[float] | ArrayLike[float]") -> pd.DataFrame:
+139
| def __call__(self, t: "Sequence[float] | ArrayLike[float]") -> pd.DataFrame:
"""Generate time series data for the harmonic oscillator chain.
Returns float(s) representing the displacement at the given time(s).
@@ -1366,158 +1400,6 @@
-
-
-
-
- __init__(omega, initial_conditions, odd_dof)
-
-
-
-
-
-
- Instantiate an oscillator chain.
-
-
-
- Parameters:
-
-
-
- Name |
- Type |
- Description |
- Default |
-
-
-
-
- omega |
-
- float
- |
-
-
- |
-
- required
- |
-
-
- initial_conditions |
-
- Sequence[Mapping[str, float | tuple[float, float]]]
- |
-
-
- a sequence of initial conditions on the Fourier modes. The first element in the sequence is that of the zero mode, taking a position and a velocity. Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases.
-
- |
-
- required
- |
-
-
- odd_dof |
-
- bool
- |
-
-
- The system will have 2 * len(initial_conditions) + int(odd_dof) - 2 degrees of freedom.
-
- |
-
- required
- |
-
-
-
-
-
- Source code in hamilflow/models/harmonic_oscillator_chain.py
- 27
-28
-29
-30
-31
-32
-33
-34
-35
-36
-37
-38
-39
-40
-41
-42
-43
-44
-45
-46
-47
-48
-49
-50
-51
-52
-53
-54
-55
-56
-57
-58
-59
-60
-61
-62 | def __init__(
- self,
- omega: float,
- initial_conditions: Sequence[Mapping[str, float | tuple[float, float]]],
- odd_dof: bool,
-) -> None:
- """Instantiate an oscillator chain.
-
- :param omega: frequence parameter
- :param initial_conditions: a sequence of initial conditions on the Fourier modes.
- The first element in the sequence is that of the zero mode, taking a position and a velocity.
- Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases.
- :param odd_dof: The system will have `2 * len(initial_conditions) + int(odd_dof) - 2` degrees of freedom.
- """
- self.n_dof = 2 * len(initial_conditions) + odd_dof - 2
- if not odd_dof:
- prefix = "For even degrees of freedom, "
- if self.n_dof == 0:
- raise ValueError(prefix + "at least 1 travelling wave is needed")
- amp = cast(tuple[float, float], initial_conditions[-1]["amp"])
- if amp[0] != amp[1]:
- msg = "k == N // 2 must have equal positive and negative amplitudes."
- raise ValueError(prefix + msg)
- self.omega = omega
- self.odd_dof = odd_dof
-
- self.free_mode = FreeParticle(cast(Mapping[str, float], initial_conditions[0]))
-
- self.independent_csho_modes = [
- self._sho_factory(
- k,
- cast(tuple[float, float], ic["amp"]),
- cast(tuple[float, float] | None, ic.get("phi")),
- )
- for k, ic in enumerate(initial_conditions[1:], 1)
- ]
-
|
-
-
-
-
-
-
diff --git a/pr-preview/pr-58/search/search_index.json b/pr-preview/pr-58/search/search_index.json
index b6538db..30587c4 100644
--- a/pr-preview/pr-58/search/search_index.json
+++ b/pr-preview/pr-58/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Documentation for hamilflow ","text":"Generating dataset for physical systems. "},{"location":"#development","title":"Development","text":"We use poetry to manage our python environment. Use poetry install\n to install the requirements. Or run poetry install --with test\n to install the base environment and test environment for development. If this is the first you clone the repo and committing code, run pre-commit install\n first. "},{"location":"changelog/","title":"hamilflow Changelog","text":""},{"location":"changelog/#010-2024-03-21","title":"0.1.0 (2024-03-21)","text":""},{"location":"changelog/#feat","title":"Feat","text":" - model: add brownian motion (#23)
- ops: #18 use commitizen (#21)
"},{"location":"changelog/#fix","title":"Fix","text":" - docs: fix docstrings for pydantic models cvar (#27)
"},{"location":"changelog/#2024-02-18-001","title":"2024-02-18, 0.0.1","text":"Setting up repository. "},{"location":"references/","title":"References","text":""},{"location":"references/models/brownian_motion/","title":"Brownian Motion","text":""},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotion","title":"BrownianMotion ","text":"Brownian motion describes motion of small particles with stochastic forces applied to them. The math of Brownian motion can be modeled with Wiener process. For consistency, we always use \\(\\mathbf x\\) for displacement, and \\(t\\) for steps. The model we are using is \\[ \\begin{align} \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) + \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t}) \\end{align} \\] References: - Brownian motion and random walks. [cited 13 Mar 2024]. Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html
- Contributors to Wikimedia projects. Brownian motion. In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024]. Available: https://en.wikipedia.org/wiki/Brownian_motion
1D Brownian Motion The dimsion of our Brownian motion is specified by the dimension of the initial condition. To simulate a 1D Browian motion, we define the system and initial condition: system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n}\n\ninitial_condition = {\n \"x0\": 0\n}\n The Brownian motion can be simulated using bm = BrownianMotion(system=system, initial_condition=initial_condition)\n\nbm(n_steps=100)\n 2D Brownian Motion To simulate a 2D Browian motion, system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n}\n\ninitial_condition = {\n \"x0\": [0, 0]\n}\n\nbm = BrownianMotion(system=system, initial_condition=initial_condition)\n\nbm(n_steps=100)\n Parameters: Name Type Description Default system Mapping[str, float] the Brownian motion system definition required initial_condition Mapping[str, float] | None the initial condition for the simulation None Source code in hamilflow/models/brownian_motion.py class BrownianMotion:\n r\"\"\"Brownian motion describes motion of small particles\n with stochastic forces applied to them.\n The math of Brownian motion can be modeled\n with Wiener process.\n\n For consistency, we always use\n $\\mathbf x$ for displacement, and\n $t$ for steps. The model we are using is\n\n $$\n \\begin{align}\n \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) +\n \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t})\n \\end{align}\n $$\n\n References:\n\n 1. Brownian motion and random walks. [cited 13 Mar 2024].\n Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html\n 2. Contributors to Wikimedia projects. Brownian motion.\n In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024].\n Available: https://en.wikipedia.org/wiki/Brownian_motion\n\n\n !!! example \"1D Brownian Motion\"\n\n The dimsion of our Brownian motion is specified by\n the dimension of the initial condition.\n\n To simulate a 1D Browian motion, we define the system and initial condition:\n\n ```python\n system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n }\n\n initial_condition = {\n \"x0\": 0\n }\n ```\n\n The Brownian motion can be simulated using\n\n ```python\n bm = BrownianMotion(system=system, initial_condition=initial_condition)\n\n bm(n_steps=100)\n ```\n\n !!! example \"2D Brownian Motion\"\n\n To simulate a 2D Browian motion,\n\n ```python\n system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n }\n\n initial_condition = {\n \"x0\": [0, 0]\n }\n\n bm = BrownianMotion(system=system, initial_condition=initial_condition)\n\n bm(n_steps=100)\n ```\n\n :param system: the Brownian motion system definition\n :param initial_condition: the initial condition for the simulation\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ):\n initial_condition = initial_condition or {}\n self.system = BrownianMotionSystem.model_validate(system)\n self.initial_condition = BrownianMotionIC.model_validate(initial_condition)\n\n @property\n def dim(self) -> int:\n \"\"\"Dimension of the Brownian motion\"\"\"\n return self.initial_condition.x0.size\n\n @property\n def _axis_names(self) -> list[str]:\n return [f\"x_{i}\" for i in range(self.dim)]\n\n def _trajectory(self, n_new_steps: int, seed: int) -> np.ndarray:\n \"\"\"The trajectory of the particle.\n\n We first compute the delta displacement in each step.\n With the displacement at each step, we perform a cumsum\n including the initial coordinate to get the displacement at each step.\n\n :param n_new_steps: number of new steps to simulate, excluding the initial step.\n :param seed: seed for the random generator.\n \"\"\"\n step_history = sp.stats.norm.rvs(\n size=(n_new_steps, self.dim) if self.dim > 1 else n_new_steps,\n scale=self.system.gaussian_scale,\n random_state=np.random.RandomState(seed=seed),\n )\n\n step_history = np.concatenate(\n (np.expand_dims(self.initial_condition.x0, axis=0), step_history)\n )\n\n trajectory = np.cumsum(step_history, axis=0)\n\n return trajectory\n\n def __call__(self, n_steps: int, seed: int = 42) -> pd.DataFrame:\n \"\"\"Simulate the coordinates of the particle\n\n :param n_steps: total number of steps to be simulated, including the inital step.\n :param seed: random generator seed for the stochastic process.\n Use it to reproduce results.\n \"\"\"\n trajectory = self._trajectory(n_new_steps=n_steps - 1, seed=seed)\n\n df = pd.DataFrame(trajectory, columns=self._axis_names)\n\n df[\"t\"] = np.arange(0, n_steps) * self.system.delta_t\n\n return df\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotion.dim","title":"dim: int property ","text":"Dimension of the Brownian motion "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotion.__call__","title":"__call__(n_steps, seed=42) ","text":"Simulate the coordinates of the particle Parameters: Name Type Description Default n_steps int total number of steps to be simulated, including the inital step. required seed int random generator seed for the stochastic process. Use it to reproduce results. 42 Source code in hamilflow/models/brownian_motion.py def __call__(self, n_steps: int, seed: int = 42) -> pd.DataFrame:\n \"\"\"Simulate the coordinates of the particle\n\n :param n_steps: total number of steps to be simulated, including the inital step.\n :param seed: random generator seed for the stochastic process.\n Use it to reproduce results.\n \"\"\"\n trajectory = self._trajectory(n_new_steps=n_steps - 1, seed=seed)\n\n df = pd.DataFrame(trajectory, columns=self._axis_names)\n\n df[\"t\"] = np.arange(0, n_steps) * self.system.delta_t\n\n return df\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotionIC","title":"BrownianMotionIC ","text":" Bases: BaseModel The initial condition for a Brownian motion Attributes: Name Type Description x0 float | Sequence[float] initial displacement of the particle, the diminsion of this initial condition determines the dimension of the model too. Source code in hamilflow/models/brownian_motion.py class BrownianMotionIC(BaseModel):\n \"\"\"The initial condition for a Brownian motion\n\n :cvar x0: initial displacement of the particle,\n the diminsion of this initial condition determines\n the dimension of the model too.\n \"\"\"\n\n x0: float | Sequence[float] = Field(default=1.0)\n\n @field_validator(\"x0\")\n @classmethod\n def check_x0_types(cls, v: float | Sequence[float]) -> np.ndarray:\n if not isinstance(v, (float, int, Sequence)):\n # TODO I do not think this raise can be reached\n raise ValueError(f\"Value of x0 should be int/float/list of int/float: {v=}\")\n\n return np.array(v, copy=False)\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotionSystem","title":"BrownianMotionSystem ","text":" Bases: BaseModel Definition of the Brownian Motion system For consistency, we always use \\(\\mathbf x\\) for displacement, and \\(t\\) for steps. The model we are using is \\[ \\begin{align} \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) + \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t}) \\end{align} \\] References: - Brownian motion and random walks. [cited 13 Mar 2024]. Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html
- Contributors to Wikimedia projects. Brownian motion. In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024]. Available: https://en.wikipedia.org/wiki/Brownian_motion
Attributes: Name Type Description sigma float base standard deviation to be used to compute the variance delta_t float time granunality of the motion Source code in hamilflow/models/brownian_motion.py class BrownianMotionSystem(BaseModel):\n r\"\"\"Definition of the Brownian Motion system\n\n For consistency, we always use\n $\\mathbf x$ for displacement, and\n $t$ for steps. The model we are using is\n\n $$\n \\begin{align}\n \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) +\n \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t})\n \\end{align}\n $$\n\n References:\n\n 1. Brownian motion and random walks. [cited 13 Mar 2024].\n Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html\n 2. Contributors to Wikimedia projects. Brownian motion.\n In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024].\n Available: https://en.wikipedia.org/wiki/Brownian_motion\n\n :cvar sigma: base standard deviation\n to be used to compute the variance\n :cvar delta_t: time granunality of the motion\n \"\"\"\n\n sigma: float = Field(ge=0.0)\n delta_t: float = Field(ge=0.0, default=1.0)\n\n @computed_field # type: ignore[misc]\n @cached_property\n def gaussian_scale(self) -> float:\n \"\"\"The scale (standard deviation) of the Gaussian term\n in Brownian motion\n \"\"\"\n return self.sigma**2 * self.delta_t\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotionSystem.gaussian_scale","title":"gaussian_scale: float cached property ","text":"The scale (standard deviation) of the Gaussian term in Brownian motion "},{"location":"references/models/harmonic_oscillator/","title":"Harmonic Oscillator","text":""},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillator","title":"ComplexSimpleHarmonicOscillator ","text":"Generate time series data for a complex simple harmonic oscillator. Parameters: Name Type Description Default system Mapping[str, float] all the params that defines the complex harmonic oscillator. required initial_condition Mapping[str, tuple[float, float]] the initial condition of the complex harmonic oscillator. required Source code in hamilflow/models/harmonic_oscillator.py class ComplexSimpleHarmonicOscillator:\n r\"\"\"Generate time series data for a complex simple harmonic oscillator.\n\n :param system: all the params that defines the complex harmonic oscillator.\n :param initial_condition: the initial condition of the complex harmonic oscillator.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, tuple[float, float]],\n ) -> None:\n self.system = HarmonicOscillatorSystem.model_validate(system)\n self.initial_condition = ComplexSimpleHarmonicOscillatorIC.model_validate(\n initial_condition\n )\n if self.system.type != \"simple\":\n raise ValueError(\n f\"System is not a Simple Harmonic Oscillator: {self.system}\"\n )\n\n @cached_property\n def definition(\n self,\n ) -> dict[str, dict[str, float | tuple[float, float]]]:\n \"\"\"model params and initial conditions defined as a dictionary.\"\"\"\n\n return dict(\n system=self.system.model_dump(),\n initial_condition=self.initial_condition.model_dump(),\n )\n\n def _z(\n self, t: \"Sequence[float] | ArrayLike[float] | ArrayLike[float]\"\n ) -> ArrayLike:\n r\"\"\"Solution to complex simple harmonic oscillators:\n\n $$\n x(t) = x_+ \\exp(-\\mathbb{i} (\\omega t + \\phi_+)) + x_- \\exp(+\\mathbb{i} (\\omega t + \\phi_-)).\n $$\n \"\"\"\n t = np.array(t, copy=False)\n omega = self.system.omega\n x0, phi = self.initial_condition.x0, self.initial_condition.phi\n phases = -omega * t - phi[0], omega * t + phi[1]\n return x0[0] * np.exp(1j * phases[0]) + x0[1] * np.exp(1j * phases[1])\n\n def __call__(\n self, t: \"Sequence[float] | ArrayLike[float] | ArrayLike[float]\"\n ) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time.\n\n :param t: time(s).\n \"\"\"\n t = t if isinstance(t, (Sequence, np.ndarray)) else [t]\n data = self._z(t)\n\n return pd.DataFrame({\"t\": t, \"z\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillator.definition","title":"definition: dict[str, dict[str, float | tuple[float, float]]] cached property ","text":"model params and initial conditions defined as a dictionary. "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillator.__call__","title":"__call__(t) ","text":"Generate time series data for the harmonic oscillator. Returns a list of floats representing the displacement at each time. Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] | ArrayLike[float] time(s). required Source code in hamilflow/models/harmonic_oscillator.py def __call__(\n self, t: \"Sequence[float] | ArrayLike[float] | ArrayLike[float]\"\n) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time.\n\n :param t: time(s).\n \"\"\"\n t = t if isinstance(t, (Sequence, np.ndarray)) else [t]\n data = self._z(t)\n\n return pd.DataFrame({\"t\": t, \"z\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillatorIC","title":"ComplexSimpleHarmonicOscillatorIC ","text":" Bases: BaseModel The initial condition for a complex harmonic oscillator Attributes: Name Type Description x0 tuple[float, float] the initial displacements phi tuple[float, float] initial phases Source code in hamilflow/models/harmonic_oscillator.py class ComplexSimpleHarmonicOscillatorIC(BaseModel):\n \"\"\"The initial condition for a complex harmonic oscillator\n\n :cvar x0: the initial displacements\n :cvar phi: initial phases\n \"\"\"\n\n x0: tuple[float, float] = Field()\n phi: tuple[float, float] = Field(default=(0, 0))\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.DampedHarmonicOscillator","title":"DampedHarmonicOscillator ","text":" Bases: HarmonicOscillatorBase Generate time series data for a damped harmonic oscillator. The equation for a general un-driven harmonic oscillator is12 \\[ \\frac{\\mathrm d x^2}{\\mathrm d t^2} + 2\\zeta \\omega \\frac{\\mathrm d x}{\\mathrm dt} + \\omega^2 x = 0, \\] where \\(x\\) is the displacement, \\(\\omega\\) is the angular frequency of an undamped oscillator (\\(\\zeta=0\\)), and \\(\\zeta\\) is the damping ratio. The solution to the above harmonic oscillator is \\[ x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right) e^{-\\zeta \\omega t}, \\] where \\[ \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}. \\] To use this generator, params = {\"omega\": omega, \"zeta\"=0.2}\n\nho = DampedHarmonicOscillator(params=params)\n\ndf = ho(n_periods=1, n_samples_per_period=10)\n df will be a pandas dataframe with two columns: t and x . -
Contributors to Wikimedia projects. Harmonic oscillator. In: Wikipedia [Internet]. 18 Feb 2024 [cited 20 Feb 2024]. Available: https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator \u21a9 -
Libretexts. 5.3: General Solution for the Damped Harmonic Oscillator. Libretexts. 13 Apr 2021. Available: https://t.ly/cWTIo. Accessed 20 Feb 2024.\u00a0\u21a9 Parameters: Name Type Description Default system Mapping[str, float] all the params that defines the harmonic oscillator. required initial_condition Mapping[str, float] | None the initial condition of the harmonic oscillator. None Source code in hamilflow/models/harmonic_oscillator.py class DampedHarmonicOscillator(HarmonicOscillatorBase):\n r\"\"\"Generate time series data for a [damped harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator).\n\n The equation for a general un-driven harmonic oscillator is[^wiki_ho][^libretext_ho]\n\n $$\n \\frac{\\mathrm d x^2}{\\mathrm d t^2} + 2\\zeta \\omega \\frac{\\mathrm d x}{\\mathrm dt} + \\omega^2 x = 0,\n $$\n\n where $x$ is the displacement, $\\omega$ is the angular frequency of an undamped oscillator ($\\zeta=0$),\n and $\\zeta$ is the damping ratio.\n\n [^wiki_ho]: Contributors to Wikimedia projects. Harmonic oscillator.\n In: Wikipedia [Internet]. 18 Feb 2024 [cited 20 Feb 2024].\n Available: https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator\n\n [^libretext_ho]: Libretexts. 5.3: General Solution for the Damped Harmonic Oscillator. Libretexts. 13 Apr 2021.\n Available: https://t.ly/cWTIo. Accessed 20 Feb 2024.\n\n\n The solution to the above harmonic oscillator is\n\n $$\n x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}.\n $$\n\n To use this generator,\n\n ```python\n params = {\"omega\": omega, \"zeta\"=0.2}\n\n ho = DampedHarmonicOscillator(params=params)\n\n df = ho(n_periods=1, n_samples_per_period=10)\n ```\n\n `df` will be a pandas dataframe with two columns: `t` and `x`.\n\n :param system: all the params that defines the harmonic oscillator.\n :param initial_condition: the initial condition of the harmonic oscillator.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ) -> None:\n super().__init__(system, initial_condition)\n if self.system.type == \"simple\":\n raise ValueError(\n f\"System is not a Damped Harmonic Oscillator: {self.system}\\n\"\n f\"This is a simple harmonic oscillator, use `SimpleHarmonicOscillator`.\"\n )\n\n def _x_under_damped(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to under damped harmonic oscillators:\n\n $$\n x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}.\n $$\n \"\"\"\n omega_damp = self.system.omega * np.sqrt(1 - self.system.zeta)\n return (\n self.initial_condition.x0 * np.cos(omega_damp * t)\n + (\n self.system.zeta * self.system.omega * self.initial_condition.x0\n + self.initial_condition.v0\n )\n / omega_damp\n * np.sin(omega_damp * t)\n ) * np.exp(-self.system.zeta * self.system.omega * t)\n\n def _x_critical_damped(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to critical damped harmonic oscillators:\n\n $$\n x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}.\n $$\n \"\"\"\n return self.initial_condition.x0 * np.exp(\n -self.system.zeta * self.system.omega * t\n )\n\n def _x_over_damped(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to over harmonic oscillators:\n\n $$\n x(t) = \\left( x_0 \\cosh(\\Gamma t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Gamma} \\sinh(\\Gamma t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Gamma = \\omega\\sqrt{ \\zeta^2 - 1 }.\n $$\n \"\"\"\n gamma_damp = self.system.omega * np.sqrt(self.system.zeta - 1)\n\n return (\n self.initial_condition.x0 * np.cosh(gamma_damp * t)\n + (\n self.system.zeta * self.system.omega * self.initial_condition.x0\n + self.initial_condition.v0\n )\n / gamma_damp\n * np.sinh(gamma_damp * t)\n ) * np.exp(-self.system.zeta * self.system.omega * t)\n\n def _x(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to damped harmonic oscillators.\"\"\"\n t = np.array(t, copy=False)\n if self.system.type == \"under_damped\":\n x = self._x_under_damped(t)\n elif self.system.type == \"over_damped\":\n x = self._x_over_damped(t)\n elif self.system.type == \"critical_damped\":\n x = self._x_critical_damped(t)\n else:\n raise ValueError(\n \"System type is not damped harmonic oscillator: {self.system.type}\"\n )\n\n return x\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorBase","title":"HarmonicOscillatorBase ","text":" Bases: ABC Base class to generate time series data for a harmonic oscillator. Parameters: Name Type Description Default system Mapping[str, float] all the params that defines the harmonic oscillator. required initial_condition Mapping[str, float] | None the initial condition of the harmonic oscillator. None Source code in hamilflow/models/harmonic_oscillator.py class HarmonicOscillatorBase(ABC):\n r\"\"\"Base class to generate time series data\n for a [harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator).\n\n :param system: all the params that defines the harmonic oscillator.\n :param initial_condition: the initial condition of the harmonic oscillator.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ) -> None:\n initial_condition = initial_condition or {}\n self.system = HarmonicOscillatorSystem.model_validate(system)\n self.initial_condition = HarmonicOscillatorIC.model_validate(initial_condition)\n\n @cached_property\n def definition(self) -> dict[str, dict[str, float]]:\n \"\"\"model params and initial conditions defined as a dictionary.\"\"\"\n return {\n \"system\": self.system.model_dump(),\n \"initial_condition\": self.initial_condition.model_dump(),\n }\n\n @abstractmethod\n def _x(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to simple harmonic oscillators.\"\"\"\n ...\n\n def __call__(self, n_periods: int, n_samples_per_period: int) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time step.\n\n :param n_periods: Number of periods to generate.\n :param n_samples_per_period: Number of samples per period.\n \"\"\"\n time_delta = self.system.period / n_samples_per_period\n time_steps = np.arange(0, n_periods * n_samples_per_period) * time_delta\n\n data = self._x(time_steps)\n\n return pd.DataFrame({\"t\": time_steps, \"x\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorBase.definition","title":"definition: dict[str, dict[str, float]] cached property ","text":"model params and initial conditions defined as a dictionary. "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorBase.__call__","title":"__call__(n_periods, n_samples_per_period) ","text":"Generate time series data for the harmonic oscillator. Returns a list of floats representing the displacement at each time step. Parameters: Name Type Description Default n_periods int Number of periods to generate. required n_samples_per_period int Number of samples per period. required Source code in hamilflow/models/harmonic_oscillator.py def __call__(self, n_periods: int, n_samples_per_period: int) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time step.\n\n :param n_periods: Number of periods to generate.\n :param n_samples_per_period: Number of samples per period.\n \"\"\"\n time_delta = self.system.period / n_samples_per_period\n time_steps = np.arange(0, n_periods * n_samples_per_period) * time_delta\n\n data = self._x(time_steps)\n\n return pd.DataFrame({\"t\": time_steps, \"x\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorIC","title":"HarmonicOscillatorIC ","text":" Bases: BaseModel The initial condition for a harmonic oscillator Attributes: Name Type Description x0 float the initial displacement v0 float the initial velocity phi float initial phase Source code in hamilflow/models/harmonic_oscillator.py class HarmonicOscillatorIC(BaseModel):\n \"\"\"The initial condition for a harmonic oscillator\n\n :cvar x0: the initial displacement\n :cvar v0: the initial velocity\n :cvar phi: initial phase\n \"\"\"\n\n x0: float = Field(default=1.0)\n v0: float = Field(default=0.0)\n phi: float = Field(default=0.0)\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem","title":"HarmonicOscillatorSystem ","text":" Bases: BaseModel The params for the harmonic oscillator Attributes: Name Type Description omega float angular frequency of the harmonic oscillator zeta float damping ratio Source code in hamilflow/models/harmonic_oscillator.py class HarmonicOscillatorSystem(BaseModel):\n \"\"\"The params for the harmonic oscillator\n\n :cvar omega: angular frequency of the harmonic oscillator\n :cvar zeta: damping ratio\n \"\"\"\n\n omega: float = Field()\n zeta: float = Field(default=0.0)\n\n @computed_field # type: ignore[misc]\n @cached_property\n def period(self) -> float:\n \"\"\"period of the oscillator\"\"\"\n return 2 * np.pi / self.omega\n\n @computed_field # type: ignore[misc]\n @cached_property\n def frequency(self) -> float:\n \"\"\"frequency of the oscillator\"\"\"\n return 1 / self.period\n\n @computed_field # type: ignore[misc]\n @cached_property\n def type(\n self,\n ) -> Literal[\"simple\", \"under_damped\", \"critical_damped\", \"over_damped\"]:\n \"\"\"which type of harmonic oscillators\"\"\"\n if self.zeta == 0:\n return \"simple\"\n elif self.zeta < 1:\n return \"under_damped\"\n elif self.zeta == 1:\n return \"critical_damped\"\n else:\n return \"over_damped\"\n\n @field_validator(\"zeta\")\n @classmethod\n def check_zeta_non_negative(cls, v: float) -> float:\n if v < 0:\n raise ValueError(f\"Value of zeta should be positive: {v=}\")\n\n return v\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem.frequency","title":"frequency: float cached property ","text":"frequency of the oscillator "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem.period","title":"period: float cached property ","text":"period of the oscillator "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem.type","title":"type: Literal['simple', 'under_damped', 'critical_damped', 'over_damped'] cached property ","text":"which type of harmonic oscillators "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.SimpleHarmonicOscillator","title":"SimpleHarmonicOscillator ","text":" Bases: HarmonicOscillatorBase Generate time series data for a simple harmonic oscillator. In a one dimensional world, a mass \\(m\\), driven by a force \\(F=-kx\\), is described as \\[ \\begin{align} F &= - k x \\\\ F &= m a \\end{align} \\] The mass behaves like a simple harmonic oscillator. In general, the solution to a simple harmonic oscillator is \\[ x(t) = A \\cos(\\omega t + \\phi), \\] where \\(\\omega\\) is the angular frequency, \\(\\phi\\) is the initial phase, and \\(A\\) is the amplitude. To use this generator, params = {\"omega\": omega}\n\nho = SimpleHarmonicOscillator(params=params)\n\ndf = ho(n_periods=1, n_samples_per_period=10)\n df will be a pandas dataframe with two columns: t and x . Source code in hamilflow/models/harmonic_oscillator.py class SimpleHarmonicOscillator(HarmonicOscillatorBase):\n r\"\"\"Generate time series data for a\n [simple harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator).\n\n\n In a one dimensional world, a mass $m$, driven by a force $F=-kx$, is described as\n\n $$\n \\begin{align}\n F &= - k x \\\\\n F &= m a\n \\end{align}\n $$\n\n The mass behaves like a simple harmonic oscillator.\n\n In general, the solution to a simple harmonic oscillator is\n\n $$\n x(t) = A \\cos(\\omega t + \\phi),\n $$\n\n where $\\omega$ is the angular frequency, $\\phi$ is the initial phase, and $A$ is the amplitude.\n\n\n To use this generator,\n\n ```python\n params = {\"omega\": omega}\n\n ho = SimpleHarmonicOscillator(params=params)\n\n df = ho(n_periods=1, n_samples_per_period=10)\n ```\n\n `df` will be a pandas dataframe with two columns: `t` and `x`.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ) -> None:\n super().__init__(system, initial_condition)\n if self.system.type != \"simple\":\n raise ValueError(\n f\"System is not a Simple Harmonic Oscillator: {self.system}\"\n )\n\n def _x(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray:\n r\"\"\"Solution to simple harmonic oscillators:\n\n $$\n x(t) = x_0 \\cos(\\omega t + \\phi).\n $$\n \"\"\"\n return self.initial_condition.x0 * np.cos(\n self.system.omega * np.array(t, copy=False) + self.initial_condition.phi\n )\n "},{"location":"references/models/harmonic_oscillator_chain/","title":"Harmonic Oscillator Chain","text":""},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain","title":"HarmonicOscillatorsChain ","text":"Generate time series data for a coupled harmonic oscillator chain with periodic boundary condition. A one-dimensional circle of \\(N\\) interacting harmonic oscillators can be described by the Lagrangian action \\(\\(S_L[x_i] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\{ \\sum_{i=0}^{N-1} \\frac{1}{2}m \\dot x_i^2 - \\frac{1}{2}m\\omega^2\\left(x_i - x_{i+1}\\right)^2 \\right\\}\\,,\\)\\) where \\(x_N \\coloneqq x_0\\). This system can be solved in terms of travelling waves, obtained by discrete Fourier transform. Since the original degrees of freedom are real, the initial conditions of the propagating waves need to satisfy \\(Y_k = Y^*_{-k \\mod N}\\), see Wikipedia. Source code in hamilflow/models/harmonic_oscillator_chain.py class HarmonicOscillatorsChain:\n r\"\"\"Generate time series data for a coupled harmonic oscillator chain\n with periodic boundary condition.\n\n A one-dimensional circle of $N$ interacting harmonic oscillators can be described by the Lagrangian action\n $$S_L[x_i] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\{ \\sum_{i=0}^{N-1} \\frac{1}{2}m \\dot x_i^2 - \\frac{1}{2}m\\omega^2\\left(x_i - x_{i+1}\\right)^2 \\right\\}\\,,$$\n where $x_N \\coloneqq x_0$.\n\n This system can be solved in terms of _travelling waves_, obtained by discrete Fourier transform.\n\n Since the original degrees of freedom are real, the initial conditions of the propagating waves need to satisfy\n $Y_k = Y^*_{-k \\mod N}$, see [Wikipedia](https://en.wikipedia.org/wiki/Discrete_Fourier_transform#DFT_of_real_and_purely_imaginary_signals).\n \"\"\"\n\n def __init__(\n self,\n omega: float,\n initial_conditions: Sequence[Mapping[str, float | tuple[float, float]]],\n odd_dof: bool,\n ) -> None:\n \"\"\"Instantiate an oscillator chain.\n\n :param omega: frequence parameter\n :param initial_conditions: a sequence of initial conditions on the Fourier modes.\n The first element in the sequence is that of the zero mode, taking a position and a velocity.\n Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases.\n :param odd_dof: The system will have `2 * len(initial_conditions) + int(odd_dof) - 2` degrees of freedom.\n \"\"\"\n self.n_dof = 2 * len(initial_conditions) + odd_dof - 2\n if not odd_dof:\n prefix = \"For even degrees of freedom, \"\n if self.n_dof == 0:\n raise ValueError(prefix + \"at least 1 travelling wave is needed\")\n amp = cast(tuple[float, float], initial_conditions[-1][\"amp\"])\n if amp[0] != amp[1]:\n msg = \"k == N // 2 must have equal positive and negative amplitudes.\"\n raise ValueError(prefix + msg)\n self.omega = omega\n self.odd_dof = odd_dof\n\n self.free_mode = FreeParticle(cast(Mapping[str, float], initial_conditions[0]))\n\n self.independent_csho_modes = [\n self._sho_factory(\n k,\n cast(tuple[float, float], ic[\"amp\"]),\n cast(tuple[float, float] | None, ic.get(\"phi\")),\n )\n for k, ic in enumerate(initial_conditions[1:], 1)\n ]\n\n def _sho_factory(\n self,\n k: int,\n amp: tuple[float, float],\n phi: tuple[float, float] | None = None,\n ) -> ComplexSimpleHarmonicOscillator:\n return ComplexSimpleHarmonicOscillator(\n dict(\n omega=2 * self.omega * np.sin(np.pi * k / self.n_dof),\n ),\n dict(x0=amp) | (dict(phi=phi) if phi else {}),\n )\n\n @cached_property\n def definition(\n self,\n ) -> dict[\n str,\n float\n | int\n | dict[str, dict[str, float | list[float]]]\n | list[dict[str, dict[str, float | tuple[float, float]]]],\n ]:\n \"\"\"model params and initial conditions defined as a dictionary.\"\"\"\n return dict(\n omega=self.omega,\n n_dof=self.n_dof,\n free_mode=self.free_mode.definition,\n independent_csho_modes=[\n rwm.definition for rwm in self.independent_csho_modes\n ],\n )\n\n def _z(\n self, t: \"Sequence[float] | ArrayLike[float]\"\n ) -> tuple[np.ndarray, np.ndarray]:\n t = np.array(t, copy=False).reshape(-1)\n all_travelling_waves = [self.free_mode._x(t).reshape(1, -1)]\n\n if self.independent_csho_modes:\n independent_cshos = np.array(\n [o._z(t) for o in self.independent_csho_modes], copy=False\n )\n all_travelling_waves.extend(\n (independent_cshos, independent_cshos[::-1].conj())\n if self.odd_dof\n else (\n independent_cshos[:-1],\n independent_cshos[[-1]],\n independent_cshos[-2::-1].conj(),\n )\n )\n\n travelling_waves = np.concatenate(all_travelling_waves)\n original_zs = ifft(travelling_waves, axis=0, norm=\"ortho\")\n return original_zs, travelling_waves\n\n def _x(\n self, t: \"Sequence[float] | ArrayLike[float]\"\n ) -> tuple[np.ndarray, np.ndarray]:\n original_xs, travelling_waves = self._z(t)\n\n return np.real(original_xs), travelling_waves\n\n def __call__(self, t: \"Sequence[float] | ArrayLike[float]\") -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator chain.\n\n Returns float(s) representing the displacement at the given time(s).\n\n :param t: time.\n \"\"\"\n original_xs, travelling_waves = self._x(t)\n data = {\n f\"{name}{i}\": values\n for name, xs in zip((\"x\", \"y\"), (original_xs, travelling_waves))\n for i, values in enumerate(xs)\n }\n\n return pd.DataFrame(data, index=t)\n "},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain.definition","title":"definition: dict[str, float | int | dict[str, dict[str, float | list[float]]] | list[dict[str, dict[str, float | tuple[float, float]]]]] cached property ","text":"model params and initial conditions defined as a dictionary. "},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain.__call__","title":"__call__(t) ","text":"Generate time series data for the harmonic oscillator chain. Returns float(s) representing the displacement at the given time(s). Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] time. required Source code in hamilflow/models/harmonic_oscillator_chain.py def __call__(self, t: \"Sequence[float] | ArrayLike[float]\") -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator chain.\n\n Returns float(s) representing the displacement at the given time(s).\n\n :param t: time.\n \"\"\"\n original_xs, travelling_waves = self._x(t)\n data = {\n f\"{name}{i}\": values\n for name, xs in zip((\"x\", \"y\"), (original_xs, travelling_waves))\n for i, values in enumerate(xs)\n }\n\n return pd.DataFrame(data, index=t)\n "},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain.__init__","title":"__init__(omega, initial_conditions, odd_dof) ","text":"Instantiate an oscillator chain. Parameters: Name Type Description Default omega float frequence parameter required initial_conditions Sequence[Mapping[str, float | tuple[float, float]]] a sequence of initial conditions on the Fourier modes. The first element in the sequence is that of the zero mode, taking a position and a velocity. Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases. required odd_dof bool The system will have 2 * len(initial_conditions) + int(odd_dof) - 2 degrees of freedom. required Source code in hamilflow/models/harmonic_oscillator_chain.py def __init__(\n self,\n omega: float,\n initial_conditions: Sequence[Mapping[str, float | tuple[float, float]]],\n odd_dof: bool,\n) -> None:\n \"\"\"Instantiate an oscillator chain.\n\n :param omega: frequence parameter\n :param initial_conditions: a sequence of initial conditions on the Fourier modes.\n The first element in the sequence is that of the zero mode, taking a position and a velocity.\n Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases.\n :param odd_dof: The system will have `2 * len(initial_conditions) + int(odd_dof) - 2` degrees of freedom.\n \"\"\"\n self.n_dof = 2 * len(initial_conditions) + odd_dof - 2\n if not odd_dof:\n prefix = \"For even degrees of freedom, \"\n if self.n_dof == 0:\n raise ValueError(prefix + \"at least 1 travelling wave is needed\")\n amp = cast(tuple[float, float], initial_conditions[-1][\"amp\"])\n if amp[0] != amp[1]:\n msg = \"k == N // 2 must have equal positive and negative amplitudes.\"\n raise ValueError(prefix + msg)\n self.omega = omega\n self.odd_dof = odd_dof\n\n self.free_mode = FreeParticle(cast(Mapping[str, float], initial_conditions[0]))\n\n self.independent_csho_modes = [\n self._sho_factory(\n k,\n cast(tuple[float, float], ic[\"amp\"]),\n cast(tuple[float, float] | None, ic.get(\"phi\")),\n )\n for k, ic in enumerate(initial_conditions[1:], 1)\n ]\n "},{"location":"references/models/pendulum/","title":"Pendulum","text":""},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum","title":"Pendulum ","text":"Generate time series data for a pendulum. We describe a generic pendulum system by the Lagrangian action $$ S_L[\\theta] = I \\int_{t_0}^{t_1} \\mathbb{d}t \\left\\{\\frac{1}{2} \\dot\\theta^2 + \\omega_0^2 \\cos\\theta \\right\\}\\,, $$ where \\(\\theta\\) is the angle from the vertical to the pendulum; \\(I\\) is the inertia parameter introduced for dimensional reasons, and \\(\\omega_0\\) the frequency parameter. Details are collected in the tutorial. Source code in hamilflow/models/pendulum.py class Pendulum:\n r\"\"\"Generate time series data for a pendulum.\n\n We describe a generic pendulum system by the Lagrangian action\n $$\n S_L\\[\\theta\\] = I \\int_{t_0}^{t_1} \\mathbb{d}t\n \\left\\\\{\\frac{1}{2} \\dot\\theta^2 + \\omega_0^2 \\cos\\theta \\right\\\\}\\,,\n $$\n where $\\theta$ is the _angle_ from the vertical to the pendulum;\n $I$ is the _inertia parameter_ introduced for dimensional reasons,\n and $\\omega_0$ the _frequency parameter_.\n\n Details are collected in the tutorial.\n \"\"\"\n\n def __init__(\n self,\n system: float | Mapping[str, float],\n initial_condition: float | Mapping[str, float],\n ) -> None:\n if isinstance(system, (float, int)):\n system = {\"omega0\": system}\n if isinstance(initial_condition, (float, int)):\n initial_condition = {\"theta0\": initial_condition}\n self.system = PendulumSystem.model_validate(system)\n self.initial_condition = PendulumIC.model_validate(initial_condition)\n\n @cached_property\n def definition(self) -> dict[str, float]:\n \"\"\"Model params and initial conditions defined as a dictionary.\"\"\"\n return dict(\n system=self.system.model_dump(),\n initial_condition=self.initial_condition.model_dump(),\n )\n\n @property\n def omega0(self) -> float:\n return self.system.omega0\n\n @property\n def _k(self) -> float:\n return self.initial_condition.k\n\n @property\n def _math_m(self) -> float:\n return self._k**2\n\n @cached_property\n def freq(self) -> float:\n r\"\"\"Frequency.\n\n :return: $\\frac{\\pi}{2K(k^2)}\\omega_0$, where\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n return math.pi * self.omega0 / (2 * ellipk(self._math_m))\n\n @cached_property\n def period(self) -> float:\n r\"\"\"Period.\n\n :return: $\\frac{4K(k^2)}{\\omega_0}$, where\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n return 4 * ellipk(self._math_m) / self.omega0\n\n def _math_u(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n return self.omega0 * np.array(t, copy=False)\n\n def u(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"The convenient generalised coordinate $u$,\n $\\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{\\sin\\frac{\\theta_0}{2}}$.\n\n :param t: time\n :return: $u(t) = \\mathrm{am}\\!\\big(\\omega_0 t + K(k^2), k^2\\big)$, where\n $\\mathrm{am}(x, k)$ is [Jacobi's amplitude function](https://dlmf.nist.gov/22.16#E1),\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n _, _, _, ph = ellipj(self._math_u(t) + ellipk(self._math_m), self._math_m)\n\n return ph\n\n def theta(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"Angle $\\theta$.\n\n :param t: time\n :return: $\\theta(t) = 2\\arcsin\\!\\big(k\\cdot\\mathrm{cd}(\\omega_0 t, k^2)\\big)$, where\n $\\mathrm{cd}(z, k)$ is a [Jacobian elliptic function](https://dlmf.nist.gov/22.2#E8)\n \"\"\"\n _, cn, dn, _ = ellipj(self._math_u(t), self._math_m)\n\n return 2 * np.arcsin(cn / dn * self._k)\n\n def __call__(self, n_periods: int, n_samples_per_period: int) -> pd.DataFrame:\n time_delta = self.period / n_samples_per_period\n time_steps = np.arange(0, n_periods * n_samples_per_period) * time_delta\n\n thetas = self.theta(time_steps)\n us = self.u(time_steps)\n\n return pd.DataFrame(dict(t=time_steps, x=thetas, u=us))\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.definition","title":"definition: dict[str, float] cached property ","text":"Model params and initial conditions defined as a dictionary. "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.freq","title":"freq: float cached property ","text":"Frequency. Returns: Type Description float \\(\\frac{\\pi}{2K(k^2)}\\omega_0\\), where \\(K(m)\\) is Legendre's complete elliptic integral of the first kind "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.period","title":"period: float cached property ","text":"Period. Returns: Type Description float \\(\\frac{4K(k^2)}{\\omega_0}\\), where \\(K(m)\\) is Legendre's complete elliptic integral of the first kind "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.theta","title":"theta(t) ","text":"Angle \\(\\theta\\). Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] time required Returns: Type Description ndarray[float] \\(\\theta(t) = 2\\arcsin\\!\\big(k\\cdot\\mathrm{cd}(\\omega_0 t, k^2)\\big)\\), where \\(\\mathrm{cd}(z, k)\\) is a Jacobian elliptic function Source code in hamilflow/models/pendulum.py def theta(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"Angle $\\theta$.\n\n :param t: time\n :return: $\\theta(t) = 2\\arcsin\\!\\big(k\\cdot\\mathrm{cd}(\\omega_0 t, k^2)\\big)$, where\n $\\mathrm{cd}(z, k)$ is a [Jacobian elliptic function](https://dlmf.nist.gov/22.2#E8)\n \"\"\"\n _, cn, dn, _ = ellipj(self._math_u(t), self._math_m)\n\n return 2 * np.arcsin(cn / dn * self._k)\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.u","title":"u(t) ","text":"The convenient generalised coordinate \\(u\\), \\(\\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{\\sin\\frac{\\theta_0}{2}}\\). Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] time required Returns: Type Description ndarray[float] \\(u(t) = \\mathrm{am}\\!\\big(\\omega_0 t + K(k^2), k^2\\big)\\), where \\(\\mathrm{am}(x, k)\\) is Jacobi's amplitude function, \\(K(m)\\) is Legendre's complete elliptic integral of the first kind Source code in hamilflow/models/pendulum.py def u(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"The convenient generalised coordinate $u$,\n $\\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{\\sin\\frac{\\theta_0}{2}}$.\n\n :param t: time\n :return: $u(t) = \\mathrm{am}\\!\\big(\\omega_0 t + K(k^2), k^2\\big)$, where\n $\\mathrm{am}(x, k)$ is [Jacobi's amplitude function](https://dlmf.nist.gov/22.16#E1),\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n _, _, _, ph = ellipj(self._math_u(t) + ellipk(self._math_m), self._math_m)\n\n return ph\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.PendulumIC","title":"PendulumIC ","text":" Bases: BaseModel The initial condition for a pendulum. Parameters: Name Type Description Default theta0 \\(-\\frac{\\pi}{2} \\le \\theta_0 \\le \\frac{\\pi}{2}\\), the initial angle required Source code in hamilflow/models/pendulum.py class PendulumIC(BaseModel):\n r\"\"\"The initial condition for a pendulum.\n\n :param theta0: $-\\frac{\\pi}{2} \\le \\theta_0 \\le \\frac{\\pi}{2}$, the\n initial angle\n \"\"\"\n\n theta0: float = Field(ge=-math.pi / 2, le=math.pi / 2, frozen=True)\n\n @computed_field # type: ignore[misc]\n @cached_property\n def k(self) -> float:\n r\"\"\"A convenient number for elliptic functions.\n\n :return: $\\sin\\frac{\\theta_0}{2}$\n \"\"\"\n return math.sin(self.theta0 / 2)\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.PendulumIC.k","title":"k: float cached property ","text":"A convenient number for elliptic functions. Returns: Type Description float \\(\\sin\\frac{\\theta_0}{2}\\) "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.PendulumSystem","title":"PendulumSystem ","text":" Bases: BaseModel The params for the pendulum. Parameters: Name Type Description Default omega0 \\(\\omega_0 \\coloneqq \\sqrt{\\frac{U}{I}} > 0\\), frequency parameter required Source code in hamilflow/models/pendulum.py class PendulumSystem(BaseModel):\n r\"\"\"The params for the pendulum.\n\n :param omega0: $\\omega_0 \\coloneqq \\sqrt{\\frac{U}{I}} > 0$, frequency\n parameter\n \"\"\"\n\n omega0: float = Field(gt=0.0, frozen=True)\n "},{"location":"tutorials/","title":"Tutorials","text":"We provide some tutorials to help you get started. "},{"location":"tutorials/brownian_motion/","title":"Brownian Motion","text":"In\u00a0[1]: Copied! import plotly.express as px\n\nfrom hamilflow.models.brownian_motion import BrownianMotion\n import plotly.express as px from hamilflow.models.brownian_motion import BrownianMotion In\u00a0[2]: Copied! bm_1d = BrownianMotion(\n system={\n \"sigma\": 1,\n \"delta_t\": 1,\n },\n initial_condition={\"x0\": 0},\n)\n bm_1d = BrownianMotion( system={ \"sigma\": 1, \"delta_t\": 1, }, initial_condition={\"x0\": 0}, ) Call the model to generate 1000 steps. In\u00a0[3]: Copied! df_1d = bm_1d(n_steps=1000)\n df_1d = bm_1d(n_steps=1000) In\u00a0[4]: Copied! px.line(df_1d, x=\"t\", y=\"x_0\")\n px.line(df_1d, x=\"t\", y=\"x_0\") In\u00a0[5]: Copied! bm_2d = BrownianMotion(\n system={\n \"sigma\": 1,\n \"delta_t\": 1,\n },\n initial_condition={\"x0\": [0, 0]},\n)\n bm_2d = BrownianMotion( system={ \"sigma\": 1, \"delta_t\": 1, }, initial_condition={\"x0\": [0, 0]}, ) We call the model to generate 1000 steps. In\u00a0[6]: Copied! df_2d = bm_2d(n_steps=500)\n df_2d = bm_2d(n_steps=500) In\u00a0[7]: Copied! (\n px.scatter(df_2d, x=\"x_0\", y=\"x_1\", color=\"t\")\n .update_traces(\n mode=\"lines+markers\",\n marker=dict(\n size=2.5,\n ),\n line=dict(width=1),\n )\n .update_yaxes(\n scaleanchor=\"x\",\n scaleratio=1,\n )\n)\n ( px.scatter(df_2d, x=\"x_0\", y=\"x_1\", color=\"t\") .update_traces( mode=\"lines+markers\", marker=dict( size=2.5, ), line=dict(width=1), ) .update_yaxes( scaleanchor=\"x\", scaleratio=1, ) ) In\u00a0[\u00a0]: Copied! \n "},{"location":"tutorials/brownian_motion/#brownian-motion","title":"Brownian Motion\u00b6","text":"Brownian motion describes motion of small particles with stochastic forces applied to them. The math of Brownian motion can be modeled with Wiener process. In this tutorial, we take a simple form of the model and treat the stochastic forces as Gaussian. For consistency, we always use $\\mathbf x$ for displacement, and $t$ for steps. The model we are using is $$ \\begin{align} \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) + \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t}) \\end{align} $$ Read more: - Brownian motion and random walks. [cited 13 Mar 2024]. Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html
- Contributors to Wikimedia projects. Brownian motion. In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024]. Available: https://en.wikipedia.org/wiki/Brownian_motion
hamilflow implemented a Brownian motion model called BrownianMotion . "},{"location":"tutorials/brownian_motion/#1d-brownian-motion","title":"1D Brownian Motion\u00b6","text":""},{"location":"tutorials/brownian_motion/#2d-brownian-motion","title":"2D Brownian Motion\u00b6","text":"Our BrownianMotion model calculates the dimension of the space based on the dimension of the initial condition $x_0$. To create a 2D Brownian motion model, we need the initial condition to be length 2. "},{"location":"tutorials/complex_harmonic_oscillator/","title":"Complex Harmonic Oscillator","text":"In\u00a0[1]: Copied! import math\n\nimport numpy as np\nfrom plotly import express as px\n\nfrom hamilflow.models.harmonic_oscillator import ComplexSimpleHarmonicOscillator\n import math import numpy as np from plotly import express as px from hamilflow.models.harmonic_oscillator import ComplexSimpleHarmonicOscillator In\u00a0[2]: Copied! t = np.linspace(0, 3, 257)\nsystem_specs = dict(omega=2 * math.pi)\n t = np.linspace(0, 3, 257) system_specs = dict(omega=2 * math.pi) In\u00a0[3]: Copied! csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(1, 0)))\n\ndf = csho(t)\n\narr_z = df[\"z\"].to_numpy(copy=False)\n\npx.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))\n csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(1, 0))) df = csho(t) arr_z = df[\"z\"].to_numpy(copy=False) px.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\")) In\u00a0[4]: Copied! csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(0, 1)))\n\ndf = csho(t)\n\narr_z = df[\"z\"].to_numpy(copy=False)\n\npx.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))\n csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(0, 1))) df = csho(t) arr_z = df[\"z\"].to_numpy(copy=False) px.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\")) In\u00a0[5]: Copied! csho = ComplexSimpleHarmonicOscillator(\n system_specs,\n initial_condition=dict(x0=(math.cos(math.pi / 12), math.sin(math.pi / 12))),\n)\n\ndf = csho(t)\n\narr_z = df[\"z\"].to_numpy(copy=False)\n\npx.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))\n csho = ComplexSimpleHarmonicOscillator( system_specs, initial_condition=dict(x0=(math.cos(math.pi / 12), math.sin(math.pi / 12))), ) df = csho(t) arr_z = df[\"z\"].to_numpy(copy=False) px.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))"},{"location":"tutorials/complex_harmonic_oscillator/#complex-harmonic-oscillator","title":"Complex Harmonic Oscillator\u00b6","text":"In this tutorial, we show a few interesting examples of the complex simple harmonic oscillator. "},{"location":"tutorials/complex_harmonic_oscillator/#basic-setups","title":"Basic setups\u00b6","text":""},{"location":"tutorials/complex_harmonic_oscillator/#positive-frequency-circular-polarised-mode","title":"Positive-frequency, circular-polarised mode\u00b6","text":"Also known as the left-rotating mode. "},{"location":"tutorials/complex_harmonic_oscillator/#negative-frequency-circular-polarised-mode","title":"Negative-frequency, circular-polarised mode\u00b6","text":"Also known as the right-rotating mode. "},{"location":"tutorials/complex_harmonic_oscillator/#positive-frequency-elliptic-polarised-mode","title":"Positive-frequency, elliptic-polarised mode\u00b6","text":""},{"location":"tutorials/complex_harmonic_oscillator/#end-of-notebook","title":"End of Notebook\u00b6","text":""},{"location":"tutorials/harmonic_oscillator/","title":"Harmonic Oscillators","text":"In\u00a0[1]: Copied! import pandas as pd\nimport plotly.express as px\n\nfrom hamilflow.models.harmonic_oscillator import (\n DampedHarmonicOscillator,\n SimpleHarmonicOscillator,\n)\n import pandas as pd import plotly.express as px from hamilflow.models.harmonic_oscillator import ( DampedHarmonicOscillator, SimpleHarmonicOscillator, ) In\u00a0[2]: Copied! n_periods = 3\nn_samples_per_period = 200\n n_periods = 3 n_samples_per_period = 200 In\u00a0[3]: Copied! sho_omega = 0.5\n\nsho = SimpleHarmonicOscillator(system={\"omega\": sho_omega})\n sho_omega = 0.5 sho = SimpleHarmonicOscillator(system={\"omega\": sho_omega}) In\u00a0[4]: Copied! df_sho = sho(n_periods=n_periods, n_samples_per_period=n_samples_per_period)\ndf_sho.head()\n df_sho = sho(n_periods=n_periods, n_samples_per_period=n_samples_per_period) df_sho.head() Out[4]: t x 0 0.000000 1.000000 1 0.062832 0.999507 2 0.125664 0.998027 3 0.188496 0.995562 4 0.251327 0.992115 In\u00a0[5]: Copied! px.line(\n df_sho,\n x=\"t\",\n y=\"x\",\n title=rf\"Simple Harmonic Oscillator (omega = {sho_omega})\",\n labels={\n \"x\": r\"Displacement $x(t)$\",\n \"t\": r\"$t$\",\n },\n)\n px.line( df_sho, x=\"t\", y=\"x\", title=rf\"Simple Harmonic Oscillator (omega = {sho_omega})\", labels={ \"x\": r\"Displacement $x(t)$\", \"t\": r\"$t$\", }, ) In\u00a0[6]: Copied! dho_systems = {\n \"Underdamped\": {\"omega\": 0.5, \"zeta\": 0.2},\n \"Critical Damped\": {\"omega\": 0.5, \"zeta\": 1},\n \"Overdamped\": {\n \"omega\": 0.5,\n \"zeta\": 1.2,\n },\n}\n\ndfs_dho = []\n\nfor s_name, s in dho_systems.items():\n\n dfs_dho.append(\n DampedHarmonicOscillator(system=s)(\n n_periods=n_periods, n_samples_per_period=n_samples_per_period\n ).assign(system=rf\"{s_name} (omega = {s.get('omega')}, zeta = {s.get('zeta')})\")\n )\n\nfig = px.line(\n pd.concat(dfs_dho),\n x=\"t\",\n y=\"x\",\n color=\"system\",\n title=rf\"Damped Harmonic Oscillator\",\n labels={\n \"x\": r\"Displacement $x(t)$\",\n \"t\": r\"$t$\",\n },\n)\nfig.update_layout(legend={\"yanchor\": \"top\", \"y\": -0.2, \"xanchor\": \"left\", \"x\": 0})\n dho_systems = { \"Underdamped\": {\"omega\": 0.5, \"zeta\": 0.2}, \"Critical Damped\": {\"omega\": 0.5, \"zeta\": 1}, \"Overdamped\": { \"omega\": 0.5, \"zeta\": 1.2, }, } dfs_dho = [] for s_name, s in dho_systems.items(): dfs_dho.append( DampedHarmonicOscillator(system=s)( n_periods=n_periods, n_samples_per_period=n_samples_per_period ).assign(system=rf\"{s_name} (omega = {s.get('omega')}, zeta = {s.get('zeta')})\") ) fig = px.line( pd.concat(dfs_dho), x=\"t\", y=\"x\", color=\"system\", title=rf\"Damped Harmonic Oscillator\", labels={ \"x\": r\"Displacement $x(t)$\", \"t\": r\"$t$\", }, ) fig.update_layout(legend={\"yanchor\": \"top\", \"y\": -0.2, \"xanchor\": \"left\", \"x\": 0}) In\u00a0[\u00a0]: Copied! \n "},{"location":"tutorials/harmonic_oscillator/#harmonic-oscillators","title":"Harmonic Oscillators\u00b6","text":"In this tutorial, we demo how to generate data of harmonic oscillators. "},{"location":"tutorials/harmonic_oscillator/#simple-harmonic-oscillator","title":"Simple Harmonic Oscillator\u00b6","text":"For an simple harmonic oscillator, the action of a simple harmonic oscillator is $$S_L[x] = \\int_{t_0}^{t_1} \\mathbb{d}t \\left\\{\\frac{1}{2} m \\dot x^2 - \\frac{1}{2} m \\omega^2 x^2 \\right\\}\\,,$$ where the least action principle leads to the following equation of motion, $$ \\ddot x + \\omega^2 x = 0\\,. $$ A simple harmonic oscillator is a periodic motion. "},{"location":"tutorials/harmonic_oscillator/#damped-harmonic-oscillator","title":"Damped Harmonic Oscillator\u00b6","text":"A damped harmonic oscillator is a simple harmonic oscillator with damping force that is proportional to its velocity, $$ \\ddot x + \\omega^2 x = - 2\\xi\\omega \\dot x\\,. $$ In this section, we demonstrate three scenarios of a damped harmonic oscillator. "},{"location":"tutorials/harmonic_oscillator_chain/","title":"Harmonic oscillator circle","text":"In\u00a0[1]: Copied! import math\n\nimport numpy as np\nfrom plotly import express as px\n\nfrom hamilflow.models.harmonic_oscillator_chain import HarmonicOscillatorsChain\n import math import numpy as np from plotly import express as px from hamilflow.models.harmonic_oscillator_chain import HarmonicOscillatorsChain In\u00a0[2]: Copied! t = np.linspace(0, 3, 257)\nomega = 2 * math.pi\npattern_x = r\"x\\d+\"\n t = np.linspace(0, 3, 257) omega = 2 * math.pi pattern_x = r\"x\\d+\" In\u00a0[3]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[4]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(0, 1))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(2 * math.pi, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(0, 1))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(2 * math.pi, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[5]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(0, 0)), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 4\nhoc = HarmonicOscillatorsChain(omega, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(0, 0)), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 4 hoc = HarmonicOscillatorsChain(omega, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[6]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[7]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, False)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, False) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[8]: Copied! ics = [dict(x0=0, v0=2), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, False)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=2), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, False) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})"},{"location":"tutorials/harmonic_oscillator_chain/#harmonic-oscillator-circle","title":"Harmonic oscillator circle\u00b6","text":"In this tutorial, we show a few interesting examples of the harmonic oscillator circle. "},{"location":"tutorials/harmonic_oscillator_chain/#basic-setups","title":"Basic setups\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-right-moving-wave","title":"Fundamental right-moving wave\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-left-moving-wave","title":"Fundamental left-moving wave\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#faster-right-moving-wave","title":"Faster right-moving wave\u00b6","text":"Also known as the first harmonic. "},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-stationary-wave-odd-dof","title":"Fundamental stationary wave, odd dof\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-stationary-wave-even-dof","title":"Fundamental stationary wave, even dof\u00b6","text":"There are stationary nodes at $i = 3, 9$. "},{"location":"tutorials/harmonic_oscillator_chain/#linearly-moving-chain-and-fundamental-right-moving-wave","title":"Linearly moving chain and fundamental right-moving wave\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#mathematical-physical-discription","title":"Mathematical-physical discription\u00b6","text":"A one-dimensional circle of $N$ interacting harmonic oscillators can be described by the Lagrangian action $$S_L[x_i] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\sum_{i=0}^{N-1} \\frac{1}{2}m \\dot x_i^2 - \\frac{1}{2}m\\omega^2\\left(x_i - x_{i+1}\\right)^2 \\right\\\\}\\\\,,$$ where $x_N \\coloneqq x_0$. This system can be solved in terms of travelling waves, obtained by discrete Fourier transform. We can complexify the system $$S_L[x_i] = S_L[x_i, \\phi_j] \\equiv S_L[X^\\ast_i, X_j] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\frac{1}{2}m \\dot X^\\ast_i \\delta_{ij} \\dot X_j - \\frac{1}{2}m X^\\ast_i A_{ij} X_j\\right\\\\}\\\\,,$$ where $A_{ij} / \\omega^2$ is equal to $(-2)$ if $i=j$, $1$ if $|i-j|=1$ or $|i-j|=N$, and $0$ otherwise; $X_i \\coloneqq x_i \\mathbb{e}^{-\\phi_i}$, $X^\\ast_i \\coloneqq x_i \\mathbb{e}^{+\\phi_i}$. $A_{ij}$ can be diagonalised by the inverse discrete Fourier transform $$X_i = (F^{-1})_{ik} Y_k = \\frac{1}{\\sqrt{N}}\\sum_k \\mathbb{e}^{i \\frac{2\\mathbb{\\pi}}{N} k\\mathbb{i}} Y_k\\\\,.$$ Calculating gives $$S_L[X^\\ast_i, X_j] = S_L[Y^\\ast_i, Y_j] = \\sum_{k=0}^{N-1} \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\frac{1}{2}m \\dot Y^\\ast_k \\dot Y_k - \\frac{1}{2}m \\omega^2\\cdot4\\sin^2\\frac{2\\mathbb{\\pi}k}{N} Y^\\ast_k Y_k\\right\\\\}\\\\,.$$ We can arrive at an action for the Fourier modes $$S_L[Y, Y^*] = \\sum_{k=0}^{N-1} \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\frac{1}{2}m \\dot Y_k^* Y_k - \\frac{1}{2}m \\omega^2\\cdot4\\sin^2\\frac{2\\mathbb{\\pi}k}{N} Y_k^* Y_k\\right\\\\}\\\\,.$$ The origional system can then be solved by $N$ complex oscillators. Since the original degrees of freedom are real, the initial conditions of the propagating waves need to satisfy $Y_k = Y^*_{-k \\mod N}$, see Wikipedia. "},{"location":"tutorials/harmonic_oscillator_chain/#end-of-notebook","title":"End of Notebook\u00b6","text":""},{"location":"tutorials/pendulum/","title":"Pendulum","text":"In\u00a0[1]: Copied! import math\n\nimport plotly.express as px\n\nfrom hamilflow.models.pendulum import Pendulum\n import math import plotly.express as px from hamilflow.models.pendulum import Pendulum In\u00a0[2]: Copied! omega0 = 2 * math.pi\ntheta0 = math.pi / 3\n\nn_periods = 2**2\nn_samples_per_period = 2**8\n omega0 = 2 * math.pi theta0 = math.pi / 3 n_periods = 2**2 n_samples_per_period = 2**8 In\u00a0[3]: Copied! pen = Pendulum(system=omega0, initial_condition=theta0)\n pen = Pendulum(system=omega0, initial_condition=theta0) In\u00a0[4]: Copied! df_pen = pen(n_periods=n_periods, n_samples_per_period=n_samples_per_period)\ndf_pen.head()\n df_pen = pen(n_periods=n_periods, n_samples_per_period=n_samples_per_period) df_pen.head() Out[4]: t x u 0 0.000000 1.047198 1.570796 1 0.004192 1.046897 1.593608 2 0.008384 1.045996 1.616424 3 0.012576 1.044494 1.639247 4 0.016768 1.042393 1.662082 In\u00a0[5]: Copied! df_pen.describe()\n df_pen.describe() Out[5]: t x u count 1024.000000 1.024000e+03 1024.000000 mean 2.144268 2.320193e-17 14.124895 std 1.239809 7.453532e-01 7.261249 min 0.000000 -1.047198e+00 1.570796 25% 1.072134 -7.494689e-01 7.848279 50% 2.144268 -3.535251e-16 14.125761 75% 3.216402 7.494689e-01 20.403244 max 4.288536 1.047198e+00 26.680726 In\u00a0[6]: Copied! px.line(\n df_pen,\n x=\"t\",\n y=\"x\",\n title=r\"Simple Harmonic Oscillator ($\\omega_0 = {:.4f})$\".format(omega0),\n labels=dict(x=r\"Angle $\\theta(t)$\", t=r\"Time $t$\"),\n)\n px.line( df_pen, x=\"t\", y=\"x\", title=r\"Simple Harmonic Oscillator ($\\omega_0 = {:.4f})$\".format(omega0), labels=dict(x=r\"Angle $\\theta(t)$\", t=r\"Time $t$\"), )"},{"location":"tutorials/pendulum/#pendulum","title":"Pendulum\u00b6","text":"In this tutorial, we demonstrate how to generate data of a pendulum, and introduce the mathematics of a pendulum. "},{"location":"tutorials/pendulum/#constants","title":"Constants\u00b6","text":""},{"location":"tutorials/pendulum/#a-pendulum","title":"A pendulum\u00b6","text":""},{"location":"tutorials/pendulum/#data","title":"Data\u00b6","text":""},{"location":"tutorials/pendulum/#plot","title":"Plot\u00b6","text":""},{"location":"tutorials/pendulum/#todo","title":"TODO\u00b6","text":" - Compare with a harmonic oscillator in terms of frequency and profile
- Animate the plot as a single pendulum
- Add references to the derivation
- Complete the derivation
"},{"location":"tutorials/pendulum/#mathematical-physical-description","title":"Mathematical-physical description\u00b6","text":""},{"location":"tutorials/pendulum/#lagrangian-action","title":"Lagrangian action\u00b6","text":"We describe a generic pendulum system by the Lagrangian action $$ S_L[\\theta] \\equiv \\int_{t_0}^{t_1} \\mathbb{d}t\\,L(\\theta, \\dot\\theta) \\eqqcolon I \\int_{t_0}^{t_1} \\mathbb{d}t \\left\\{\\frac{1}{2} \\dot\\theta^2 + \\omega_0^2 \\cos\\theta \\right\\}\\,, $$ where $L$ is the Lagrangian; $\\theta$ is the angle from the vertical to the pendulum as the generalised position; $I$ is the inertia parameter, $\\omega_0$ the frequency parameter, and we also call $U \\coloneqq I\\omega_0^2$ the potential parameter. This setup contains both the single and the physical pendula. For a single pendulum, $$ I = m l^2\\,,\\qquad U = mgl\\,, $$ where $m$ is the mass of the pendulum, $l$ is the length of the rod or cord, and $g$ is the gravitational acceleration. "},{"location":"tutorials/pendulum/#integral-of-motion","title":"Integral of motion\u00b6","text":"The Lagrangian action does not contain time $t$ explicitly. As a result, the system is invariant under a variation of time, or $\\mathbb{\\delta}S / \\mathbb{\\delta}{t} = 0$. This gives an integral of motion $$ \\dot\\theta\\frac{\\partial L}{\\partial \\dot\\theta} - L \\equiv E \\eqqcolon I \\omega_0^2 \\cos\\theta_0\\,, $$ where $\\theta_0$ is the initial angle. Substitution gives $$ \\left(\\frac{\\mathbb{d}t}{\\mathbb{d}\\theta}\\right)^2 = \\frac{1}{2\\omega_0^2} \\frac{1}{\\cos\\theta - \\cos\\theta_0}\\,. $$ "},{"location":"tutorials/pendulum/#coordinate-transformation","title":"Coordinate transformation\u00b6","text":"For convenience, introduce the coordinate $u$ and the parameter $k$ $$ \\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{k}\\,,\\qquad k \\coloneqq \\sin\\frac{\\theta_0}{2} \\in [-1, 1]\\,. $$ One arrives at $$ \\left(\\frac{\\mathbb{d}t}{\\mathbb{d}u}\\right)^2 = \\frac{1}{\\omega_0^2} \\frac{1}{1-k^2\\sin^2 u}\\,. $$ The square root of the second factor on the right-hand side makes an elliptic integral. "},{"location":"tutorials/pendulum/#end-of-notebook","title":"End of Notebook\u00b6","text":""}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Documentation for hamilflow ","text":"Generating dataset for physical systems. "},{"location":"#development","title":"Development","text":"We use poetry to manage our python environment. Use poetry install\n to install the requirements. Or run poetry install --with test\n to install the base environment and test environment for development. If this is the first you clone the repo and committing code, run pre-commit install\n first. "},{"location":"changelog/","title":"hamilflow Changelog","text":""},{"location":"changelog/#010-2024-03-21","title":"0.1.0 (2024-03-21)","text":""},{"location":"changelog/#feat","title":"Feat","text":" - model: add brownian motion (#23)
- ops: #18 use commitizen (#21)
"},{"location":"changelog/#fix","title":"Fix","text":" - docs: fix docstrings for pydantic models cvar (#27)
"},{"location":"changelog/#2024-02-18-001","title":"2024-02-18, 0.0.1","text":"Setting up repository. "},{"location":"references/","title":"References","text":""},{"location":"references/models/brownian_motion/","title":"Brownian Motion","text":""},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotion","title":"BrownianMotion ","text":"Brownian motion describes motion of small particles with stochastic forces applied to them. The math of Brownian motion can be modeled with Wiener process. For consistency, we always use \\(\\mathbf x\\) for displacement, and \\(t\\) for steps. The model we are using is \\[ \\begin{align} \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) + \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t}) \\end{align} \\] References: - Brownian motion and random walks. [cited 13 Mar 2024]. Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html
- Contributors to Wikimedia projects. Brownian motion. In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024]. Available: https://en.wikipedia.org/wiki/Brownian_motion
1D Brownian Motion The dimsion of our Brownian motion is specified by the dimension of the initial condition. To simulate a 1D Browian motion, we define the system and initial condition: system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n}\n\ninitial_condition = {\n \"x0\": 0\n}\n The Brownian motion can be simulated using bm = BrownianMotion(system=system, initial_condition=initial_condition)\n\nbm(n_steps=100)\n 2D Brownian Motion To simulate a 2D Browian motion, system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n}\n\ninitial_condition = {\n \"x0\": [0, 0]\n}\n\nbm = BrownianMotion(system=system, initial_condition=initial_condition)\n\nbm(n_steps=100)\n Parameters: Name Type Description Default system Mapping[str, float] the Brownian motion system definition required initial_condition Mapping[str, float] | None the initial condition for the simulation None Source code in hamilflow/models/brownian_motion.py class BrownianMotion:\n r\"\"\"Brownian motion describes motion of small particles\n with stochastic forces applied to them.\n The math of Brownian motion can be modeled\n with Wiener process.\n\n For consistency, we always use\n $\\mathbf x$ for displacement, and\n $t$ for steps. The model we are using is\n\n $$\n \\begin{align}\n \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) +\n \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t})\n \\end{align}\n $$\n\n References:\n\n 1. Brownian motion and random walks. [cited 13 Mar 2024].\n Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html\n 2. Contributors to Wikimedia projects. Brownian motion.\n In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024].\n Available: https://en.wikipedia.org/wiki/Brownian_motion\n\n\n !!! example \"1D Brownian Motion\"\n\n The dimsion of our Brownian motion is specified by\n the dimension of the initial condition.\n\n To simulate a 1D Browian motion, we define the system and initial condition:\n\n ```python\n system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n }\n\n initial_condition = {\n \"x0\": 0\n }\n ```\n\n The Brownian motion can be simulated using\n\n ```python\n bm = BrownianMotion(system=system, initial_condition=initial_condition)\n\n bm(n_steps=100)\n ```\n\n !!! example \"2D Brownian Motion\"\n\n To simulate a 2D Browian motion,\n\n ```python\n system = {\n \"sigma\": 1,\n \"delta_t\": 1,\n }\n\n initial_condition = {\n \"x0\": [0, 0]\n }\n\n bm = BrownianMotion(system=system, initial_condition=initial_condition)\n\n bm(n_steps=100)\n ```\n\n :param system: the Brownian motion system definition\n :param initial_condition: the initial condition for the simulation\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ):\n initial_condition = initial_condition or {}\n self.system = BrownianMotionSystem.model_validate(system)\n self.initial_condition = BrownianMotionIC.model_validate(initial_condition)\n\n @property\n def dim(self) -> int:\n \"\"\"Dimension of the Brownian motion\"\"\"\n return self.initial_condition.x0.size\n\n @property\n def _axis_names(self) -> list[str]:\n return [f\"x_{i}\" for i in range(self.dim)]\n\n def _trajectory(self, n_new_steps: int, seed: int) -> np.ndarray:\n \"\"\"The trajectory of the particle.\n\n We first compute the delta displacement in each step.\n With the displacement at each step, we perform a cumsum\n including the initial coordinate to get the displacement at each step.\n\n :param n_new_steps: number of new steps to simulate, excluding the initial step.\n :param seed: seed for the random generator.\n \"\"\"\n step_history = sp.stats.norm.rvs(\n size=(n_new_steps, self.dim) if self.dim > 1 else n_new_steps,\n scale=self.system.gaussian_scale,\n random_state=np.random.RandomState(seed=seed),\n )\n\n step_history = np.concatenate(\n (np.expand_dims(self.initial_condition.x0, axis=0), step_history)\n )\n\n trajectory = np.cumsum(step_history, axis=0)\n\n return trajectory\n\n def __call__(self, n_steps: int, seed: int = 42) -> pd.DataFrame:\n \"\"\"Simulate the coordinates of the particle\n\n :param n_steps: total number of steps to be simulated, including the inital step.\n :param seed: random generator seed for the stochastic process.\n Use it to reproduce results.\n \"\"\"\n trajectory = self._trajectory(n_new_steps=n_steps - 1, seed=seed)\n\n df = pd.DataFrame(trajectory, columns=self._axis_names)\n\n df[\"t\"] = np.arange(0, n_steps) * self.system.delta_t\n\n return df\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotion.dim","title":"dim: int property ","text":"Dimension of the Brownian motion "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotion.__call__","title":"__call__(n_steps, seed=42) ","text":"Simulate the coordinates of the particle Parameters: Name Type Description Default n_steps int total number of steps to be simulated, including the inital step. required seed int random generator seed for the stochastic process. Use it to reproduce results. 42 Source code in hamilflow/models/brownian_motion.py def __call__(self, n_steps: int, seed: int = 42) -> pd.DataFrame:\n \"\"\"Simulate the coordinates of the particle\n\n :param n_steps: total number of steps to be simulated, including the inital step.\n :param seed: random generator seed for the stochastic process.\n Use it to reproduce results.\n \"\"\"\n trajectory = self._trajectory(n_new_steps=n_steps - 1, seed=seed)\n\n df = pd.DataFrame(trajectory, columns=self._axis_names)\n\n df[\"t\"] = np.arange(0, n_steps) * self.system.delta_t\n\n return df\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotionIC","title":"BrownianMotionIC ","text":" Bases: BaseModel The initial condition for a Brownian motion Attributes: Name Type Description x0 float | Sequence[float] initial displacement of the particle, the diminsion of this initial condition determines the dimension of the model too. Source code in hamilflow/models/brownian_motion.py class BrownianMotionIC(BaseModel):\n \"\"\"The initial condition for a Brownian motion\n\n :cvar x0: initial displacement of the particle,\n the diminsion of this initial condition determines\n the dimension of the model too.\n \"\"\"\n\n x0: float | Sequence[float] = Field(default=1.0)\n\n @field_validator(\"x0\")\n @classmethod\n def check_x0_types(cls, v: float | Sequence[float]) -> np.ndarray:\n if not isinstance(v, (float, int, Sequence)):\n # TODO I do not think this raise can be reached\n raise ValueError(f\"Value of x0 should be int/float/list of int/float: {v=}\")\n\n return np.array(v, copy=False)\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotionSystem","title":"BrownianMotionSystem ","text":" Bases: BaseModel Definition of the Brownian Motion system For consistency, we always use \\(\\mathbf x\\) for displacement, and \\(t\\) for steps. The model we are using is \\[ \\begin{align} \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) + \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t}) \\end{align} \\] References: - Brownian motion and random walks. [cited 13 Mar 2024]. Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html
- Contributors to Wikimedia projects. Brownian motion. In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024]. Available: https://en.wikipedia.org/wiki/Brownian_motion
Attributes: Name Type Description sigma float base standard deviation to be used to compute the variance delta_t float time granunality of the motion Source code in hamilflow/models/brownian_motion.py class BrownianMotionSystem(BaseModel):\n r\"\"\"Definition of the Brownian Motion system\n\n For consistency, we always use\n $\\mathbf x$ for displacement, and\n $t$ for steps. The model we are using is\n\n $$\n \\begin{align}\n \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) +\n \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t})\n \\end{align}\n $$\n\n References:\n\n 1. Brownian motion and random walks. [cited 13 Mar 2024].\n Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html\n 2. Contributors to Wikimedia projects. Brownian motion.\n In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024].\n Available: https://en.wikipedia.org/wiki/Brownian_motion\n\n :cvar sigma: base standard deviation\n to be used to compute the variance\n :cvar delta_t: time granunality of the motion\n \"\"\"\n\n sigma: float = Field(ge=0.0)\n delta_t: float = Field(ge=0.0, default=1.0)\n\n @computed_field # type: ignore[misc]\n @cached_property\n def gaussian_scale(self) -> float:\n \"\"\"The scale (standard deviation) of the Gaussian term\n in Brownian motion\n \"\"\"\n return self.sigma**2 * self.delta_t\n "},{"location":"references/models/brownian_motion/#hamilflow.models.brownian_motion.BrownianMotionSystem.gaussian_scale","title":"gaussian_scale: float cached property ","text":"The scale (standard deviation) of the Gaussian term in Brownian motion "},{"location":"references/models/harmonic_oscillator/","title":"Harmonic Oscillator","text":""},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillator","title":"ComplexSimpleHarmonicOscillator ","text":"Generate time series data for a complex simple harmonic oscillator. Parameters: Name Type Description Default system Mapping[str, float] all the params that defines the complex harmonic oscillator. required initial_condition Mapping[str, tuple[float, float]] the initial condition of the complex harmonic oscillator. required Source code in hamilflow/models/harmonic_oscillator.py class ComplexSimpleHarmonicOscillator:\n r\"\"\"Generate time series data for a complex simple harmonic oscillator.\n\n :param system: all the params that defines the complex harmonic oscillator.\n :param initial_condition: the initial condition of the complex harmonic oscillator.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, tuple[float, float]],\n ) -> None:\n self.system = HarmonicOscillatorSystem.model_validate(system)\n self.initial_condition = ComplexSimpleHarmonicOscillatorIC.model_validate(\n initial_condition\n )\n if self.system.type != \"simple\":\n raise ValueError(\n f\"System is not a Simple Harmonic Oscillator: {self.system}\"\n )\n\n @cached_property\n def definition(\n self,\n ) -> dict[str, dict[str, float | tuple[float, float]]]:\n \"\"\"model params and initial conditions defined as a dictionary.\"\"\"\n\n return dict(\n system=self.system.model_dump(),\n initial_condition=self.initial_condition.model_dump(),\n )\n\n def _z(\n self, t: \"Sequence[float] | ArrayLike[float] | ArrayLike[float]\"\n ) -> ArrayLike:\n r\"\"\"Solution to complex simple harmonic oscillators:\n\n $$\n x(t) = x_+ \\exp(-\\mathbb{i} (\\omega t + \\phi_+)) + x_- \\exp(+\\mathbb{i} (\\omega t + \\phi_-)).\n $$\n \"\"\"\n t = np.array(t, copy=False)\n omega = self.system.omega\n x0, phi = self.initial_condition.x0, self.initial_condition.phi\n phases = -omega * t - phi[0], omega * t + phi[1]\n return x0[0] * np.exp(1j * phases[0]) + x0[1] * np.exp(1j * phases[1])\n\n def __call__(\n self, t: \"Sequence[float] | ArrayLike[float] | ArrayLike[float]\"\n ) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time.\n\n :param t: time(s).\n \"\"\"\n t = t if isinstance(t, (Sequence, np.ndarray)) else [t]\n data = self._z(t)\n\n return pd.DataFrame({\"t\": t, \"z\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillator.definition","title":"definition: dict[str, dict[str, float | tuple[float, float]]] cached property ","text":"model params and initial conditions defined as a dictionary. "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillator.__call__","title":"__call__(t) ","text":"Generate time series data for the harmonic oscillator. Returns a list of floats representing the displacement at each time. Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] | ArrayLike[float] time(s). required Source code in hamilflow/models/harmonic_oscillator.py def __call__(\n self, t: \"Sequence[float] | ArrayLike[float] | ArrayLike[float]\"\n) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time.\n\n :param t: time(s).\n \"\"\"\n t = t if isinstance(t, (Sequence, np.ndarray)) else [t]\n data = self._z(t)\n\n return pd.DataFrame({\"t\": t, \"z\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.ComplexSimpleHarmonicOscillatorIC","title":"ComplexSimpleHarmonicOscillatorIC ","text":" Bases: BaseModel The initial condition for a complex harmonic oscillator Attributes: Name Type Description x0 tuple[float, float] the initial displacements phi tuple[float, float] initial phases Source code in hamilflow/models/harmonic_oscillator.py class ComplexSimpleHarmonicOscillatorIC(BaseModel):\n \"\"\"The initial condition for a complex harmonic oscillator\n\n :cvar x0: the initial displacements\n :cvar phi: initial phases\n \"\"\"\n\n x0: tuple[float, float] = Field()\n phi: tuple[float, float] = Field(default=(0, 0))\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.DampedHarmonicOscillator","title":"DampedHarmonicOscillator ","text":" Bases: HarmonicOscillatorBase Generate time series data for a damped harmonic oscillator. The equation for a general un-driven harmonic oscillator is12 \\[ \\frac{\\mathrm d x^2}{\\mathrm d t^2} + 2\\zeta \\omega \\frac{\\mathrm d x}{\\mathrm dt} + \\omega^2 x = 0, \\] where \\(x\\) is the displacement, \\(\\omega\\) is the angular frequency of an undamped oscillator (\\(\\zeta=0\\)), and \\(\\zeta\\) is the damping ratio. The solution to the above harmonic oscillator is \\[ x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right) e^{-\\zeta \\omega t}, \\] where \\[ \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}. \\] To use this generator, params = {\"omega\": omega, \"zeta\"=0.2}\n\nho = DampedHarmonicOscillator(params=params)\n\ndf = ho(n_periods=1, n_samples_per_period=10)\n df will be a pandas dataframe with two columns: t and x . -
Contributors to Wikimedia projects. Harmonic oscillator. In: Wikipedia [Internet]. 18 Feb 2024 [cited 20 Feb 2024]. Available: https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator \u21a9 -
Libretexts. 5.3: General Solution for the Damped Harmonic Oscillator. Libretexts. 13 Apr 2021. Available: https://t.ly/cWTIo. Accessed 20 Feb 2024.\u00a0\u21a9 Parameters: Name Type Description Default system Mapping[str, float] all the params that defines the harmonic oscillator. required initial_condition Mapping[str, float] | None the initial condition of the harmonic oscillator. None Source code in hamilflow/models/harmonic_oscillator.py class DampedHarmonicOscillator(HarmonicOscillatorBase):\n r\"\"\"Generate time series data for a [damped harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator).\n\n The equation for a general un-driven harmonic oscillator is[^wiki_ho][^libretext_ho]\n\n $$\n \\frac{\\mathrm d x^2}{\\mathrm d t^2} + 2\\zeta \\omega \\frac{\\mathrm d x}{\\mathrm dt} + \\omega^2 x = 0,\n $$\n\n where $x$ is the displacement, $\\omega$ is the angular frequency of an undamped oscillator ($\\zeta=0$),\n and $\\zeta$ is the damping ratio.\n\n [^wiki_ho]: Contributors to Wikimedia projects. Harmonic oscillator.\n In: Wikipedia [Internet]. 18 Feb 2024 [cited 20 Feb 2024].\n Available: https://en.wikipedia.org/wiki/Harmonic_oscillator#Damped_harmonic_oscillator\n\n [^libretext_ho]: Libretexts. 5.3: General Solution for the Damped Harmonic Oscillator. Libretexts. 13 Apr 2021.\n Available: https://t.ly/cWTIo. Accessed 20 Feb 2024.\n\n\n The solution to the above harmonic oscillator is\n\n $$\n x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}.\n $$\n\n To use this generator,\n\n ```python\n params = {\"omega\": omega, \"zeta\"=0.2}\n\n ho = DampedHarmonicOscillator(params=params)\n\n df = ho(n_periods=1, n_samples_per_period=10)\n ```\n\n `df` will be a pandas dataframe with two columns: `t` and `x`.\n\n :param system: all the params that defines the harmonic oscillator.\n :param initial_condition: the initial condition of the harmonic oscillator.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ) -> None:\n super().__init__(system, initial_condition)\n if self.system.type == \"simple\":\n raise ValueError(\n f\"System is not a Damped Harmonic Oscillator: {self.system}\\n\"\n f\"This is a simple harmonic oscillator, use `SimpleHarmonicOscillator`.\"\n )\n\n def _x_under_damped(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to under damped harmonic oscillators:\n\n $$\n x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}.\n $$\n \"\"\"\n omega_damp = self.system.omega * np.sqrt(1 - self.system.zeta)\n return (\n self.initial_condition.x0 * np.cos(omega_damp * t)\n + (\n self.system.zeta * self.system.omega * self.initial_condition.x0\n + self.initial_condition.v0\n )\n / omega_damp\n * np.sin(omega_damp * t)\n ) * np.exp(-self.system.zeta * self.system.omega * t)\n\n def _x_critical_damped(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to critical damped harmonic oscillators:\n\n $$\n x(t) = \\left( x_0 \\cos(\\Omega t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Omega} \\sin(\\Omega t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Omega = \\omega\\sqrt{ 1 - \\zeta^2}.\n $$\n \"\"\"\n return self.initial_condition.x0 * np.exp(\n -self.system.zeta * self.system.omega * t\n )\n\n def _x_over_damped(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to over harmonic oscillators:\n\n $$\n x(t) = \\left( x_0 \\cosh(\\Gamma t) + \\frac{\\zeta \\omega x_0 + v_0}{\\Gamma} \\sinh(\\Gamma t) \\right)\n e^{-\\zeta \\omega t},\n $$\n\n where\n\n $$\n \\Gamma = \\omega\\sqrt{ \\zeta^2 - 1 }.\n $$\n \"\"\"\n gamma_damp = self.system.omega * np.sqrt(self.system.zeta - 1)\n\n return (\n self.initial_condition.x0 * np.cosh(gamma_damp * t)\n + (\n self.system.zeta * self.system.omega * self.initial_condition.x0\n + self.initial_condition.v0\n )\n / gamma_damp\n * np.sinh(gamma_damp * t)\n ) * np.exp(-self.system.zeta * self.system.omega * t)\n\n def _x(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to damped harmonic oscillators.\"\"\"\n t = np.array(t, copy=False)\n if self.system.type == \"under_damped\":\n x = self._x_under_damped(t)\n elif self.system.type == \"over_damped\":\n x = self._x_over_damped(t)\n elif self.system.type == \"critical_damped\":\n x = self._x_critical_damped(t)\n else:\n raise ValueError(\n \"System type is not damped harmonic oscillator: {self.system.type}\"\n )\n\n return x\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorBase","title":"HarmonicOscillatorBase ","text":" Bases: ABC Base class to generate time series data for a harmonic oscillator. Parameters: Name Type Description Default system Mapping[str, float] all the params that defines the harmonic oscillator. required initial_condition Mapping[str, float] | None the initial condition of the harmonic oscillator. None Source code in hamilflow/models/harmonic_oscillator.py class HarmonicOscillatorBase(ABC):\n r\"\"\"Base class to generate time series data\n for a [harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator).\n\n :param system: all the params that defines the harmonic oscillator.\n :param initial_condition: the initial condition of the harmonic oscillator.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ) -> None:\n initial_condition = initial_condition or {}\n self.system = HarmonicOscillatorSystem.model_validate(system)\n self.initial_condition = HarmonicOscillatorIC.model_validate(initial_condition)\n\n @cached_property\n def definition(self) -> dict[str, dict[str, float]]:\n \"\"\"model params and initial conditions defined as a dictionary.\"\"\"\n return {\n \"system\": self.system.model_dump(),\n \"initial_condition\": self.initial_condition.model_dump(),\n }\n\n @abstractmethod\n def _x(self, t: \"Sequence[float] | ArrayLike[float]\") -> ArrayLike:\n r\"\"\"Solution to simple harmonic oscillators.\"\"\"\n ...\n\n def __call__(self, n_periods: int, n_samples_per_period: int) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time step.\n\n :param n_periods: Number of periods to generate.\n :param n_samples_per_period: Number of samples per period.\n \"\"\"\n time_delta = self.system.period / n_samples_per_period\n time_steps = np.arange(0, n_periods * n_samples_per_period) * time_delta\n\n data = self._x(time_steps)\n\n return pd.DataFrame({\"t\": time_steps, \"x\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorBase.definition","title":"definition: dict[str, dict[str, float]] cached property ","text":"model params and initial conditions defined as a dictionary. "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorBase.__call__","title":"__call__(n_periods, n_samples_per_period) ","text":"Generate time series data for the harmonic oscillator. Returns a list of floats representing the displacement at each time step. Parameters: Name Type Description Default n_periods int Number of periods to generate. required n_samples_per_period int Number of samples per period. required Source code in hamilflow/models/harmonic_oscillator.py def __call__(self, n_periods: int, n_samples_per_period: int) -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator.\n\n Returns a list of floats representing the displacement at each time step.\n\n :param n_periods: Number of periods to generate.\n :param n_samples_per_period: Number of samples per period.\n \"\"\"\n time_delta = self.system.period / n_samples_per_period\n time_steps = np.arange(0, n_periods * n_samples_per_period) * time_delta\n\n data = self._x(time_steps)\n\n return pd.DataFrame({\"t\": time_steps, \"x\": data})\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorIC","title":"HarmonicOscillatorIC ","text":" Bases: BaseModel The initial condition for a harmonic oscillator Attributes: Name Type Description x0 float the initial displacement v0 float the initial velocity phi float initial phase Source code in hamilflow/models/harmonic_oscillator.py class HarmonicOscillatorIC(BaseModel):\n \"\"\"The initial condition for a harmonic oscillator\n\n :cvar x0: the initial displacement\n :cvar v0: the initial velocity\n :cvar phi: initial phase\n \"\"\"\n\n x0: float = Field(default=1.0)\n v0: float = Field(default=0.0)\n phi: float = Field(default=0.0)\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem","title":"HarmonicOscillatorSystem ","text":" Bases: BaseModel The params for the harmonic oscillator Attributes: Name Type Description omega float angular frequency of the harmonic oscillator zeta float damping ratio Source code in hamilflow/models/harmonic_oscillator.py class HarmonicOscillatorSystem(BaseModel):\n \"\"\"The params for the harmonic oscillator\n\n :cvar omega: angular frequency of the harmonic oscillator\n :cvar zeta: damping ratio\n \"\"\"\n\n omega: float = Field()\n zeta: float = Field(default=0.0)\n\n @computed_field # type: ignore[misc]\n @cached_property\n def period(self) -> float:\n \"\"\"period of the oscillator\"\"\"\n return 2 * np.pi / self.omega\n\n @computed_field # type: ignore[misc]\n @cached_property\n def frequency(self) -> float:\n \"\"\"frequency of the oscillator\"\"\"\n return 1 / self.period\n\n @computed_field # type: ignore[misc]\n @cached_property\n def type(\n self,\n ) -> Literal[\"simple\", \"under_damped\", \"critical_damped\", \"over_damped\"]:\n \"\"\"which type of harmonic oscillators\"\"\"\n if self.zeta == 0:\n return \"simple\"\n elif self.zeta < 1:\n return \"under_damped\"\n elif self.zeta == 1:\n return \"critical_damped\"\n else:\n return \"over_damped\"\n\n @field_validator(\"zeta\")\n @classmethod\n def check_zeta_non_negative(cls, v: float) -> float:\n if v < 0:\n raise ValueError(f\"Value of zeta should be positive: {v=}\")\n\n return v\n "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem.frequency","title":"frequency: float cached property ","text":"frequency of the oscillator "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem.period","title":"period: float cached property ","text":"period of the oscillator "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.HarmonicOscillatorSystem.type","title":"type: Literal['simple', 'under_damped', 'critical_damped', 'over_damped'] cached property ","text":"which type of harmonic oscillators "},{"location":"references/models/harmonic_oscillator/#hamilflow.models.harmonic_oscillator.SimpleHarmonicOscillator","title":"SimpleHarmonicOscillator ","text":" Bases: HarmonicOscillatorBase Generate time series data for a simple harmonic oscillator. In a one dimensional world, a mass \\(m\\), driven by a force \\(F=-kx\\), is described as \\[ \\begin{align} F &= - k x \\\\ F &= m a \\end{align} \\] The mass behaves like a simple harmonic oscillator. In general, the solution to a simple harmonic oscillator is \\[ x(t) = A \\cos(\\omega t + \\phi), \\] where \\(\\omega\\) is the angular frequency, \\(\\phi\\) is the initial phase, and \\(A\\) is the amplitude. To use this generator, params = {\"omega\": omega}\n\nho = SimpleHarmonicOscillator(params=params)\n\ndf = ho(n_periods=1, n_samples_per_period=10)\n df will be a pandas dataframe with two columns: t and x . Source code in hamilflow/models/harmonic_oscillator.py class SimpleHarmonicOscillator(HarmonicOscillatorBase):\n r\"\"\"Generate time series data for a\n [simple harmonic oscillator](https://en.wikipedia.org/wiki/Harmonic_oscillator).\n\n\n In a one dimensional world, a mass $m$, driven by a force $F=-kx$, is described as\n\n $$\n \\begin{align}\n F &= - k x \\\\\n F &= m a\n \\end{align}\n $$\n\n The mass behaves like a simple harmonic oscillator.\n\n In general, the solution to a simple harmonic oscillator is\n\n $$\n x(t) = A \\cos(\\omega t + \\phi),\n $$\n\n where $\\omega$ is the angular frequency, $\\phi$ is the initial phase, and $A$ is the amplitude.\n\n\n To use this generator,\n\n ```python\n params = {\"omega\": omega}\n\n ho = SimpleHarmonicOscillator(params=params)\n\n df = ho(n_periods=1, n_samples_per_period=10)\n ```\n\n `df` will be a pandas dataframe with two columns: `t` and `x`.\n \"\"\"\n\n def __init__(\n self,\n system: Mapping[str, float],\n initial_condition: Mapping[str, float] | None = None,\n ) -> None:\n super().__init__(system, initial_condition)\n if self.system.type != \"simple\":\n raise ValueError(\n f\"System is not a Simple Harmonic Oscillator: {self.system}\"\n )\n\n def _x(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray:\n r\"\"\"Solution to simple harmonic oscillators:\n\n $$\n x(t) = x_0 \\cos(\\omega t + \\phi).\n $$\n \"\"\"\n return self.initial_condition.x0 * np.cos(\n self.system.omega * np.array(t, copy=False) + self.initial_condition.phi\n )\n "},{"location":"references/models/harmonic_oscillator_chain/","title":"Harmonic Oscillator Chain","text":""},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain","title":"HarmonicOscillatorsChain ","text":"Generate time series data for a coupled harmonic oscillator chain with periodic boundary condition. A one-dimensional circle of \\(N\\) interacting harmonic oscillators can be described by the Lagrangian action \\(\\(S_L[x_i] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\{ \\sum_{i=0}^{N-1} \\frac{1}{2}m \\dot x_i^2 - \\frac{1}{2}m\\omega^2\\left(x_i - x_{i+1}\\right)^2 \\right\\}\\,,\\)\\) where \\(x_N \\coloneqq x_0\\). This system can be solved in terms of travelling waves, obtained by discrete Fourier transform. Since the original degrees of freedom are real, the initial conditions of the propagating waves need to satisfy \\(Y_k = Y^*_{-k \\mod N}\\), see Wikipedia. Parameters: Name Type Description Default omega float frequence parameter required initial_conditions Sequence[Mapping[str, float | tuple[float, float]]] a sequence of initial conditions on the Fourier modes. The first element in the sequence is that of the zero mode, taking a position and a velocity. Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases. required odd_dof bool The system will have 2 * len(initial_conditions) + int(odd_dof) - 2 degrees of freedom. required Source code in hamilflow/models/harmonic_oscillator_chain.py class HarmonicOscillatorsChain:\n r\"\"\"Generate time series data for a coupled harmonic oscillator chain\n with periodic boundary condition.\n\n A one-dimensional circle of $N$ interacting harmonic oscillators can be described by the Lagrangian action\n $$S_L[x_i] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\{ \\sum_{i=0}^{N-1} \\frac{1}{2}m \\dot x_i^2 - \\frac{1}{2}m\\omega^2\\left(x_i - x_{i+1}\\right)^2 \\right\\}\\,,$$\n where $x_N \\coloneqq x_0$.\n\n This system can be solved in terms of _travelling waves_, obtained by discrete Fourier transform.\n\n Since the original degrees of freedom are real, the initial conditions of the propagating waves need to satisfy\n $Y_k = Y^*_{-k \\mod N}$, see [Wikipedia](https://en.wikipedia.org/wiki/Discrete_Fourier_transform#DFT_of_real_and_purely_imaginary_signals).\n\n :param omega: frequence parameter\n :param initial_conditions: a sequence of initial conditions on the Fourier modes.\n The first element in the sequence is that of the zero mode, taking a position and a velocity.\n Rest of the elements are that of the independent travelling waves, taking two amplitudes and two initial phases.\n :param odd_dof: The system will have `2 * len(initial_conditions) + int(odd_dof) - 2` degrees of freedom.\n \"\"\"\n\n def __init__(\n self,\n omega: float,\n initial_conditions: Sequence[Mapping[str, float | tuple[float, float]]],\n odd_dof: bool,\n ) -> None:\n self.n_dof = 2 * len(initial_conditions) + odd_dof - 2\n if not odd_dof:\n prefix = \"For even degrees of freedom, \"\n if self.n_dof == 0:\n raise ValueError(prefix + \"at least 1 travelling wave is needed\")\n amp = cast(tuple[float, float], initial_conditions[-1][\"amp\"])\n if amp[0] != amp[1]:\n msg = \"k == N // 2 must have equal positive and negative amplitudes.\"\n raise ValueError(prefix + msg)\n self.omega = omega\n self.odd_dof = odd_dof\n\n self.free_mode = FreeParticle(cast(Mapping[str, float], initial_conditions[0]))\n\n self.independent_csho_modes = [\n self._sho_factory(\n k,\n cast(tuple[float, float], ic[\"amp\"]),\n cast(tuple[float, float] | None, ic.get(\"phi\")),\n )\n for k, ic in enumerate(initial_conditions[1:], 1)\n ]\n\n def _sho_factory(\n self,\n k: int,\n amp: tuple[float, float],\n phi: tuple[float, float] | None = None,\n ) -> ComplexSimpleHarmonicOscillator:\n return ComplexSimpleHarmonicOscillator(\n dict(\n omega=2 * self.omega * np.sin(np.pi * k / self.n_dof),\n ),\n dict(x0=amp) | (dict(phi=phi) if phi else {}),\n )\n\n @cached_property\n def definition(\n self,\n ) -> dict[\n str,\n float\n | dict[str, dict[str, float | list[float]]]\n | list[dict[str, dict[str, float | tuple[float, float]]]],\n ]:\n \"\"\"model params and initial conditions defined as a dictionary.\"\"\"\n return dict(\n omega=self.omega,\n n_dof=self.n_dof,\n free_mode=self.free_mode.definition,\n independent_csho_modes=[\n rwm.definition for rwm in self.independent_csho_modes\n ],\n )\n\n def _z(\n self, t: \"Sequence[float] | ArrayLike[float]\"\n ) -> tuple[np.ndarray, np.ndarray]:\n t = np.array(t, copy=False).reshape(-1)\n all_travelling_waves = [self.free_mode._x(t).reshape(1, -1)]\n\n if self.independent_csho_modes:\n independent_cshos = np.array(\n [o._z(t) for o in self.independent_csho_modes], copy=False\n )\n all_travelling_waves.extend(\n (independent_cshos, independent_cshos[::-1].conj())\n if self.odd_dof\n else (\n independent_cshos[:-1],\n independent_cshos[[-1]],\n independent_cshos[-2::-1].conj(),\n )\n )\n\n travelling_waves = np.concatenate(all_travelling_waves)\n original_zs = ifft(travelling_waves, axis=0, norm=\"ortho\")\n return original_zs, travelling_waves\n\n def _x(\n self, t: \"Sequence[float] | ArrayLike[float]\"\n ) -> tuple[np.ndarray, np.ndarray]:\n original_xs, travelling_waves = self._z(t)\n\n return np.real(original_xs), travelling_waves\n\n def __call__(self, t: \"Sequence[float] | ArrayLike[float]\") -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator chain.\n\n Returns float(s) representing the displacement at the given time(s).\n\n :param t: time.\n \"\"\"\n original_xs, travelling_waves = self._x(t)\n data = {\n f\"{name}{i}\": values\n for name, xs in zip((\"x\", \"y\"), (original_xs, travelling_waves))\n for i, values in enumerate(xs)\n }\n\n return pd.DataFrame(data, index=t)\n "},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain.definition","title":"definition: dict[str, float | dict[str, dict[str, float | list[float]]] | list[dict[str, dict[str, float | tuple[float, float]]]]] cached property ","text":"model params and initial conditions defined as a dictionary. "},{"location":"references/models/harmonic_oscillator_chain/#hamilflow.models.harmonic_oscillator_chain.HarmonicOscillatorsChain.__call__","title":"__call__(t) ","text":"Generate time series data for the harmonic oscillator chain. Returns float(s) representing the displacement at the given time(s). Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] time. required Source code in hamilflow/models/harmonic_oscillator_chain.py def __call__(self, t: \"Sequence[float] | ArrayLike[float]\") -> pd.DataFrame:\n \"\"\"Generate time series data for the harmonic oscillator chain.\n\n Returns float(s) representing the displacement at the given time(s).\n\n :param t: time.\n \"\"\"\n original_xs, travelling_waves = self._x(t)\n data = {\n f\"{name}{i}\": values\n for name, xs in zip((\"x\", \"y\"), (original_xs, travelling_waves))\n for i, values in enumerate(xs)\n }\n\n return pd.DataFrame(data, index=t)\n "},{"location":"references/models/pendulum/","title":"Pendulum","text":""},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum","title":"Pendulum ","text":"Generate time series data for a pendulum. We describe a generic pendulum system by the Lagrangian action $$ S_L[\\theta] = I \\int_{t_0}^{t_1} \\mathbb{d}t \\left\\{\\frac{1}{2} \\dot\\theta^2 + \\omega_0^2 \\cos\\theta \\right\\}\\,, $$ where \\(\\theta\\) is the angle from the vertical to the pendulum; \\(I\\) is the inertia parameter introduced for dimensional reasons, and \\(\\omega_0\\) the frequency parameter. Details are collected in the tutorial. Source code in hamilflow/models/pendulum.py class Pendulum:\n r\"\"\"Generate time series data for a pendulum.\n\n We describe a generic pendulum system by the Lagrangian action\n $$\n S_L\\[\\theta\\] = I \\int_{t_0}^{t_1} \\mathbb{d}t\n \\left\\\\{\\frac{1}{2} \\dot\\theta^2 + \\omega_0^2 \\cos\\theta \\right\\\\}\\,,\n $$\n where $\\theta$ is the _angle_ from the vertical to the pendulum;\n $I$ is the _inertia parameter_ introduced for dimensional reasons,\n and $\\omega_0$ the _frequency parameter_.\n\n Details are collected in the tutorial.\n \"\"\"\n\n def __init__(\n self,\n system: float | Mapping[str, float],\n initial_condition: float | Mapping[str, float],\n ) -> None:\n if isinstance(system, (float, int)):\n system = {\"omega0\": system}\n if isinstance(initial_condition, (float, int)):\n initial_condition = {\"theta0\": initial_condition}\n self.system = PendulumSystem.model_validate(system)\n self.initial_condition = PendulumIC.model_validate(initial_condition)\n\n @cached_property\n def definition(self) -> dict[str, float]:\n \"\"\"Model params and initial conditions defined as a dictionary.\"\"\"\n return dict(\n system=self.system.model_dump(),\n initial_condition=self.initial_condition.model_dump(),\n )\n\n @property\n def omega0(self) -> float:\n return self.system.omega0\n\n @property\n def _k(self) -> float:\n return self.initial_condition.k\n\n @property\n def _math_m(self) -> float:\n return self._k**2\n\n @cached_property\n def freq(self) -> float:\n r\"\"\"Frequency.\n\n :return: $\\frac{\\pi}{2K(k^2)}\\omega_0$, where\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n return math.pi * self.omega0 / (2 * ellipk(self._math_m))\n\n @cached_property\n def period(self) -> float:\n r\"\"\"Period.\n\n :return: $\\frac{4K(k^2)}{\\omega_0}$, where\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n return 4 * ellipk(self._math_m) / self.omega0\n\n def _math_u(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n return self.omega0 * np.array(t, copy=False)\n\n def u(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"The convenient generalised coordinate $u$,\n $\\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{\\sin\\frac{\\theta_0}{2}}$.\n\n :param t: time\n :return: $u(t) = \\mathrm{am}\\!\\big(\\omega_0 t + K(k^2), k^2\\big)$, where\n $\\mathrm{am}(x, k)$ is [Jacobi's amplitude function](https://dlmf.nist.gov/22.16#E1),\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n _, _, _, ph = ellipj(self._math_u(t) + ellipk(self._math_m), self._math_m)\n\n return ph\n\n def theta(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"Angle $\\theta$.\n\n :param t: time\n :return: $\\theta(t) = 2\\arcsin\\!\\big(k\\cdot\\mathrm{cd}(\\omega_0 t, k^2)\\big)$, where\n $\\mathrm{cd}(z, k)$ is a [Jacobian elliptic function](https://dlmf.nist.gov/22.2#E8)\n \"\"\"\n _, cn, dn, _ = ellipj(self._math_u(t), self._math_m)\n\n return 2 * np.arcsin(cn / dn * self._k)\n\n def __call__(self, n_periods: int, n_samples_per_period: int) -> pd.DataFrame:\n time_delta = self.period / n_samples_per_period\n time_steps = np.arange(0, n_periods * n_samples_per_period) * time_delta\n\n thetas = self.theta(time_steps)\n us = self.u(time_steps)\n\n return pd.DataFrame(dict(t=time_steps, x=thetas, u=us))\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.definition","title":"definition: dict[str, float] cached property ","text":"Model params and initial conditions defined as a dictionary. "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.freq","title":"freq: float cached property ","text":"Frequency. Returns: Type Description float \\(\\frac{\\pi}{2K(k^2)}\\omega_0\\), where \\(K(m)\\) is Legendre's complete elliptic integral of the first kind "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.period","title":"period: float cached property ","text":"Period. Returns: Type Description float \\(\\frac{4K(k^2)}{\\omega_0}\\), where \\(K(m)\\) is Legendre's complete elliptic integral of the first kind "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.theta","title":"theta(t) ","text":"Angle \\(\\theta\\). Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] time required Returns: Type Description ndarray[float] \\(\\theta(t) = 2\\arcsin\\!\\big(k\\cdot\\mathrm{cd}(\\omega_0 t, k^2)\\big)\\), where \\(\\mathrm{cd}(z, k)\\) is a Jacobian elliptic function Source code in hamilflow/models/pendulum.py def theta(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"Angle $\\theta$.\n\n :param t: time\n :return: $\\theta(t) = 2\\arcsin\\!\\big(k\\cdot\\mathrm{cd}(\\omega_0 t, k^2)\\big)$, where\n $\\mathrm{cd}(z, k)$ is a [Jacobian elliptic function](https://dlmf.nist.gov/22.2#E8)\n \"\"\"\n _, cn, dn, _ = ellipj(self._math_u(t), self._math_m)\n\n return 2 * np.arcsin(cn / dn * self._k)\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.Pendulum.u","title":"u(t) ","text":"The convenient generalised coordinate \\(u\\), \\(\\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{\\sin\\frac{\\theta_0}{2}}\\). Parameters: Name Type Description Default t Sequence[float] | ArrayLike[float] time required Returns: Type Description ndarray[float] \\(u(t) = \\mathrm{am}\\!\\big(\\omega_0 t + K(k^2), k^2\\big)\\), where \\(\\mathrm{am}(x, k)\\) is Jacobi's amplitude function, \\(K(m)\\) is Legendre's complete elliptic integral of the first kind Source code in hamilflow/models/pendulum.py def u(self, t: \"Sequence[float] | ArrayLike[float]\") -> np.ndarray[float]:\n r\"\"\"The convenient generalised coordinate $u$,\n $\\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{\\sin\\frac{\\theta_0}{2}}$.\n\n :param t: time\n :return: $u(t) = \\mathrm{am}\\!\\big(\\omega_0 t + K(k^2), k^2\\big)$, where\n $\\mathrm{am}(x, k)$ is [Jacobi's amplitude function](https://dlmf.nist.gov/22.16#E1),\n $K(m)$ is [Legendre's complete elliptic integral of the first kind](https://dlmf.nist.gov/19.2#E8)\n \"\"\"\n _, _, _, ph = ellipj(self._math_u(t) + ellipk(self._math_m), self._math_m)\n\n return ph\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.PendulumIC","title":"PendulumIC ","text":" Bases: BaseModel The initial condition for a pendulum. Parameters: Name Type Description Default theta0 \\(-\\frac{\\pi}{2} \\le \\theta_0 \\le \\frac{\\pi}{2}\\), the initial angle required Source code in hamilflow/models/pendulum.py class PendulumIC(BaseModel):\n r\"\"\"The initial condition for a pendulum.\n\n :param theta0: $-\\frac{\\pi}{2} \\le \\theta_0 \\le \\frac{\\pi}{2}$, the\n initial angle\n \"\"\"\n\n theta0: float = Field(ge=-math.pi / 2, le=math.pi / 2, frozen=True)\n\n @computed_field # type: ignore[misc]\n @cached_property\n def k(self) -> float:\n r\"\"\"A convenient number for elliptic functions.\n\n :return: $\\sin\\frac{\\theta_0}{2}$\n \"\"\"\n return math.sin(self.theta0 / 2)\n "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.PendulumIC.k","title":"k: float cached property ","text":"A convenient number for elliptic functions. Returns: Type Description float \\(\\sin\\frac{\\theta_0}{2}\\) "},{"location":"references/models/pendulum/#hamilflow.models.pendulum.PendulumSystem","title":"PendulumSystem ","text":" Bases: BaseModel The params for the pendulum. Parameters: Name Type Description Default omega0 \\(\\omega_0 \\coloneqq \\sqrt{\\frac{U}{I}} > 0\\), frequency parameter required Source code in hamilflow/models/pendulum.py class PendulumSystem(BaseModel):\n r\"\"\"The params for the pendulum.\n\n :param omega0: $\\omega_0 \\coloneqq \\sqrt{\\frac{U}{I}} > 0$, frequency\n parameter\n \"\"\"\n\n omega0: float = Field(gt=0.0, frozen=True)\n "},{"location":"tutorials/","title":"Tutorials","text":"We provide some tutorials to help you get started. "},{"location":"tutorials/brownian_motion/","title":"Brownian Motion","text":"In\u00a0[1]: Copied! import plotly.express as px\n\nfrom hamilflow.models.brownian_motion import BrownianMotion\n import plotly.express as px from hamilflow.models.brownian_motion import BrownianMotion In\u00a0[2]: Copied! bm_1d = BrownianMotion(\n system={\n \"sigma\": 1,\n \"delta_t\": 1,\n },\n initial_condition={\"x0\": 0},\n)\n bm_1d = BrownianMotion( system={ \"sigma\": 1, \"delta_t\": 1, }, initial_condition={\"x0\": 0}, ) Call the model to generate 1000 steps. In\u00a0[3]: Copied! df_1d = bm_1d(n_steps=1000)\n df_1d = bm_1d(n_steps=1000) In\u00a0[4]: Copied! px.line(df_1d, x=\"t\", y=\"x_0\")\n px.line(df_1d, x=\"t\", y=\"x_0\") In\u00a0[5]: Copied! bm_2d = BrownianMotion(\n system={\n \"sigma\": 1,\n \"delta_t\": 1,\n },\n initial_condition={\"x0\": [0, 0]},\n)\n bm_2d = BrownianMotion( system={ \"sigma\": 1, \"delta_t\": 1, }, initial_condition={\"x0\": [0, 0]}, ) We call the model to generate 1000 steps. In\u00a0[6]: Copied! df_2d = bm_2d(n_steps=500)\n df_2d = bm_2d(n_steps=500) In\u00a0[7]: Copied! (\n px.scatter(df_2d, x=\"x_0\", y=\"x_1\", color=\"t\")\n .update_traces(\n mode=\"lines+markers\",\n marker=dict(\n size=2.5,\n ),\n line=dict(width=1),\n )\n .update_yaxes(\n scaleanchor=\"x\",\n scaleratio=1,\n )\n)\n ( px.scatter(df_2d, x=\"x_0\", y=\"x_1\", color=\"t\") .update_traces( mode=\"lines+markers\", marker=dict( size=2.5, ), line=dict(width=1), ) .update_yaxes( scaleanchor=\"x\", scaleratio=1, ) ) In\u00a0[\u00a0]: Copied! \n "},{"location":"tutorials/brownian_motion/#brownian-motion","title":"Brownian Motion\u00b6","text":"Brownian motion describes motion of small particles with stochastic forces applied to them. The math of Brownian motion can be modeled with Wiener process. In this tutorial, we take a simple form of the model and treat the stochastic forces as Gaussian. For consistency, we always use $\\mathbf x$ for displacement, and $t$ for steps. The model we are using is $$ \\begin{align} \\mathbf x(t + \\mathrm dt) &= \\mathbf x(t) + \\mathcal{N}(\\mu=0, \\sigma=\\sigma \\sqrt{\\mathrm d t}) \\end{align} $$ Read more: - Brownian motion and random walks. [cited 13 Mar 2024]. Available: https://web.mit.edu/8.334/www/grades/projects/projects17/OscarMickelin/brownian.html
- Contributors to Wikimedia projects. Brownian motion. In: Wikipedia [Internet]. 22 Jan 2024 [cited 13 Mar 2024]. Available: https://en.wikipedia.org/wiki/Brownian_motion
hamilflow implemented a Brownian motion model called BrownianMotion . "},{"location":"tutorials/brownian_motion/#1d-brownian-motion","title":"1D Brownian Motion\u00b6","text":""},{"location":"tutorials/brownian_motion/#2d-brownian-motion","title":"2D Brownian Motion\u00b6","text":"Our BrownianMotion model calculates the dimension of the space based on the dimension of the initial condition $x_0$. To create a 2D Brownian motion model, we need the initial condition to be length 2. "},{"location":"tutorials/complex_harmonic_oscillator/","title":"Complex Harmonic Oscillator","text":"In\u00a0[1]: Copied! import math\n\nimport numpy as np\nfrom plotly import express as px\n\nfrom hamilflow.models.harmonic_oscillator import ComplexSimpleHarmonicOscillator\n import math import numpy as np from plotly import express as px from hamilflow.models.harmonic_oscillator import ComplexSimpleHarmonicOscillator In\u00a0[2]: Copied! t = np.linspace(0, 3, 257)\nsystem_specs = dict(omega=2 * math.pi)\n t = np.linspace(0, 3, 257) system_specs = dict(omega=2 * math.pi) In\u00a0[3]: Copied! csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(1, 0)))\n\ndf = csho(t)\n\narr_z = df[\"z\"].to_numpy(copy=False)\n\npx.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))\n csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(1, 0))) df = csho(t) arr_z = df[\"z\"].to_numpy(copy=False) px.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\")) In\u00a0[4]: Copied! csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(0, 1)))\n\ndf = csho(t)\n\narr_z = df[\"z\"].to_numpy(copy=False)\n\npx.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))\n csho = ComplexSimpleHarmonicOscillator(system_specs, initial_condition=dict(x0=(0, 1))) df = csho(t) arr_z = df[\"z\"].to_numpy(copy=False) px.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\")) In\u00a0[5]: Copied! csho = ComplexSimpleHarmonicOscillator(\n system_specs,\n initial_condition=dict(x0=(math.cos(math.pi / 12), math.sin(math.pi / 12))),\n)\n\ndf = csho(t)\n\narr_z = df[\"z\"].to_numpy(copy=False)\n\npx.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))\n csho = ComplexSimpleHarmonicOscillator( system_specs, initial_condition=dict(x0=(math.cos(math.pi / 12), math.sin(math.pi / 12))), ) df = csho(t) arr_z = df[\"z\"].to_numpy(copy=False) px.line_3d(x=arr_z.real, y=arr_z.imag, z=t, labels=dict(x=\"real\", y=\"imag\", z=\"time\"))"},{"location":"tutorials/complex_harmonic_oscillator/#complex-harmonic-oscillator","title":"Complex Harmonic Oscillator\u00b6","text":"In this tutorial, we show a few interesting examples of the complex simple harmonic oscillator. "},{"location":"tutorials/complex_harmonic_oscillator/#basic-setups","title":"Basic setups\u00b6","text":""},{"location":"tutorials/complex_harmonic_oscillator/#positive-frequency-circular-polarised-mode","title":"Positive-frequency, circular-polarised mode\u00b6","text":"Also known as the left-rotating mode. "},{"location":"tutorials/complex_harmonic_oscillator/#negative-frequency-circular-polarised-mode","title":"Negative-frequency, circular-polarised mode\u00b6","text":"Also known as the right-rotating mode. "},{"location":"tutorials/complex_harmonic_oscillator/#positive-frequency-elliptic-polarised-mode","title":"Positive-frequency, elliptic-polarised mode\u00b6","text":""},{"location":"tutorials/complex_harmonic_oscillator/#end-of-notebook","title":"End of Notebook\u00b6","text":""},{"location":"tutorials/harmonic_oscillator/","title":"Harmonic Oscillators","text":"In\u00a0[1]: Copied! import pandas as pd\nimport plotly.express as px\n\nfrom hamilflow.models.harmonic_oscillator import (\n DampedHarmonicOscillator,\n SimpleHarmonicOscillator,\n)\n import pandas as pd import plotly.express as px from hamilflow.models.harmonic_oscillator import ( DampedHarmonicOscillator, SimpleHarmonicOscillator, ) In\u00a0[2]: Copied! n_periods = 3\nn_samples_per_period = 200\n n_periods = 3 n_samples_per_period = 200 In\u00a0[3]: Copied! sho_omega = 0.5\n\nsho = SimpleHarmonicOscillator(system={\"omega\": sho_omega})\n sho_omega = 0.5 sho = SimpleHarmonicOscillator(system={\"omega\": sho_omega}) In\u00a0[4]: Copied! df_sho = sho(n_periods=n_periods, n_samples_per_period=n_samples_per_period)\ndf_sho.head()\n df_sho = sho(n_periods=n_periods, n_samples_per_period=n_samples_per_period) df_sho.head() Out[4]: t x 0 0.000000 1.000000 1 0.062832 0.999507 2 0.125664 0.998027 3 0.188496 0.995562 4 0.251327 0.992115 In\u00a0[5]: Copied! px.line(\n df_sho,\n x=\"t\",\n y=\"x\",\n title=rf\"Simple Harmonic Oscillator (omega = {sho_omega})\",\n labels={\n \"x\": r\"Displacement $x(t)$\",\n \"t\": r\"$t$\",\n },\n)\n px.line( df_sho, x=\"t\", y=\"x\", title=rf\"Simple Harmonic Oscillator (omega = {sho_omega})\", labels={ \"x\": r\"Displacement $x(t)$\", \"t\": r\"$t$\", }, ) In\u00a0[6]: Copied! dho_systems = {\n \"Underdamped\": {\"omega\": 0.5, \"zeta\": 0.2},\n \"Critical Damped\": {\"omega\": 0.5, \"zeta\": 1},\n \"Overdamped\": {\n \"omega\": 0.5,\n \"zeta\": 1.2,\n },\n}\n\ndfs_dho = []\n\nfor s_name, s in dho_systems.items():\n\n dfs_dho.append(\n DampedHarmonicOscillator(system=s)(\n n_periods=n_periods, n_samples_per_period=n_samples_per_period\n ).assign(system=rf\"{s_name} (omega = {s.get('omega')}, zeta = {s.get('zeta')})\")\n )\n\nfig = px.line(\n pd.concat(dfs_dho),\n x=\"t\",\n y=\"x\",\n color=\"system\",\n title=rf\"Damped Harmonic Oscillator\",\n labels={\n \"x\": r\"Displacement $x(t)$\",\n \"t\": r\"$t$\",\n },\n)\nfig.update_layout(legend={\"yanchor\": \"top\", \"y\": -0.2, \"xanchor\": \"left\", \"x\": 0})\n dho_systems = { \"Underdamped\": {\"omega\": 0.5, \"zeta\": 0.2}, \"Critical Damped\": {\"omega\": 0.5, \"zeta\": 1}, \"Overdamped\": { \"omega\": 0.5, \"zeta\": 1.2, }, } dfs_dho = [] for s_name, s in dho_systems.items(): dfs_dho.append( DampedHarmonicOscillator(system=s)( n_periods=n_periods, n_samples_per_period=n_samples_per_period ).assign(system=rf\"{s_name} (omega = {s.get('omega')}, zeta = {s.get('zeta')})\") ) fig = px.line( pd.concat(dfs_dho), x=\"t\", y=\"x\", color=\"system\", title=rf\"Damped Harmonic Oscillator\", labels={ \"x\": r\"Displacement $x(t)$\", \"t\": r\"$t$\", }, ) fig.update_layout(legend={\"yanchor\": \"top\", \"y\": -0.2, \"xanchor\": \"left\", \"x\": 0}) In\u00a0[\u00a0]: Copied! \n "},{"location":"tutorials/harmonic_oscillator/#harmonic-oscillators","title":"Harmonic Oscillators\u00b6","text":"In this tutorial, we demo how to generate data of harmonic oscillators. "},{"location":"tutorials/harmonic_oscillator/#simple-harmonic-oscillator","title":"Simple Harmonic Oscillator\u00b6","text":"For an simple harmonic oscillator, the action of a simple harmonic oscillator is $$S_L[x] = \\int_{t_0}^{t_1} \\mathbb{d}t \\left\\{\\frac{1}{2} m \\dot x^2 - \\frac{1}{2} m \\omega^2 x^2 \\right\\}\\,,$$ where the least action principle leads to the following equation of motion, $$ \\ddot x + \\omega^2 x = 0\\,. $$ A simple harmonic oscillator is a periodic motion. "},{"location":"tutorials/harmonic_oscillator/#damped-harmonic-oscillator","title":"Damped Harmonic Oscillator\u00b6","text":"A damped harmonic oscillator is a simple harmonic oscillator with damping force that is proportional to its velocity, $$ \\ddot x + \\omega^2 x = - 2\\xi\\omega \\dot x\\,. $$ In this section, we demonstrate three scenarios of a damped harmonic oscillator. "},{"location":"tutorials/harmonic_oscillator_chain/","title":"Harmonic oscillator circle","text":"In\u00a0[1]: Copied! import math\n\nimport numpy as np\nfrom plotly import express as px\n\nfrom hamilflow.models.harmonic_oscillator_chain import HarmonicOscillatorsChain\n import math import numpy as np from plotly import express as px from hamilflow.models.harmonic_oscillator_chain import HarmonicOscillatorsChain In\u00a0[2]: Copied! t = np.linspace(0, 3, 257)\nomega = 2 * math.pi\npattern_x = r\"x\\d+\"\n t = np.linspace(0, 3, 257) omega = 2 * math.pi pattern_x = r\"x\\d+\" In\u00a0[3]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[4]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(0, 1))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(2 * math.pi, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(0, 1))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(2 * math.pi, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[5]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(0, 0)), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 4\nhoc = HarmonicOscillatorsChain(omega, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(0, 0)), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 4 hoc = HarmonicOscillatorsChain(omega, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[6]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, True)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, True) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[7]: Copied! ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, False)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=0), dict(amp=(1, 1))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, False) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"}) In\u00a0[8]: Copied! ics = [dict(x0=0, v0=2), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5\nhoc = HarmonicOscillatorsChain(omega, ics, False)\n\ndf_res = hoc(t)\ndf_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)]\npx.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})\n ics = [dict(x0=0, v0=2), dict(amp=(1, 0))] + [dict(amp=(0, 0))] * 5 hoc = HarmonicOscillatorsChain(omega, ics, False) df_res = hoc(t) df_x_wide = df_res.loc[:, df_res.columns.str.match(pattern_x)] px.imshow(df_x_wide, origin=\"lower\", labels={\"y\": \"t\"})"},{"location":"tutorials/harmonic_oscillator_chain/#harmonic-oscillator-circle","title":"Harmonic oscillator circle\u00b6","text":"In this tutorial, we show a few interesting examples of the harmonic oscillator circle. "},{"location":"tutorials/harmonic_oscillator_chain/#basic-setups","title":"Basic setups\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-right-moving-wave","title":"Fundamental right-moving wave\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-left-moving-wave","title":"Fundamental left-moving wave\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#faster-right-moving-wave","title":"Faster right-moving wave\u00b6","text":"Also known as the first harmonic. "},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-stationary-wave-odd-dof","title":"Fundamental stationary wave, odd dof\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#fundamental-stationary-wave-even-dof","title":"Fundamental stationary wave, even dof\u00b6","text":"There are stationary nodes at $i = 3, 9$. "},{"location":"tutorials/harmonic_oscillator_chain/#linearly-moving-chain-and-fundamental-right-moving-wave","title":"Linearly moving chain and fundamental right-moving wave\u00b6","text":""},{"location":"tutorials/harmonic_oscillator_chain/#mathematical-physical-discription","title":"Mathematical-physical discription\u00b6","text":"A one-dimensional circle of $N$ interacting harmonic oscillators can be described by the Lagrangian action $$S_L[x_i] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\sum_{i=0}^{N-1} \\frac{1}{2}m \\dot x_i^2 - \\frac{1}{2}m\\omega^2\\left(x_i - x_{i+1}\\right)^2 \\right\\\\}\\\\,,$$ where $x_N \\coloneqq x_0$. This system can be solved in terms of travelling waves, obtained by discrete Fourier transform. We can complexify the system $$S_L[x_i] = S_L[x_i, \\phi_j] \\equiv S_L[X^\\ast_i, X_j] = \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\frac{1}{2}m \\dot X^\\ast_i \\delta_{ij} \\dot X_j - \\frac{1}{2}m X^\\ast_i A_{ij} X_j\\right\\\\}\\\\,,$$ where $A_{ij} / \\omega^2$ is equal to $(-2)$ if $i=j$, $1$ if $|i-j|=1$ or $|i-j|=N$, and $0$ otherwise; $X_i \\coloneqq x_i \\mathbb{e}^{-\\phi_i}$, $X^\\ast_i \\coloneqq x_i \\mathbb{e}^{+\\phi_i}$. $A_{ij}$ can be diagonalised by the inverse discrete Fourier transform $$X_i = (F^{-1})_{ik} Y_k = \\frac{1}{\\sqrt{N}}\\sum_k \\mathbb{e}^{i \\frac{2\\mathbb{\\pi}}{N} k\\mathbb{i}} Y_k\\\\,.$$ Calculating gives $$S_L[X^\\ast_i, X_j] = S_L[Y^\\ast_i, Y_j] = \\sum_{k=0}^{N-1} \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\frac{1}{2}m \\dot Y^\\ast_k \\dot Y_k - \\frac{1}{2}m \\omega^2\\cdot4\\sin^2\\frac{2\\mathbb{\\pi}k}{N} Y^\\ast_k Y_k\\right\\\\}\\\\,.$$ We can arrive at an action for the Fourier modes $$S_L[Y, Y^*] = \\sum_{k=0}^{N-1} \\int_{t_0}^{t_1}\\mathbb{d} t \\left\\\\{ \\frac{1}{2}m \\dot Y_k^* Y_k - \\frac{1}{2}m \\omega^2\\cdot4\\sin^2\\frac{2\\mathbb{\\pi}k}{N} Y_k^* Y_k\\right\\\\}\\\\,.$$ The origional system can then be solved by $N$ complex oscillators. Since the original degrees of freedom are real, the initial conditions of the propagating waves need to satisfy $Y_k = Y^*_{-k \\mod N}$, see Wikipedia. "},{"location":"tutorials/harmonic_oscillator_chain/#end-of-notebook","title":"End of Notebook\u00b6","text":""},{"location":"tutorials/pendulum/","title":"Pendulum","text":"In\u00a0[1]: Copied! import math\n\nimport plotly.express as px\n\nfrom hamilflow.models.pendulum import Pendulum\n import math import plotly.express as px from hamilflow.models.pendulum import Pendulum In\u00a0[2]: Copied! omega0 = 2 * math.pi\ntheta0 = math.pi / 3\n\nn_periods = 2**2\nn_samples_per_period = 2**8\n omega0 = 2 * math.pi theta0 = math.pi / 3 n_periods = 2**2 n_samples_per_period = 2**8 In\u00a0[3]: Copied! pen = Pendulum(system=omega0, initial_condition=theta0)\n pen = Pendulum(system=omega0, initial_condition=theta0) In\u00a0[4]: Copied! df_pen = pen(n_periods=n_periods, n_samples_per_period=n_samples_per_period)\ndf_pen.head()\n df_pen = pen(n_periods=n_periods, n_samples_per_period=n_samples_per_period) df_pen.head() Out[4]: t x u 0 0.000000 1.047198 1.570796 1 0.004192 1.046897 1.593608 2 0.008384 1.045996 1.616424 3 0.012576 1.044494 1.639247 4 0.016768 1.042393 1.662082 In\u00a0[5]: Copied! df_pen.describe()\n df_pen.describe() Out[5]: t x u count 1024.000000 1.024000e+03 1024.000000 mean 2.144268 2.320193e-17 14.124895 std 1.239809 7.453532e-01 7.261249 min 0.000000 -1.047198e+00 1.570796 25% 1.072134 -7.494689e-01 7.848279 50% 2.144268 -3.535251e-16 14.125761 75% 3.216402 7.494689e-01 20.403244 max 4.288536 1.047198e+00 26.680726 In\u00a0[6]: Copied! px.line(\n df_pen,\n x=\"t\",\n y=\"x\",\n title=r\"Simple Harmonic Oscillator ($\\omega_0 = {:.4f})$\".format(omega0),\n labels=dict(x=r\"Angle $\\theta(t)$\", t=r\"Time $t$\"),\n)\n px.line( df_pen, x=\"t\", y=\"x\", title=r\"Simple Harmonic Oscillator ($\\omega_0 = {:.4f})$\".format(omega0), labels=dict(x=r\"Angle $\\theta(t)$\", t=r\"Time $t$\"), )"},{"location":"tutorials/pendulum/#pendulum","title":"Pendulum\u00b6","text":"In this tutorial, we demonstrate how to generate data of a pendulum, and introduce the mathematics of a pendulum. "},{"location":"tutorials/pendulum/#constants","title":"Constants\u00b6","text":""},{"location":"tutorials/pendulum/#a-pendulum","title":"A pendulum\u00b6","text":""},{"location":"tutorials/pendulum/#data","title":"Data\u00b6","text":""},{"location":"tutorials/pendulum/#plot","title":"Plot\u00b6","text":""},{"location":"tutorials/pendulum/#todo","title":"TODO\u00b6","text":" - Compare with a harmonic oscillator in terms of frequency and profile
- Animate the plot as a single pendulum
- Add references to the derivation
- Complete the derivation
"},{"location":"tutorials/pendulum/#mathematical-physical-description","title":"Mathematical-physical description\u00b6","text":""},{"location":"tutorials/pendulum/#lagrangian-action","title":"Lagrangian action\u00b6","text":"We describe a generic pendulum system by the Lagrangian action $$ S_L[\\theta] \\equiv \\int_{t_0}^{t_1} \\mathbb{d}t\\,L(\\theta, \\dot\\theta) \\eqqcolon I \\int_{t_0}^{t_1} \\mathbb{d}t \\left\\{\\frac{1}{2} \\dot\\theta^2 + \\omega_0^2 \\cos\\theta \\right\\}\\,, $$ where $L$ is the Lagrangian; $\\theta$ is the angle from the vertical to the pendulum as the generalised position; $I$ is the inertia parameter, $\\omega_0$ the frequency parameter, and we also call $U \\coloneqq I\\omega_0^2$ the potential parameter. This setup contains both the single and the physical pendula. For a single pendulum, $$ I = m l^2\\,,\\qquad U = mgl\\,, $$ where $m$ is the mass of the pendulum, $l$ is the length of the rod or cord, and $g$ is the gravitational acceleration. "},{"location":"tutorials/pendulum/#integral-of-motion","title":"Integral of motion\u00b6","text":"The Lagrangian action does not contain time $t$ explicitly. As a result, the system is invariant under a variation of time, or $\\mathbb{\\delta}S / \\mathbb{\\delta}{t} = 0$. This gives an integral of motion $$ \\dot\\theta\\frac{\\partial L}{\\partial \\dot\\theta} - L \\equiv E \\eqqcolon I \\omega_0^2 \\cos\\theta_0\\,, $$ where $\\theta_0$ is the initial angle. Substitution gives $$ \\left(\\frac{\\mathbb{d}t}{\\mathbb{d}\\theta}\\right)^2 = \\frac{1}{2\\omega_0^2} \\frac{1}{\\cos\\theta - \\cos\\theta_0}\\,. $$ "},{"location":"tutorials/pendulum/#coordinate-transformation","title":"Coordinate transformation\u00b6","text":"For convenience, introduce the coordinate $u$ and the parameter $k$ $$ \\sin u \\coloneqq \\frac{\\sin\\frac{\\theta}{2}}{k}\\,,\\qquad k \\coloneqq \\sin\\frac{\\theta_0}{2} \\in [-1, 1]\\,. $$ One arrives at $$ \\left(\\frac{\\mathbb{d}t}{\\mathbb{d}u}\\right)^2 = \\frac{1}{\\omega_0^2} \\frac{1}{1-k^2\\sin^2 u}\\,. $$ The square root of the second factor on the right-hand side makes an elliptic integral. "},{"location":"tutorials/pendulum/#end-of-notebook","title":"End of Notebook\u00b6","text":""}]}
\ No newline at end of file
diff --git a/pr-preview/pr-58/sitemap.xml b/pr-preview/pr-58/sitemap.xml
index 2ed88ab..6ab499a 100644
--- a/pr-preview/pr-58/sitemap.xml
+++ b/pr-preview/pr-58/sitemap.xml
@@ -2,67 +2,67 @@
https://kausalflow.github.io/hamilflow/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/changelog/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/references/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/references/models/brownian_motion/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/references/models/harmonic_oscillator/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/references/models/harmonic_oscillator_chain/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/references/models/pendulum/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/tutorials/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/tutorials/brownian_motion/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/tutorials/complex_harmonic_oscillator/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/tutorials/harmonic_oscillator/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/tutorials/harmonic_oscillator_chain/
- 2024-07-30
+ 2024-08-04
daily
https://kausalflow.github.io/hamilflow/tutorials/pendulum/
- 2024-07-30
+ 2024-08-04
daily
\ No newline at end of file
diff --git a/pr-preview/pr-58/sitemap.xml.gz b/pr-preview/pr-58/sitemap.xml.gz
index 179a74b..6aa5b07 100644
Binary files a/pr-preview/pr-58/sitemap.xml.gz and b/pr-preview/pr-58/sitemap.xml.gz differ
diff --git a/pr-preview/pr-58/tutorials/brownian_motion/index.html b/pr-preview/pr-58/tutorials/brownian_motion/index.html
index 6939fc8..b436b60 100644
--- a/pr-preview/pr-58/tutorials/brownian_motion/index.html
+++ b/pr-preview/pr-58/tutorials/brownian_motion/index.html
@@ -1305,7 +1305,7 @@
|