Build Cython Module from Templates
Contents
Build Cython Module from Templates¶
In the last chapter, we used our simpleNework to generate 9 files from dengo. We outline how can we build the library from here and use cython to invoke the function written in C.
simpleNetwork_solver.C simpleNetwork_solver_run.pyx
simpleNetwork_solver.h simpleNetwork_solver_run.pyxbld
simpleNetwork_solver_main.C simpleNetwork_solver_run.pyxdep
simpleNetwork_solver_main.py simpleNetwork_tables.h5
simpleNetwork_solver_run.pxd
import dengo
from dengo.chemical_network import \
ChemicalNetwork, \
reaction_registry, \
cooling_registry, species_registry
import dengo.primordial_rates
import dengo.primordial_cooling
dengo.primordial_rates.setup_primordial()
simpleNetwork = ChemicalNetwork()
simpleNetwork.add_reaction("k01")
simpleNetwork.add_reaction("k02")
simpleNetwork.add_cooling("reHII")
simpleNetwork.init_temperature((1e0, 1e8))
Adding reaction: k01 : 1*H_1 + 1*de => 1*H_2 + 2*de
Adding reaction: k02 : 1*H_2 + 1*de => 1*H_1
Compile the Cython Module¶
The solver templates come with a .pyx files that lets you build a python interface to the C-library. A more well-rounded Cython tutorial can be found here [Cython Tutorial].(https://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html).
import pyximport
import numpy as np
solver_name = "simpleNetwork"
pyximport.install(
setup_args={"include_dirs":np.get_include()},
reload_support=True,
inplace=True,
language_level=3
)
simple_solver_run = pyximport.load_module(
f"{solver_name}_solver_run",
f"{solver_name}_solver_run.pyx",
build_inplace = True,
pyxbuild_dir = "_dengo_temp",
)
You have suitesparse!
/home/kwoksun2/anaconda3/lib/python3.8/site-packages/Cython/Compiler/Main.py:369: FutureWarning: Cython directive 'language_level' not set, using 2 for now (Py2). This will change in a later release! File: /mnt/gv0/homes/kwoksun2/dengo-merge/cookbook/tutorial/buildingblocks/simpleNetwork_solver_run.pxd
tree = Parsing.p_module(s, pxd, full_module_name)
cc1plus: warning: command line option '-Wstrict-prototypes' is valid for C/ObjC but not for C++
cc1plus: warning: command line option '-Wstrict-prototypes' is valid for C/ObjC but not for C++
cc1plus: warning: command line option '-Wstrict-prototypes' is valid for C/ObjC but not for C++
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
File ~/anaconda3/lib/python3.8/site-packages/pyximport/pyximport.py:216, in load_module(name, pyxfilename, pyxbuild_dir, is_package, build_inplace, language_level, so_path)
214 so_path = build_module(module_name, pyxfilename, pyxbuild_dir,
215 inplace=build_inplace, language_level=language_level)
--> 216 mod = imp.load_dynamic(name, so_path)
217 if is_package and not hasattr(mod, '__path__'):
File ~/anaconda3/lib/python3.8/imp.py:342, in load_dynamic(name, path, file)
340 spec = importlib.machinery.ModuleSpec(
341 name=name, loader=loader, origin=path)
--> 342 return _load(spec)
File <frozen importlib._bootstrap>:702, in _load(spec)
File <frozen importlib._bootstrap>:657, in _load_unlocked(spec)
File <frozen importlib._bootstrap>:556, in module_from_spec(spec)
File <frozen importlib._bootstrap_external>:1101, in create_module(self, spec)
File <frozen importlib._bootstrap>:219, in _call_with_frames_removed(f, *args, **kwds)
ImportError: /mnt/gv0/homes/kwoksun2/dengo-merge/cookbook/tutorial/buildingblocks/simpleNetwork_solver_run.cpython-38-x86_64-linux-gnu.so.reload1: undefined symbol: _Z18setup_cvode_solverPFidP17_generic_N_VectorS0_PvEPFidS0_S0_P18_generic_SUNMatrixS1_S0_S0_S0_EiP18simpleNetwork_dataP24_generic_SUNLinearSolverS5_S0_dS0_
During handling of the above exception, another exception occurred:
ImportError Traceback (most recent call last)
Input In [2], in <cell line: 12>()
3 solver_name = "simpleNetwork"
5 pyximport.install(
6 setup_args={"include_dirs":np.get_include()},
7 reload_support=True,
8 inplace=True,
9 language_level=3
10 )
---> 12 simple_solver_run = pyximport.load_module(
13 f"{solver_name}_solver_run",
14 f"{solver_name}_solver_run.pyx",
15 build_inplace = True,
16 pyxbuild_dir = "_dengo_temp",
17 )
File ~/anaconda3/lib/python3.8/site-packages/pyximport/pyximport.py:231, in load_module(name, pyxfilename, pyxbuild_dir, is_package, build_inplace, language_level, so_path)
228 exc = ImportError("Building module %s failed: %s" % (
229 name, traceback.format_exception_only(*sys.exc_info()[:2])))
230 if sys.version_info[0] >= 3:
--> 231 raise exc.with_traceback(tb)
232 else:
233 exec("raise exc, None, tb", {'exc': exc, 'tb': tb})
File ~/anaconda3/lib/python3.8/site-packages/pyximport/pyximport.py:216, in load_module(name, pyxfilename, pyxbuild_dir, is_package, build_inplace, language_level, so_path)
213 module_name = name
214 so_path = build_module(module_name, pyxfilename, pyxbuild_dir,
215 inplace=build_inplace, language_level=language_level)
--> 216 mod = imp.load_dynamic(name, so_path)
217 if is_package and not hasattr(mod, '__path__'):
218 mod.__path__ = [os.path.dirname(so_path)]
File ~/anaconda3/lib/python3.8/imp.py:342, in load_dynamic(name, path, file)
338 # Issue #24748: Skip the sys.modules check in _load_module_shim;
339 # always load new extension
340 spec = importlib.machinery.ModuleSpec(
341 name=name, loader=loader, origin=path)
--> 342 return _load(spec)
File <frozen importlib._bootstrap>:702, in _load(spec)
File <frozen importlib._bootstrap>:657, in _load_unlocked(spec)
File <frozen importlib._bootstrap>:556, in module_from_spec(spec)
File <frozen importlib._bootstrap_external>:1101, in create_module(self, spec)
File <frozen importlib._bootstrap>:219, in _call_with_frames_removed(f, *args, **kwds)
ImportError: Building module simpleNetwork_solver_run failed: ['ImportError: /mnt/gv0/homes/kwoksun2/dengo-merge/cookbook/tutorial/buildingblocks/simpleNetwork_solver_run.cpython-38-x86_64-linux-gnu.so.reload1: undefined symbol: _Z18setup_cvode_solverPFidP17_generic_N_VectorS0_PvEPFidS0_S0_P18_generic_SUNMatrixS1_S0_S0_S0_EiP18simpleNetwork_dataP24_generic_SUNLinearSolverS5_S0_dS0_\n']
Invoking the Cython Solver¶
In the below we show how the cython module can be built and used.
{solver_name}_solver_run.run_{solver_name}(init_values, dt, niter) is the entry point for the built solver.
It takes an dictionary that contains the abundances, and thermal energy, an \(\rm dt\), time to advance the fluid parcel, and niter the maximum number of iterations as arguments.
It assumes that abundances are in number density with the units of \(\rm cm^{-3}\), and thermal energy in \(\rm erg/g\). niter implicitly sets the initial timestep for the solver, i.e. \(dt_{solver} = \rm dt / \rm niter\).
Our cython module gives the same trajectories as what we had done from scratch with the previous chapters!
NCELLS = 1
density = 1e-2
init_array = np.ones(NCELLS) * density
init_values = dict()
init_values['H_1'] = init_array
init_values['H_2'] = init_array
init_values['de'] = init_array
init_values['ge'] = np.ones(NCELLS)*1e13
total_density = simpleNetwork.calculate_total_density(init_values)
init_values = simpleNetwork.convert_to_mass_density(init_values)
init_values['de'] = simpleNetwork.calculate_free_electrons(init_values)
init_values['density'] = simpleNetwork.calculate_total_density(init_values)
number_density = simpleNetwork.calculate_number_density(init_values)
rv, rv_int = simple_solver_run.run_simpleNetwork(init_values, 1e16, niter = 1e5)
Successful iteration[ 0]: (1.000e+11) 1.000e+11 / 1.000e+16
End in 97 iterations: 1.00000e+16 / 1.00000e+16 (0.00000e+00)
Visualize the Trajectories¶
They match with what we have built from scratch before!
import matplotlib.pyplot as plt
flags = rv_int['t'] > 0
sp_names = [s.name for s in sorted(simpleNetwork.required_species)]
output = np.zeros((len(sp_names),sum(flags)))
for i, s in enumerate(sp_names):
output[i] = rv_int[s][:,flags]
H_1_traj, H_2_traj, de_traj, ge_traj = output
T_traj = rv_int['T'][0,flags]
timesteps = rv_int['t'][flags]
f, ax = plt.subplots()
l1= ax.loglog(timesteps, H_1_traj)
l2= ax.loglog(timesteps, H_2_traj)
l3= ax.loglog(timesteps, de_traj)
ax2 = ax.twinx()
l4= ax2.loglog(timesteps, T_traj, color='C3')
ax.set_ylabel(r"Abundance ($\rm cm^{-3}$)")
ax2.set_ylabel("Temperature (K)")
ax.set_xlabel("Time (s)")
f.legend(
[l1,l2,l3,l4],
labels=[r'$\rm H$',r'$\rm H^+$',r'$\rm e^-$',r'$\rm T$'],
loc=[0.15,0.4]
)
plt.subplots_adjust(right=0.85)
/tmp/ipykernel_22118/4291065558.py:27: UserWarning: You have mixed positional and keyword arguments, some input may be discarded.
f.legend(