musica 0.14.4__cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- musica/__init__.py +11 -0
- musica/_musica.cpython-310-aarch64-linux-gnu.so +0 -0
- musica/_version.py +1 -0
- musica/backend.py +58 -0
- musica/carma/__init__.py +20 -0
- musica/carma/carma.py +1727 -0
- musica/constants.py +3 -0
- musica/cuda.py +13 -0
- musica/examples/__init__.py +1 -0
- musica/examples/carma_aluminum.py +124 -0
- musica/examples/carma_sulfate.py +246 -0
- musica/examples/examples.py +175 -0
- musica/examples/lorenz.py +295 -0
- musica/examples/sulfate_box_model.py +439 -0
- musica/examples/ts1_latin_hypercube.py +245 -0
- musica/main.py +128 -0
- musica/mechanism_configuration/__init__.py +18 -0
- musica/mechanism_configuration/ancillary.py +6 -0
- musica/mechanism_configuration/arrhenius.py +149 -0
- musica/mechanism_configuration/branched.py +140 -0
- musica/mechanism_configuration/emission.py +82 -0
- musica/mechanism_configuration/first_order_loss.py +90 -0
- musica/mechanism_configuration/mechanism.py +93 -0
- musica/mechanism_configuration/phase.py +58 -0
- musica/mechanism_configuration/phase_species.py +58 -0
- musica/mechanism_configuration/photolysis.py +98 -0
- musica/mechanism_configuration/reaction_component.py +54 -0
- musica/mechanism_configuration/reactions.py +32 -0
- musica/mechanism_configuration/species.py +65 -0
- musica/mechanism_configuration/surface.py +98 -0
- musica/mechanism_configuration/taylor_series.py +136 -0
- musica/mechanism_configuration/ternary_chemical_activation.py +160 -0
- musica/mechanism_configuration/troe.py +160 -0
- musica/mechanism_configuration/tunneling.py +126 -0
- musica/mechanism_configuration/user_defined.py +99 -0
- musica/mechanism_configuration/utils.py +10 -0
- musica/micm/__init__.py +10 -0
- musica/micm/conditions.py +49 -0
- musica/micm/micm.py +135 -0
- musica/micm/solver.py +8 -0
- musica/micm/solver_result.py +24 -0
- musica/micm/state.py +220 -0
- musica/micm/utils.py +18 -0
- musica/tuvx/__init__.py +11 -0
- musica/tuvx/grid.py +98 -0
- musica/tuvx/grid_map.py +167 -0
- musica/tuvx/profile.py +130 -0
- musica/tuvx/profile_map.py +167 -0
- musica/tuvx/radiator.py +95 -0
- musica/tuvx/radiator_map.py +173 -0
- musica/tuvx/tuvx.py +283 -0
- musica-0.14.4.dist-info/METADATA +427 -0
- musica-0.14.4.dist-info/RECORD +92 -0
- musica-0.14.4.dist-info/WHEEL +6 -0
- musica-0.14.4.dist-info/entry_points.txt +3 -0
- musica-0.14.4.dist-info/licenses/AUTHORS.md +59 -0
- musica-0.14.4.dist-info/licenses/LICENSE +201 -0
- musica.libs/libaec-34bb4966.so.0.0.8 +0 -0
- musica.libs/libblas-8ed0a6f9.so.3.8.0 +0 -0
- musica.libs/libbrotlicommon-b6e6c8bd.so.1.0.6 +0 -0
- musica.libs/libbrotlidec-5094ef0a.so.1.0.6 +0 -0
- musica.libs/libcom_err-6d8d18aa.so.2.1 +0 -0
- musica.libs/libcrypt-258f54d5.so.1.1.0 +0 -0
- musica.libs/libcrypto-3eda328c.so.1.1.1k +0 -0
- musica.libs/libcurl-7faeef02.so.4.5.0 +0 -0
- musica.libs/libdf-9661c601.so.0.0.0 +0 -0
- musica.libs/libgfortran-e1b7dfc8.so.5.0.0 +0 -0
- musica.libs/libgssapi_krb5-fe951f80.so.2.2 +0 -0
- musica.libs/libhdf5-463e48d5.so.103.1.0 +0 -0
- musica.libs/libhdf5_hl-74316838.so.100.1.2 +0 -0
- musica.libs/libidn2-1b2a13b7.so.0.3.6 +0 -0
- musica.libs/libjpeg-ee25248c.so.62.2.0 +0 -0
- musica.libs/libk5crypto-84470bb3.so.3.1 +0 -0
- musica.libs/libkeyutils-fe6e95a9.so.1.6 +0 -0
- musica.libs/libkrb5-26ef5d84.so.3.3 +0 -0
- musica.libs/libkrb5support-875e89dc.so.0.1 +0 -0
- musica.libs/liblapack-8d137073.so.3.8.0 +0 -0
- musica.libs/liblber-2-86b08e65.4.so.2.10.9 +0 -0
- musica.libs/libldap-2-5c1dd279.4.so.2.10.9 +0 -0
- musica.libs/libmfhdf-9c336c5f.so.0.0.0 +0 -0
- musica.libs/libnetcdf-71a067be.so.15.0.1 +0 -0
- musica.libs/libnetcdff-6a455dd4.so.7.0.0 +0 -0
- musica.libs/libnghttp2-3a94c239.so.14.17.0 +0 -0
- musica.libs/libpcre2-8-8701a61e.so.0.7.1 +0 -0
- musica.libs/libpsl-130094ea.so.5.3.1 +0 -0
- musica.libs/libsasl2-076b3c1f.so.3.0.0 +0 -0
- musica.libs/libselinux-5700a1fd.so.1 +0 -0
- musica.libs/libssh-e0d3bd94.so.4.8.7 +0 -0
- musica.libs/libssl-f60bf0e2.so.1.1.1k +0 -0
- musica.libs/libsz-81b556a2.so.2.0.1 +0 -0
- musica.libs/libtirpc-1fa9018c.so.3.0.0 +0 -0
- musica.libs/libunistring-be03fd41.so.2.1.0 +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from musica.micm import MICM, SolverType
|
|
2
|
+
import musica.mechanism_configuration as mc
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
from matplotlib import animation
|
|
5
|
+
from matplotlib.animation import FFMpegWriter, PillowWriter
|
|
6
|
+
import argparse
|
|
7
|
+
import os
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def full_step(micm, state, time_step):
|
|
12
|
+
"""
|
|
13
|
+
Advance the MICM state by a full time step, retrying if necessary.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
micm : MICM
|
|
18
|
+
The MICM solver instance.
|
|
19
|
+
state : MICM.State
|
|
20
|
+
The current state of the system.
|
|
21
|
+
time_step : float
|
|
22
|
+
The time step to advance.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
elapsed_time = 0
|
|
26
|
+
while elapsed_time < time_step:
|
|
27
|
+
result = micm.solve(state, time_step=time_step)
|
|
28
|
+
elapsed_time += result.stats.final_time
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def create_lorenz_mechanism():
|
|
32
|
+
"""
|
|
33
|
+
Create a Lorenz mechanism
|
|
34
|
+
|
|
35
|
+
from https://doi.org/10.1007/s11071-025-11622-1
|
|
36
|
+
"""
|
|
37
|
+
# Species
|
|
38
|
+
X = mc.Species(name="X")
|
|
39
|
+
Y = mc.Species(name="Y")
|
|
40
|
+
Z = mc.Species(name="Z")
|
|
41
|
+
|
|
42
|
+
# Gas phase
|
|
43
|
+
gas = mc.Phase(name="gas", species=[X, Y, Z])
|
|
44
|
+
|
|
45
|
+
# User-defined reactions
|
|
46
|
+
reactions = [
|
|
47
|
+
mc.UserDefined(
|
|
48
|
+
name="X_decay",
|
|
49
|
+
gas_phase=gas,
|
|
50
|
+
reactants=[X],
|
|
51
|
+
products=[]
|
|
52
|
+
),
|
|
53
|
+
mc.UserDefined(
|
|
54
|
+
name="Y_to_XY",
|
|
55
|
+
gas_phase=gas,
|
|
56
|
+
reactants=[Y],
|
|
57
|
+
products=[X, Y]
|
|
58
|
+
),
|
|
59
|
+
mc.UserDefined(
|
|
60
|
+
name="Y_source",
|
|
61
|
+
gas_phase=gas,
|
|
62
|
+
reactants=[],
|
|
63
|
+
products=[Y]
|
|
64
|
+
),
|
|
65
|
+
mc.UserDefined(
|
|
66
|
+
name="Y_sink",
|
|
67
|
+
gas_phase=gas,
|
|
68
|
+
reactants=[Y],
|
|
69
|
+
products=[]
|
|
70
|
+
),
|
|
71
|
+
mc.UserDefined(
|
|
72
|
+
name="XY_to_X2Y",
|
|
73
|
+
gas_phase=gas,
|
|
74
|
+
reactants=[X, Y],
|
|
75
|
+
products=[X, Y, Y]
|
|
76
|
+
),
|
|
77
|
+
mc.UserDefined(
|
|
78
|
+
name="YZ_to_2Y",
|
|
79
|
+
gas_phase=gas,
|
|
80
|
+
reactants=[Y, Z],
|
|
81
|
+
products=[Y, Y]
|
|
82
|
+
),
|
|
83
|
+
mc.UserDefined(
|
|
84
|
+
name="XYZ_to_X2Z",
|
|
85
|
+
gas_phase=gas,
|
|
86
|
+
reactants=[X, Y, Z],
|
|
87
|
+
products=[X, Z, Z]
|
|
88
|
+
),
|
|
89
|
+
mc.UserDefined(
|
|
90
|
+
name="Z_source",
|
|
91
|
+
gas_phase=gas,
|
|
92
|
+
reactants=[],
|
|
93
|
+
products=[Z]
|
|
94
|
+
),
|
|
95
|
+
mc.UserDefined(
|
|
96
|
+
name="Z_autocatalytic",
|
|
97
|
+
gas_phase=gas,
|
|
98
|
+
reactants=[Z],
|
|
99
|
+
products=[Z, Z]
|
|
100
|
+
),
|
|
101
|
+
mc.UserDefined(
|
|
102
|
+
name="XZ_quench",
|
|
103
|
+
gas_phase=gas,
|
|
104
|
+
reactants=[X, Z],
|
|
105
|
+
products=[X]
|
|
106
|
+
)
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
# Mechanism
|
|
110
|
+
mechanism = mc.Mechanism(
|
|
111
|
+
name="Lorenz Polynomial CRN",
|
|
112
|
+
species=[X, Y, Z],
|
|
113
|
+
phases=[gas],
|
|
114
|
+
reactions=reactions
|
|
115
|
+
)
|
|
116
|
+
return mechanism
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def main(output='lorenz.mp4', fps=30, n=2):
|
|
120
|
+
"""
|
|
121
|
+
Run the Lorenz polynomial chemical reaction network simulation and save an animation.
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
output : str, optional
|
|
125
|
+
Path to the output animation file (e.g., MP4 or GIF) to be written. Defaults
|
|
126
|
+
to ``"lorenz.mp4"``.
|
|
127
|
+
fps : int, optional
|
|
128
|
+
Frames per second for the generated animation. Defaults to 30.
|
|
129
|
+
n : int, optional
|
|
130
|
+
The number of grid cells to simulate the attractor in
|
|
131
|
+
Notes
|
|
132
|
+
-----
|
|
133
|
+
This function constructs the Lorenz mechanism, initializes the MICM solver, sets
|
|
134
|
+
rate parameters and initial concentrations, advances the system for a fixed number
|
|
135
|
+
of time steps, and produces a time-evolving visualization of the state variables.
|
|
136
|
+
"""
|
|
137
|
+
mechanism = create_lorenz_mechanism()
|
|
138
|
+
|
|
139
|
+
# Initialize MICM with `n` grid cells (one State containing N cells)
|
|
140
|
+
micm = MICM(mechanism=mechanism, solver_type=SolverType.rosenbrock_standard_order)
|
|
141
|
+
state = micm.create_state(n)
|
|
142
|
+
|
|
143
|
+
# Rate parameters (broadcast to each grid cell)
|
|
144
|
+
mu = 1.0 / 100
|
|
145
|
+
rate_params = {
|
|
146
|
+
"USER.X_decay": 10,
|
|
147
|
+
"USER.Y_to_XY": 10,
|
|
148
|
+
"USER.Y_source": 1 / mu,
|
|
149
|
+
"USER.Y_sink": 1 / mu + 29,
|
|
150
|
+
"USER.XY_to_X2Y": 1 + mu * 28,
|
|
151
|
+
"USER.YZ_to_2Y": 1,
|
|
152
|
+
"USER.XYZ_to_X2Z": mu,
|
|
153
|
+
"USER.Z_source": 8 / (3 * mu),
|
|
154
|
+
"USER.Z_autocatalytic": 1 / mu - 8 / 3,
|
|
155
|
+
"USER.XZ_quench": 1
|
|
156
|
+
}
|
|
157
|
+
# Broadcast scalar rate parameters to lists for each grid cell
|
|
158
|
+
rate_params_broadcast = {k: [v] * n for k, v in rate_params.items()}
|
|
159
|
+
state.set_user_defined_rate_parameters(rate_params_broadcast)
|
|
160
|
+
|
|
161
|
+
# Base initial concentrations for each grid cell; apply small perturbations
|
|
162
|
+
base_init = {"X": 1.0, "Y": 28.0, "Z": 1.0}
|
|
163
|
+
delta = 1e-1
|
|
164
|
+
X_init = [base_init["X"] + i * delta for i in range(n)]
|
|
165
|
+
Y_init = [base_init["Y"] + i * delta for i in range(n)]
|
|
166
|
+
Z_init = [base_init["Z"] for _ in range(n)]
|
|
167
|
+
state.set_concentrations({"X": X_init, "Y": Y_init, "Z": Z_init})
|
|
168
|
+
|
|
169
|
+
nsteps = 2000
|
|
170
|
+
burnout = 200
|
|
171
|
+
|
|
172
|
+
# store trajectories per grid cell: lists of length n containing lists of values
|
|
173
|
+
Xs = [[] for _ in range(n)]
|
|
174
|
+
Ys = [[] for _ in range(n)]
|
|
175
|
+
Zs = [[] for _ in range(n)]
|
|
176
|
+
time_step = 0.01
|
|
177
|
+
for step in range(nsteps):
|
|
178
|
+
# advance the multi-cell state by one time_step
|
|
179
|
+
full_step(micm, state, time_step)
|
|
180
|
+
|
|
181
|
+
if step < burnout:
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
concs = state.get_concentrations()
|
|
185
|
+
# Ensure arrays and flatten
|
|
186
|
+
Xvals = np.ravel(concs["X"])
|
|
187
|
+
Yvals = np.ravel(concs["Y"])
|
|
188
|
+
Zvals = np.ravel(concs["Z"])
|
|
189
|
+
|
|
190
|
+
# Append each grid cell's current concentration
|
|
191
|
+
for i in range(n):
|
|
192
|
+
Xs[i].append(float(Xvals[i]))
|
|
193
|
+
Ys[i].append(float(Yvals[i]))
|
|
194
|
+
Zs[i].append(float(Zvals[i]))
|
|
195
|
+
|
|
196
|
+
print("Simulation complete.")
|
|
197
|
+
|
|
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 = []
|
|
246
|
+
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:
|
|
275
|
+
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
|
+
# Save animation with defaults; CLI can override via args below
|
|
284
|
+
create_animation(Xs, Ys, Zs, outpath=output, fps=fps)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
if __name__ == "__main__":
|
|
288
|
+
parser = argparse.ArgumentParser(description='Run Lorenz CRN simulation and create animation')
|
|
289
|
+
parser.add_argument('--output', '-o', default='lorenz.mp4', help='Output movie file (mp4 preferred)')
|
|
290
|
+
parser.add_argument('--fps', type=int, default=30, help='Frames per second for the movie')
|
|
291
|
+
parser.add_argument('--n', type=int, default=2, help='Number of grid cells / trajectories')
|
|
292
|
+
args = parser.parse_args()
|
|
293
|
+
|
|
294
|
+
# Rerun main with requested output and fps
|
|
295
|
+
main(output=args.output, fps=args.fps, n=args.n)
|