Source code for pyjac.utils

# -*- coding: utf-8 -*-
"""Module containing utility functions.
"""

# Standard libraries
import os
import errno
from math import log10, floor
from argparse import ArgumentParser

__all__ = ['line_start', 'comment', 'langs', 'file_ext', 'restrict',
           'header_ext', 'line_end', 'exp_10_fun', 'array_chars',
           'get_species_mappings', 'get_nu', 'read_str_num', 'split_str',
           'create_dir', 'get_array', 'get_index', 'reassign_species_lists',
           'is_integer', 'get_parser'
           ]

line_start = '  '
comment = dict(c='//', cuda='//',
               fortran='!', matlab='%'
               )
"""dict: comment characters for each language"""

langs = ['c', 'cuda', 'fortran', 'matlab']
"""list(`str`): list of supported languages"""

file_ext = dict(c='.c', cuda='.cu', fortran='.f90', matlab='.m')
"""dict: source code file extensions based on language"""

restrict = {'c' : '__restrict__',
            'cuda' : '__restrict__'}
"""dict: language-dependent keyword for restrict"""

header_ext = dict(c='.h', cuda='.cuh')
"""dict: header extensions based on language"""

line_end = dict(c=';\n', cuda=';\n',
                fortran='\n', matlab=';\n'
                )
"""dict: line endings dependent on language"""

exp_10_fun = dict(c="pow(10.0, ", cuda='exp10(',
                  fortran='exp(log(10) * ', matlab='exp(log(10.0) * '
                  )
"""dict: exp10 functions for various languages"""

array_chars = dict(c="[{}]", cuda="[INDEX({})]",
                   fortran="({})", matlab="({})"
                   )
"""dict: the characters to format an index into an array per language"""

# if false, zero values will be assumed to have been set previously (by memset etc.)
# and can be skipped, to increase efficiency

