/** * @file CKParser.cpp * */ // Copyright 2001 California Institute of Technology #include #include #include #include #include #include "CKParser.h" #include "ckr_utils.h" #include "writelog.h" #include #include using namespace std; namespace ckr { static string int2s(int n, std::string fmt="%d") { char buf[30]; sprintf(buf, fmt.c_str(), n); return string(buf); } /// Exception class for syntax errors. CK_SyntaxError::CK_SyntaxError(std::ostream& f, const std::string& s, int linenum) : m_out(f) { m_msg += "Syntax error: " + s; if (linenum > 0) { m_msg += " (line " + int2s(linenum) + ")\n"; } } static int parseGroupString(std::string str, std::vector& esyms, vector_int& result); /** * Throw an exception if one of the four lines that must have * 1, 2, 3, or 4 in column 80 do not. */ static void illegalThermoLine(std::ostream& f, char n, int linenum = -1) { throw CK_SyntaxError(f, "column 80 must " "contain an integer", linenum); }; /** * Throw an exception if number string is bad */ static void illegalNumber(std::ostream& f, std::string s, int linenum = -1) { string msg = "illegal number: "+s; throw CK_SyntaxError(f, msg, linenum); }; extern void getDefaultAtomicWeights(std::map& weights); static string d2e(string s) { size_t n; size_t sz = s.size(); string r = s; char ch; for (n = 0; n < sz; n++) { ch = s[n]; if (ch == 'D') { r[n] = 'E'; } else if (ch == 'd') { r[n] = 'e'; } } return r; } static double de_atof(std::string s) { string r = d2e(s); //double rval = Cantera::atofCheck(r.c_str()); double rval = atof(r.c_str()); return rval; } /** * Check validity of the temperatures defining the * temperature ranges for the NASA9 polynomial species thermodynamic * property fits. * @param log log file output stream * @param temp Vector of temperatures */ static void checkNASA9Temps(std::ostream& log, vector_fp& temp) { int i; for (i = 1; i < (int) temp.size(); i++) { double tlow = temp[i-1]; double thigh = temp[i]; if (thigh <= tlow) { string sss = "error reading temperature"; throw CK_SyntaxError(log, sss); } } } static double getNumberFromString(std::string s) { bool inexp = false; removeWhiteSpace(s); int sz = static_cast(s.size()); char ch; for (int n = 0; n < sz; n++) { ch = s[n]; if (!inexp && (ch == 'E' || ch == 'e' || ch == 'D' || ch == 'd')) { inexp = true; } else if (ch == '+' || ch == '-') { if (n > 0 && (s[n-1] != 'E' && s[n-1] != 'e' && s[n-1] != 'd' && s[n-1] != 'D')) { return UNDEF; } } else if (ch != '.' && (ch < '0' || ch > '9')) { return UNDEF; } } return de_atof(s); } /** * Add an element to a species. * @param symbol element symbol * @param atoms number of atoms of this element in the * species (may be non-integral) * @param sp Species object to add element to * @param log log file output stream */ static void addElement(std::string symbol, double atoms, Species& sp, std::ostream& log) { if (atoms != 0.0) { Constituent e; e.name = symbol; e.number = atoms; sp.elements.push_back(e); sp.comp[symbol] = atoms; } } /** * Check validity of the three temperatures defining the two * temperature ranges for the NASA polynomial species thermodynamic * property fits. * @param log log file output stream * @param tmin minimum temperature * @param tmid intermediate temperature * @param tmax maximum temperature */ static void checkTemps(std::ostream& log, double tmin, double tmid, double tmax) { if (tmin == 0.0 || tmid == 0.0 || tmax == 0.0) { throw CK_SyntaxError(log, "error reading Tmin, Tmid, or Tmax"); } } static void getSpecies(std::string s, int n, vector& species, bool debug, std::ostream& log) { removeWhiteSpace(s); // break string into substrings at the '+' characters separating // species symbols bool inplus = true; vector pluses; vector sp; for (int i = n-1; i >= 0; i--) { if (!inplus && s[i] == '+') { pluses.push_back(i); inplus = true; } else if (inplus && s[i] != '+') { inplus = false; } } pluses.push_back(-1); size_t np = pluses.size(); size_t loc, nxt; for (size_t nn = 0; nn < np; nn++) { loc = pluses.back(); pluses.pop_back(); if (nn == np-1) { nxt = s.size(); } else { nxt = pluses.back(); } sp.push_back(s.substr(loc+1,nxt-loc-1)); } string r, num; size_t sz, j, strt=0; RxnSpecies ss; for (size_t nn = 0; nn < sp.size(); nn++) { r = sp[nn]; sz = r.size(); for (j = 0; j < sz; j++) { if (!((r[j] >= '0' && r[j] <= '9') || r[j] == '.')) { strt = j; break; } } ss.name = r.substr(strt,sz); if (strt == 0) { ss.number = 1.0; } else { ss.number = atof(r.substr(0,strt).c_str()); } species.push_back(ss); if (debug) { log << ss.number << " " << ss.name << endl; } } } /** * given a string specifying either the reactant or product side of a * reaction equation, construct a list of Constituent objects * containing the species symbols and stoichiometric coefficients. * @todo allow non-integral stoichiometric coefficients */ int getGroups(std::string::const_iterator begin, std::string::const_iterator end, std::vector& esyms, std::vector& rxngroups) { bool ingroup = false; rxngroups.clear(); string g = ""; group_t igrp; grouplist_t groups; for (; begin != end; ++begin) { if (*begin == '(') { ingroup = true; g = ""; } else if (*begin == ')') { ingroup = false; igrp.clear(); if (parseGroupString(g, esyms, igrp) >= 0) { groups.push_back(igrp); } else { return -1; } } else if (*begin == '+') { rxngroups.push_back(groups); groups.clear(); } else if (ingroup && *begin != ' ') { g += *begin; } } rxngroups.push_back(groups); return 1; } /** * Constructor. Construct a parser for the specified input file. */ CKParser::CKParser(std::istream* infile, const std::string& fname, std::ostream* log) : verbose(true), debug(false), m_line(0), m_nasafmt(false), m_nasa9fmt(false) { m_ckfile = infile; m_ckfilename = fname; m_log = log; m_last_eol = '\n'; } // Get a line from the input file, and return it in string s. /* * If the line contains a comment character (!), then return only the * portion preceding it. Non-printing characters are replaced by * spaces. * * The input file is m_ckfile, an istream. * * @param s On return, s contains the line read from the * input file. * @param comment On return, comment contains the text following the * comment character on the line, if any. */ void CKParser::getCKLine(std::string& s, std::string& comment) { // Chemkin comment character const char commentChar = '!'; // Cantera anti-comment character const char undoCommentChar = '%'; // carriage return const char char13 = char(13); // linefeed const char char10 = char(10); istream& f = *m_ckfile; // if putCKLine was called to 'put back' a line, then return this // line, instead of reading a new one if (!m_buf.empty()) { s = m_buf; m_buf = ""; comment = m_comment; m_comment = ""; return; } // read a line, convert non-printing characters to ' ', // and remove comments comment = ""; string line; line = ""; char ch = ' '; while (1 > 0) { f.get(ch); if (!f || f.eof()) { break; } // convert tabs to spaces if (ch == '\t') { ch = ' '; } // if an end-of-line character is seen, then break. // Check for all common end-of-line characters. if (ch == char13 || (ch == char10 && (m_last_eol != char13))) { m_last_eol = ch; break; } if (isprint(ch)) { line += ch; } } string::size_type icom = line.find(commentChar); // lines that begin with !% are not comments for Cantera if (icom == 0 && line.size() > 1 && line[1] == undoCommentChar) { line[0] = '%'; line[1] = ' '; icom = line.find(commentChar); } int len = static_cast(line.size()); for (int i = 0; i < len; i++) if (!isprint(line[i])) { line[i] = ' '; } if (icom != string::npos) { s = line.substr(0, icom); comment = line.substr(icom+1,len-icom-1); } else { s = line; } if (!f || f.eof()) { s = ""; comment = ""; return; } m_line++; } /** * * Put back a line read from the input file. The next call to * getCKLine will return this line. * */ void CKParser::putCKLine(std::string& s, std::string& comment) { m_buf = s; m_comment = comment; } bool CKParser::advanceToKeyword(const std::string& kw, const std::string& stop) { string s, c; do { getCKLine(s,c); if (match(s,"")) { return false; } if (match(s,kw)) { putCKLine(s,c); return true; } } while (!match(s,stop)); putCKLine(s,c); return false; } /** * * Read the element section of the input file, and return * a list of the elements found. * */ bool CKParser::readElementSection(elementList& elements) { string s, comment; int firsttok; vector toks; map defaultWeights; //ct::ctmap_sd defaultWeights; getDefaultAtomicWeights(defaultWeights); //istream& f = m_ckfile; int ntok; elements.clear(); while (1 > 0) { next: if (advanceToKeyword("ELEM", "SPEC")) { firsttok = 1; while (1 > 0) { do { getCKLine(s, comment); getTokens(s, static_cast(s.size()), toks); ntok = static_cast(toks.size()); } while (ntok == 0); if (firsttok == 0 && isKeyword(toks[0])) { putCKLine(s,comment); goto next; } for (int i = firsttok; i < ntok; i++) { if (match(toks[i],"END")) { goto next; } else { Element el; string wtstr; el.comment = comment; el.index = static_cast(elements.size()); if (extractSlashData(toks[i], el.name, wtstr)) { el.atomicWeight = de_atof(wtstr); el.weightFromDB = false; } else { el.atomicWeight = defaultWeights[capitalize(el.name)]; el.weightFromDB = true; } if (el.atomicWeight > 0.0) { el.valid = 1; } else { el.valid = 0; } if (find(elements.begin(), elements.end(), el) < elements.end()) { if (m_log) *m_log << "warning... duplicate element " << el.name << " (ignored)." << endl; } else { elements.push_back(el); } } } firsttok = 0; } } else { if (elements.size() == 0) { *m_log << "no elements found." << endl; return false; } else { return valid(elements); } } } return false; } /** * * Read the SPECIES section of the input file, and return * a list of species names. * */ bool CKParser::readSpeciesSection(speciesList& species) { string s, comment; int firsttok; vector toks; int ntok; int nsp = 0; while (1 > 0) { next: if (advanceToKeyword("SPEC", "THER")) { firsttok = 1; while (1 > 0) { do { getCKLine(s, comment); getTokens(s, static_cast(s.size()), toks); ntok = static_cast(toks.size()); } while (ntok == 0); if (firsttok == 0 && isKeyword(toks[0])) { putCKLine(s,comment); goto next; } for (int i = firsttok; i < ntok; i++) { if (match(toks[i],"END")) { goto next; } else { Species sp; sp.name = toks[i]; if (find(species.begin(), species.end(), sp) < species.end()) { if (m_log) *m_log << "warning... duplicate species " << sp.name << " (ignored)." << endl; } else { nsp++; sp.index = nsp; species.push_back(sp); } } } firsttok = 0; } } else { if (species.size() == 0) { return false; } else { return true; } } } return false; } /** * * Read species data from THERMO section records. * * @param names List of species names (input). * @param species Table of species objects holding data from records * in THERMO section (output). */ bool CKParser::readThermoSection(std::vector& names, speciesTable& species, vector_fp& temp, int& optionFlag, std::ostream& log) { string s; vector toks; double tmin = -1.0, tmid = -1.0, tmax = -1.0; if (temp.size() == 3) { tmin = temp[0]; tmid = temp[1]; tmax = temp[2]; } int nsp = static_cast(names.size()); string comment; // read lines until THERMO section is found. But if EOF reached or // start of REACTIONS section, then there is no THERMO section. do { getCKLine(s,comment); if (match(s,"")) { return false; } if (match(s,"REAC")) { putCKLine(s,comment); return false; } } while (!match(s,"THER")); // read the tokens on the THERMO line getTokens(s, static_cast(s.size()), toks); m_nasafmt = false; if (toks.size() >= 2) { unsigned int itt; for (itt = 1; itt < toks.size(); itt++) { if (match(toks[itt],"ALL")) { optionFlag = NoThermoDatabase; } else if (match(toks[itt],"NO_TMID")) { m_nasafmt = true; log << "\nOption 'NO_TMID' specified. Default " "midpoint temperature\n"; log << "will be used for all species.\n\n"; } else if (match(toks[itt], "NASA9")) { m_nasa9fmt = true; log << "Option NASA9 specified: Use new " "nasa input file format\n\n"; } else if (match(toks[itt], "NASA")) { m_nasa9fmt = false; log << "Option NASA specified: Use old " "nasa input file format\n\n"; } else throw CK_SyntaxError(log, "unrecognized THERMO option.", m_line); } } // if "THERMO ALL" specified, or if optionFlag is set to HasTempRange, // then the next line must contain the default temperatures // for the database. if (optionFlag == NoThermoDatabase || optionFlag == HasTempRange) { getCKLine(s, comment); getTokens(s, static_cast(s.size()), toks); if (m_nasa9fmt) { // // For NASA9 polynomials, the format is // t1 t2 t3 t4 date // when there are 3 temperature regions // size_t nreg = toks.size() - 2; if (nreg >= 1) { temp.resize(nreg+1); for (size_t i = 0; i <= nreg; i++) { temp[i] = de_atof(toks[i]); } string defaultDate = toks[nreg+1]; } else { throw CK_SyntaxError(log, "Default temp region card is bad", m_line); } if (verbose) { log.flags(ios::showpoint | ios::fixed); log.precision(2); log << endl << " Default # of temperature regions: " << nreg << endl; log << " "; for (size_t i = 0; i <= nreg; i++) { log << temp[i] << " "; } log << endl; } checkNASA9Temps(log, temp); } else { // // For NASA polynomials, the format is // tlow tmid thigh // There are always 2 temperature regions // if (toks.size() >= 3) { tmin = de_atof(toks[0]); tmid = de_atof(toks[1]); tmax = de_atof(toks[2]); } if (verbose) { log.flags(ios::showpoint | ios::fixed); log.precision(2); log << endl << " default Tlow, Tmid, Thigh: " << tmin << " " << tmid << " " << tmax << endl; } checkTemps(log, tmin, tmid, tmax); temp.clear(); temp.push_back(tmin); temp.push_back(tmid); temp.push_back(tmax); } } /// XXXX BRANCH TO THE DIFFERENT THERMO READERS HERE // Check to see that we expect to be reading a NASA formatted file if (m_nasa9fmt) { bool ok = readNASA9ThermoSection(names, species, temp, optionFlag, log); if (!ok) { throw CK_SyntaxError(log, "In NASA parser. However, we expect a NASA9 file format", -1); } return ok; } // now read in all species records that have names in list 'names' bool getAllSpecies = (nsp > 0 && match(names[0],"")); if (getAllSpecies) { names.clear(); } map dup; // used to check for duplicate THERMO records bool already_read; while (1 > 0) { if (nsp == 0) { break; } already_read = false; Species spec; readThermoRecord(spec); if (spec.name == "") { break; } // check for duplicate thermo data if (dup[spec.name] == 2) { log << "Warning: more than one THERMO record for " << "species " << spec.name << endl; log << "Record at line " << m_line << " of " << m_ckfilename << " ignored." << endl; already_read = true; } dup[spec.name] = 2; if (!already_read && (getAllSpecies || (find(names.begin(), names.end(), spec.name) < names.end()))) { if (spec.tmid == 0.0) { spec.tmid = tmid; log << "Warning: default Tmid used for species " << spec.name << endl; if (spec.tmid < 0.0) { log << "Error: no default Tmid has been entered!" << endl; } } species[spec.name] = spec; if (verbose) { log << endl << "found species " << spec.name; log << " at line " << m_line << " of " << m_ckfilename; writeSpeciesData(log, spec); } checkTemps(log, spec.tlow, spec.tmid, spec.thigh); if (getAllSpecies) { names.push_back(spec.name); nsp = static_cast(names.size()); } else { nsp--; } } } return true; } /** * * Read one 4-line species definition record in NASA format. * */ void CKParser::readThermoRecord(Species& sp) { string s; string numstr; double cf; // look for line 1, but if a keyword is found first or the end of // the file is reached, return "" as the species name string comment; do { getCKLine(s, comment); if (isKeyword(s) || match(s, "")) { sp.name = ""; putCKLine(s, comment); return; } } while ((s.size() < 80) || (s[79] != '1')); // next 4 lines must be the NASA-format lines without intervening // comments. //------------- line 1 --------------------------- if (s[79] != '1') { illegalThermoLine(*m_log, s[79], m_line); } // extract the species name and the id string (date) string nameid = s.substr(0,24); vector toks; getTokens(nameid, static_cast(nameid.size()), toks); sp.name = toks[0]; sp.id = ""; unsigned int j; for (j = 1; j < toks.size(); j++) { if (j > 1) { sp.id += ' '; } sp.id += toks[j]; } int iloc; string elementSym; double atoms; int i; // elemental composition (first 4 elements) for (i = 0; i < 4; i++) { elementSym = ""; iloc = 24 + 5*i; if (s[iloc] != ' ') { if (s[iloc+1] != ' ') { elementSym = s.substr(iloc,2); } else { elementSym = s.substr(iloc,1); } } else if (s[iloc+1] != ' ') { elementSym = s.substr(iloc+1,1); } atoms = de_atof(s.substr(iloc+2,3)); addElement(elementSym, atoms, sp, *m_log); } // single-character phase descriptor sp.phase = s[44]; // low, high, and mid temperatures sp.tlow = de_atof(s.substr(45,10)); sp.thigh = de_atof(s.substr(55,10)); if (!m_nasafmt) { sp.tmid = de_atof(s.substr(65,8)); // fifth element, if any elementSym = ""; if (s[73] != ' ') { elementSym += s[73]; } if (s[74] != ' ') { elementSym += s[74]; } atoms = de_atof(s.substr(75,3)); addElement(elementSym, atoms, sp, *m_log); // additional elements, if any elementSym = ""; int loc = 80; while (loc < (int)(s.size()-9)) { elementSym = ""; if (s[loc] != ' ') { elementSym += s[loc]; } if (s[loc+1] != ' ') { elementSym += s[loc+1]; } atoms = de_atof(s.substr(loc+2,8)); addElement(elementSym, atoms, sp, *m_log); loc += 10; } } //-------------- line 2 ---------------------------- getCKLine(s, comment); if (s[79] != '2') { illegalThermoLine(*m_log, s[79], m_line); } for (i = 0; i < 5; i++) { numstr = s.substr(i*15, 15); cf = getNumberFromString(numstr); if (cf == UNDEF) { illegalNumber(*m_log, numstr, m_line); } sp.highCoeffs.push_back(cf); } //-------------- line 3 ---------------------------- getCKLine(s, comment); if (s[79] != '3') { illegalThermoLine(*m_log, s[79], m_line); } for (i = 0; i < 2; i++) { numstr = s.substr(i*15, 15); cf = getNumberFromString(numstr); if (cf == UNDEF) { illegalNumber(*m_log, numstr, m_line); } sp.highCoeffs.push_back(cf); } for (i = 2; i < 5; i++) { numstr = s.substr(i*15, 15); cf = getNumberFromString(numstr); if (cf == UNDEF) { illegalNumber(*m_log, numstr, m_line); } sp.lowCoeffs.push_back(cf); } //--------------- line 4 ---------------------------- getCKLine(s, comment); if (s[79] != '4') { illegalThermoLine(*m_log, s[79], m_line); } for (i = 0; i < 4; i++) { numstr = s.substr(i*15, 15); cf = getNumberFromString(numstr); if (cf == UNDEF) { illegalNumber(*m_log, numstr, m_line); } sp.lowCoeffs.push_back(cf); } sp.valid = 1; } void CKParser::missingAuxData(const std::string& kw) { throw CK_SyntaxError(*m_log, kw + " keyword must be followed by slash-delimited data.", m_line); } /** * Parse the REACTION section of the input file, and return * a list of Reaction objects and the units. */ bool CKParser::readReactionSection(const std::vector& speciesNames, vector& elementNames, reactionList& reactions, ReactionUnits& units) { string s, comment; vector toks; int nRxns = 0; vector rc, pr; vector_int c; // advance to the beginning of the REACTION section do { getCKLine(s, comment); if (match(s, "")) { return false; } } while (!match(s,"REAC")); // look for units specifications getTokens(s, static_cast(s.size()), toks); string tok; units.ActEnergy = Cal_per_Mole; units.Quantity = Moles; unsigned int ir; for (ir = 1; ir < toks.size(); ir++) { tok = toks[ir]; if (match(tok,"CAL/MOLE")) { units.ActEnergy = Cal_per_Mole; } else if (match(tok,"KCAL/MOLE")) { units.ActEnergy = Kcal_per_Mole; } else if (match(tok,"JOULES/MOLE")) { units.ActEnergy = Joules_per_Mole; } else if (match(tok,"KJOULES/MOLE")) { units.ActEnergy = Kjoules_per_Mole; } else if (match(tok,"KELVINS")) { units.ActEnergy = Kelvin; } else if (match(tok,"EVOLTS")) { units.ActEnergy = Electron_Volts; } else if (match(tok,"MOLES")) { units.Quantity = Moles; } else if (match(tok,"MOLECULES")) { units.Quantity = Molecules; } } Reaction rxn; vector cm; bool ok = true; if (debug) { *m_log << "CKParser::readReactions ---> DEBUG MODE" << endl; } while (1 > 0) { // skip blank or comment lines do { getCKLine(s, comment); cm.push_back(comment); } while (s == "" && comment[0] != '%'); #undef DEBUG_LINE #ifdef DEBUG_LINE *m_log << "Line: " << s << endl; #endif // end of REACTION section or EOF /// @todo does this handle case of 1 reaction correctly? if (isKeyword(s) || s == "") { if (nRxns > 0) { rxn.number = nRxns; reactions.push_back(rxn); //rxn.comment.clear(); } if (nRxns > 0) { return ok; } return false; } // rxn line //string::size_type eqloc; string sleft, sright; bool auxDataLine, metaDataLine; // if the line contains an '=', it is the start of a new reaction. // In this case, add the previous reaction to the output list, // increment the number of reactions, and start processing the // new reaction. size_t eqloc = s.find_first_of("="); metaDataLine = false; auxDataLine = false; // look for a metadata line if (s[0] == '%') { metaDataLine = true; if (eqloc > 0 && eqloc < s.size()) { int ierr, ierp; vector rg, pg; s[eqloc] = ' '; ierr = getGroups(s.begin(), s.begin() + eqloc, elementNames, rg); ierp = getGroups(s.begin() + eqloc, s.end(), elementNames, pg); unsigned int nr = static_cast(rxn.reactants.size()); unsigned int nratoms = 0; for (unsigned int ij = 0; ij < nr; ij++) { nratoms += int(rxn.reactants[ij].number); } if (rg.size() != nratoms) throw CK_SyntaxError(*m_log, " groups not specified for all reactants", m_line); else if (ierr < 0) throw CK_SyntaxError(*m_log, " error in reactant group specification", m_line); for (unsigned int ir = 0; ir < nr; ir++) { rxn.reactants[ir].groups = rg[ir]; } unsigned int np = static_cast(rxn.products.size()); unsigned int npatoms = 0; for (unsigned int ik = 0; ik < np; ik++) { npatoms += int(rxn.products[ik].number); } if (pg.size() != npatoms) throw CK_SyntaxError(*m_log, " groups not specified for all products", m_line); else if (ierp < 0) throw CK_SyntaxError(*m_log, " error in product group specification", m_line); for (unsigned int ip = 0; ip < np; ip++) { rxn.products[ip].groups = pg[ip]; } } } else if (eqloc != string::npos && eqloc < s.size()) { if (nRxns > 0) { rxn.number = nRxns; reactions.push_back(rxn); } nRxns++; rxn = Reaction(); rxn.comment = cm; cm.clear(); if (debug) { *m_log << "Parsing reaction " << nRxns << endl; } } else { auxDataLine = true; } if (comment != "") { rxn.lines.push_back(s+'!'+comment); } else { rxn.lines.push_back(s); } if (!auxDataLine && !metaDataLine) { // depending on the form of the 'equals' symbol, // determine whether the reaction is reversible or // irreversible, and separate it into strings for // each side. if (eqloc = s.find("<=>"), eqloc != string::npos) { rxn.isReversible = true; sleft = s.substr(0, eqloc); sright = s.substr(eqloc+3,1000); } else if (eqloc = s.find("=>"), eqloc != string::npos) { rxn.isReversible = false; sleft = s.substr(0, eqloc); sright = s.substr(eqloc+2,1000); } else if (eqloc = s.find("="), eqloc != string::npos) { rxn.isReversible = true; sleft = s.substr(0, eqloc); sright = s.substr(eqloc+1,1000); } else throw CK_SyntaxError(*m_log, "expected <=>, =>, or =", m_line); if (debug) { *m_log << s << endl; if (rxn.isReversible) { *m_log << "Reaction is reversible." << endl; } else { *m_log << "Reaction is irreversible." << endl; } } string::size_type mloc, mloc2; // process reactants if (debug) { *m_log << "Processing reactants..." << sleft << endl; } removeWhiteSpace(sleft); if (debug) *m_log << "After removing white space: " << sleft << endl; rxn.isFalloffRxn = false; string sm, mspecies; mloc = sleft.find("(+"); if (mloc != string::npos) { sm = sleft.substr(mloc+2, 1000); mloc2 = sm.find(")"); if (mloc2 != string::npos) { mspecies = sm.substr(0,mloc2); rxn.isFalloffRxn = true; rxn.type = Falloff; sleft = sleft.substr(0, mloc); if (mspecies == "M" || mspecies == "m") { rxn.thirdBody = "M"; } else { rxn.thirdBody = mspecies; } if (debug) { *m_log << "Falloff reaction. Third body = " << rxn.thirdBody << endl; } } else throw CK_SyntaxError(*m_log, "missing )", m_line); } else if ((mloc = sleft.find("+M"), mloc != string::npos) || (mloc = sleft.find("+m"), mloc != string::npos)) { if (static_cast(mloc) == static_cast(sleft.size()) - 2) { rxn.isThreeBodyRxn = true; rxn.type = ThreeBody; sleft = sleft.substr(0, mloc); rxn.thirdBody = "M"; if (debug) { *m_log << "Three-body reaction." << endl; } } else if (debug) { *m_log << "Reactant string contains +M or +m, but \n" << "not last two characters of string: " << "\"" << sleft << "\"\n" << "NOT a three-body reaction." << endl; } } getSpecies(sleft.c_str(),static_cast(sleft.size()), rxn.reactants, debug, *m_log); int ir = static_cast(rxn.reactants.size()); for (int iir = 0; iir < ir; iir++) { if (find(speciesNames.begin(), speciesNames.end(), rxn.reactants[iir].name) >= speciesNames.end()) throw CK_SyntaxError(*m_log, "undeclared reactant species " +rxn.reactants[iir].name, m_line); } // process Arrhenius coefficients getTokens(sright, static_cast(sright.size()), toks); int ntoks = static_cast(toks.size()); if (ntoks < 3) { throw CK_SyntaxError(*m_log, "expected 3 Arrhenius parameters", m_line); } rxn.kf.A = de_atof(toks[ntoks - 3]); rxn.kf.n = de_atof(toks[ntoks - 2]); rxn.kf.E = de_atof(toks[ntoks - 1]); // 2/10/03: allow negative prefactor but print a warning if (rxn.kf.A < 0.0) *m_log << "Warning: negative prefactor at line " << m_line << endl; //throw CK_SyntaxError(*m_log, "negative prefactor", m_line); if (debug) { *m_log << "Processing products..." << sright << endl; } sright = sright.substr(0, sright.find(toks[ntoks - 3]) - 1); if (debug) *m_log << "After removing Arrhenius parameters, " << "\nproduct string = " << sright << endl; removeWhiteSpace(sright); if (debug) *m_log << "After removing white space: " << sright << endl; mloc = sright.find("(+"); if (mloc != string::npos) { sm = sright.substr(mloc+2, 1000); mloc2 = sm.find(")"); if (mloc2 != string::npos) { mspecies = sm.substr(0,mloc2); if (rxn.type == ThreeBody) throw CK_SyntaxError(*m_log, "mismatched +M or (+M)", m_line); rxn.isFalloffRxn = true; rxn.type = Falloff; if (debug) { *m_log << "Falloff reaction. Third body = " << rxn.thirdBody << endl; } } else throw CK_SyntaxError(*m_log, "missing )", m_line); sright = sright.substr(0, mloc); if (mspecies == "M" || mspecies == "m") { rxn.thirdBody = "M"; } else { if (rxn.thirdBody != mspecies) throw CK_SyntaxError(*m_log, "mismatched third body", m_line); rxn.thirdBody = mspecies; } } else if ((mloc = sright.find("+M"), mloc != string::npos) || (mloc = sright.find("+m"), mloc != string::npos)) { if (static_cast(mloc) == static_cast(sright.size()) - 2) { if (rxn.type == Falloff) throw CK_SyntaxError(*m_log, "mismatched +M or (+M)", m_line); rxn.isThreeBodyRxn = true; rxn.thirdBody = "M"; sright = sright.substr(0, mloc); if (debug) { *m_log << "Three-body reaction." << endl; } } else if (debug) { *m_log << "Product string contains +M or +m, but \n" << "not last two characters of string: " << "\"" << sright << "\"\n" << "NOT a three-body reaction." << endl; } } getSpecies(sright.c_str(),static_cast(sright.size()), rxn.products, debug, *m_log); int ip = static_cast(rxn.products.size()); for (int iip = 0; iip < ip; iip++) { if (find(speciesNames.begin(), speciesNames.end(), rxn.products[iip].name) >= speciesNames.end()) throw CK_SyntaxError(*m_log, "undeclared product species "+rxn.products[iip].name, m_line); } } // auxiliary data line else if (auxDataLine) { bool hasAuxData; string name, data; map kwindex; while (1 > 0) { hasAuxData = extractSlashData(s, name, data); if (!hasAuxData && name == "") { break; } // check for duplicate keyword if (kwindex[name]) { throw CK_SyntaxError(*m_log, "duplicate auxiliary data keyword " + name, m_line); } else { kwindex[name] = 1; } // low-pressure rate coefficient for falloff rxn if (match(name,"LOW")) { vector klow; rxn.type = Falloff; if (hasAuxData) { getTokens(data, static_cast(data.size()), klow); if (klow.size() != 3) { throw CK_SyntaxError(*m_log, "expected 3 low-pressure Arrhenius parameters", m_line); } rxn.kf_aux.A = de_atof(klow[0]); rxn.kf_aux.n = de_atof(klow[1]); rxn.kf_aux.E = de_atof(klow[2]); } else { missingAuxData("LOW"); } } // falloff parameters else if (match(name,"TROE")) { vector falloff; if (kwindex["SRI"] > 0) { throw CK_SyntaxError(*m_log, "cannot specify both SRI and TROE", m_line); } if (hasAuxData) { getTokens(data, static_cast(data.size()), falloff); int nf = static_cast(falloff.size()); double ff; rxn.falloffType = Troe; for (int jf = 0; jf < nf; jf++) { ff = de_atof(falloff[jf]); rxn.falloffParameters.push_back(ff); } } else { missingAuxData("TROE"); } } else if (match(name,"SRI")) { vector falloff; if (kwindex["TROE"] > 0) { throw CK_SyntaxError(*m_log, "cannot specify both SRI and TROE", m_line); } if (hasAuxData) { getTokens(data, static_cast(data.size()), falloff); int nf = static_cast(falloff.size()); rxn.falloffType = SRI; double ff; for (int jf = 0; jf < nf; jf++) { ff = de_atof(falloff[jf]); rxn.falloffParameters.push_back(ff); } } else { missingAuxData("SRI"); } } // reverse rate coefficient else if (match(name,"REV")) { vector krev; if (!rxn.isReversible) { throw CK_SyntaxError(*m_log, "reverse rate parameters can only be " "specified for reversible reactions", m_line); } if (hasAuxData) { getTokens(data, static_cast(data.size()), krev); if (krev.size() != 3) { throw CK_SyntaxError(*m_log, "expected 3 Arrhenius parameters", m_line); } rxn.krev.A = de_atof(krev[0]); rxn.krev.n = de_atof(krev[1]); rxn.krev.E = de_atof(krev[2]); } else { missingAuxData("REV"); } } else if (match(name,"DUP")) { rxn.isDuplicate = true; } else if (match(name,"END")) { string c = ""; putCKLine(name,c); break; } // Landau-Teller reaction rate parameters else if (match(name,"LT")) { vector bc; rxn.kf.type = LandauTeller; if (hasAuxData) { getTokens(data, static_cast(data.size()), bc); rxn.kf.B = de_atof(bc[0]); rxn.kf.C = de_atof(bc[1]); } else { missingAuxData("LT"); } } else if (match(name,"RLT")) { vector bc; rxn.krev.type = LandauTeller; if (hasAuxData) { getTokens(data, static_cast(data.size()), bc); rxn.krev.B = de_atof(bc[0]); rxn.krev.C = de_atof(bc[1]); } else { missingAuxData("RLT"); } } // chem activation reactions else if (match(name,"HIGH")) { vector khigh; rxn.type = ChemAct; if (hasAuxData) { getTokens(data, static_cast(data.size()), khigh); rxn.kf_aux.A = de_atof(khigh[0]); rxn.kf_aux.n = de_atof(khigh[1]); rxn.kf_aux.E = de_atof(khigh[2]); } else { missingAuxData("HIGH"); } } else if (match(name,"FORD")) { vector nmord; if (hasAuxData) { getTokens(data, static_cast(data.size()), nmord); rxn.fwdOrder[nmord[0]] = de_atof(nmord[1]); } else { missingAuxData("FORD"); } } else if (find(speciesNames.begin(), speciesNames.end(), name) < speciesNames.end()) { if (hasAuxData) { if (rxn.thirdBody == name || rxn.thirdBody == "M") { rxn.e3b[name] = de_atof(data); } else if (rxn.thirdBody == "") { *m_log << "Error in reaction " << nRxns << ": third-body collision efficiencies cannot be specified" << " for this reaction type." << endl; throw CK_SyntaxError(*m_log, "third-body efficiency error", m_line); } else { *m_log << "Reaction " << nRxns << ": illegal species in enhanced " << "efficiency specification. Species = " << name << " rxn.thirdBody = " << rxn.thirdBody << endl; throw CK_SyntaxError(*m_log, "third-body efficiency error", m_line); } } else { missingAuxData(name); } } else { Reaction::auxdata vals; vector toks; getTokens(data, static_cast(data.size()), toks); int ntoks = static_cast(toks.size()); for (int itok = 0; itok < ntoks; itok++) { vals.push_back(de_atof(toks[itok])); } rxn.otherAuxData[name] = vals; } } } } return false; } int parseGroupString(std::string str, std::vector& esyms, group_t& result) { bool inSymbol=true; string s = str + '-'; int i; string num, sym; int eindx; string::const_iterator begin = s.begin(); string::const_iterator end = s.end(); vector::iterator e; result.resize(static_cast(esyms.size()),0); for (; begin != end; ++begin) { // new element if (*begin == '-') { e = find(esyms.begin(), esyms.end(), sym); if (e == esyms.end()) { return -1; } eindx = static_cast(e - esyms.begin()); if (num != "") { i = atoi(num.c_str()); } else { i = 1; } result[eindx] = i; sym = ""; num = ""; inSymbol = true; } else if (isdigit(*begin)) { inSymbol = false; num += *begin; } else if (isalpha(*begin) && inSymbol) { sym += *begin; } } return 1; } } // ckr namespace