Skip to content

Commit 00c9f0b

Browse files
authored
refactoring tests (#781)
* refactoring tests * import mode * following same example testing pattern * respoding to pr comments * excluding anything running tuvx on windows
1 parent 76e4abf commit 00c9f0b

11 files changed

Lines changed: 731 additions & 732 deletions

pyproject.toml

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ version = "${version}"
130130
# and build the musica docker image locally first with:
131131
# docker build --platform linux/amd64 -t musica -f docker/Dockerfile.manylinux.gpu .
132132
#
133+
[tool.pytest.ini_options]
134+
testpaths = ["python/test"]
135+
133136
[tool.cibuildwheel]
134137
# Increase pip debugging output
135138
build-verbosity = 3
@@ -154,19 +157,12 @@ test-command = [
154157
"pytest {project}/python/test",
155158
"musica-cli --convert configs/v0/TS1/config.json -o ts1.json",
156159
"musica-cli -e CARMA_Aluminum -o .",
157-
"python carma_aluminum.py",
158160
"musica-cli -e CARMA_Sulfate -o .",
159-
"python carma_sulfate.py",
160161
"musica-cli -e Lorenz_Attractor -o .",
161-
"python lorenz.py",
162162
"musica-cli -e Sulfate_Box_Model -o .",
163-
"python sulfate_box_model.py",
164163
"musica-cli -e TS1BoxModel -o .",
165-
"python ts1_box_model.py",
166164
"musica-cli -e TS1LatinHyperCube -o .",
167-
"python ts1_latin_hypercube.py",
168165
"musica-cli -e Chapman -o .",
169-
"python chapman.py",
170166
]
171167

172168
# Set up pre-build hooks
@@ -204,7 +200,7 @@ skip = ["cp39-win_arm64", "cp310-win_arm64"]
204200
test-command = [
205201
"python -c \"import musica; print(musica.__version__)\"",
206202
"musica-cli --help",
207-
"pytest {project}\\python\\test -k \"not tuvx\"",
203+
"pytest {project}\\python\\test -k \"not tuvx and not ts1_box_model\"",
208204
]
209205

210206
[tool.cibuildwheel.windows.environment]

python/musica/examples/lorenz.py

Lines changed: 100 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
from musica.micm import MICM, SolverType
22
import musica.mechanism_configuration as mc
3-
import matplotlib.pyplot as plt
4-
from matplotlib import animation
5-
from matplotlib.animation import FFMpegWriter, PillowWriter
63
import argparse
74
import os
85
import numpy as np
@@ -116,7 +113,7 @@ def create_lorenz_mechanism():
116113
return mechanism
117114

118115

119-
def main(output='lorenz.mp4', fps=30, n=2):
116+
def main(output='lorenz.mp4', fps=30, n=2, plot=True):
120117
"""
121118
Run the Lorenz polynomial chemical reaction network simulation and save an animation.
122119
Parameters
@@ -128,6 +125,8 @@ def main(output='lorenz.mp4', fps=30, n=2):
128125
Frames per second for the generated animation. Defaults to 30.
129126
n : int, optional
130127
The number of grid cells to simulate the attractor in
128+
plot : bool, optional
129+
Whether to generate the animation. Defaults to True.
131130
Notes
132131
-----
133132
This function constructs the Lorenz mechanism, initializes the MICM solver, sets
@@ -195,98 +194,105 @@ def main(output='lorenz.mp4', fps=30, n=2):
195194

196195
print("Simulation complete.")
197196

198-
def create_animation(Xs, Ys, Zs, outpath='lorenz.mp4', fps=30):
199-
# Xs, Ys, Zs are lists of length n each containing a time-series list
200-
Ncells = len(Xs)
201-
if Ncells == 0:
202-
print("No trajectory data to animate.")
203-
return
204-
205-
fig = plt.figure()
206-
ax = fig.add_subplot(111, projection='3d')
207-
208-
ax.set_xlabel('X Concentration')
209-
ax.set_ylabel('Y Concentration')
210-
ax.set_zlabel('Z Concentration')
211-
ax.set_title('Lorenz Attractor from Chemical Reaction Network')
212-
213-
xmin = min(min(xs) for xs in Xs)
214-
xmax = max(max(xs) for xs in Xs)
215-
ymin = min(min(ys) for ys in Ys)
216-
ymax = max(max(ys) for ys in Ys)
217-
zmin = min(min(zs) for zs in Zs)
218-
zmax = max(max(zs) for zs in Zs)
219-
ax.set_xlim(xmin, xmax)
220-
ax.set_ylim(ymin, ymax)
221-
ax.set_zlim(zmin, zmax)
222-
# create one line+point per grid cell
223-
cmap = plt.get_cmap('tab10')
224-
colors = [cmap(i % 10) for i in range(Ncells)]
225-
226-
lines = []
227-
points = []
228-
for idx in range(Ncells):
229-
ln, = ax.plot([], [], [], lw=1, color=colors[idx], alpha=0.5, label=f'cell {idx}')
230-
pt, = ax.plot([], [], [], 'o', color=colors[idx], markersize=3)
231-
lines.append(ln)
232-
points.append(pt)
233-
234-
def init():
235-
artists = []
236-
for ln, pt in zip(lines, points):
237-
ln.set_data([], [])
238-
ln.set_3d_properties([])
239-
pt.set_data([], [])
240-
pt.set_3d_properties([])
241-
artists.extend([ln, pt])
242-
return artists
243-
244-
def update(i):
245-
artists = []
197+
if plot:
198+
import matplotlib.pyplot as plt
199+
from matplotlib import animation
200+
from matplotlib.animation import FFMpegWriter, PillowWriter
201+
202+
running_in_cibw = os.environ.get("CIBUILDWHEEL", "").lower() in {"1", "true", "yes"}
203+
if running_in_cibw:
204+
print("Detected cibuildwheel; skipping animation generation.")
205+
return Xs, Ys, Zs
206+
207+
def create_animation(Xs, Ys, Zs, outpath='lorenz.mp4', fps=30):
208+
# Xs, Ys, Zs are lists of length n each containing a time-series list
209+
Ncells = len(Xs)
210+
if Ncells == 0:
211+
print("No trajectory data to animate.")
212+
return
213+
214+
fig = plt.figure()
215+
ax = fig.add_subplot(111, projection='3d')
216+
217+
ax.set_xlabel('X Concentration')
218+
ax.set_ylabel('Y Concentration')
219+
ax.set_zlabel('Z Concentration')
220+
ax.set_title('Lorenz Attractor from Chemical Reaction Network')
221+
222+
xmin = min(min(xs) for xs in Xs)
223+
xmax = max(max(xs) for xs in Xs)
224+
ymin = min(min(ys) for ys in Ys)
225+
ymax = max(max(ys) for ys in Ys)
226+
zmin = min(min(zs) for zs in Zs)
227+
zmax = max(max(zs) for zs in Zs)
228+
ax.set_xlim(xmin, xmax)
229+
ax.set_ylim(ymin, ymax)
230+
ax.set_zlim(zmin, zmax)
231+
# create one line+point per grid cell
232+
cmap = plt.get_cmap('tab10')
233+
colors = [cmap(i % 10) for i in range(Ncells)]
234+
235+
lines = []
236+
points = []
246237
for idx in range(Ncells):
247-
xs = Xs[idx]
248-
ys = Ys[idx]
249-
zs = Zs[idx]
250-
# clamp i for safety
251-
j = min(i, len(xs) - 1)
252-
lines[idx].set_data(xs[:j], ys[:j])
253-
lines[idx].set_3d_properties(zs[:j])
254-
if j > 0:
255-
points[idx].set_data([xs[j - 1]], [ys[j - 1]])
256-
points[idx].set_3d_properties([zs[j - 1]])
257-
artists.extend([lines[idx], points[idx]])
258-
return artists
259-
260-
frames = len(Xs[0])
261-
interval = 1000.0 / fps
262-
263-
anim = animation.FuncAnimation(
264-
fig, update, init_func=init, frames=frames, interval=interval, blit=True)
265-
266-
outdir = os.path.dirname(outpath) or '.'
267-
os.makedirs(outdir, exist_ok=True)
268-
269-
try:
270-
ax.legend()
271-
writer = FFMpegWriter(fps=fps)
272-
anim.save(outpath, writer=writer)
273-
print(f"Saved animation to {outpath}")
274-
except Exception:
238+
ln, = ax.plot([], [], [], lw=1, color=colors[idx], alpha=0.5, label=f'cell {idx}')
239+
pt, = ax.plot([], [], [], 'o', color=colors[idx], markersize=3)
240+
lines.append(ln)
241+
points.append(pt)
242+
243+
def init():
244+
artists = []
245+
for ln, pt in zip(lines, points):
246+
ln.set_data([], [])
247+
ln.set_3d_properties([])
248+
pt.set_data([], [])
249+
pt.set_3d_properties([])
250+
artists.extend([ln, pt])
251+
return artists
252+
253+
def update(i):
254+
artists = []
255+
for idx in range(Ncells):
256+
xs = Xs[idx]
257+
ys = Ys[idx]
258+
zs = Zs[idx]
259+
# clamp i for safety
260+
j = min(i, len(xs) - 1)
261+
lines[idx].set_data(xs[:j], ys[:j])
262+
lines[idx].set_3d_properties(zs[:j])
263+
if j > 0:
264+
points[idx].set_data([xs[j - 1]], [ys[j - 1]])
265+
points[idx].set_3d_properties([zs[j - 1]])
266+
artists.extend([lines[idx], points[idx]])
267+
return artists
268+
269+
frames = len(Xs[0])
270+
interval = 1000.0 / fps
271+
272+
anim = animation.FuncAnimation(
273+
fig, update, init_func=init, frames=frames, interval=interval, blit=True)
274+
275+
outdir = os.path.dirname(outpath) or '.'
276+
os.makedirs(outdir, exist_ok=True)
277+
275278
try:
276-
gif_path = os.path.splitext(outpath)[0] + '.gif'
277-
writer = PillowWriter(fps=fps)
278-
anim.save(gif_path, writer=writer)
279-
print(f"FFmpeg unavailable; saved GIF to {gif_path}")
280-
except Exception as e:
281-
print("Failed to save animation:", e)
282-
283-
running_in_cibw = os.environ.get("CIBUILDWHEEL", "").lower() in {"1", "true", "yes"}
284-
if running_in_cibw:
285-
print("Detected cibuildwheel; skipping animation generation.")
286-
return
287-
288-
# Save animation with defaults; CLI can override via args below
289-
create_animation(Xs, Ys, Zs, outpath=output, fps=fps)
279+
ax.legend()
280+
writer = FFMpegWriter(fps=fps)
281+
anim.save(outpath, writer=writer)
282+
print(f"Saved animation to {outpath}")
283+
except Exception:
284+
try:
285+
gif_path = os.path.splitext(outpath)[0] + '.gif'
286+
writer = PillowWriter(fps=fps)
287+
anim.save(gif_path, writer=writer)
288+
print(f"FFmpeg unavailable; saved GIF to {gif_path}")
289+
except Exception as e:
290+
print("Failed to save animation:", e)
291+
292+
# Save animation with defaults; CLI can override via args below
293+
create_animation(Xs, Ys, Zs, outpath=output, fps=fps)
294+
295+
return Xs, Ys, Zs
290296

291297

292298
if __name__ == "__main__":

0 commit comments

Comments
 (0)