[docs]def get_species_mappings(num_specs, last_species): """ Maps species indices around species moved to last position. Parameters ---------- num_specs : int Number of species. last_species : int Index of species being moved to end of system. Returns ------- fwd_species_map : list of `int` List of original indices in new order back_species_map : list of `int` List of new indicies in original order """ fwd_species_map = list(range(num_specs)) back_species_map = list(range(num_specs)) #in the forward mapping process #last_species -> end #all entries after last_species are reduced by one back_species_map[last_species + 1:] = back_species_map[last_species:-1] back_species_map[last_species] = num_specs - 1 #in the backwards mapping #end -> last_species #all entries with value >= last_species are increased by one ind = fwd_species_map.index(last_species) fwd_species_map[ind:-1] = fwd_species_map[ind + 1:] fwd_species_map[-1] = last_species return fwd_species_map, back_species_map
[docs]def get_nu(isp, rxn): """Returns the net nu of species isp for the reaction rxn Parameters ---------- isp : int Species index rxn : `ReacInfo` Reaction Returns ------- nu : int Overall stoichiometric coefficient of species ``isp`` in reaction ``rxn`` """ if isp in rxn.prod and isp in rxn.reac: nu = (rxn.prod_nu[rxn.prod.index(isp)] - rxn.reac_nu[rxn.reac.index(isp)]) # check if net production zero if nu == 0: return 0 elif isp in rxn.prod: nu = rxn.prod_nu[rxn.prod.index(isp)] elif isp in rxn.reac: nu = -rxn.reac_nu[rxn.reac.index(isp)] else: # doesn't participate in reaction return 0 return nu
[docs]def read_str_num(string, sep=None): """Returns a list of floats pulled from a string. Delimiter is optional; if not specified, uses whitespace. Parameters ---------- string : str String to be parsed. sep : str, optional Delimiter (default is None, which means consecutive whitespace). Returns ------- list of `float` Floats separated by ``sep`` in ``string``. """ # separate string into space-delimited strings of numbers num_str = string.split(sep) return [float(n) for n in num_str]
[docs]def split_str(seq, length): """Separate a string seq into length-sized pieces. Parameters ---------- seq : str String containing sequence of smaller strings of constant length. length : int Length of individual sequences. Returns ------- list of `str` List of strings of length ``length`` from ``seq``. """ return [seq[i: i + length] for i in range(0, len(seq), length)]
[docs]def create_dir(path): """Creates a new directory based on input path. No error if path already exists, but other error is reported. Parameters ---------- path : str Path of directory to be created Returns ------- None """ try: os.makedirs(path) except OSError as exception: if exception.errno != errno.EEXIST: raise
[docs]def get_array(lang, name, index, twod=None): """ Given a language and an index, returns the proper string index formatted into the appropriate array characters (e.g., [] or ()). Parameters ---------- lang : str One of the accepted languages name : str The name of the array index : int The index to format twod : int, optional If not ``None`` and the lang is 'fortan' or 'matlab' this will be formatted as a second index in the array. Returns ------- name : str String with indexed array. """ if index is None: #a dummy call to see if it's in shared memory return name if lang in ['fortran', 'matlab']: if twod is not None: return name +'({}, {})'.format(index + 1, twod + 1) return name + array_chars[lang].format(index + 1) return name + array_chars[lang].format(index)
[docs]def get_index(lang, index): """ Given an integer index this function will return the proper string version of the index based on the language and other considerations Parameters ---------- lang : str One of the supported languages, {'c', 'cuda', 'fortran', 'matlab'} index : int Returns ------- str The string corresponding to the correct index to be formatted into the code """ retval = None if lang in ['fortran', 'matlab']: return str(index + 1) if lang in ['c', 'cuda']: return str(index)
[docs]def reassign_species_lists(reacs, specs): """ Given a list of `ReacInfo`, and `SpecInfo`, this method will update the `ReacInfo` reactants / products / third body list to integers representing the species' index in the list. Parameters ---------- reacs : list of `ReacInfo` List of reactions to be updated. specs : list of `SpecInfo` List of species Returns ------- None """ species_map = {sp.name: i for i, sp in enumerate(specs)} for rxn in reacs: rxn.reac = [species_map[sp] for sp in rxn.reac] rxn.prod = [species_map[sp] for sp in rxn.prod] rxn.thd_body_eff = [(species_map[thd[0]], thd[1]) for thd in rxn.thd_body_eff] if rxn.pdep_sp != '': rxn.pdep_sp = species_map[rxn.pdep_sp] else: rxn.pdep_sp = None
[docs]def is_integer(val): """Returns `True` if argument is an integer or whole number. Parameters ---------- val : int, float Value to be checked. Returns ------- bool ``True`` if ``val`` is `int` or whole number (if `float`). """ try: return val.is_integer() except: if isinstance(val, int): return True #last ditch effort try: return int(val) == float(val) except: return False
[docs]def get_parser(): """ Parameters ---------- None Returns ------- args : `argparse.Namespace` Command line arguments for running pyJac. """ import multiprocessing # command line arguments parser = ArgumentParser(description='pyJac: Generates source code ' 'for analytical chemical ' 'Jacobians.' ) parser.add_argument('-l', '--lang', type=str, choices=langs, required=True, help='Programming language for output source files.' ) parser.add_argument('-i', '--input', type=str, required=True, help='Input mechanism filename (e.g., mech.dat).' ) parser.add_argument('-t', '--thermo', type=str, default=None, help='Thermodynamic database filename (e.g., ' 'therm.dat), or nothing if in mechanism.' ) parser.add_argument('-ic', '--initial-conditions', type=str, dest='initial_conditions', default='', required=False, help='A comma separated list of initial initial ' 'conditions to set in the ' 'set_same_initial_conditions method.\n' ' Expected Form: T,P,Species1=...,Species2=...,...\n' ' Temperature in K\n' ' Pressure in Atm\n' ' Species in moles' ) # cuda specific parser.add_argument('-co', '--cache-optimizer', dest='cache_optimizer', action='store_true', default=False, help='Attempt to optimize cache store/loading ' 'via use of a greedy selection algorithm. (Experimental)' ) parser.add_argument('-nosmem', '--no-shared-memory', dest='no_shared', action='store_true', default=False, help='Use this option to turn off attempted shared ' 'memory acceleration for CUDA.' ) parser.add_argument('-pshare', '--prefer-shared', dest='L1_preferred', action='store_false', default=True, help='Use this option to allocate more space for ' 'shared memory than the L1 cache for CUDA ' '(not recommended).' ) parser.add_argument('-nb', '--num-blocks', type=int, dest='num_blocks', default=8, required=False, help='The target number of blocks / sm for CUDA.' ) parser.add_argument('-nt', '--num-threads', type=int, dest='num_threads', default=64, required=False, help='The target number of threads / block for CUDA.' ) parser.add_argument('-mt', '--multi-threaded', type=int, dest='multi_thread', default=multiprocessing.cpu_count(), required=False, help='The number of threads to use during the ' 'optimization process.' ) parser.add_argument('-fopt', '--force-optimize', dest='force_optimize', action='store_true', default=False, help='Use this option to force a reoptimization of ' 'the mechanism (usually only happens when ' 'generating for a different mechanism).' ) parser.add_argument('-b', '--build_path', required=False, default='./out/', help='The folder to generate the Jacobian and rate subroutines in.' ) parser.add_argument('-ls', '--last_species', required=False, type=str, default=None, help='The name of the species to set as the last in ' 'the mechanism. If not specifed, defaults to ' 'the first of N2, AR, and HE in the mechanism.' ) parser.add_argument('-ad', '--auto_diff', default=False, action='store_true', help='Use this option to generate file for use with the ' 'Adept autodifferentiation library.') parser.add_argument('-sj', '--skip_jac', required=False, default=False, action='store_true', help='If specified, this option turns off Jacobian generation ' '(only rate subs are generated)') args = parser.parse_args() return args