reaxion 0.1.1__tar.gz

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.
Files changed (37) hide show
  1. reaxion-0.1.1/LICENSE +21 -0
  2. reaxion-0.1.1/PKG-INFO +411 -0
  3. reaxion-0.1.1/README.md +390 -0
  4. reaxion-0.1.1/pyproject.toml +27 -0
  5. reaxion-0.1.1/setup.cfg +4 -0
  6. reaxion-0.1.1/src/reaxion/__init__.py +5 -0
  7. reaxion-0.1.1/src/reaxion/data/__init__.py +1 -0
  8. reaxion-0.1.1/src/reaxion/data/atomic_weights.py +123 -0
  9. reaxion-0.1.1/src/reaxion/data/solar_abundances.py +49 -0
  10. reaxion-0.1.1/src/reaxion/eos/__init__.py +1 -0
  11. reaxion-0.1.1/src/reaxion/eos/eos.py +3 -0
  12. reaxion-0.1.1/src/reaxion/equation.py +41 -0
  13. reaxion-0.1.1/src/reaxion/equation_system.py +380 -0
  14. reaxion-0.1.1/src/reaxion/localstate.py +21 -0
  15. reaxion-0.1.1/src/reaxion/misc.py +66 -0
  16. reaxion-0.1.1/src/reaxion/networks/__init__.py +0 -0
  17. reaxion-0.1.1/src/reaxion/numerics/__init__.py +1 -0
  18. reaxion-0.1.1/src/reaxion/numerics/solvers.py +98 -0
  19. reaxion-0.1.1/src/reaxion/numerics/tests/__init__.py +0 -0
  20. reaxion-0.1.1/src/reaxion/numerics/tests/test_linear.py +33 -0
  21. reaxion-0.1.1/src/reaxion/numerics/tests/test_newton_rootsolve.py +34 -0
  22. reaxion-0.1.1/src/reaxion/process.py +126 -0
  23. reaxion-0.1.1/src/reaxion/processes/__init__.py +7 -0
  24. reaxion-0.1.1/src/reaxion/processes/freefree_emission.py +32 -0
  25. reaxion-0.1.1/src/reaxion/processes/ionization.py +95 -0
  26. reaxion-0.1.1/src/reaxion/processes/line_cooling.py +56 -0
  27. reaxion-0.1.1/src/reaxion/processes/nbody_process.py +71 -0
  28. reaxion-0.1.1/src/reaxion/processes/recombination.py +112 -0
  29. reaxion-0.1.1/src/reaxion/processes/thermal_process.py +22 -0
  30. reaxion-0.1.1/src/reaxion/symbols.py +57 -0
  31. reaxion-0.1.1/src/reaxion.egg-info/PKG-INFO +411 -0
  32. reaxion-0.1.1/src/reaxion.egg-info/SOURCES.txt +35 -0
  33. reaxion-0.1.1/src/reaxion.egg-info/dependency_links.txt +1 -0
  34. reaxion-0.1.1/src/reaxion.egg-info/requires.txt +6 -0
  35. reaxion-0.1.1/src/reaxion.egg-info/top_level.txt +1 -0
  36. reaxion-0.1.1/tests/test_CIE.py +37 -0
  37. reaxion-0.1.1/tests/test_neutral_cooling.py +84 -0
