Skip to content

Commit

Permalink
Added static folder with gif generation in readme.
Browse files Browse the repository at this point in the history
- added matplotlib to plot dependencies.
- changed Lorenz63 default time step.
  • Loading branch information
DuncDennis committed Oct 20, 2023
1 parent 0aa771b commit 128e18d
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 2 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ repos:
hooks:
- id: trailing-whitespace
- id: check-added-large-files
args: ['--maxkb=1500']
- repo: https://github.com/psf/black
rev: "23.1.0"
hooks:
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
# LorenzPy

A Python package to simulate and measure chaotic dynamical systems.

[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v1.json)](https://github.com/charliermarsh/ruff)
[![codecov](https://codecov.io/gh/DuncDennis/lorenzpy/branch/main/graph/badge.svg?token=ATWAEQHBYB)](https://codecov.io/gh/DuncDennis/lorenzpy)
[![license: MIT](https://img.shields.io/badge/License-MIT-purple.svg)](LICENSE)
[![Python versions](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)

------

![Flow-Attractors](static/attractor_animation.gif)

------

## ⚙️ Installation

To install only the core functionality:
Expand All @@ -19,6 +27,7 @@ This also installs `plotly`.
$ pip install lorenzpy[plot]
```


## ▶️ Usage

LorenzPy can be used to simulate and measure chaotic dynamical systems.
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dev = [
]
plot = [
"plotly>=5.11",
"matplotlib>=3.5"
]

[tool.pytest.ini_options]
Expand All @@ -58,7 +59,7 @@ line-length = 88
files = "src/lorenzpy/"

[[tool.mypy.overrides]]
module = ['plotly.*', 'numpy', 'pytest', "scipy.*"] # ignore missing imports from the plotly package.
module = ['plotly.*', 'numpy', 'pytest', "scipy.*", "matplotlib.*", "PIL"] # ignore missing imports from the plotly package.
ignore_missing_imports = true

[tool.ruff]
Expand Down
2 changes: 1 addition & 1 deletion src/lorenzpy/simulations/autonomous_flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(
sigma: float = 10.0,
rho: float = 28.0,
beta: float = 8 / 3,
dt: float = 0.05,
dt: float = 0.03,
solver: str | str | Callable[[Callable, float, np.ndarray], np.ndarray] = "rk4",
):
"""Initialize the Lorenz63 simulation object.
Expand Down
Binary file added static/attractor_animation.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
108 changes: 108 additions & 0 deletions static/gen_attractor_animation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"""Generate 3 x 3 subplots with rotating attractors."""
import os
from pathlib import Path

import matplotlib.pyplot as plt
from PIL import Image

from lorenzpy import simulations as sims

systems = [
"Lorenz63",
"Chen",
"ChuaCircuit",
"ComplexButterfly",
"DoubleScroll",
"Halvorsen",
"Roessler",
"Rucklidge",
"Thomas",
"WindmiAttractor",
# "DoublePendulum",
# "Lorenz96",
# "Logistic",
# "Henon",
# "SimplestDrivenChaotic",
# "KuramotoSivashinsky",
# "MackeyGlass",
]

# create data:
N = 1000
data_dict = {}
for i_sys, system in enumerate(systems):
sys_class = sim_class = getattr(sims, system)
data = sys_class().simulate(N)
data_dict[system] = data

# Plot:
plt.style.use("dark_background")
fig, axs = plt.subplots(2, 5, figsize=(10, 3), subplot_kw=dict(projection="3d"))
# plt.subplots_adjust(wspace=5)
plt.subplots_adjust(hspace=0.5)
# fig.tight_layout()
axs = axs.flatten()
for i_ax, ax in enumerate(axs):
system = systems[i_ax]
data = data_dict[system]
ax.title.set_text(system)
ax.plot(data[:, 0], data[:, 1], data[:, 2], linewidth=0.2, color="white")
ax.set_xticks([]) # Remove x-axis ticks
ax.set_yticks([]) # Remove y-axis ticks
ax.set_zticks([]) # Remove y-axis ticks
ax.set_xticklabels([]) # Remove x-axis tick labels
ax.set_yticklabels([])
ax.set_zticklabels([])
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.axis("off")


def update_graph(num):
"""Rotate camera angle."""
azim = num
roll = 0
elev = 0
for i_ax, ax in enumerate(axs):
ax.view_init(elev, azim, roll)


# Create frames:

frames_dir = "frames"
Path(frames_dir).mkdir(exist_ok=True)
previous_files = [f"{frames_dir}/" + x for x in os.listdir(frames_dir)]
for prev_file in previous_files:
os.remove(prev_file)

for num in range(0, 360):
update_graph(num)
plt.savefig(f"{frames_dir}/frame_{str(num).zfill(3)}.png", transparent=True, dpi=50)

# gif:
image_filenames = [
f"{frames_dir}/" + x
for x in os.listdir("frames")
if x.endswith(".png") and x.startswith("frame_")
]

images = [Image.open(filename) for filename in image_filenames]

# Create a GIF
images[0].save(
"attractor_animation.gif",
format="GIF",
save_all=True,
append_images=images[1:],
duration=20,
loop=0,
transparency=0,
disposal=2,
)

# Cleanup:
previous_files = [f"{frames_dir}/" + x for x in os.listdir(frames_dir)]
for prev_file in previous_files:
os.remove(prev_file)
os.rmdir(frames_dir)

0 comments on commit 128e18d

Please sign in to comment.