def _split_reagent_string(rgt_str): """ Parses out the names of all the species given in a string with the chemical equation within the reactions block. :param rgt_str: string with the reaction chemical equation :type rgt_str: str :return rgts: names of the species in the reaction :type rgts: list(str) """ def _interpret_reagent_count(rgt_cnt_str): """ Count the species in a string containing one side of a chemical equation. :param rgt_cnt_str: string of one side of chemcial equation :type rgt_cnt_str: str :return: rgts: names of species from string :rtype: list(str) """ _pattern = (app.STRING_START + app.capturing(app.maybe(app.DIGIT)) + app.capturing(app.one_or_more(app.NONSPACE))) cnt, rgt = apf.first_capture(_pattern, rgt_cnt_str) cnt = int(cnt) if cnt else 1 rgts = (rgt, ) * cnt return rgts rgt_str = apf.remove(app.LINESPACES, rgt_str) rgt_str = apf.remove(CHEMKIN_PAREN_PLUS_EM, rgt_str) rgt_str = apf.remove(CHEMKIN_PLUS_EM, rgt_str) pattern = app.PLUS + app.not_followed_by(app.PLUS) rgt_cnt_strs = apf.split(pattern, rgt_str) rgts = tuple(itertools.chain(*map(_interpret_reagent_count, rgt_cnt_strs))) return rgts
def get_default_temp_limits(block_str): """ Gets the default temperatures from the header of a thermo block str """ block_str = apf.remove(COMMENTS_PATTERN, block_str) line_lst = list(apf.split_lines(block_str)) # Loop over each line for line in line_lst: try: # Note that this order is different than # that in the individual thermo entries low_limit = float(line[0:10]) midpoint = float(line[10:20]) high_limit = float(line[20:30]) break except ValueError: pass # Check if the first thermo entry has been reached if len(line) >= 80 and line[79] == '1': low_limit, midpoint, high_limit = None, None, None # Note that this order is different than # that in the individual thermo entries default_temp_limits = [low_limit, midpoint, high_limit] return default_temp_limits
def third_body(rxn_str): """ Parses the data string for a reaction in the reactions block for the line containing the chemical equation in order to read the names of the third body collider if present :param rxn_str: raw Chemkin string for a single reaction :type rxn_str: str :return trd_body: names of the colliders and corresponding fraction :rtype: tuple(str) """ pattern = _first_line_pattern(rct_ptt=app.capturing(SPECIES_NAMES_PATTERN), prd_ptt=SPECIES_NAMES_PATTERN, param_ptt=app.maybe(COEFF_PATTERN)) rgt_str = apf.first_capture(pattern, rxn_str) rgt_str = apf.remove(app.LINESPACES, rgt_str) rgt_split_paren = apf.split(CHEMKIN_PAREN_PLUS, rgt_str) rgt_split_plus = apf.split(app.PLUS, rgt_str) if len(rgt_split_paren) > 1: trd_body = '(+' + apf.split(CHEMKIN_PAREN_CLOSE, rgt_split_paren[1])[0] + ')' elif 'M' in rgt_split_plus: trd_body = '+M' else: trd_body = None trd_body = (trd_body, ) return trd_body
def clean_up_whitespace(string): """ remove leading spaces, trailing spaces, and empty lines from a string """ empty_line = app.LINE_START + app.maybe(app.LINESPACES) + app.NEWLINE trailing_spaces = app.LINESPACES + app.LINE_END leading_spaces = app.LINE_START + app.LINESPACES pattern = app.one_of_these([empty_line, trailing_spaces, leading_spaces]) return apf.remove(pattern, string)
def _split_reagent_string(rgt_str): def _interpret_reagent_count(rgt_cnt_str): _pattern = (app.STRING_START + app.capturing(app.maybe(app.DIGIT)) + app.capturing(app.one_or_more(app.NONSPACE))) cnt, rgt = apf.first_capture(_pattern, rgt_cnt_str) cnt = int(cnt) if cnt else 1 rgts = (rgt, ) * cnt return rgts rgt_str = apf.remove(app.LINESPACES, rgt_str) rgt_str = apf.remove(CHEMKIN_PAREN_PLUS_EM, rgt_str) rgt_str = apf.remove(CHEMKIN_PLUS_EM, rgt_str) pattern = app.PLUS + app.not_followed_by(app.PLUS) rgt_cnt_strs = apf.split(pattern, rgt_str) rgts = tuple(itertools.chain(*map(_interpret_reagent_count, rgt_cnt_strs))) return rgts
def remove_empty_lines(string): """ Removes empty lines from a input string. :param string: string to remove trailing whitespace :type string: str :rtype: str """ empty_line = app.LINE_START + app.maybe(app.LINESPACES) + app.NEWLINE return apf.remove(empty_line, string)
def remove_comment_lines(string, delim_pattern): """ Remove comment lines marked by a delimiter pattern. :param string: string to remove comments :type string: str :param delim_pattern: pattern of delimiter to identify comment lines :type delim_pattern: str :rtype: str """ pattern = delim_pattern + app.zero_or_more(app.NONNEWLINE) return apf.remove(pattern, string)
def remove_trail_whitespace(string): """ Removes trailing spaces and empty lines from a input string. :param string: string to remove trailing whitespace :type string: str :rtype: str """ empty_line = app.LINE_START + app.maybe(app.LINESPACES) + app.NEWLINE trailing_spaces = app.LINESPACES + app.LINE_END pattern = app.one_of_these([empty_line, trailing_spaces]) return apf.remove(pattern, string)
def remove_whitespace_from_string(string): """ Removes leading spaces, trailing spaces, and empty lines from a string. :param string: string to clean up :type string: str :rtype: str """ empty_line = app.LINE_START + app.maybe(app.LINESPACES) + app.NEWLINE trailing_spaces = app.LINESPACES + app.LINE_END leading_spaces = app.LINE_START + app.LINESPACES pattern = app.one_of_these([empty_line, trailing_spaces, leading_spaces]) return apf.remove(pattern, string)
def create_entry_list(block_str, add_spaces=True): """ Creates a list with each line of the thermo block_str as an entry in that list :param block_str: string for thermo block :return line_lst: list of strs for each line """ block_str = apf.remove(COMMENTS_PATTERN, block_str) # print('type of block_str\n', type(block_str)) line_lst = list(apf.split_lines(block_str)) #line_lst = list(block_str.splitlines) # print(type(line_lst)) # Get the indices of the entry header lines header_idxs = [] for idx, line in enumerate(line_lst): if len(line) >= 80 and line[79] == '1': header_idxs.append(idx) if not header_idxs: raise ImportError( 'No thermo headers in the file could be read. A "1" in column 80 marks this.' ) header_idxs.append( len(line_lst)) # adds an entry to mark the end of the block_str # Check that there are at least 4 lines in each entry for idx, difference in enumerate(np.diff(header_idxs)): assert difference >= 4, ( f'There are less than 4 lines for this thermo entry: \n{line_lst[header_idxs[idx]]}' ) # Put together the thermo entries into a list of tuples entry_lst = [] for idx in range(len(header_idxs) - 1): # skips the last entry entry = line_lst[header_idxs[idx]:header_idxs[idx + 1]] entry_lst.append(entry) # Fix the problem of the missing spaces at the beginning of lines without negative signs entry_lst = fix_lines(entry_lst) return entry_lst
def cheb(rxn_str): """ Parses the data string for a reaction in the reactions block for the lines containing the Chebyshevs fitting parameters, then reads the parameters from these lines. :param rxn_str: raw Chemkin string for a single reaction :type rxn_str: str :return params: Chebyshev fitting parameters :rtype: dict[param: value] """ original_rxn_str = rxn_str rxn_str = apf.remove(COMMENTS_PATTERN, rxn_str) tcheb_pattern = ('TCHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.zero_or_more(app.SPACE) + app.escape('/')) pcheb_pattern = ('PCHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.zero_or_more(app.SPACE) + app.escape('/')) cheb_pattern = (app.not_preceded_by(app.one_of_these(['T', 'P'])) + 'CHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.capturing(app.one_or_more(app.WILDCARD2)) + app.escape('/')) cheb_params_raw = apf.all_captures(cheb_pattern, rxn_str) if cheb_params_raw: params = {} # Get temp and pressure limits or use Chemkin defaults if non-existent cheb_temps = apf.first_capture(tcheb_pattern, rxn_str) cheb_pressures = apf.first_capture(pcheb_pattern, rxn_str) if cheb_temps is None: cheb_temps = ('300.00', '2500.00') print('No Chebyshev temperature limits specified' + ' for the below reaction.' + f' Assuming 300 and 2500 K. \n \n {original_rxn_str}\n') if cheb_pressures is None: cheb_pressures = ('0.001', '100.00') print('No Chebyshev pressure limits specified' + ' for the below reaction.' + f' Assuming 0.001 and 100 atm. \n \n {original_rxn_str}\n') # Get all the numbers from the CHEB parameters cheb_params = [] for cheb_line in cheb_params_raw: cheb_params.extend(cheb_line.split()) # Get alpha dimensions N and M, which are the first two CHEB entries cheb_n = int(float(cheb_params[0])) cheb_m = int(float(cheb_params[1])) # Start on third value (after N and M) and get all polynomial coeffs coeffs = [] for idx, coeff in enumerate(cheb_params[2:]): # extra coefficients are allowed but ignored if idx + 1 > (cheb_n * cheb_m): break coeffs.append(coeff) assert len(coeffs) == (cheb_n * cheb_m), ( f'For the below reaction, there should be {cheb_n*cheb_m}' + ' Chebyshev polynomial' + f' coefficients, but there are only {len(coeffs)}.' + f' \n \n {original_rxn_str}\n') alpha = np.array(list(map(float, coeffs))) params['tlim'] = tuple(float(val) for val in cheb_temps) params['plim'] = tuple(float(val) for val in cheb_pressures) params['alpha'] = alpha.reshape([cheb_n, cheb_m]) else: params = None return params
def chebyshev_parameters(rxn_dstr, a_units='moles'): """ Parses the data string for a reaction in the reactions block for the lines containing the Chebyshevs fitting parameters, then reads the parameters from these lines. :param rxn_dstr: data string for species in reaction block :type rxn_dstr: str :return params: Chebyshev fitting parameters :rtype: dict[param: value] """ original_rxn_dstr = rxn_dstr rxn_dstr = apf.remove(COMMENTS_PATTERN, rxn_dstr) tcheb_pattern = ('TCHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.zero_or_more(app.SPACE) + app.escape('/')) pcheb_pattern = ('PCHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.zero_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.one_or_more(app.SPACE) + app.capturing(app.NUMBER) + app.zero_or_more(app.SPACE) + app.escape('/')) cheb_pattern = (app.not_preceded_by(app.one_of_these(['T', 'P'])) + 'CHEB' + app.zero_or_more(app.SPACE) + app.escape('/') + app.capturing(app.one_or_more(app.WILDCARD2)) + app.escape('/')) cheb_params_raw = apf.all_captures(cheb_pattern, rxn_dstr) if cheb_params_raw: params = {} # Get the temp and pressure limits; add the Chemkin default values if they don't exist cheb_temps = apf.first_capture(tcheb_pattern, rxn_dstr) cheb_pressures = apf.first_capture(pcheb_pattern, rxn_dstr) if cheb_temps is None: cheb_temps = ('300.00', '2500.00') print( f'No Chebyshev temperature limits specified for the below reaction. Assuming 300 and 2500 K. \n \n {original_rxn_dstr}\n' ) if cheb_pressures is None: cheb_pressures = ('0.001', '100.00') print( f'No Chebyshev pressure limits specified for the below reaction. Assuming 0.001 and 100 atm. \n \n {original_rxn_dstr}\n' ) # Get all the numbers from the CHEB parameters cheb_params = [] for cheb_line in cheb_params_raw: cheb_params.extend(cheb_line.split()) # Get the cheb array dimensions N and M, which are the first two entries of the CHEB params cheb_n = int( math.floor(float(cheb_params[0])) ) # rounds down to match the Chemkin parser, although it should be an integer already cheb_m = int(math.floor(float(cheb_params[1]))) # Start on the third value (after N and M) and get all the polynomial coefficients coeffs = [] for idx, coeff in enumerate(cheb_params[2:]): if idx + 1 > ( cheb_n * cheb_m ): # there are allowed to be extra coefficients, but just ignore them break coeffs.append(coeff) assert len(coeffs) == (cheb_n * cheb_m), ( f'For the below reaction, there should be {cheb_n*cheb_m} Chebyshev polynomial coefficients, but there are only {len(coeffs)}. \n \n {original_rxn_dstr}\n' ) alpha = numpy.array(list(map(float, coeffs))) params['t_limits'] = [float(val) for val in cheb_temps] params['p_limits'] = [float(val) for val in cheb_pressures] params['alpha_elm'] = alpha.reshape([cheb_n, cheb_m]) params['a_units'] = a_units else: params = None return params
def remove_line_comments(string, delim_pattern): """ remove line comments marked by a delimiter pattern """ pattern = delim_pattern + app.zero_or_more(app.NONNEWLINE) return apf.remove(pattern, string)