reaxion-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Mike Grudić
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
reaxion-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,411 @@
1
+ Metadata-Version: 2.4
2
+ Name: reaxion
3
+ Version: 0.1.1
4
+ Summary: object-oriented ISM processes
5
+ Author-email: Mike Grudić <mike.grudic@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/mikegrudic/reaxion
8
+ Project-URL: Issues, https://github.com/mikegrudic/reaxion/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.10
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: jax
15
+ Requires-Dist: sympy
16
+ Requires-Dist: numpy
17
+ Requires-Dist: sphinx_rtd_theme
18
+ Requires-Dist: astropy
19
+ Requires-Dist: matplotlib
20
+ Dynamic: license-file
21
+
22
+ # reaxion
23
+
24
+ [![Python package](https://github.com/mikegrudic/reaxion/actions/workflows/test.yml/badge.svg)](https://github.com/mikegrudic/reaxion/actions/workflows/test.yml)
25
+ [![Readthedocs Status][docs-badge]][docs-link]
26
+ [![codecov](https://codecov.io/github/mikegrudic/reaxion/graph/badge.svg?token=OWJQMWGABZ)](https://codecov.io/github/mikegrudic/reaxion)
27
+
28
+ [docs-link]: https://reaxion.readthedocs.io
29
+ [docs-badge]: https://readthedocs.org/projects/reaxion/badge
30
+
31
+ `reaxion` is a flexible, object-oriented implementation for systems of ISM microphysics and chemistry equations, with numerical solvers implemented in JAX, and interfaces for embedding the equations and their Jacobians into other codes.
32
+
33
+ ## Do we really need yet another ISM code?
34
+
35
+ `reaxion` might be interesting because it combines two powerful concepts:
36
+ 1. **Object-oriented implementation of microphysics and chemistry via the `Process` class**, which implements methods for representing physical processes, composing them into a network in a fully-symbolic `sympy` representation. OOP is nice here because if you want to add a new process to `reaxion`, you typically only have to do it in one file. Rate expressions never have to be repeated in-code. Most processes one would want to implement follow very common patterns (e.g. 2-body processes), so class inheritance is also used to minimize new lines of code.
37
+ Once you've constructed your system, `reaxion` can give you the symbolic equations to manipulate and analyze as you please. If you want to solve the equations numerically, `Process` has methods for substituting known values into numerical solvers. It can also automatically generate compilable implementations of the RHS of the system to embed in your choice of simulation code and plug into your choice of solver.
38
+ 2. **Fast, differentiable implementation of nonlinear algebraic and differential-algebraic equation solvers with JAX**, implemented in its functional programming paradigm (e.g. `reaxion.numerics.newton_rootsolve`). These can achieve excellent numerical throughput running natively on GPUs - in fact, crunching iterates in-place is essentially the best-case application of numerics on GPUs. Differentiability enables sensitivity analysis with respect to all parameters in a single pass, instead of constructing a grid of `N` parameter variations for `N` parameters. This makes it easier in principle to directly answer questions like "How sensitive is this temperature to the abundance of C or the ionization energy of H?", etc.
39
+
40
+ ## Roadmap
41
+
42
+ `reaxion` is in an early prototyping phase right now. Here are some things I would eventually like to add:
43
+ * Flexible implementation of a reduced network suitable for RHD simulations in GIZMO and potentially other codes.
44
+ * Dust and radiation physics: add the dust energy equation and evolution of photon number densities to the network.
45
+ * Interfaces to convert from other existing chemistry network formats to the `Process` representation.
46
+ * Solver robustness upgrades: thermochemical networks can be quite challenging numerically, due to how steeply terms switch on with increasing `T`. In can be hard to get a solution without good initial guesses.
47
+ * If possible, glue interface allowing an existing compiled hydro code to call the JAX solvers on-the-fly.
48
+
49
+ ## Installation
50
+
51
+ Clone the repo and run `pip install .` from the directory.
52
+
53
+ # Quickstart: Collisional Ionization Equilibrium
54
+
55
+ Example of using `reaxion` to solve for collisional ionization equilibrium (CIE) for a hydrogen-helium mixture and plot the ionization states as a function of temperature.
56
+
57
+
58
+ ```python
59
+ %matplotlib inline
60
+ %config InlineBackend.figure_format='retina'
61
+ import numpy as np
62
+ from matplotlib import pyplot as plt
63
+ import sympy as sp
64
+ ```
65
+
66
+ ## Simple processes
67
+ A simple process is defined by a single reaction, with a specified rate.
68
+
69
+ Let's inspect the structure of a single process, the gas-phase recombination of H+: `H+ + e- -> H + hν`
70
+
71
+
72
+ ```python
73
+ from reaxion.processes import CollisionalIonization, GasPhaseRecombination
74
+
75
+ process = GasPhaseRecombination("H+")
76
+ print(f"Name: {process.name}")
77
+ print(f"Heating rate coefficient: {process.heat_rate_coefficient}")
78
+ print(f"Heating rate per cm^-3: {process.heat}"),
79
+ print(f"Rate coefficient: {process.rate_coefficient}")
80
+ print(f"Recombination rate per cm^-3: {process.rate}")
81
+ print(f"RHS of e- number density equation: {process.network['e-']}")
82
+ ```
83
+
84
+ Name: Gas-phase recombination of H+
85
+ Heating rate coefficient: -1.46719838641439e-26*sqrt(T)/((0.00119216696847702*sqrt(T) + 1.0)**1.748*(0.563615123664978*sqrt(T) + 1.0)**0.252)
86
+ Heating rate per cm^-3: -1.46719838641439e-26*sqrt(T)*n_H+*n_e-/((0.00119216696847702*sqrt(T) + 1.0)**1.748*(0.563615123664978*sqrt(T) + 1.0)**0.252)
87
+ Rate coefficient: 1.41621465870114e-10/(sqrt(T)*(0.00119216696847702*sqrt(T) + 1.0)**1.748*(0.563615123664978*sqrt(T) + 1.0)**0.252)
88
+ Recombination rate per cm^-3: 1.41621465870114e-10*n_H+*n_e-/(sqrt(T)*(0.00119216696847702*sqrt(T) + 1.0)**1.748*(0.563615123664978*sqrt(T) + 1.0)**0.252)
89
+ RHS of e- number density equation: Eq(Derivative(n_e-(t), t), -1.41621465870114e-10*n_H+*n_e-/(sqrt(T)*(0.00119216696847702*sqrt(T) + 1.0)**1.748*(0.563615123664978*sqrt(T) + 1.0)**0.252))
90
+
91
+
92
+ Note that all symbolic representations assume CGS units as is standard in ISM physics.
93
+
94
+ ## Composing processes
95
+ Now let's define our full network as a sum of simple processes
96
+
97
+
98
+ ```python
99
+ processes = [CollisionalIonization(s) for s in ("H", "He", "He+")] + [GasPhaseRecombination(i) for i in ("H+", "He+", "He++")]
100
+ system = sum(processes)
101
+
102
+ system.subprocesses
103
+ ```
104
+
105
+
106
+
107
+
108
+ [Collisional Ionization of H,
109
+ Collisional Ionization of He,
110
+ Collisional Ionization of He+,
111
+ Gas-phase recombination of H+,
112
+ Gas-phase recombination of He+,
113
+ Gas-phase recombination of He++]
114
+
115
+
116
+
117
+ Summed processes keep track of all subprocesses, e.g. the total net heating rate is:
118
+
119
+
120
+ ```python
121
+ system.heat
122
+ ```
123
+
124
+
125
+
126
+
127
+ $\displaystyle - \frac{1.55 \cdot 10^{-26} n_{He+} n_{e-}}{T^{0.3647}} - \frac{1.2746917300104 \cdot 10^{-21} \sqrt{T} n_{H} n_{e-} e^{- \frac{157809.1}{T}}}{\frac{\sqrt{10} \sqrt{T}}{1000} + 1} - \frac{1.46719838641439 \cdot 10^{-26} \sqrt{T} n_{H+} n_{e-}}{\left(0.00119216696847702 \sqrt{T} + 1.0\right)^{1.748} \left(0.563615123664978 \sqrt{T} + 1.0\right)^{0.252}} - \frac{9.37661057635428 \cdot 10^{-22} \sqrt{T} n_{He} n_{e-} e^{- \frac{285335.4}{T}}}{\frac{\sqrt{10} \sqrt{T}}{1000} + 1} - \frac{4.9524176975855 \cdot 10^{-22} \sqrt{T} n_{He+} n_{e-} e^{- \frac{631515}{T}}}{\frac{\sqrt{10} \sqrt{T}}{1000} + 1} - \frac{5.86879354565754 \cdot 10^{-26} \sqrt{T} n_{He++} n_{e-}}{\left(0.00119216696847702 \sqrt{T} + 1.0\right)^{1.748} \left(0.563615123664978 \sqrt{T} + 1.0\right)^{0.252}}$
128
+
129
+
130
+
131
+ Summing processes also sums all chemical and gas/dust cooling/heating rates.
132
+
133
+ ## Solving ionization equilibrium
134
+
135
+ We would like to solve for ionization equilibrium given a temperature $T$, overall H number density $n_{\rm H,tot}$. We define a dictionary of those input quantities and also one for the initial guesses of the number densities of the species in the reduced network.
136
+
137
+
138
+ ```python
139
+ Tgrid = np.logspace(3,6,10**6)
140
+ ngrid = np.ones_like(Tgrid) * 100
141
+
142
+ knowns = {"T": Tgrid, "n_Htot": ngrid}
143
+
144
+ guesses = {
145
+ "H": 0.5*np.ones_like(Tgrid),
146
+ "He": 1e-5*np.ones_like(Tgrid),
147
+ "He+": 1e-5*np.ones_like(Tgrid)
148
+ }
149
+ ```
150
+
151
+ Note that by default, the solver only directly solves for $n_{\rm H}$, $n_{\rm He}$ and $n_{\rm He+}$ because $n_{\rm H+}$, $n_{\rm He++}$, and $n_{\rm e-}$ are eliminated by conservation equations. So we only need initial guesses for those 3 quantities. By default the solver takes abundances $x_i = n_i / n_{\rm H,tot}$ as inputs and outputs.
152
+
153
+ The `solve` method calls the JAX solver and computes the solution:
154
+
155
+
156
+ ```python
157
+ sol = system.solve(knowns, guesses,tol=1e-3)
158
+ print(sol)
159
+ ```
160
+
161
+ {'He': Array([9.2546351e-02, 9.2546351e-02, 9.2546351e-02, ..., 2.7493625e-09,
162
+ 2.7493037e-09, 2.7492442e-09], dtype=float32), 'H': Array([9.9999994e-01, 9.9999994e-01, 9.9999994e-01, ..., 6.0612075e-07,
163
+ 6.0611501e-07, 6.0610921e-07], dtype=float32), 'He+': Array([3.1222404e-13, 3.1222396e-13, 3.1222374e-13, ..., 7.6922206e-06,
164
+ 7.6921306e-06, 7.6920396e-06], dtype=float32), 'He++': Array([0. , 0. , 0. , ..., 0.09253865, 0.09253865,
165
+ 0.09253865], dtype=float32), 'H+': Array([5.9604645e-08, 5.9604645e-08, 5.9604645e-08, ..., 9.9999940e-01,
166
+ 9.9999940e-01, 9.9999940e-01], dtype=float32), 'e-': Array([5.9604957e-08, 5.9604957e-08, 5.9604957e-08, ..., 1.1850843e+00,
167
+ 1.1850843e+00, 1.1850843e+00], dtype=float32)}
168
+
169
+
170
+
171
+ ```python
172
+ for i, xi in sorted(sol.items()):
173
+ plt.loglog(Tgrid, xi, label=i)
174
+ plt.legend(labelspacing=0)
175
+ plt.ylabel("$x_i$")
176
+ plt.xlabel("T (K)")
177
+ plt.ylim(1e-4,3)
178
+ ```
179
+
180
+
181
+
182
+
183
+ (0.0001, 3)
184
+
185
+
186
+
187
+
188
+
189
+ ![png](CIE_files/CIE_15_1.png)
190
+
191
+
192
+
193
+ ## Generating code
194
+
195
+ Suppose you just want the RHS of the system you're solving, or its Jacobian, because you have a better solver and/or want to embed these equations in some old C or Fortran code without any dependencies. You can do that too with `generate_code`.
196
+
197
+
198
+ ```python
199
+ print(system.generate_code(('H','He','He+'),language='c'))
200
+ ```
201
+
202
+ # Computes the RHS function and Jacobianto solve for [x_He, x_H, x_Heplus]
203
+
204
+ # INDEX CONVENTION: (0: x_He) (1: x_H) (2: x_Heplus)
205
+
206
+ x0 = 1.0/T;
207
+ x1 = sqrt(T);
208
+ x2 = pow(n_Htot, 2);
209
+ x3 = 1.0/((1.0/1000.0)*sqrt(10)*x1 + 1);
210
+ x4 = x1*x2*x3;
211
+ x5 = x4*exp(-285335.40000000002*x0);
212
+ x6 = x5*x_He;
213
+ x7 = x_H - 1;
214
+ x8 = -x7 - 2*x_He - x_Heplus + 2*y;
215
+ x9 = 2.3800000000000001e-11*x8;
216
+ x10 = 1.0/x1;
217
+ x11 = x2*(0.0019*pow(T, -1.5)*(1 + 0.29999999999999999*exp(-94000.0*x0))*exp(-470000.0*x0) + 1.9324160622805846e-10*x10*pow(0.00016493478118851054*x1 + 1.0, -1.7891999999999999)*pow(4.8416074481177231*x1 + 1.0, -0.21079999999999999));
218
+ x12 = x11*x_Heplus;
219
+ x13 = -x12*x8 + x6*x9;
220
+ x14 = exp(-157809.10000000001*x0);
221
+ x15 = x14*x4;
222
+ x16 = x15*x_H;
223
+ x17 = 5.8500000000000005e-11*x16;
224
+ x18 = -x7;
225
+ x19 = pow(0.0011921669684770192*x1 + 1.0, -1.748);
226
+ x20 = pow(0.56361512366497779*x1 + 1.0, -0.252);
227
+ x21 = -x_He - x_Heplus + y;
228
+ x22 = x10*x2;
229
+ x23 = x22*pow(0.00059608348423850961*x1 + 1.0, -1.748)*pow(0.2818075618324889*x1 + 1.0, -0.252);
230
+ x24 = 5.664858634804579e-10*x23;
231
+ x25 = x24*x8;
232
+ x26 = exp(-631515*x0);
233
+ x27 = x26*x4;
234
+ x28 = 4.7600000000000002e-11*x6;
235
+ x29 = x5*x9;
236
+ x30 = 2*x12;
237
+ x31 = -x12 + 2.3800000000000001e-11*x6;
238
+ x32 = x11*x8 + x31;
239
+ x33 = x19*x20*x22;
240
+ x34 = x18*x33;
241
+ x35 = 1.4162146587011448e-10*x34;
242
+ x36 = -5.68e-12*x1*x2*x26*x3*x_Heplus + x21*x24;
243
+
244
+ rhs_result[0] = -x13;
245
+ rhs_result[1] = 1.4162146587011448e-10*x10*x18*x19*x2*x20*x8 - x17*x8;
246
+ rhs_result[2] = x13 + x21*x25 - 5.68e-12*x27*x8*x_Heplus;
247
+
248
+ jac_result[0] = x28 - x29 - x30;
249
+ jac_result[1] = x31;
250
+ jac_result[2] = x32;
251
+ jac_result[3] = 1.1700000000000001e-10*x16 - 2.8324293174022895e-10*x34;
252
+ jac_result[4] = 5.8500000000000005e-11*x1*x14*x2*x3*x_H - 5.8500000000000005e-11*x15*x8 - 1.4162146587011448e-10*x33*x8 - x35;
253
+ jac_result[5] = x17 - x35;
254
+ jac_result[6] = 1.136e-11*x1*x2*x26*x3*x_Heplus - 1.1329717269609158e-9*x21*x23 - x25 - x28 + x29 + x30;
255
+ jac_result[7] = -x31 - x36;
256
+ jac_result[8] = -x25 - 5.68e-12*x27*x8 - x32 - x36;
257
+
258
+
259
+ Let's break down what happened there. First, reaxion is generating the symbolic functions needed to solve the system, as it needs to do before it solves the system with its own solver:
260
+
261
+
262
+ ```python
263
+ func, jac, _ = system.network.solver_functions(('H','He','He+'),return_jac=True)
264
+ ```
265
+
266
+ Here `func` represents the set of functions $f_i$ such that $f_i = 0$ solves the system. `jac` encodes the Jacbian of f $J_{ij} = \frac{\partial f_i}{\partial x_j}$ of derivatives with respect to the solved variables. Note that the two have many common expressions - before being implemented, one should employ common expression elimination to simplify the code and evaluate the functions more efficiently:
267
+
268
+
269
+ ```python
270
+ cse, (cse_func, cse_jac) = sp.cse((sp.Matrix(func),sp.Matrix(jac)))
271
+
272
+ cse
273
+ ```
274
+
275
+
276
+
277
+
278
+ [(x0, 1/T),
279
+ (x1, sqrt(T)),
280
+ (x2, n_Htot**2),
281
+ (x3, 1/(sqrt(10)*x1/1000 + 1)),
282
+ (x4, x1*x2*x3),
283
+ (x5, x4*exp(-285335.4*x0)),
284
+ (x6, x5*x_He),
285
+ (x7, x_H - 1),
286
+ (x8, -x7 - 2*x_He - x_He+ + 2*y),
287
+ (x9, 2.38e-11*x8),
288
+ (x10, 1/x1),
289
+ (x11,
290
+ x2*(0.0019*(1 + 0.3*exp(-94000.0*x0))*exp(-470000.0*x0)/T**1.5 + 1.93241606228058e-10*x10/((0.000164934781188511*x1 + 1.0)**1.7892*(4.84160744811772*x1 + 1.0)**0.2108))),
291
+ (x12, x11*x_He+),
292
+ (x13, -x12*x8 + x6*x9),
293
+ (x14, exp(-157809.1*x0)),
294
+ (x15, x14*x4),
295
+ (x16, x15*x_H),
296
+ (x17, 5.85e-11*x16),
297
+ (x18, -x7),
298
+ (x19, (0.00119216696847702*x1 + 1.0)**(-1.748)),
299
+ (x20, (0.563615123664978*x1 + 1.0)**(-0.252)),
300
+ (x21, -x_He - x_He+ + y),
301
+ (x22, x10*x2),
302
+ (x23,
303
+ x22/((0.00059608348423851*x1 + 1.0)**1.748*(0.281807561832489*x1 + 1.0)**0.252)),
304
+ (x24, 5.66485863480458e-10*x23),
305
+ (x25, x24*x8),
306
+ (x26, exp(-631515*x0)),
307
+ (x27, x26*x4),
308
+ (x28, 4.76e-11*x6),
309
+ (x29, x5*x9),
310
+ (x30, 2*x12),
311
+ (x31, -x12 + 2.38e-11*x6),
312
+ (x32, x11*x8 + x31),
313
+ (x33, x19*x20*x22),
314
+ (x34, x18*x33),
315
+ (x35, 1.41621465870114e-10*x34),
316
+ (x36, -5.68e-12*x1*x2*x26*x3*x_He+ + x21*x24)]
317
+
318
+
319
+
320
+
321
+ ```python
322
+ cse_func
323
+ ```
324
+
325
+
326
+
327
+
328
+ $\displaystyle \left[\begin{matrix}- x_{13}\\1.41621465870114 \cdot 10^{-10} x_{10} x_{18} x_{19} x_{2} x_{20} x_{8} - x_{17} x_{8}\\x_{13} + x_{21} x_{25} - 5.68 \cdot 10^{-12} x_{27} x_{8} x_{He+}\end{matrix}\right]$
329
+
330
+
331
+
332
+
333
+ ```python
334
+ cse_jac
335
+ ```
336
+
337
+
338
+
339
+
340
+ $\displaystyle \left[\begin{matrix}x_{28} - x_{29} - x_{30} & x_{31} & x_{32}\\1.17 \cdot 10^{-10} x_{16} - 2.83242931740229 \cdot 10^{-10} x_{34} & 5.85 \cdot 10^{-11} x_{1} x_{14} x_{2} x_{3} x_{H} - 5.85 \cdot 10^{-11} x_{15} x_{8} - 1.41621465870114 \cdot 10^{-10} x_{33} x_{8} - x_{35} & x_{17} - x_{35}\\1.136 \cdot 10^{-11} x_{1} x_{2} x_{26} x_{3} x_{He+} - 1.13297172696092 \cdot 10^{-9} x_{21} x_{23} - x_{25} - x_{28} + x_{29} + x_{30} & - x_{31} - x_{36} & - x_{25} - 5.68 \cdot 10^{-12} x_{27} x_{8} - x_{32} - x_{36}\end{matrix}\right]$
341
+
342
+
343
+
344
+ One can then take these expressions and convert them to the syntax of the code you wish to embed them in:
345
+
346
+
347
+ ```python
348
+ from sympy.codegen.ast import Assignment
349
+ for expr in cse:
350
+ print(sp.ccode(Assignment(*expr),standard='c99'))
351
+
352
+ rhs_result = sp.MatrixSymbol('rhs_result', len(func), 1)
353
+ jac_result = sp.MatrixSymbol('jac_result', len(func),len(func))
354
+ print()
355
+ print(sp.ccode(Assignment(rhs_result, cse_func),standard='c99'))
356
+ print()
357
+ print(sp.ccode(Assignment(jac_result, cse_jac),standard='c99'))
358
+ ```
359
+
360
+ x0 = 1.0/T;
361
+ x1 = sqrt(T);
362
+ x2 = pow(n_Htot, 2);
363
+ x3 = 1.0/((1.0/1000.0)*sqrt(10)*x1 + 1);
364
+ x4 = x1*x2*x3;
365
+ x5 = x4*exp(-285335.40000000002*x0);
366
+ x6 = x5*x_He;
367
+ x7 = x_H - 1;
368
+ x8 = -x7 - 2*x_He - x_He+ + 2*y;
369
+ x9 = 2.3800000000000001e-11*x8;
370
+ x10 = 1.0/x1;
371
+ x11 = x2*(0.0019*pow(T, -1.5)*(1 + 0.29999999999999999*exp(-94000.0*x0))*exp(-470000.0*x0) + 1.9324160622805846e-10*x10*pow(0.00016493478118851054*x1 + 1.0, -1.7891999999999999)*pow(4.8416074481177231*x1 + 1.0, -0.21079999999999999));
372
+ x12 = x11*x_He+;
373
+ x13 = -x12*x8 + x6*x9;
374
+ x14 = exp(-157809.10000000001*x0);
375
+ x15 = x14*x4;
376
+ x16 = x15*x_H;
377
+ x17 = 5.8500000000000005e-11*x16;
378
+ x18 = -x7;
379
+ x19 = pow(0.0011921669684770192*x1 + 1.0, -1.748);
380
+ x20 = pow(0.56361512366497779*x1 + 1.0, -0.252);
381
+ x21 = -x_He - x_He+ + y;
382
+ x22 = x10*x2;
383
+ x23 = x22*pow(0.00059608348423850961*x1 + 1.0, -1.748)*pow(0.2818075618324889*x1 + 1.0, -0.252);
384
+ x24 = 5.664858634804579e-10*x23;
385
+ x25 = x24*x8;
386
+ x26 = exp(-631515*x0);
387
+ x27 = x26*x4;
388
+ x28 = 4.7600000000000002e-11*x6;
389
+ x29 = x5*x9;
390
+ x30 = 2*x12;
391
+ x31 = -x12 + 2.3800000000000001e-11*x6;
392
+ x32 = x11*x8 + x31;
393
+ x33 = x19*x20*x22;
394
+ x34 = x18*x33;
395
+ x35 = 1.4162146587011448e-10*x34;
396
+ x36 = -5.68e-12*x1*x2*x26*x3*x_He+ + x21*x24;
397
+
398
+ rhs_result[0] = -x13;
399
+ rhs_result[1] = 1.4162146587011448e-10*x10*x18*x19*x2*x20*x8 - x17*x8;
400
+ rhs_result[2] = x13 + x21*x25 - 5.68e-12*x27*x8*x_He+;
401
+
402
+ jac_result[0] = x28 - x29 - x30;
403
+ jac_result[1] = x31;
404
+ jac_result[2] = x32;
405
+ jac_result[3] = 1.1700000000000001e-10*x16 - 2.8324293174022895e-10*x34;
406
+ jac_result[4] = 5.8500000000000005e-11*x1*x14*x2*x3*x_H - 5.8500000000000005e-11*x15*x8 - 1.4162146587011448e-10*x33*x8 - x35;
407
+ jac_result[5] = x17 - x35;
408
+ jac_result[6] = 1.136e-11*x1*x2*x26*x3*x_He+ - 1.1329717269609158e-9*x21*x23 - x25 - x28 + x29 + x30;
409
+ jac_result[7] = -x31 - x36;
410
+ jac_result[8] = -x25 - 5.68e-12*x27*x8 - x32 - x36;
411
+