# -*- coding: utf-8 -*-
"""Module for writing species/reaction rate subroutines.
This is kept separate from Jacobian creation module in order
to create only the rate subroutines if desired.
"""
# Python 2 compatibility
from __future__ import division
from __future__ import print_function
# Standard libraries
import sys
import math
import os
# Local imports
from .. import utils
from . import chem_utilities as chem
from . import mech_interpret as mech
from . import CUDAParams
from . import cache_optimizer as cache
from . import mech_auxiliary as aux
from . import shared_memory as shared
[docs]def rxn_rate_const(A, b, E):
r"""Returns line with reaction rate calculation (after = sign).
Parameters
----------
A : float
Arrhenius pre-exponential coefficient
b : float
Arrhenius temperature exponent
E : float
Arrhenius activation energy
Returns
-------
line : str
String with expression for reaction rate.
Notes
-----
Form of the reaction rate constant (from, e.g., Lu and Law [1]_):
.. math::
:nowrap:
k_f = \begin{cases}
A & \text{if } \beta = 0 \text{ and } T_a = 0 \\
\exp \left( \log A + \beta \log T \right) &
\text{if } \beta \neq 0 \text{ and } \text{if } T_a = 0 \\
\exp \left( \log A + \beta \log T - T_a / T \right) &
\text{if } \beta \neq 0 \text{ and } T_a \neq 0 \\
\exp \left( \log A - T_a / T \right)
& \text{if } \beta = 0 \text{ and } T_a \neq 0 \\
A \prod^b T & \text{if } T_a = 0 \text{ and }
b \in \mathbb{Z} \text{ (integers) }
\end{cases}
References
----------
.. [1] TF Lu and CK Law, "Toward accommodating realistic fuel chemistry
in large-scale computations," Progress in Energy and Combustion
Science, vol. 35, pp. 192-215, 2009. doi:10.1016/j.pecs.2008.10.002
"""
line = ''
if A > 0:
logA = math.log(A)
if not E:
# E = 0
if not b:
# b = 0
line += str(A)
else:
# b != 0
if isinstance(b, int):
line += str(A)
for i in range(b):
line += ' * T'
else:
line += 'exp({:.16e}'.format(logA)
if b > 0:
line += ' + ' + str(b)
else:
line += ' - ' + str(abs(b))
line += ' * logT)'
else:
# E != 0
if not b:
# b = 0
line += 'exp({:.16e}'.format(logA) + ' - ({:.16e} / T))'.format(E)
else:
# b!= 0
line += 'exp({:.16e}'.format(logA)
if b > 0:
line += ' + ' + str(b)
else:
line += ' - ' + str(abs(b))
line += ' * logT - ({:.16e} / T))'.format(E)
elif A < 0:
#a < 0, can't take the log of it
#the reaction, should also be a duplicate to make any sort of sense
if not E:
#E = 0
if not b:
#b = 0
line += str(A)
else:
#b != 0
if utils.is_integer(b):
line += str(A)
for i in range(int(b)):
line += ' * T'
else:
line += '{:.16e} * exp('.format(A)
if b > 0:
line += str(b)
else:
line += '-' + str(abs(b))
line += ' * logT)'
else:
#E != 0
if not b:
# b = 0
line += '{:.16e} * exp(-({:.16e} / T))'.format(A, E)
else:
# b!= 0
line += '{:.16e} * exp('.format(A)
if b > 0:
line += str(b)
else:
line += '-' + str(abs(b))
line += ' * logT - ({:.16e} / T))'.format(E)
else:
raise NotImplementedError
return line
[docs]def get_cheb_rate(lang, rxn, write_defns=True):
"""
Given a reaction, and a temperature and pressure, this routine
will generate code to evaluate the Chebyshev rate efficiently.
Note
----
Assumes existence of variables dot_prod* of sized at least rxn.cheb_n_temp
Pred and Tred, T and pres, and kf, cheb_temp_0 and cheb_temp_1.
Parameters
----------
lang : str
Programming language
rxn : `ReacInfo`
Reaction with Chebyshev pressure dependence.
write_defns : bool, optional
If ``True`` (default), write calculation of ``Tred`` and ``Pred``.
Returns
-------
line : list of `str`
Line with evaluation of Chebyshev reaction rate.
"""
line_list = []
tlim_inv_sum = 1.0 / rxn.cheb_tlim[0] + 1.0 / rxn.cheb_tlim[1]
tlim_inv_sub = 1.0 / rxn.cheb_tlim[1] - 1.0 / rxn.cheb_tlim[0]
if write_defns:
line_list.append(
'Tred = ((2.0 / T) - ' +
'{:.8e}) / {:.8e}'.format(tlim_inv_sum, tlim_inv_sub)
)
plim_log_sum = (math.log10(rxn.cheb_plim[0]) +
math.log10(rxn.cheb_plim[1])
)
plim_log_sub = (math.log10(rxn.cheb_plim[1]) -
math.log10(rxn.cheb_plim[0])
)
if write_defns:
line_list.append(
'Pred = (2.0 * log10(pres) - ' +
'{:.8e}) / {:.8e}'.format(plim_log_sum, plim_log_sub)
)
line_list.append('cheb_temp_0 = 1')
line_list.append('cheb_temp_1 = Pred')
#start pressure dot product
for i in range(rxn.cheb_n_temp):
line_list.append(utils.get_array(lang, 'dot_prod', i) +
'= {:.8e} + Pred * {:.8e}'.format(rxn.cheb_par[i, 0],
rxn.cheb_par[i, 1]))
#finish pressure dot product
update_one = True
for j in range(2, rxn.cheb_n_pres):
if update_one:
new = 1
old = 0
else:
new = 0
old = 1
line = 'cheb_temp_{}'.format(old)
line += ' = 2 * Pred * cheb_temp_{}'.format(new)
line += ' - cheb_temp_{}'.format(old)
line_list.append(line)
for i in range(rxn.cheb_n_temp):
line_list.append(utils.get_array(lang, 'dot_prod', i) +
' += {:.8e} * cheb_temp_{}'.format(
rxn.cheb_par[i, j], old))
update_one = not update_one
line_list.append('cheb_temp_0 = 1')
line_list.append('cheb_temp_1 = Tred')
#finally, do the temperature portion
line_list.append('kf = ' + utils.get_array(lang, 'dot_prod', 0) +
' + Tred * ' + utils.get_array(lang, 'dot_prod', 1))
update_one = True
for i in range(2, rxn.cheb_n_temp):
if update_one:
new = 1
old = 0
else:
new = 0
old = 1
line = 'cheb_temp_{}'.format(old)
line += ' = 2 * Tred * cheb_temp_{}'.format(new)
line += ' - cheb_temp_{}'.format(old)
line_list.append(line)
line_list.append('kf += ' + utils.get_array(lang, 'dot_prod', i) +
' * ' + 'cheb_temp_{}'.format(old))
update_one = not update_one
line_list.append('kf = ' + utils.exp_10_fun[lang] + 'kf)')
line_list = [utils.line_start + line + utils.line_end[lang] for
line in line_list]
return ''.join(line_list)
[docs]def write_rxn_rates(path, lang, specs, reacs, fwd_rxn_mapping,
smm=None, auto_diff=False):
"""Write reaction rate subroutine.
Includes conditionals for reversible reactions.
Parameters
----------
path : str
Path to build directory for file.
lang : {'c', 'cuda', 'fortran', 'matlab'}
Programming language.
specs : list of `SpecInfo`
List of species in the mechanism.
reacs : list of `ReacInfo`
List of reactions in the mechanism.
fwd_rxn_mapping : List of integers
The index of the reaction in the original mechanism
smm : `shared_memory_manager`, optional
If not ``None`` (default), `shared_memory_manager` for CUDA optimizations
auto_diff : Optional[bool]
If ``True``, generate files for Adept autodifferention library.
Returns
-------
None
"""
num_s = len(specs)
num_r = len(reacs)
rev_reacs = [i for i, rxn in enumerate(reacs) if rxn.rev]
num_rev = len(rev_reacs)
pdep_reacs = [i for i, rxn in enumerate(reacs) if rxn.thd_body or rxn.pdep]
pre = '__device__ ' if lang == 'cuda' else ''
file_prefix = 'ad_' if auto_diff else ''
pres_ref = '&' if auto_diff else ''
file = open(os.path.join(path, '{}rates'.format(file_prefix)
+ utils.header_ext[lang]), 'w')
file.write('#ifndef RATES_HEAD\n'
'#define RATES_HEAD\n'
'\n'
'#include "header{}"\n'.format(utils.header_ext[lang]) +
'\n'
)
if lang == 'cuda':
file.write('#include "gpu_memory.cuh"\n')
double_type = 'double'
if auto_diff:
double_type = 'adouble'
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
cuda_cheb = any(rxn.cheb for rxn in reacs) and lang == 'cuda'
line = ('{0}void eval_rxn_rates (const {1},'
' const {1}{2}, const {1} * {3}, {1} * {3}, {1} * {3}'
+ (', {1} * {3}' if cuda_cheb else '') +');\n'
'{0}void eval_spec_rates (const {1} * {3},'
' const {1} * {3}, const {1} * {3}, {1} * {3}, {1} * {3});\n')
file.write(line.format(pre, double_type, pres_ref, utils.restrict[lang]))
if pdep_reacs:
file.write('{0}void get_rxn_pres_mod (const {1}, const '
'{1}{2}, const {1} * {3}, {1} * {3});\n'.format(
pre, double_type, pres_ref, utils.restrict[lang])
)
file.write('\n')
file.write('#endif\n')
file.close()
filename = file_prefix + 'rxn_rates' + utils.file_ext[lang]
file = open(os.path.join(path, filename), 'w')
do_unroll = False
if lang == 'cuda' and len(reacs) > CUDAParams.Rates_Unroll:
# make paths for separate rate files
utils.create_dir(os.path.join(path, 'rates'))
rate_count = 0
do_unroll = True
next_file = 0
get_array = utils.get_array
if lang == 'cuda' and smm is not None:
smm.reset()
get_array = smm.get_array
if not do_unroll:
smm.write_init(file, indent=2)
def write_header(lang, rate_count):
"""Writes reaction rate header file.
Parameters
----------
lang : str
Programming language
rate_count : int
Number of reaction rate files.
Returns
-------
None
"""
with open(os.path.join(path, 'rates', 'rxn_rates_{}{}'.format(
rate_count, utils.header_ext[lang])), 'w') as file:
line = ('#ifndef RATES_HEAD_{0}\n'
'#define RATES_HEAD_{0}\n'
'\n'
'#include "header{1}"\n'
'\n'
).format(rate_count, utils.header_ext[lang])
file.write(line)
pre = ' ' if lang == 'c' else '__device__ '
line = ('{0}void eval_rxn_rates_{4}(const {1},'
' const {1}{2}, const {1} * {3}, {1} * {3}, {1} * {3}'
)
if cuda_cheb:
line += ', {1} * {3}'
line += ');\n'
file.write(line.format(
pre, double_type, pres_ref, utils.restrict[lang], rate_count))
file.write('#endif\n')
def write_sub_intro(file, defines, start, end, rate_count=None):
"""Write introduction to reaction rate subroutine.
Parameters
----------
file : file
Open file for reaction rate subroutines
defines : bool
If ``True``, write variable definitions
start : int
Index of initial reaction for this subroutine
end : int
Index of final reaction for this subroutine
rate_count : int, optional
Number of reaction rate files.
Returns
-------
None
"""
pre = ' '
line = ''
if lang == 'cuda': line = '__device__ '
my_reacs = reacs[start:end]
if not defines:
file.write('#include "rates/rates_include{}"\n'.format(utils.header_ext[lang]))
if lang in ['c', 'cuda']:
file.write('#include "{}rates'.format(
file_prefix)
+ utils.header_ext[lang] + '"\n')
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
line += ('void eval_rxn_rates{0} (const {1} T, const {1}{2} pres,'
' const {1} * {3} C, {1} * {3} fwd_rxn_rates, '
'{1} * {3} rev_rxn_rates{4}) {{\n'.format(
'_{}'.format(rate_count) if rate_count is not None else '',
double_type, pres_ref, utils.restrict[lang],
', {} * {} dot_prod'.format(double_type, utils.restrict[lang]) if cuda_cheb else ''
)
)
elif lang == 'fortran':
line += ('subroutine eval_rxn_rates(T, pres, C, fwd_rxn_rates,'
' rev_rxn_rates)\n\n'
)
# fortran needs type declarations
line += (' implicit none\n'
' double precision, intent(in) :: '
'T, pres, C({})\n'.format(num_s)
)
line += (' double precision, intent(out) :: '
'fwd_rxn_rates({}), '.format(num_r) +
'rev_rxn_rates({})\n'.format(num_rev)
)
line += (' \n'
' double precision :: logT\n'
)
kf_flag = True
if rev_reacs and any([not r.rev_par for r in my_reacs]):
line += ' double precision :: kf, Kc\n'
kf_flag = False
if any([rxn.cheb for rxn in my_reacs]):
if kf_flag:
line += ' double precision :: kf, Tred, Pred\n'
kf_flag = False
else:
line += ' double precision :: Tred, Pred\n'
if any([rxn.plog for rxn in my_reacs]):
if kf_flag:
line += ' double precision :: kf, kf2\n'
kf_flag = False
else:
line += ' double precision :: kf2\n'
line += '\n'
elif lang == 'matlab':
line += ('function [fwd_rxn_rates, rev_rxn_rates] = '
'eval_rxn_rates (T, pres, C)\n\n'
' fwd_rxn_rates = zeros({},1);\n'.format(num_r) +
' rev_rxn_rates = fwd_rxn_rates;\n'
)
file.write(line)
if smm is not None:
smm.reset()
if defines:
smm.write_init(file, indent=2)
if defines:
if lang == 'c':
pre += double_type + ' '
elif lang == 'cuda':
pre += 'register {} '.format(double_type)
line = (pre + 'logT = log(T)' +
utils.line_end[lang]
)
file.write(line)
file.write('\n')
kf_flag = True
if rev_reacs and any([not r.rev_par for r in my_reacs]):
kf_flag = False
if lang == 'c':
file.write(' {0} kf;\n'
' {0} Kc;\n'.format(double_type)
)
elif lang == 'cuda':
file.write(' register double kf;\n'
' register double Kc;\n'
)
if any([rxn.cheb for rxn in my_reacs]):
# Other variables needed for Chebyshev
if lang == 'c':
if kf_flag:
file.write(' {0} kf;\n'.format(double_type))
kf_flag = False
file.write(' {0} Tred;\n'
' {0} Pred;\n'.format(double_type))
file.write(utils.line_start + '{0} cheb_temp_0, cheb_temp_1'.format(
double_type) + utils.line_end[lang]
)
dim = max(rxn.cheb_n_temp for rxn in my_reacs if rxn.cheb)
file.write(utils.line_start + '{0} dot_prod[{1}]'.format(double_type, dim) +
utils.line_end[lang])
elif lang == 'cuda':
if kf_flag:
file.write(' register double kf;\n')
kf_flag = False
file.write(' register double Tred;\n'
' register double Pred;\n')
file.write(utils.line_start + 'double cheb_temp_0, cheb_temp_1' +
utils.line_end[lang]
)
if any([rxn.plog for rxn in my_reacs]):
# Variables needed for Plog
if lang == 'c':
if kf_flag:
file.write(' {0} kf;\n'.format(double_type))
file.write(' {0} kf2;\n'.format(double_type))
if lang == 'cuda':
if kf_flag:
file.write(' register double kf;\n')
file.write(' register double kf2;\n')
file.write('\n')
rrange = (0, len(reacs)) if not do_unroll else (0, CUDAParams.Rates_Unroll)
write_sub_intro(file, not do_unroll, rrange[0], rrange[1])
def __get_arrays(sp, factor=1.0):
# put together all our coeffs
lo_array = [nu * factor] + [
sp.lo[6], sp.lo[0], sp.lo[0] - 1.0, sp.lo[1] / 2.0,
sp.lo[2] / 6.0, sp.lo[3] / 12.0, sp.lo[4] / 20.0,
sp.lo[5]
]
lo_array = [x * lo_array[0] for x in
[lo_array[1] - lo_array[2]] + lo_array[3:]
]
hi_array = [nu * factor] + [
sp.hi[6], sp.hi[0], sp.hi[0] - 1.0, sp.hi[1] / 2.0,
sp.hi[2] / 6.0, sp.hi[3] / 12.0, sp.hi[4] / 20.0,
sp.hi[5]
]
hi_array = [x * hi_array[0] for x in
[hi_array[1] - hi_array[2]] + hi_array[3:]
]
return lo_array, hi_array
for i_rxn in range(len(reacs)):
if do_unroll and i_rxn == next_file:
file_store = file
file = open(os.path.join(path, 'rates', 'rxn_rates_{}{}'.format(
rate_count, utils.file_ext[lang])), 'w')
next_file = min(len(reacs), i_rxn + CUDAParams.Rates_Unroll)
write_sub_intro(file, True, i_rxn + 1, next_file, rate_count)
rate_count += 1
file.write(utils.line_start + utils.comment[lang] +
'rxn {}'.format(fwd_rxn_mapping[i_rxn]) + '\n')
rxn = reacs[i_rxn]
if lang == 'cuda' and smm is not None:
indexes = sorted(list(set(rxn.reac + rxn.prod)))
the_vars = [shared.variable('C', index) for index in indexes]
# estimate usages as the number of consequitive reactions
usages = []
for sp_i in indexes:
temp = i_rxn + 1
while (temp < len(reacs) and
sp_i in set(reacs[temp].reac +
reacs[temp].prod)
):
temp += 1
usages.append(temp - i_rxn - 1)
smm.load_into_shared(file, the_vars, usages)
# if reversible, save forward rate constant for use
if rxn.rev and not rxn.rev_par and not (rxn.cheb or rxn.plog):
line = (' kf = ' + rxn_rate_const(rxn.A, rxn.b, rxn.E) +
utils.line_end[lang]
)
file.write(line)
elif rxn.cheb:
file.write(get_cheb_rate(lang, rxn))
elif rxn.plog:
# Special forward rate evaluation for Plog reacions
vals = rxn.plog_par[0]
file.write(' if (pres <= {:.4e}) {{\n'.format(vals[0]))
line = (' kf = ' + rxn_rate_const(vals[1], vals[2], vals[3]))
file.write(line + utils.line_end[lang])
for idx, vals in enumerate(rxn.plog_par[:-1]):
vals2 = rxn.plog_par[idx + 1]
line = (' }} else if ((pres > {:.4e}) '.format(vals[0]) +
'&& (pres <= {:.4e})) {{\n'.format(vals2[0]))
file.write(line)
line = (' kf = log(' +
rxn_rate_const(vals[1], vals[2], vals[3]) + ')'
)
file.write(line + utils.line_end[lang])
line = (' kf2 = log(' +
rxn_rate_const(vals2[1], vals2[2], vals2[3]) + ')'
)
file.write(line + utils.line_end[lang])
pres_log_diff = math.log(vals2[0]) - math.log(vals[0])
line = (' kf = exp(kf + (kf2 - kf) * (log(pres) - ' +
'{:.16e}) / '.format(math.log(vals[0])) +
'{:.16e})'.format(pres_log_diff)
)
file.write(line + utils.line_end[lang])
vals = rxn.plog_par[-1]
file.write(' }} else if (pres > {:.4e}) {{\n'.format(vals[0]))
line = (' kf = ' + rxn_rate_const(vals[1], vals[2], vals[3]))
file.write(line + utils.line_end[lang])
file.write(' }\n')
line = ' ' + get_array(lang, 'fwd_rxn_rates', i_rxn) + ' = '
# reactants
for i, isp in enumerate(rxn.reac):
nu = rxn.reac_nu[i]
# check if stoichiometric coefficient is double or integer
if utils.is_integer(nu):
# integer, so just use multiplication
for i in range(int(nu)):
line += '' + get_array(lang, 'C', isp) + ' * '
else:
line += ('pow(' + get_array(lang, 'C', isp) +
', {}) *'.format(nu)
)
# Rate constant: print if not reversible, or reversible but
# with explicit reverse parameters.
if (rxn.rev and not rxn.rev_par) or rxn.plog or rxn.cheb:
line += 'kf'
else:
line += rxn_rate_const(rxn.A, rxn.b, rxn.E)
line += utils.line_end[lang]
file.write(line)
if rxn.rev:
if not rxn.rev_par:
# line = ' Kc = 0.0' + utils.line_end[lang]
# file.write(line)
# sum of stoichiometric coefficients
sum_nu = 0
coeffs = {}
# go through product species
for isp, prod_sp in enumerate(rxn.prod):
# check if species also in reactants
if prod_sp in rxn.reac:
isp2 = rxn.reac.index(prod_sp)
nu = rxn.prod_nu[isp] - rxn.reac_nu[isp2]
else:
nu = rxn.prod_nu[isp]
# Skip species with zero overall
# stoichiometric coefficient.
if (nu == 0):
continue
sum_nu += nu
# get species object
sp = specs[prod_sp]
if not sp:
print('Error: species ' + prod_sp + ' in reaction '
'{} not found.\n'.format(i_rxn)
)
sys.exit()
lo_array, hi_array = __get_arrays(sp)
if not sp.Trange[1] in coeffs:
coeffs[sp.Trange[1]] = lo_array, hi_array
else:
coeffs[sp.Trange[1]] = [
lo_array[i] + coeffs[sp.Trange[1]][0][i]
for i in range(len(lo_array))
], [
hi_array[i] + coeffs[sp.Trange[1]][1][i]
for i in range(len(hi_array))
]
# now loop through reactants
for isp, reac_sp in enumerate(rxn.reac):
# Check if species also in products;
# if so, already considered).
if reac_sp in rxn.prod: continue
nu = rxn.reac_nu[isp]
sum_nu -= nu
# get species object
sp = specs[reac_sp]
if not sp:
print('Error: species ' + reac_sp + ' in reaction '
'{} not found.\n'.format(i_rxn)
)
sys.exit()
lo_array, hi_array = __get_arrays(sp, factor=-1.0)
if not sp.Trange[1] in coeffs:
coeffs[sp.Trange[1]] = lo_array, hi_array
else:
coeffs[sp.Trange[1]] = [
lo_array[i] +
coeffs[sp.Trange[1]][0][i]
for i in range(len(lo_array))
], [hi_array[i] +
coeffs[sp.Trange[1]][1][i]
for i in range(len(hi_array))
]
isFirst = True
for T_mid in coeffs:
# need temperature conditional for equilibrium constants
line = ' if (T <= {:})'.format(T_mid)
if lang in ['c', 'cuda']:
line += ' {\n'
elif lang == 'fortran':
line += ' then\n'
elif lang == 'matlab':
line += '\n'
file.write(line)
lo_array, hi_array = coeffs[T_mid]
if isFirst:
line = ' Kc = '
else:
if lang in ['cuda', 'c']:
line = ' Kc += '
else:
line = ' Kc = Kc + '
line += ('({:.16e} + '.format(lo_array[0]) +
'{:.16e} * '.format(lo_array[1]) +
'logT + T * ('
'{:.16e} + T * ('.format(lo_array[2]) +
'{:.16e} + T * ('.format(lo_array[3]) +
'{:.16e} + '.format(lo_array[4]) +
'{:.16e} * T))) - '.format(lo_array[5]) +
'{:.16e} / T)'.format(lo_array[6]) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' } else {\n')
elif lang in ['fortran', 'matlab']:
file.write(' else\n')
if isFirst:
line = ' Kc = '
else:
if lang in ['cuda', 'c']:
line = ' Kc += '
else:
line = ' Kc = Kc + '
line += ('({:.16e} + '.format(hi_array[0]) +
'{:.16e} * '.format(hi_array[1]) +
'logT + T * ('
'{:.16e} + T * ('.format(hi_array[2]) +
'{:.16e} + T * ('.format(hi_array[3]) +
'{:.16e} + '.format(hi_array[4]) +
'{:.16e} * T))) - '.format(hi_array[5]) +
'{:.16e} / T)'.format(hi_array[6]) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' }\n\n')
elif lang == 'fortran':
file.write(' end if\n\n')
elif lang == 'matlab':
file.write(' end\n\n')
isFirst = False
line = (' Kc = '
'{:.16e}'.format((chem.PA / chem.RU) ** sum_nu) +
' * exp(Kc)' +
utils.line_end[lang]
)
file.write(line)
line = ' ' + get_array(lang, 'rev_rxn_rates',
rev_reacs.index(i_rxn)
) + ' = '
# reactants (products from forward reaction)
for isp in rxn.prod:
nu = rxn.prod_nu[rxn.prod.index(isp)]
# check if stoichiometric coefficient is double or integer
if utils.is_integer(nu):
# integer, so just use multiplication
for i in range(int(nu)):
line += '' + get_array(lang, 'C', isp) + ' * '
else:
line += ('pow(' + get_array(lang, 'C', isp) +
', {}) * '.format(nu)
)
# rate constant
if rxn.rev_par:
# explicit reverse Arrhenius parameters
line += rxn_rate_const(rxn.rev_par[0],
rxn.rev_par[1],
rxn.rev_par[2]
)
else:
# use equilibrium constant
line += 'kf / Kc'
line += utils.line_end[lang]
file.write(line)
file.write('\n')
if do_unroll and (i_rxn == next_file - 1 or i_rxn == len(reacs) - 1):
file.write('}\n\n')
file.close()
file = file_store
file.write(' eval_rxn_rates_{}(T, pres, C, fwd_rxn_rates, rev_rxn_rates{})'.format(
rate_count - 1, ', dot_prod' if cuda_cheb else '') + utils.line_end[lang])
if lang in ['c', 'cuda']:
file.write('} // end eval_rxn_rates\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_rxn_rates\n\n')
elif lang == 'matlab':
file.write('end\n\n')
if do_unroll:
with open(os.path.join(path, 'rates', 'rates_include' + utils.header_ext[lang]), 'w') as file:
file.write('#ifndef RATES_INCLUDE_{}\n'.format(lang))
file.write('#define RATES_INCLUDE_{}\n'.format(lang))
for i in range(rate_count):
file.write('#include "rxn_rates_{}{}"\n'.format(i, utils.header_ext[lang]))
file.write('\n')
file.write('#endif\n')
for i in range(rate_count):
write_header(lang, i)
with open(os.path.join(path, 'rates', 'rate_list_{}'.format(lang)), 'w') as file:
file.write(' '.join(['rxn_rates_{}{}'.format(i,
utils.file_ext[lang]) for i in range(rate_count)])
)
file.close()
return
[docs]def write_rxn_pressure_mod(path, lang, specs, reacs,
fwd_rxn_mapping, smm=None, auto_diff=False
):
"""Write subroutine to for reaction pressure dependence modifications.
Parameters
----------
path : str
Path to build directory for file.
lang : {'c', 'cuda', 'fortran', 'matlab'}
Language type.
specs : list of `SpecInfo`
List of species in mechanism.
reacs : list of `ReacInfo`
List of reactions in mechanism.
fwd_rxn_mapping : List of int
The order of the reaction in the original mechanism
smm : `shared_memory_manager`, optional
If not ```None```, `shared_memory_manager` to use for CUDA optimizations
auto_diff : bool, optional
If ```True```, generate files for Adept autodifferention library.
Returns
-------
None
"""
double_type = 'double'
file_prefix = ''
pres_ref = ''
if auto_diff:
double_type = 'adouble'
file_prefix = 'ad_'
pres_ref = '&'
filename = file_prefix + 'rxn_rates_pres_mod' + utils.file_ext[lang]
file = open(os.path.join(path, filename), 'w')
# headers
if lang in ['c', 'cuda']:
file.write('#include <math.h>\n'
'#include "header{1}"\n'
'#include "{0}rates{1}"\n'.format(file_prefix, utils.header_ext[lang])
)
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n'
'#define fmax(a, b) (a.value() > b ? a : adouble(b))\n')
file.write('\n')
# list of reactions with third-body or pressure-dependence
pdep_reacs = []
thd_flag = False
pdep_flag = False
troe_flag = False
sri_flag = False
for i_rxn, reac in enumerate(reacs):
if reac.thd_body:
# add reaction index to list
thd_flag = True
pdep_reacs.append(i_rxn)
elif reac.pdep:
# add reaction index to list
pdep_flag = True
pdep_reacs.append(i_rxn)
if reac.troe and not troe_flag: troe_flag = True
if reac.sri and not sri_flag: sri_flag = True
line = ''
if lang == 'cuda': line = '__device__ '
if lang in ['c', 'cuda']:
line += ('void get_rxn_pres_mod (const {0} T, const {0}{1} pres, '
'const {0} * {2} C, {0} * {2} pres_mod) {{\n'.format(
double_type, pres_ref, utils.restrict[lang])
)
elif lang == 'fortran':
line += 'subroutine get_rxn_pres_mod ( T, pres, C, pres_mod )\n\n'
# fortran needs type declarations
line += (' implicit none\n'
' double precision, intent(in) :: T, pres, '
'C({})\n'.format(len(specs)) +
' double precision, intent(out) :: '
'pres_mod({})\n'.format(len(pdep_reacs)) +
' \n'
' double precision :: logT, m\n')
elif lang == 'matlab':
line += ('function pres_mod = get_rxn_pres_mod (T, pres, C)\n\n'
' pres_mod = zeros({},1);\n'.format(len(pdep_reacs))
)
file.write(line)
get_array = utils.get_array
if lang == 'cuda' and smm is not None:
smm.reset()
get_array = smm.get_array
smm.write_init(file, indent=2)
# declarations for third-body variables
if thd_flag or pdep_flag:
if lang == 'c':
file.write(' // third body variable declaration\n'
' {0} thd;\n\n'.format(double_type)
)
elif lang == 'cuda':
file.write(' // third body variable declaration\n'
' register double thd;\n'
'\n'
)
elif lang == 'fortran':
file.write(' ! third body variable declaration\n'
' double precision :: thd\n'
)
# declarations for pressure-dependence variables
if pdep_flag:
if lang == 'c':
file.write(' // pressure dependence variable declarations\n'
' {0} k0;\n'
' {0} kinf;\n'
' {0} Pr;\n'
'\n'.format(double_type)
)
if troe_flag:
# troe variables
file.write(' // troe variable declarations\n'
' {0} logFcent;\n'
' {0} A;\n'
' {0} B;\n'
'\n'.format(double_type)
)
if sri_flag:
# sri variables
file.write(' // sri variable declarations\n')
file.write(' {0} X;\n'
'\n'.format(double_type)
)
elif lang == 'cuda':
file.write(' // pressure dependence variable declarations\n')
file.write(' register double k0;\n'
' register double kinf;\n'
' register double Pr;\n'
'\n'
)
if troe_flag:
# troe variables
file.write(' // troe variable declarations\n'
' register double logFcent;\n'
' register double A;\n'
' register double B;\n'
'\n'
)
if sri_flag:
# sri variables
file.write(' // sri variable declarations\n')
file.write(' register double X;\n'
'\n')
elif lang == 'fortran':
file.write(' ! pressure dependence variable declarations\n'
' double precision :: k0, kinf, Pr\n'
'\n'
)
if troe_flag:
# troe variables
file.write(' ! troe variable declarations\n'
' double precision :: logFcent, A, B\n'
'\n'
)
if sri_flag:
# sri variables
file.write(' ! sri variable declarations\n')
file.write(' double precision :: X\n'
'\n')
if lang == 'c':
file.write(' {} logT = log(T);\n'
' {} m = pres / ({:.8e} * T);\n'.format(double_type,
double_type, chem.RU)
)
elif lang == 'cuda':
file.write(' register double logT = log(T);\n'
' register double m = pres / ('
'{:.8e} * T);\n'.format(chem.RU)
)
elif lang == 'fortran':
file.write(' logT = log(T)\n'
' m = pres / ({:.8e} * T)\n'.format(chem.RU)
)
elif lang == 'matlab':
file.write(' logT = log(T);\n'
' m = pres / ({:.8e} * T);\n'.format(chem.RU)
)
file.write('\n')
pind = 0
# loop through third-body and pressure-dependent reactions
for rind in range(len(reacs)):
reac = reacs[rind] # index in reaction list
if not (reac.pdep or reac.thd_body):
continue
printind = fwd_rxn_mapping[rind]
# print reaction index
if lang in ['c', 'cuda']:
line = ' // reaction ' + str(printind)
elif lang == 'fortran':
line = ' ! reaction ' + str(printind + 1)
elif lang == 'matlab':
line = ' % reaction ' + str(printind + 1)
line += utils.line_end[lang]
file.write(line)
if reac.thd_body_eff:
if lang == 'cuda' and smm is not None:
the_vars = []
indexes = sorted([sp[0] for sp in reac.thd_body_eff
if sp[1] != 1.0]
)
the_vars = [shared.variable('C', index) for index in indexes]
# estimate usages as the number of consecutive reactions
usages = []
for sp_i in indexes:
temp = i_rxn + 1
count = 0
while temp < len(reacs):
rxn = reacs[temp]
if sp_i in set([x[0] for x in rxn.thd_body_eff
if sp[1] != 1.0]
):
count += 1
else:
break
temp += 1
usages.append(count)
smm.load_into_shared(file, the_vars, usages)
# third-body reaction
if reac.thd_body:
line = ' ' + get_array(lang, 'pres_mod', pind) + ' = m'
for sp in reac.thd_body_eff:
if sp[1] == 1.0:
continue
elif sp[1] > 1.0:
line += ' + {}'.format(sp[1] - 1.0)
elif sp[1] < 1.0:
line += ' - {}'.format(1.0 - sp[1])
line += ' * ' + get_array(lang, 'C', sp[0])
line += utils.line_end[lang]
file.write(line)
# pressure dependence
if reac.pdep:
if reac.pdep_sp is None:
line = ' thd = m'
for sp in reac.thd_body_eff:
if sp[1] == 1.0:
continue
elif sp[1] > 1.0:
line += ' + {}'.format(sp[1] - 1.0)
elif sp[1] < 1.0:
line += ' - {}'.format(1.0 - sp[1])
line += ' * ' + get_array(lang, 'C', sp[0])
file.write(line + utils.line_end[lang])
# low-pressure limit rate
line = ' k0 = '
if reac.low:
line += rxn_rate_const(reac.low[0],
reac.low[1],
reac.low[2]
)
else:
line += rxn_rate_const(reac.A, reac.b, reac.E)
line += utils.line_end[lang]
file.write(line)
# high-pressure limit rate
line = ' kinf = '
if reac.high:
line += rxn_rate_const(reac.high[0],
reac.high[1],
reac.high[2]
)
else:
line += rxn_rate_const(reac.A, reac.b, reac.E)
line += utils.line_end[lang]
file.write(line)
# reduced pressure
if reac.pdep_sp is not None:
line = (' Pr = k0 * ' +
get_array(lang, 'C', reac.pdep_sp) + ' / kinf'
)
else:
line = ' Pr = k0 * thd / kinf'
line += utils.line_end[lang]
file.write(line)
simple = False
if reac.troe:
# Troe form
line = (' logFcent = log10( fmax('
'{:.8e} * '.format(1.0 - reac.troe_par[0])
)
if reac.troe_par[1] > 0.0:
line += 'exp(-T / {:.8e})'.format(reac.troe_par[1])
else:
line += 'exp(T / {:.8e})'.format(abs(reac.troe_par[1]))
line += ' + {:.8e} * '.format(reac.troe_par[0])
if reac.troe_par[2] > 0.0:
line += 'exp(-T / {:.8e})'.format(reac.troe_par[2])
else:
line += 'exp(T / {:.8e})'.format(abs(reac.troe_par[2]))
if len(reac.troe_par) == 4 and reac.troe_par[3] != 0.0:
line += ' + '
if reac.troe_par[3] > 0.0:
line += 'exp(-{:.8e} / T)'.format(reac.troe_par[3])
else:
line += 'exp({:.8e} / T)'.format(abs(reac.troe_par[3]))
line += ', 1.0e-300))' + utils.line_end[lang]
file.write(line)
line = (' A = log10(fmax(Pr, 1.0e-300)) - '
'0.67 * logFcent - 0.4' +
utils.line_end[lang]
)
file.write(line)
line = (' B = 0.806 - 1.1762 * logFcent - '
'0.14 * log10(fmax(Pr, 1.0e-300))' +
utils.line_end[lang]
)
file.write(line)
line = (' ' + get_array(lang, 'pres_mod', pind) +
' = ' + utils.exp_10_fun[lang]
)
line += 'logFcent / (1.0 + A * A / (B * B))) '
elif reac.sri:
# SRI form
line = (' X = 1.0 / (1.0 + log10(fmax(Pr, 1.0e-300)) * '
'log10(fmax(Pr, 1.0e-300)))' +
utils.line_end[lang]
)
file.write(line)
line = ' ' + get_array(lang, 'pres_mod', pind)
line += ' = pow({:.6} * '.format(reac.sri_par[0])
# Need to check for negative parameters, and
# skip "-" sign if so.
if reac.sri_par[1] > 0.0:
line += 'exp(-{:.6} / T)'.format(reac.sri_par[1])
else:
line += 'exp({:.6} / T)'.format(abs(reac.sri_par[1]))
if reac.sri_par[2] > 0.0:
line += ' + exp(-T / {:.6}), X) '.format(reac.sri_par[2])
else:
line += ' + exp(T / {:.6}), X) '.format(abs(reac.sri_par[2]))
if (len(reac.sri_par) == 5 and
reac.sri_par[3] != 1.0 and reac.sri_par[4] != 0.0):
line += ('* {:.8e} * '.format(reac.sri_par[3]) +
'pow(T, {:.6}) '.format(reac.sri_par[4])
)
else:
# simple falloff fn (i.e. F = 1)
simple = True
line = ' ' + get_array(lang, 'pres_mod', pind) + ' = '
# regardless of F formulation
if reac.low:
# unimolecular/recombination fall-off reaction
line += ' Pr / (1.0 + Pr)'
elif reac.high:
# chemically-activated bimolecular reaction
line += '1.0 / (1.0 + Pr)'
if not simple:
# regardless of F formulation
if reac.low:
# unimolecular/recombination fall-off reaction
line += '* Pr / (1.0 + Pr)'
elif reac.high:
# chemically-activated bimolecular reaction
line += '/ (1.0 + Pr)'
line += utils.line_end[lang]
file.write(line)
# space in between each reaction
file.write('\n')
pind += 1
if lang in ['c', 'cuda']:
file.write('} // end get_rxn_pres_mod\n\n')
elif lang == 'fortran':
file.write('end subroutine get_rxn_pres_mod\n\n')
elif lang == 'matlab':
file.write('end\n\n')
file.close()
return
[docs]def write_spec_rates(path, lang, specs, reacs, fwd_spec_mapping,
fwd_rxn_mapping, smm=None, auto_diff=False):
"""Write subroutine to evaluate species rates of production.
Parameters
----------
path : str
Path to build directory for file.
lang : {'c', 'cuda', 'fortran', 'matlab'}
Programming language.
specs : list of `SpecInfo`
List of species in mechanism.
reacs : list of `ReacInfo`
List of reactions in mechanism.
fwd_spec_mapping : list of int
the index of the species in the original mechanism
fwd_rxn_mapping : list of int
the index of the reactions in the original mechanism
smm : shared_memory_manager, optional
If not ```None```, `shared_memory_manager` to use for CUDA optimizations
auto_diff : bool, optional
If ```True```, generate files for Adept autodifferention library.
Returns
-------
seen : list of `bool`, ``True`` if species rate i is not identically zero
"""
double_type = 'double'
file_prefix =''
if auto_diff:
double_type = 'adouble'
file_prefix = 'ad_'
filename = file_prefix + 'spec_rates' + utils.file_ext[lang]
file = open(os.path.join(path, filename), 'w')
if lang in ['c', 'cuda']:
file.write('#include "header{}"\n'.format(utils.header_ext[lang])
)
#if lang == 'cuda' and smm is not None:
# file.write('#include <assert.h>\n')
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
file.write('#include "{}rates{}"\n'.format(file_prefix, utils.header_ext[lang]))
file.write('\n')
num_s = len(specs)
num_r = len(reacs)
rev_reacs = [i for i, rxn in enumerate(reacs) if rxn.rev]
num_rev = len(rev_reacs)
# pressure dependent reactions
pdep_reacs = []
for i_rxn, reac in enumerate(reacs):
if reac.thd_body or reac.pdep:
# add reaction index to list
pdep_reacs.append(i_rxn)
line = ''
if lang == 'cuda': line = '__device__ '
if lang in ['c', 'cuda']:
line += ('void eval_spec_rates (const {0} * {1} fwd_rates,'
' const {0} * {1} rev_rates, const {0} * {1} pres_mod,'
' {0} * {1} sp_rates, {0} * {1} dy_N) {{\n'.format(double_type,
utils.restrict[lang])
)
elif lang == 'fortran':
line += ('subroutine eval_spec_rates (fwd_rates, rev_rates,'
' pres_mod, sp_rates, dy_N)\n\n'
)
# fortran needs type declarations
line += ' implicit none\n'
line += (' double precision, intent(in) :: '
'fwd_rates({0}), rev_rates({0}), '.format(num_r) +
'pres_mod({})\n'.format(len(pdep_reacs))
)
line += (' double precision, intent(out) :: '
'sp_rates({}), dy_N\n'.format(num_s) +
'\n'
)
elif lang == 'matlab':
line += ('function sp_rates = eval_spec_rates (fwd_rates,'
' rev_rates, pres_mod, dy_N)\n\n'
)
line += ' sp_rates = zeros({},1);\n'.format(len(specs))
file.write(line)
def __get_var(spind):
if spind + 1 == len(specs):
line = ' ' + get_array(lang, '(*dy_N)', None)
else:
line = ' ' + get_array(lang, 'sp_rates', spind)
return line
def __get_smm_var(sp):
return shared.variable('sp_rates', sp) if sp + 1 != len(specs) \
else shared.variable('(*dy_N)', None)
#if lang == 'cuda' and smm is not None:
# file.write(' assert(threadIdx.x + {} * blockDim.x < {});\n'.format(
# smm.shared_per_thread - 1, smm.shared_per_block))
first_use = [True for spec in specs]
first_smem_use = {}
seen = [False for spec in specs]
new_loads = []
def __on_eviction(sp, shared, shared_ind):
index = len(specs) - 1 if sp.index is None else sp.index
file.write(' {} {}= {}'.format(sp.to_string(),
# is only a += if the species in question has been updated
# previously
'+' if not first_use[index] else '',
shared) + utils.line_end[lang])
first_use[index] = False
get_array = utils.get_array
if lang == 'cuda' and smm is not None:
smm.reset()
get_array = smm.get_array
smm.write_init(file, indent=2)
smm.set_on_eviction(__on_eviction)
#loop through reaction
for rind in range(len(reacs)):
print_ind = fwd_rxn_mapping[rind]
file.write(utils.line_start + utils.comment[lang] +
'rxn {}'.format(print_ind) + '\n')
rxn = reacs[rind]
#get allowed species
my_specs = [x for x in set(rxn.reac + rxn.prod)
if utils.get_nu(x, rxn) != 0.]
if lang == 'cuda' and smm is not None:
the_vars = [__get_smm_var(sp) for sp in my_specs]
# estimate usages
usages = []
for sp in set(rxn.reac + rxn.prod):
temp = rind + 1
while (temp < len(reacs) and
utils.get_nu(sp, reacs[temp])) != 0.:
temp += 1
usages.append(temp - rind - 1)
first_smem_use = smm.load_into_shared(file, the_vars,
usages, load=False
)
# loop through species
for spind in my_specs:
sp = specs[spind]
#find nu
nu = utils.get_nu(spind, rxn)
if nu == 0.0:
continue
file.write(utils.line_start + utils.comment[lang] +
'sp {}'.format(fwd_spec_mapping[spind]) + '\n'
)
sign = '-' if nu < 0 else '+'
line = __get_var(spind)
if lang == 'cuda' and smm is not None:
tempvar = __get_smm_var(spind)
smem_ind, smem_var = smm.get_index(tempvar)
if smem_ind is not None:
#this is loaded into shared memory
if first_smem_use[smem_ind]:
#if it's the first time the value has been used
#use an ='s
line += ' = {}'.format(sign if sign == '-' else '')
else:
#otherwise +/- =
line += ' {}= '.format(sign)
first_smem_use[smem_ind] = False
else:
#this is not loaded into shared memory
if first_use[spind]:
#if it's the first time the value has been used
#use an ='s
line += ' = {}'.format(sign if sign == '-' else '')
else:
#otherwise +/- =
line += ' {}= '.format(sign)
first_use[spind] = False
else:
line += ' {}= '.format(sign if not first_use[spind] else '')
if not seen[spind] and nu < 0:
line += sign
first_use[spind] = False
nu = abs(nu)
if nu != 1.0:
if utils.is_integer(nu):
line += '{} * '.format(float(nu))
else:
line += '{:3} * '.format(nu)
if rxn.rev:
rxn_out = (
'(' + get_array(lang, 'fwd_rates', rind) +
' - ' + get_array(lang, 'rev_rates',
rev_reacs.index(rind)) + ')'
)
else:
rxn_out = get_array(lang, 'fwd_rates', rind)
seen[spind] = True
# pressure dependence modification
if rxn.thd_body or rxn.pdep:
pind = pdep_reacs.index(rind)
rxn_out += ' * ' + get_array(lang, 'pres_mod', pind)
# if lang == 'cuda':
# rxn_out = get_array(lang, rxn_out, None, preformed=True)
line += rxn_out
# done with this species
line += utils.line_end[lang]
file.write(line)
file.write('\n')
for i, seen_sp in enumerate(seen):
if not seen_sp:
file.write(utils.line_start + utils.comment[lang] +
'sp {}'.format(fwd_spec_mapping[i]) + '\n')
file.write(__get_var(i) +
' = 0.0' + utils.line_end[lang]
)
if lang == 'cuda' and smm is not None:
smm.force_eviction()
smm.set_on_eviction(None)
if lang in ['c', 'cuda']:
file.write('} // end eval_spec_rates\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_spec_rates\n\n')
elif lang == 'matlab':
file.write('end\n\n')
file.close()
return seen
[docs]def write_chem_utils(path, lang, specs, auto_diff):
"""Write subroutine to evaluate species thermodynamic properties.
Notes
-----
Thermodynamic properties include: enthalpy, energy, specific heat
(constant pressure and volume).
Parameters
----------
path : str
Path to build directory for file.
lang : {'c', 'cuda', 'fortran', 'matlab'}
Programming language.
specs : list of `SpecInfo`
List of species in the mechanism.
auto_diff : bool, optional
If ``True``, generate files for Adept autodifferention library.
Returns
-------
None
"""
file_prefix = ''
double_type = 'double'
pres_ref = ''
if auto_diff:
file_prefix = 'ad_'
double_type = 'adouble'
pres_ref = '&'
num_s = len(specs)
pre = '__device__ ' if lang == 'cuda' else ''
file = open(os.path.join(path, file_prefix + 'chem_utils'
+ utils.header_ext[lang]), 'w')
file.write('#ifndef CHEM_UTILS_HEAD\n'
'#define CHEM_UTILS_HEAD\n'
'\n'
'#include "header{}"\n'.format(utils.header_ext[lang]) +
'\n'
)
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
if lang == 'cuda':
file.write('#include "gpu_memory.cuh"\n')
file.write(
'{0}void eval_conc (const {1}{2}, const {1}{2}, '
'const {1} * {3}, {1} * {3}, {1} * {3}, {1} * {3}, {1} * {3});\n'
'{0}void eval_conc_rho (const {1}{2}, const {1}{2}, '
'const {1} * {3}, {1} * {3}, {1} * {3}, {1} * {3}, {1} * {3});\n'
'{0}void eval_h (const {1}{2}, {1} * {3});\n'
'{0}void eval_u (const {1}{2}, {1} * {3});\n'
'{0}void eval_cv (const {1}{2}, {1} * {3});\n'
'{0}void eval_cp (const {1}{2}, {1} * {3});\n'
'\n'
'#endif\n'.format(pre, double_type, pres_ref,
utils.restrict[lang])
)
file.close()
filename = file_prefix + 'chem_utils' + utils.file_ext[lang]
file = open(os.path.join(path, filename), 'w')
if lang in ['c', 'cuda']:
file.write('#include "header{}"\n'.format(utils.header_ext[lang]))
file.write('#include "{}chem_utils{}"\n'.format(file_prefix, utils.header_ext[lang]))
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
file.write('\n')
###################################
# species concentrations subroutine
###################################
line = pre
if lang in ['c', 'cuda']:
line += ('void eval_conc (const {0}{1} T, const {0}{1} pres, '
'const {0} * {2} y, {0} * {2} y_N, {0} * {2} mw_avg, '
'{0} * {2} rho, {0} * {2} conc) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang])
)
elif lang == 'fortran':
line += (
# fortran needs type declarations
'subroutine eval_conc (T, pres, y, y_N, '
'mw_avg, rho, conc)\n\n'
' implicit none\n'
' double precision, intent(in) :: T, pres, '
'mass_frac\n'.format(num_s) +
' double precision, intent(out) :: y_N, mw_avg, '
'rho, conc({})\n'.format(num_s) +
'\n'
)
elif lang == 'matlab':
line += ('function conc = eval_conc (T, pres, y, y_N, '
'mw_avg, rho, conc)\n\n'
)
file.write(line)
isfirst = True
# Get mass fraction of last species
file.write(' // mass fraction of final species\n')
line = ' *y_N = 1.0 - ('
for isp in range(len(specs[:-1])):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += utils.get_array(lang, 'y', isp)
isfirst = False
line += ')'
file.write(line + utils.line_end[lang])
# calculation of mw avg
line = ' *mw_avg = '
isfirst = True
for isp, sp in enumerate(specs[:-1]):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += '(' + utils.get_array(lang, 'y', isp) + ' * {:.16e})'.format(1.0 / sp.mw)
isfirst = False
line += ' + ((*y_N) * {:.16e})'.format(1.0 / specs[-1].mw)
line += utils.line_end[lang]
file.write(line)
file.write(' *mw_avg = 1.0 / *mw_avg;\n')
# calculation of density
file.write(' // mass-averaged density\n')
line = ' *rho = pres * (*mw_avg) / ({:.8e} * T)'.format(chem.RU)
file.write(line + utils.line_end[lang])
# calculation of species molar concentrations
# loop through species
for isp, sp in enumerate(specs[:-1]):
line = utils.line_start + utils.get_array(lang, 'conc', isp)
line += ' = '
line += '(*rho) * ' + utils.get_array(lang, 'y', isp)
line += ' * {:.16e}'.format(1.0 / sp.mw) + utils.line_end[lang]
file.write(line)
line = utils.line_start + utils.get_array(lang, 'conc', len(specs) - 1)
line += ' = (*rho) * (*y_N) * '.format(len(specs) - 1)
line += '{:.16e}'.format(1.0 / specs[-1].mw) + utils.line_end[lang]
file.write(line + '\n')
if lang in ['c', 'cuda']:
file.write('} // end eval_conc\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_conc\n\n')
elif lang == 'matlab':
file.write('end\n\n')
#######################################################################
line = pre
if lang in ['c', 'cuda']:
line += ('void eval_conc_rho (const {0}{1} T, const {0}{1} rho, '
'const {0} * {2} y, {0} * {2} y_N, {0} * {2} mw_avg, '
'{0} * {2} pres, {0} * {2} conc) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang])
)
elif lang == 'fortran':
line += (
# fortran needs type declarations
'subroutine eval_conc_rho (temp, rho, mass_frac, y_N, '
'mw_avg, pres, conc)\n'
' implicit none\n'
' double precision, intent(in) :: '
'T, rho, mass_frac({})\n'.format(num_s) +
' double precision, intent(out) :: '
'conc({}), y_N, mw_avg, pres\n'.format(num_s) +
'\n'
)
elif lang == 'matlab':
line += ('function conc = eval_conc_rho (temp, rho, mass_frac, y_N, '
'mw_avg, pres, conc)\n\n'
)
file.write(line)
# Get mass fraction of last species
file.write(' // mass fraction of final species\n')
line = ' *y_N = 1.0 - ('
isfirst = True
for isp in range(len(specs[:-1])):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += utils.get_array(lang, 'y', isp)
isfirst = False
line += ')'
file.write(line + utils.line_end[lang])
# calculation of mw avg
line = ' *mw_avg = '
isfirst = True
for isp, sp in enumerate(specs[:-1]):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += '(' + utils.get_array(lang, 'y', isp) + ' * {:.16e})'.format(1.0 / sp.mw)
isfirst = False
line += ' + ((*y_N) * {:.16e})'.format(1.0 / specs[-1].mw)
line += utils.line_end[lang]
file.write(line)
file.write(' *mw_avg = 1.0 / *mw_avg;\n')
# calculation of pressure
file.write(' // pressure\n')
line = ' *pres = rho * {:.8e} * T / (*mw_avg)'.format(chem.RU)
file.write(line + utils.line_end[lang])
# calculation of species molar concentrations
# loop through species
for isp, sp in enumerate(specs[:-1]):
line = utils.line_start + utils.get_array(lang, 'conc', isp)
line += ' = '
line += 'rho * ' + utils.get_array(lang, 'y', isp)
line += ' * {:.16e}'.format(1.0 / sp.mw) + utils.line_end[lang]
file.write(line)
line = utils.line_start + utils.get_array(lang, 'conc', len(specs) - 1)
line += ' = rho * (*y_N) * '.format(len(specs) - 1)
line += '{:.16e}'.format(1.0 / specs[-1].mw) + utils.line_end[lang]
file.write(line)
file.write('\n')
if lang in ['c', 'cuda']:
file.write('} // end eval_conc\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_conc\n\n')
elif lang == 'matlab':
file.write('end\n\n')
######################
# enthalpy subroutine
######################
line = pre
if lang in ['c', 'cuda']:
line += 'void eval_h (const {0}{1} T, {0} * {2} h) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang])
elif lang == 'fortran':
line += ('subroutine eval_h (T, h)\n\n'
# fortran needs type declarations
' implicit none\n'
' double precision, intent(in) :: T\n'
' double precision, intent(out) :: h({})\n'.format(num_s) +
'\n'
)
elif lang == 'matlab':
line += 'function h = eval_h (T)\n\n'
file.write(line)
# loop through species
for isp, sp in enumerate(specs):
line = ' if (T <= {:})'.format(sp.Trange[1])
if lang in ['c', 'cuda']:
line += ' {\n'
elif lang == 'fortran':
line += ' then\n'
elif lang == 'matlab':
line += '\n'
file.write(line)
line = ' ' + utils.get_array(lang, 'h', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} + T * ('.format(sp.lo[5]) +
'{:.16e} + T * ('.format(sp.lo[0]) +
'{:.16e} + T * ('.format(sp.lo[1] / 2.0) +
'{:.16e} + T * ('.format(sp.lo[2] / 3.0) +
'{:.16e} + '.format(sp.lo[3] / 4.0) +
'{:.16e} * T)))))'.format(sp.lo[4] / 5.0) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' } else {\n')
elif lang in ['fortran', 'matlab']:
file.write(' else\n')
line = ' ' + utils.get_array(lang, 'h', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} + T * ('.format(sp.hi[5]) +
'{:.16e} + T * ('.format(sp.hi[0]) +
'{:.16e} + T * ('.format(sp.hi[1] / 2.0) +
'{:.16e} + T * ('.format(sp.hi[2] / 3.0) +
'{:.16e} + '.format(sp.hi[3] / 4.0) +
'{:.16e} * T)))))'.format(sp.hi[4] / 5.0) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' }\n\n')
elif lang == 'fortran':
file.write(' end if\n\n')
elif lang == 'matlab':
file.write(' end\n\n')
if lang in ['c', 'cuda']:
file.write('} // end eval_h\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_h\n\n')
elif lang == 'matlab':
file.write('end\n\n')
#################################
# internal energy subroutine
#################################
line = pre
if lang in ['c', 'cuda']:
line += 'void eval_u (const {0}{1} T, {0} * {2} u) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang])
elif lang == 'fortran':
line += ('subroutine eval_u (T, u)\n\n'
# fortran needs type declarations
' implicit none\n'
' double precision, intent(in) :: T\n'
' double precision, intent(out) :: u({})\n'.format(num_s) +
'\n')
elif lang == 'matlab':
line += 'function u = eval_u (T)\n\n'
file.write(line)
# loop through species
for isp, sp in enumerate(specs):
line = ' if (T <= {:})'.format(sp.Trange[1])
if lang in ['c', 'cuda']:
line += ' {\n'
elif lang == 'fortran':
line += ' then\n'
elif lang == 'matlab':
line += '\n'
file.write(line)
line = ' ' + utils.get_array(lang, 'u', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} + T * ('.format(sp.lo[5]) +
'{:.16e} - 1.0 + T * ('.format(sp.lo[0]) +
'{:.16e} + T * ('.format(sp.lo[1] / 2.0) +
'{:.16e} + T * ('.format(sp.lo[2] / 3.0) +
'{:.16e} + '.format(sp.lo[3] / 4.0) +
'{:.16e} * T)))))'.format(sp.lo[4] / 5.0) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' } else {\n')
elif lang in ['fortran', 'matlab']:
file.write(' else\n')
line = ' ' + utils.get_array(lang, 'u', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} + T * ('.format(sp.hi[5]) +
'{:.16e} - 1.0 + T * ('.format(sp.hi[0]) +
'{:.16e} + T * ('.format(sp.hi[1] / 2.0) +
'{:.16e} + T * ('.format(sp.hi[2] / 3.0) +
'{:.16e} + '.format(sp.hi[3] / 4.0) +
'{:.16e} * T)))))'.format(sp.hi[4] / 5.0) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' }\n\n')
elif lang == 'fortran':
file.write(' end if\n\n')
elif lang == 'matlab':
file.write(' end\n\n')
if lang in ['c', 'cuda']:
file.write('} // end eval_u\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_u\n\n')
elif lang == 'matlab':
file.write('end\n\n')
##################################
# cv subroutine
##################################
if lang in ['c', 'cuda']:
line = pre + 'void eval_cv (const {0}{1} T, {0} * {2} cv) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang])
elif lang == 'fortran':
line = ('subroutine eval_cv (T, cv)\n\n'
# fortran needs type declarations
' implicit none\n'
' double precision, intent(in) :: T\n'
' double precision, intent(out) :: cv({})\n'.format(num_s) +
'\n'
)
elif lang == 'matlab':
line = 'function cv = eval_cv (T)\n\n'
file.write(line)
# loop through species
for isp, sp in enumerate(specs):
line = ' if (T <= {:})'.format(sp.Trange[1])
if lang in ['c', 'cuda']:
line += ' {\n'
elif lang == 'fortran':
line += ' then\n'
elif lang == 'matlab':
line += '\n'
file.write(line)
line = ' ' + utils.get_array(lang, 'cv', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} - 1.0 + T * ('.format(sp.lo[0]) +
'{:.16e} + T * ('.format(sp.lo[1]) +
'{:.16e} + T * ('.format(sp.lo[2]) +
'{:.16e} + '.format(sp.lo[3]) +
'{:.16e} * T))))'.format(sp.lo[4]) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' } else {\n')
elif lang in ['fortran', 'matlab']:
file.write(' else\n')
line = ' ' + utils.get_array(lang, 'cv', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} - 1.0 + T * ('.format(sp.hi[0]) +
'{:.16e} + T * ('.format(sp.hi[1]) +
'{:.16e} + T * ('.format(sp.hi[2]) +
'{:.16e} + '.format(sp.hi[3]) +
'{:.16e} * T))))'.format(sp.hi[4]) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' }\n\n')
elif lang == 'fortran':
file.write(' end if\n\n')
elif lang == 'matlab':
file.write(' end\n\n')
if lang in ['c', 'cuda']:
file.write('} // end eval_cv\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_cv\n\n')
elif lang == 'matlab':
file.write('end\n\n')
###############################
# cp subroutine
###############################
if lang in ['c', 'cuda']:
line = pre + 'void eval_cp (const {0}{1} T, {0} * {2} cp) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang])
elif lang == 'fortran':
line = ('subroutine eval_cp (T, cp)\n\n'
# fortran needs type declarations
' implicit none\n'
' double precision, intent(in) :: T\n'
' double precision, intent(out) :: cp({})\n'.format(num_s) +
'\n'
)
elif lang == 'matlab':
line = 'function cp = eval_cp (T)\n\n'
file.write(line)
# loop through species
for isp, sp in enumerate(specs):
line = ' if (T <= {:})'.format(sp.Trange[1])
if lang in ['c', 'cuda']:
line += ' {\n'
elif lang == 'fortran':
line += ' then\n'
elif lang == 'matlab':
line += '\n'
file.write(line)
line = ' ' + utils.get_array(lang, 'cp', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} + T * ('.format(sp.lo[0]) +
'{:.16e} + T * ('.format(sp.lo[1]) +
'{:.16e} + T * ('.format(sp.lo[2]) +
'{:.16e} + '.format(sp.lo[3]) +
'{:.16e} * T))))'.format(sp.lo[4]) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' } else {\n')
elif lang in ['fortran', 'matlab']:
file.write(' else\n')
line = ' ' + utils.get_array(lang, 'cp', isp)
line += (' = {:.16e} * '.format(chem.RU / sp.mw) +
'({:.16e} + T * ('.format(sp.hi[0]) +
'{:.16e} + T * ('.format(sp.hi[1]) +
'{:.16e} + T * ('.format(sp.hi[2]) +
'{:.16e} + '.format(sp.hi[3]) +
'{:.16e} * T))))'.format(sp.hi[4]) +
utils.line_end[lang]
)
file.write(line)
if lang in ['c', 'cuda']:
file.write(' }\n\n')
elif lang == 'fortran':
file.write(' end if\n\n')
elif lang == 'matlab':
file.write(' end\n\n')
if lang in ['c', 'cuda']:
file.write('} // end eval_cp\n\n')
elif lang == 'fortran':
file.write('end subroutine eval_cp\n\n')
elif lang == 'matlab':
file.write('end\n\n')
file.close()
return
[docs]def write_derivs(path, lang, specs, reacs, specs_nonzero, auto_diff=False):
"""Writes derivative function file and header.
Parameters
----------
path : str
Path to build directory for file.
lang : {'c', 'cuda', 'fortran', 'matlab'}
Programming language.
specs : list of `SpecInfo`
List of species in the mechanism.
reacs : list of `ReacInfo`
List of reactions in the mechanism.
specs_nonzero : list of bool
List of `bool` indicating species with zero net production
auto_diff : bool, optional
If ``True``, generate files for Adept autodifferention library.
Returns
-------
None
"""
file_prefix = ''
double_type = 'double'
pres_ref = ''
if auto_diff:
file_prefix = 'ad_'
double_type = 'adouble'
pres_ref = '&'
pre = ''
if lang == 'cuda': pre = '__device__ '
# first write header file
file = open(os.path.join(path, file_prefix + 'dydt' +
utils.header_ext[lang]), 'w')
file.write('#ifndef DYDT_HEAD\n'
'#define DYDT_HEAD\n'
'\n'
'#include "header{}"\n'.format(utils.header_ext[lang]) +
'\n'
)
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
file.write('{0}void dydt (const double, const {1}{2}, '
'const {1} * {3}, {1} * {3}'.format(pre, double_type, pres_ref,
utils.restrict[lang]) +
('' if lang == 'c' else
', const mechanism_memory * {}'.format(utils.restrict[lang])) +
');\n'
'\n'
'#endif\n'
)
file.close()
filename = file_prefix + 'dydt' + utils.file_ext[lang]
file = open(os.path.join(path, filename), 'w')
file.write('#include "header{}"\n'.format(utils.header_ext[lang]))
file.write('#include "{0}chem_utils{1}"\n'
'#include "{0}rates{1}"\n'.format(file_prefix,
utils.header_ext[lang]))
if lang == 'cuda':
file.write('#include "gpu_memory.cuh"\n'
)
file.write('\n')
if auto_diff:
file.write('#include "adept.h"\n'
'using adept::adouble;\n')
##################################################################
# constant pressure
##################################################################
file.write('#if defined(CONP)\n\n')
line = (pre + 'void dydt (const double t, const {0}{1} pres, '
'const {0} * {2} y, {0} * {2} dy{3}) {{\n\n'.format(
double_type, pres_ref, utils.restrict[lang],
', const mechanism_memory * {} d_mem'.format(utils.restrict[lang])
if lang == 'cuda' else '')
)
file.write(line)
# calculation of species molar concentrations
file.write(' // species molar concentrations\n')
file.write((' {0} conc[{1}]'.format(double_type, len(specs)) if lang != 'cuda'
else ' double * {} conc = d_mem->conc'.format(utils.restrict[lang]))
+ utils.line_end[lang]
)
file.write(' {0} y_N;\n'.format(double_type))
file.write(' {0} mw_avg;\n'.format(double_type))
file.write(' {0} rho;\n'.format(double_type))
# Simply call subroutine
file.write(' eval_conc (' + utils.get_array(lang, 'y', 0) +
', pres, &' + (utils.get_array(lang, 'y', 1) if lang != 'cuda'
else 'y[GRID_DIM]') + ', '
'&y_N, &mw_avg, &rho, conc);\n\n'
)
# evaluate reaction rates
rev_reacs = [i for i, rxn in enumerate(reacs) if rxn.rev]
if lang == 'cuda':
file.write(' double * {} fwd_rates = d_mem->fwd_rates'.format(utils.restrict[lang])
+ utils.line_end[lang])
else:
file.write(' // local arrays holding reaction rates\n'
' {} fwd_rates[{}];\n'.format(double_type, len(reacs))
)
if rev_reacs and lang == 'cuda':
file.write(' double * {} rev_rates = d_mem->rev_rates'.format(utils.restrict[lang])
+ utils.line_end[lang])
elif rev_reacs:
file.write(' {} rev_rates[{}];\n'.format(double_type, len(rev_reacs)))
else:
file.write(' {}* rev_rates = 0;\n'.format(double_type))
cheb = False
if any(rxn.cheb for rxn in reacs) and lang == 'cuda':
cheb = True
file.write(' double * {} dot_prod = d_mem->dot_prod'.format(utils.restrict[lang])
+ utils.line_end[lang])
file.write(' eval_rxn_rates (' + utils.get_array(lang, 'y', 0) +
', pres, conc, fwd_rates, rev_rates{});\n\n'.format(', dot_prod'
if cheb else '')
)
# reaction pressure dependence
num_dep_reacs = sum([rxn.thd_body or rxn.pdep for rxn in reacs])
if num_dep_reacs > 0:
file.write(' // get pressure modifications to reaction rates\n')
if lang == 'cuda':
file.write(' double * {} pres_mod = d_mem->pres_mod'.format(utils.restrict[lang]) +
utils.line_end[lang])
else:
file.write(' {} pres_mod[{}];\n'.format(double_type, num_dep_reacs))
file.write(' get_rxn_pres_mod (' + utils.get_array(lang, 'y', 0) +
', pres, conc, pres_mod);\n'
)
else:
file.write(' {}* pres_mod = 0;\n'.format(double_type))
file.write('\n')
if lang == 'cuda':
file.write(' double * {} spec_rates = d_mem->spec_rates'.format(utils.restrict[lang]) +
utils.line_end[lang])
# species rate of change of molar concentration
file.write(' // evaluate species molar net production rates\n')
if lang != 'cuda':
file.write(' {} dy_N;\n'.format(double_type))
file.write(' eval_spec_rates (fwd_rates, rev_rates, pres_mod, ')
if lang == 'c':
file.write('&' + utils.get_array(lang, 'dy', 1) + ', &dy_N)')
elif lang == 'cuda':
file.write('spec_rates, &' + utils.get_array(lang, 'spec_rates', len(specs) - 1) +
')')
file.write(utils.line_end[lang])
# evaluate specific heat
file.write(' // local array holding constant pressure specific heat\n')
file.write((' {} cp[{}]'.format(double_type, len(specs)) if lang != 'cuda'
else ' double * {} cp = d_mem->cp'.format(utils.restrict[lang]))
+ utils.line_end[lang])
file.write(' eval_cp (' + utils.get_array(lang, 'y', 0) + ', cp)'
+ utils.line_end[lang] + '\n')
file.write(' // constant pressure mass-average specific heat\n')
line = ' {} cp_avg = '.format(double_type)
isfirst = True
for isp, sp in enumerate(specs[:-1]):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += '(' + utils.get_array(lang, 'cp', isp) + \
' * ' + utils.get_array(lang, 'y', isp + 1) + ')'
isfirst = False
if not isfirst: line += ' + '
line += '(' + utils.get_array(lang, 'cp', len(specs) - 1) + ' * y_N)'
file.write(line + utils.line_end[lang] + '\n')
file.write(' // local array for species enthalpies\n' +
(' {} h[{}]'.format(double_type, len(specs)) if lang != 'cuda'
else ' double * {} h = d_mem->h'.format(utils.restrict[lang]))
+ utils.line_end[lang])
file.write(' eval_h(' + utils.get_array(lang, 'y', 0) + ', h);\n')
# energy equation
file.write(' // rate of change of temperature\n')
line = (' ' + utils.get_array(lang, 'dy', 0) +
' = (-1.0 / (rho * cp_avg)) * ('
)
isfirst = True
for isp, sp in enumerate(specs):
if not specs_nonzero[isp]:
continue
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
if lang == 'c':
arr = utils.get_array(lang, 'dy', isp + 1) if isp < len(specs) - 1 else 'dy_N'
elif lang == 'cuda':
arr = utils.get_array(lang, 'spec_rates', isp)
line += ('(' + arr + ' * ' +
utils.get_array(lang, 'h', isp) + ' * {:.16e})'.format(sp.mw)
)
isfirst = False
line += ')' + utils.line_end[lang] + '\n'
file.write(line)
line = ''
# rate of change of species mass fractions
file.write(' // calculate rate of change of species mass fractions\n')
for isp, sp in enumerate(specs[:-1]):
if lang == 'c':
file.write(' ' + utils.get_array(lang, 'dy', isp + 1) +
' *= ({:.16e} / rho);\n'.format(sp.mw)
)
elif lang == 'cuda':
file.write(' ' + utils.get_array(lang, 'dy', isp + 1) +
' = ' + utils.get_array(lang, 'spec_rates', isp) +
' * ({:.16e} / rho);\n'.format(sp.mw)
)
file.write('\n')
file.write('} // end dydt\n\n')
##################################################################
# constant volume
##################################################################
file.write('#elif defined(CONV)\n\n')
file.write(pre + 'void dydt (const double t, const {0} rho, '
'const {0} * {1} y, {0} * {1} dy{2}) {{\n\n'.format(double_type,
utils.restrict[lang],
'' if lang != 'cuda' else
', mechanism_memory * {} d_mem'.format(utils.restrict[lang]))
)
# calculation of species molar concentrations
file.write(' // species molar concentrations\n')
file.write((' {0} conc[{1}]'.format(double_type, len(specs)) if lang != 'cuda'
else ' double * {} conc = d_mem->conc'.format(utils.restrict[lang]))
+ utils.line_end[lang]
)
file.write(' {} y_N;\n'.format(double_type))
file.write(' {} mw_avg;\n'.format(double_type))
file.write(' {} pres;\n'.format(double_type))
# Simply call subroutine
file.write(' eval_conc_rho (' + utils.get_array(lang, 'y', 0) +
'rho, &' + (utils.get_array(lang, 'y', 1) if lang != 'cuda'
else 'y[GRID_DIM]') + ', ' +
'&y_N, &mw_avg, &pres, conc);\n\n'
)
# evaluate reaction rates
rev_reacs = [i for i, rxn in enumerate(reacs) if rxn.rev]
if lang == 'cuda':
file.write(' double * {} fwd_rates = d_mem->fwd_rates'.format(utils.restrict[lang])
+ utils.line_end[lang])
else:
file.write(' // local arrays holding reaction rates\n'
' {} fwd_rates[{}];\n'.format(double_type, len(reacs))
)
if rev_reacs and lang == 'cuda':
file.write(' double * {} rev_rates = d_mem->rev_rates'.format(utils.restrict[lang])
+ utils.line_end[lang])
elif rev_reacs:
file.write(' {} rev_rates[{}];\n'.format(double_type, len(rev_reacs)))
else:
file.write(' {}* rev_rates = 0;\n'.format(double_type))
file.write(' eval_rxn_rates (' + utils.get_array(lang, 'y', 0) + ', '
'pres, conc, fwd_rates, rev_rates);\n\n'
)
# reaction pressure dependence
num_dep_reacs = sum([rxn.thd_body or rxn.pdep for rxn in reacs])
if num_dep_reacs > 0:
file.write(' // get pressure modifications to reaction rates\n')
if lang == 'cuda':
file.write(' double * {} pres_mod = d_mem->pres_mod'.format(utils.restrict[lang]) +
utils.line_end[lang])
else:
file.write(' {} pres_mod[{}];\n'.format(double_type, num_dep_reacs))
file.write(' get_rxn_pres_mod (' + utils.get_array(lang, 'y', 0) +
', pres, conc, pres_mod);\n'
)
else:
file.write(' {}* pres_mod = 0;\n'.format(double_type))
file.write('\n')
# species rate of change of molar concentration
file.write(' // evaluate species molar net production rates\n'
' {} dy_N;'.format(double_type) +
' eval_spec_rates (fwd_rates, rev_rates, pres_mod, ')
file.write('&' + utils.get_array(lang, 'dy', 1) if lang != 'cuda'
else '&dy[GRID_DIM]')
file.write(', &dy_N)' + utils.line_end[lang] + '\n')
# evaluate specific heat
file.write((' {} cv[{}]'.format(double_type, len(specs)) if lang != 'cuda'
else ' double * {} cv = d_mem->cp'.format(utils.restrict[lang]))
+ utils.line_end[lang])
file.write(' eval_cv(' + utils.get_array(lang, 'y', 0) + ', cv);\n\n')
file.write(' // constant volume mass-average specific heat\n')
line = ' {} cv_avg = '.format(double_type)
isfirst = True
for idx, sp in enumerate(specs[:-1]):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
line += ' + ' if not isfirst else ''
line += ('(' + utils.get_array(lang, 'cv', idx) + ' * ' +
utils.get_array(lang, 'y', idx + 1) + ')'
)
isfirst = False
line += '(' + utils.get_array(lang, 'cv', len(specs) - 1) + ' * y_N)'
file.write(line + utils.line_end[lang] + '\n')
# evaluate internal energy
file.write(' // local array for species internal energies\n' +
(' {} u[{}]'.format(double_type, len(specs)) if lang != 'cuda'
else ' double * {} u = d_mem->h'.format(utils.restrict[lang]))
+ utils.line_end[lang])
file.write(' eval_u (' + utils.get_array(lang, 'y', 0) + ', u);\n\n')
# energy equation
file.write(' // rate of change of temperature\n')
line = (' ' + utils.get_array(lang, 'dy', 0) +
' = (-1.0 / (rho * cv_avg)) * ('
)
isfirst = True
for isp, sp in enumerate(specs):
if not specs_nonzero[isp]:
continue
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
if lang == 'c':
arr = utils.get_array(lang, 'dy', isp + 1) if isp < len(specs) - 1 else 'dy_N'
elif lang == 'cuda':
arr = utils.get_array(lang, 'spec_rates', isp)
line += ('(' + arr + ' * ' +
utils.get_array(lang, 'u', isp) + ' * {:.16e})'.format(sp.mw)
)
isfirst = False
line += ')' + utils.line_end[lang] + '\n'
file.write(line)
# rate of change of species mass fractions
file.write(' // calculate rate of change of species mass fractions\n')
for isp, sp in enumerate(specs[:-1]):
if lang == 'c':
file.write(' ' + utils.get_array(lang, 'dy', isp + 1) +
' *= ({:.16e} / rho);\n'.format(sp.mw)
)
elif lang == 'cuda':
file.write(' ' + utils.get_array(lang, 'dy', isp + 1) +
' = ' + utils.get_array(lang, 'spec_rates', isp) +
' * ({:.16e} / rho);\n'.format(sp.mw)
)
file.write('\n')
file.write('} // end dydt\n\n')
file.write('#endif\n')
file.close()
return
[docs]def write_mass_mole(path, lang, specs):
"""Write files for mass/molar concentration and density conversion utility.
Parameters
----------
path : str
Path to build directory for file.
lang : {'c', 'cuda', 'fortran', 'matlab'}
Programming language.
specs : list of `SpecInfo`
List of species in mechanism.
Returns
-------
None
"""
# Create header file
if lang in ['c', 'cuda']:
arr_lang = 'c'
file = open(os.path.join(path, 'mass_mole{}'.format(
utils.header_ext[lang])), 'w')
file.write(
'#ifndef MASS_MOLE_HEAD\n'
'#define MASS_MOLE_HEAD\n'
'\n'
'#include "header{0}"\n'
'\n'
'void mole2mass (const double*, double*);\n'
'void mass2mole (const double*, double*);\n'
'double getDensity (const double, const double, const double*);\n'
'\n'
'#endif\n'.format(utils.header_ext[lang])
)
file.close()
# Open file; both C and CUDA programs use C file (only used on host)
filename = 'mass_mole' + utils.file_ext[lang]
file = open(os.path.join(path, filename), 'w')
if lang in ['c', 'cuda']:
file.write('#include "mass_mole{}"\n\n'.format(
utils.header_ext[lang]))
###################################################
# Documentation and function/subroutine initializaton for mole2mass
if lang in ['c', 'cuda']:
file.write('/** Function converting species mole fractions to '
'mass fractions.\n'
' *\n'
' * \param[in] X array of species mole fractions\n'
' * \param[out] Y array of species mass fractions\n'
' */\n'
'void mole2mass (const double * X, double * Y) {\n'
'\n'
)
elif lang == 'fortran':
file.write(
'!-----------------------------------------------------------------\n'
'!> Subroutine converting species mole fractions to mass fractions.\n'
'!! @param[in] X array of species mole fractions\n'
'!! @param[out] Y array of species mass fractions\n'
'!-----------------------------------------------------------------\n'
'subroutine mole2mass (X, Y)\n'
' implicit none\n'
' double, dimension(:), intent(in) :: X\n'
' double, dimension(:), intent(out) :: X\n'
' double :: mw_avg\n'
'\n'
)
file.write(' // mole fraction of final species\n')
file.write(utils.line_start + 'double X_N' + utils.line_end[lang])
line = ' X_N = 1.0 - ('
isfirst = True
for isp in range(len(specs) - 1):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += utils.get_array(arr_lang, 'X', isp)
isfirst = False
line += ')'
file.write(line + utils.line_end[lang])
# calculate molecular weight
if lang in ['c', 'cuda']:
file.write(' // average molecular weight\n'
' double mw_avg = 0.0;\n'
)
for isp in range(len(specs) - 1):
sp = specs[isp]
file.write(' mw_avg += ' + utils.get_array(arr_lang, 'X', isp) +
' * {:.16e};\n'.format(sp.mw)
)
file.write(utils.line_start + 'mw_avg += X_N * ' +
'{:.16e};\n'.format(specs[-1].mw)
)
elif lang == 'fortran':
file.write(' ! average molecular weight\n'
' mw_avg = 0.0\n'
)
for isp, sp in enumerate(specs):
file.write(' mw_avg = mw_avg + '
'X({}) * '.format(isp + 1) +
'{:.16e}\n'.format(sp.mw)
)
file.write('\n')
# calculate mass fractions
if lang in ['c', 'cuda']:
file.write(' // calculate mass fractions\n')
for isp in range(len(specs) - 1):
sp = specs[isp]
file.write(' ' + utils.get_array(arr_lang, 'Y', isp) +
' = ' +
utils.get_array(arr_lang, 'X', isp) +
' * {:.16e} / mw_avg;\n'.format(sp.mw)
)
file.write('\n'
'} // end mole2mass\n'
'\n'
)
elif lang == 'fortran':
file.write(' ! calculate mass fractions\n')
for isp, sp in enumerate(specs):
file.write(' Y({0}) = X({0}) * '.format(isp + 1) +
'{:.16e} / mw_avg\n'.format(sp.mw)
)
file.write('\n'
'end subroutine mole2mass\n'
'\n'
)
################################
# Documentation and function/subroutine initialization for mass2mole
if lang in ['c', 'cuda']:
file.write('/** Function converting species mass fractions to mole '
'fractions.\n'
' *\n'
' * \param[in] Y array of species mass fractions\n'
' * \param[out] X array of species mole fractions\n'
' */\n'
'void mass2mole (const double * Y, double * X) {\n'
'\n'
)
elif lang == 'fortran':
file.write('!-------------------------------------------------------'
'----------\n'
'!> Subroutine converting species mass fractions to mole '
'fractions.\n'
'!! @param[in] Y array of species mass fractions\n'
'!! @param[out] X array of species mole fractions\n'
'!-------------------------------------------------------'
'----------\n'
'subroutine mass2mole (Y, X)\n'
' implicit none\n'
' double, dimension(:), intent(in) :: Y\n'
' double, dimension(:), intent(out) :: X\n'
' double :: mw_avg\n'
'\n'
)
# calculate Y_N
file.write(' // mass fraction of final species\n')
file.write(utils.line_start + 'double Y_N' + utils.line_end[lang])
line = ' Y_N = 1.0 - ('
isfirst = True
for isp in range(len(specs) - 1):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += utils.get_array(arr_lang, 'Y', isp)
isfirst = False
line += ')'
file.write(line + utils.line_end[lang])
# calculate average molecular weight
if lang in ['c', 'cuda']:
file.write(' // average molecular weight\n')
file.write(' double mw_avg = 0.0;\n')
for isp in range(len(specs) - 1):
file.write(' mw_avg += ' + utils.get_array(arr_lang, 'Y', isp) +
' / {:.16e};\n'.format(specs[isp].mw)
)
file.write(' mw_avg += Y_N / ' +
'{:.16e};\n'.format(specs[-1].mw)
)
file.write(' mw_avg = 1.0 / mw_avg;\n')
elif lang == 'fortran':
file.write(' ! average molecular weight\n')
file.write(' mw_avg = 0.0\n')
for isp, sp in enumerate(specs):
file.write(' mw_avg = mw_avg + '
'Y({}) / '.format(isp + 1) +
'{:.16e}\n'.format(sp.mw)
)
file.write('\n')
# calculate mole fractions
if lang in ['c', 'cuda']:
file.write(' // calculate mole fractions\n')
for isp in range(len(specs) - 1):
file.write(' ' + utils.get_array(arr_lang, 'X', isp)
+ ' = ' +
utils.get_array(arr_lang, 'Y', isp) +
' * mw_avg / {:.16e};\n'.format(specs[isp].mw)
)
file.write('\n'
'} // end mass2mole\n'
'\n'
)
elif lang == 'fortran':
file.write(' ! calculate mass fractions\n')
for isp, sp in enumerate(specs):
file.write(' X({0}) = Y({0}) * '.format(isp + 1) +
'mw_avg / {:.16e}\n'.format(sp.mw)
)
file.write('\n'
'end subroutine mass2mole\n'
'\n'
)
###############################
# Documentation and subroutine/function initialization for getDensity
if lang in ['c', 'cuda']:
file.write('/** Function calculating density from mole fractions.\n'
' *\n'
' * \param[in] temp temperature\n'
' * \param[in] pres pressure\n'
' * \param[in] X array of species mole fractions\n'
r' * \return rho mixture mass density' + '\n'
' */\n'
'double getDensity (const double temp, const double '
'pres, '
'const double * X) {\n'
'\n'
)
elif lang == 'fortran':
file.write('!-------------------------------------------------------'
'----------\n'
'!> Function calculating density from mole fractions.\n'
'!! @param[in] temp temperature\n'
'!! @param[in] pres pressure\n'
'!! @param[in] X array of species mole fractions\n'
'!! @return rho mixture mass density' + '\n'
'!-------------------------------------------------------'
'----------\n'
'function mass2mole (temp, pres, X) result(rho)\n'
' implicit none\n'
' double, intent(in) :: temp, pres\n'
' double, dimension(:), intent(in) :: X\n'
' double :: mw_avg, rho\n'
'\n'
)
file.write(' // mole fraction of final species\n')
file.write(utils.line_start + 'double X_N' + utils.line_end[lang])
line = ' X_N = 1.0 - ('
isfirst = True
for isp in range(len(specs) - 1):
if len(line) > 70:
line += '\n'
file.write(line)
line = ' '
if not isfirst: line += ' + '
line += utils.get_array(arr_lang, 'X', isp)
isfirst = False
line += ')'
file.write(line + utils.line_end[lang])
# get molecular weight
if lang in ['c', 'cuda']:
file.write(' // average molecular weight\n'
' double mw_avg = 0.0;\n'
)
for isp in range(len(specs) - 1):
file.write(' mw_avg += ' + utils.get_array(arr_lang, 'X', isp) +
' * {:.16e};\n'.format(specs[isp].mw)
)
file.write(utils.line_start + 'mw_avg += X_N * ' +
'{:.16e};\n'.format(specs[-1].mw))
file.write('\n')
elif lang == 'fortran':
file.write(' ! average molecular weight\n'
' mw_avg = 0.0\n'
)
for isp, sp in enumerate(specs):
file.write(' mw_avg = mw_avg + '
'X({}) * '.format(isp + 1) +
'{:.16e}\n'.format(sp.mw)
)
file.write('\n')
# calculate density
if lang in ['c', 'cuda']:
file.write(' return pres * mw_avg / ({:.8e} * temp);'.format(chem.RU))
file.write('\n')
else:
line = ' rho = pres * mw_avg / ({:.8e} * temp)'.format(chem.RU)
line += utils.line_end[lang]
file.write(line)
if lang in ['c', 'cuda']:
file.write('} // end getDensity\n\n')
elif lang == 'fortran':
file.write('end function getDensity\n\n')
file.close()
return
if __name__ == "__main__":
from . import create_jacobian
args = utils.get_parser()
create_jacobian(lang=args.lang,
mech_name=args.input,
therm_name=args.thermo,
optimize_cache=args.cache_optimizer,
initial_state=args.initial_conditions,
num_blocks=args.num_blocks,
num_threads=args.num_threads,
no_shared=args.no_shared,
L1_preferred=args.L1_preferred,
multi_thread=args.multi_thread,
force_optimize=args.force_optimize,
build_path=args.build_path,
skip_jac=True,
last_spec=args.last_species,
auto_diff=args.auto_diff
)