/**
 * @file ctml.cpp
 * Definitions for functions to read and write CTML.
 *
 */
// Copyright 2002  California Institute of Technology

#include "cantera/base/ctml.h"

//@{
#define CTML_VERSION_1_4_1
//@}

#include "cantera/base/global.h"
#include "cantera/base/stringUtils.h"

#include <cctype>
#include <cstring>
#include <cstdlib>


using namespace std;
using namespace Cantera;

namespace ctml
{

std::string FP_Format = "%23.15E";
std::string INT_Format = "%8d";

//====================================================================================================================
//! Convert a floating point value from a string to a double
/*!
 *   @param val String value input
 *
 *   @return Returns a double
 */
static doublereal fpValue(std::string val)
{
    return atof(stripws(val).c_str());
}
//====================================================================================================================
//  This function adds a child node with the name, "bool", with a value
//  consisting of a single bool
/*
 *   This function will add a child node to the current XML node, with the
 *   name "bool". It will have a title attribute, and the body
 *   of the XML node will be filled out with a single bool
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &node;
   std::string titleString = "doSpecialOp";
   bool value = true;
   addBool(node, titleString, value);
   @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
   <parentNode>
     <bool title="doSpecialOp" type="optional">
        true
     <\integer>
   <\parentNode>
 @endverbatim
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param titleString   String name of the title attribute
 *   @param value         Value - single bool
 *
 * @todo I don't think this is used. Figure out what is used for writing bools,
 *       and codify that.
 */
void addBool(Cantera::XML_Node& node, const std::string& title, const bool val)
{
    std::string v = (val ? "true" : "false");
    XML_Node& f = node.addChild("bool", v);
    f.addAttribute("title", title);
}
//====================================================================================================================
//  This function adds a child node with the name, "integer", with a value
//  consisting of a single integer
/*
 *   This function will add a child node to the current XML node, with the
 *   name "integer". It will have a title attribute, and the body
 *   of the XML node will be filled out with a single integer
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &node;
 std::string titleString = "maxIterations";
 int  value = 1000;
 std::string typeString = "optional";
 std::string units = "";
 addInteger(node, titleString, value, typeString, units);
 @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
 <parentNode>
 <integer title="maxIterations" type="optional">
 100
 <\integer>
 <\parentNode>
 @endverbatim
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param titleString   String name of the title attribute
 *   @param value         Value - single integer
 *   @param unitsString   String name of the Units attribute. The default is to
 *                        have an empty string.
 *   @param typeString    String type. This is an optional parameter. The default
 *                        is to have an empty string.
 */
void addInteger(Cantera::XML_Node& node, const std::string& title, const int val,
                const std::string units, const std::string type)
{
#ifdef CTML_VERSION_1_4
    XML_Node& f = node.addChild("integer", val);
    f.addAttribute("title", title);
#else
    XML_Node& f = node.addChild(title, val);
#endif
    f.addAttribute("vtype", "integer");
    if (type != "") {
        f.addAttribute("type",type);
    }
    if (units != "") {
        f.addAttribute("units",units);
    }
}
//====================================================================================================================
//  This function adds a child node with the name, "intArray", with a value
//  consisting of a comma separated list of integers
/*
 *   This function will add a child node to the current XML node, with the
 *   name "intArray". It will have a title attribute, and the body
 *   of the XML node will be filled out with a comma separated list of
 *   integers
 *
 *  Example:
 *
 *       @verbatim
   const XML_Node &node;
   std::string titleString = "additionalCases";
   int  n = 3;
   int cases[3] = [3, 6, 10];
   std::string typeString = "optional";
   std::string units = "";
   addIntegerArray(node, titleString, n, &cases[0], typeString, units);
   @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
   <parentNode>
     <intArray title="additionalCases" type="optional">
        3, 6, 10
     <\intArray>
   <\parentNode>
 @endverbatim
 *
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param titleString   String name of the title attribute
 *   @param n             Length of the integer vector.
 *   @param values        Pointer to a vector of integers
 *   @param unitsString   String name of the Units attribute. This is an optional
 *                        parameter. The default is to
 *                        have an empty string.
 *   @param typeString    String type. This is an optional parameter. The default
 *                        is to have an empty string.
 *   @param minval        Minimum allowed value of the int. This is an optional
 *                        parameter. The default is the
 *                        special double, Cantera::Undef, which means to ignore the
 *                        entry.
 *   @param maxval        Maximum allowed value of the int. This is an optional
 *                        parameter. The default is the
 *                        special double, Cantera::Undef, which means to ignore the
 *                        entry.
 *
 * @todo I don't think this is used. Figure out what is used for writing integers,
 *       and codify that. unitsString shouldn't be here, since it's an int.
 *       typeString should be codified as to its usage.
 */
void addIntegerArray(Cantera::XML_Node& node, const std::string& title, const size_t n,
                     const int* const vals, const std::string units, const std::string type,
                     const doublereal minval, const doublereal maxval)
{
    std::string v = "";
    for (size_t i = 0; i < n; i++) {
        v += int2str(vals[i],INT_Format);
        if (i == n-1) {
            v += "\n";
        } else if (i > 0 && (i+1) % 3 == 0) {
            v += ",\n";
        } else {
            v += ", ";
        }
    }
#ifdef CTML_VERSION_1_4
    XML_Node& f = node.addChild("intArray",v);
    f.addAttribute("title",title);
#else
    XML_Node& f = node.addChild(title, v);
#endif
    if (type != "") {
        f.addAttribute("type",type);
    }
    f.addAttribute("size", static_cast<double>(n));
#ifndef CTML_VERSION_1_4
    f.addAttribute("vtype", "intArray");
#endif
    if (units != "") {
        f.addAttribute("units",units);
    }
    if (minval != Undef) {
        f.addAttribute("min",minval);
    }
    if (maxval != Undef) {
        f.addAttribute("max",maxval);
    }
}
//====================================================================================================================
//  This function adds a child node with the name, "float", with a value
//  consisting of a single floating point number
/*
 *   This function will add a child node to the current XML node, with the
 *   name "float". It will have a title attribute, and the body
 *   of the XML node will be filled out with a single float
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &node;
 std::string titleString = "activationEnergy";
 double  value = 50.3;
 doublereal maxval = 1.0E3;
 doublereal minval = 0.0;
 std::string typeString = "optional";
 std::string unitsString = "kcal/gmol";
 addFloat(node, titleString, value, unitsString, typeString, minval, maxval);
 @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
 <parentNode>
 <float title="activationEnergy" type="optional" units="kcal/gmol" min="0.0" max="1.0E3">
 50.3
 <\float>
 <\parentNode>
 @endverbatim
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param titleString   String name of the title attribute
 *   @param value         Value - single integer
 *   @param unitsString   String name of the Units attribute. The default is to
 *                        have an empty string.
 *   @param typeString    String type. This is an optional parameter. The default
 *                        is to have an empty string.
 *
 * @todo minval and maxval should be codified. typeString should be codified
 *       as to its usage.
 */
void addFloat(Cantera::XML_Node& node, const std::string& title,
              const doublereal val, const std::string units,
              const std::string type, const doublereal minval,
              const doublereal maxval)
{
#ifdef CTML_VERSION_1_4
    XML_Node& f = node.addChild("float", val, ctml::FP_Format);
    f.addAttribute("title", title);
#else
    XML_Node& f = node.addChild(title, val, ctml::FP_Format);
#endif
    if (type != "") {
        f.addAttribute("type",type);
    }
    if (units != "") {
        f.addAttribute("units",units);
    }
    f.addAttribute("vtype", "float");
    if (minval != Undef) {
        f.addAttribute("min",minval);
    }
    if (maxval != Undef) {
        f.addAttribute("max",maxval);
    }
}
//====================================================================================================================
//  This function adds a child node with the name, "floatArray", with a value
//  consisting of a comma separated list of floats
/*
 *   This function will add a child node to the current XML node, with the
 *   name "floatArray". It will have a title attribute, and the body
 *   of the XML node will be filled out with a comma separated list of
 *   integers
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &node;
   std::string titleString = "additionalTemperatures";
   int  n = 3;
   int Tcases[3] = [273.15, 298.15, 373.15];
   std::string typeString = "optional";
   std::string units = "Kelvin";
   addFloatArray(node, titleString, n, &cases[0], typeString, units);
   @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
   <parentNode>
     <floatArray title="additionalTemperatures" type="optional" units="Kelvin">
        273.15, 298.15, 373.15
     <\floatArray>
   <\parentNode>
 @endverbatim
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param titleString   String name of the title attribute
 *   @param n             Length of the doubles vector.
 *   @param values        Pointer to a vector of doubles
 *   @param unitsString   String name of the Units attribute. This is an optional
 *                        parameter. The default is to
 *                        have an empty string.
 *   @param type          String type. This is an optional parameter. The default
 *                        is to have an empty string.
 *   @param minval        Minimum allowed value of the int. This is an optional
 *                        parameter. The default is the
 *                        special double, Cantera::Undef, which means to ignore the
 *                        entry.
 *   @param maxval        Maximum allowed value of the int. This is an optional
 *                        parameter. The default is the
 *                        special double, Cantera::Undef, which means to ignore the
 *                        entry.
 *
 * @todo typeString should be codified as to its usage.
 */
void addFloatArray(Cantera::XML_Node& node, const std::string& title, const size_t n,
                   const doublereal* const vals, const std::string units,
                   const std::string type,
                   const doublereal minval, const doublereal maxval)
{
    size_t i;
    std::string v = "";
    for (i = 0; i < n; i++) {
        v += fp2str(vals[i],FP_Format);
        if (i == n-1) {
            v += "\n";
        } else if (i > 0 && (i+1) % 3 == 0) {
            v += ",\n";
        } else {
            v += ", ";
        }
    }
    XML_Node& f = node.addChild("floatArray",v);
    f.addAttribute("title",title);
    if (type != "") {
        f.addAttribute("type",type);
    }
    f.addAttribute("size", double(n));
    if (units != "") {
        f.addAttribute("units",units);
    }
    if (minval != Undef) {
        f.addAttribute("min",minval);
    }
    if (maxval != Undef) {
        f.addAttribute("max",maxval);
    }
}
//====================================================================================================================
//  This function adds a child node with the name given by the first parameter with a value
//  consisting of a comma separated list of floats
/*
 *   This function will add a child node to the current XML node, with the
 *   name given in the list. It will have a title attribute, and the body
 *   of the XML node will be filled out with a comma separated list of
 *   integers
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &node;
   std::string titleString = "additionalTemperatures";
   int  n = 3;
   int Tcases[3] = [273.15, 298.15, 373.15];
   std::string typeString = "optional";
   std::string units = "Kelvin";
   addNamedFloatArray(node, titleString, n, &cases[0], typeString, units);
   @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
   <parentNode>
     <additionalTemperatures type="optional" vtype="floatArray" size = "3" units="Kelvin">
        273.15, 298.15, 373.15
     <\additionalTemperatures>
   <\parentNode>
 @endverbatim
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param name          Name of the XML node
 *   @param n             Length of the doubles vector.
 *   @param values        Pointer to a vector of doubles
 *   @param unitsString   String name of the Units attribute. This is an optional
 *                        parameter. The default is to
 *                        have an empty string.
 *   @param type          String type. This is an optional parameter. The default
 *                        is to have an empty string.
 *   @param minval        Minimum allowed value of the int. This is an optional
 *                        parameter. The default is the
 *                        special double, Cantera::Undef, which means to ignore the
 *                        entry.
 *   @param maxval        Maximum allowed value of the int. This is an optional
 *                        parameter. The default is the
 *                        special double, Cantera::Undef, which means to ignore the
 *                        entry.
 *
 * @todo I don't think this is used. Figure out what is used for writing integers,
 *       and codify that. unitsString shouldn't be here, since it's an int.
 *       typeString should be codified as to its usage.
 */
void addNamedFloatArray(Cantera::XML_Node& node, const std::string& name, const int n,
                        const doublereal* const vals, const std::string units,
                        const std::string type, const doublereal minval,
                        const doublereal maxval)
{
    int i;
    std::string v = "";
    for (i = 0; i < n; i++) {
        v += fp2str(vals[i],FP_Format);
        if (i == n-1) {
            v += "\n";
        } else if (i > 0 && (i+1) % 3 == 0) {
            v += ",\n";
        } else {
            v += ", ";
        }
    }
    XML_Node& f = node.addChild(name, v);
    if (type != "") {
        f.addAttribute("type",type);
    }
    /*
     *  Add vtype, which indicates the type of the value. Here we specify it as a list of floats separated
     *  by commas, with a length given by size attribute.
     */
    f.addAttribute("vtype", "floatArray");

    f.addAttribute("size", n);
    if (units != "") {
        f.addAttribute("units", units);
    }
    if (minval != Undef) {
        f.addAttribute("min", minval);
    }
    if (maxval != Undef) {
        f.addAttribute("max", maxval);
    }
}
//====================================================================================================================
//  This function adds a child node with the name string with a string value
//  to the current node
/*
 *   This function will add a child node to the current XML node, with the
 *   name "string". It will have a title attribute, and the body
 *   of the XML node will be filled out with the valueString argument verbatim.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &node;
 addString(XML_Node& node, std::string titleString, std::string valueString,
 std::string typeString);
 @endverbatim
 *
 *  Creates the following the snippet in the XML file:
 *  @verbatim
 <string title="titleString" type="typeString">
 valueString
 <\string>
 @endverbatim
 *
 *   @param node          reference to the XML_Node object of the parent XML element
 *   @param valueString   Value string to be used in the new XML node.
 *   @param titleString   String name of the title attribute
 *   @param typeString    String type. This is an optional parameter.
 */
void addString(Cantera::XML_Node& node, const std::string& titleString,
               const std::string& valueString,
               const std::string typeString)
{
    XML_Node& f = node.addChild("string", valueString);
    f.addAttribute("title", titleString);
    if (typeString != "") {
        f.addAttribute("type", typeString);
    }
}

XML_Node* getByTitle(const Cantera::XML_Node& node, const std::string& title)
{
    XML_Node* s = node.findByAttr("title", title);
    if (!s) {
        return 0;
    }
    if (s->parent() == &node) {
        return s;
    }
    return 0;
}
//====================================================================================================================
//  This function reads a child node with the name string and returns
//  its xml value as the return string
/*
 *   If the child XML_node named "name" doesn't exist, the empty string is returned.
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &parent;
 string name = "vacency_species";
 string valueString = getChildValue(parent, name
 std::string typeString);
 @endverbatim
 *
 *  returns valueString = "O(V)"
 *
 *  from the following the snippet in the XML file:
 *
 *  @verbatim
 <vacencySpecies>
 O(V)
 <\vancencySpecies>
 @endverbatim
 *
 *   @param parent   parent reference to the XML_Node object of the parent XML element
 *   @param name     Name of the child XML_Node to read the value from.
 *
 *   @return         String value of the child XML_Node
 */
std::string getChildValue(const Cantera::XML_Node& parent, const std::string& nameString)
{
    if (!parent.hasChild(nameString)) {
        return "";
    }
    return parent(nameString);
}
//====================================================================================================================
//  This function reads a child node with the name, "string", with a specific
//  title attribute named "titleString"
/*
 *   This function will read a child node to the current XML node, with the
 *   name "string". It must have a title attribute, named titleString, and the body
 *   of the XML node will be read into the valueString output argument.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &node;
 getString(XML_Node& node, std::string titleString, std::string valueString,
 std::string typeString);
 @endverbatim
 *
 *  Reads the following the snippet in the XML file:
 *  @verbatim
 <string title="titleString" type="typeString">
   valueString
 <\string>
 @endverbatim
 *
 *   @param node          Reference to the XML_Node object of the parent XML element
 *   @param titleString   String name of the title attribute of the child node
 *   @param valueString   Value string that is found in the child node. output variable
 *   @param typeString    String type. This is an optional output variable. It is filled
 *                        with the attribute "type" of the XML entry.
 */
void getString(const Cantera::XML_Node& node, const std::string& titleString, std::string& valueString,
               std::string& typeString)
{
    valueString = "";
    typeString = "";
    XML_Node* s = getByTitle(node, titleString);
    if (s)
        if (s->name() == "string") {
            valueString = (*s).value();
            typeString = (*s)["type"];
            return;
        }
}

//====================================================================================================================
//  This function attempts to read a named child node and returns with the contents in the value string.
//  title attribute named "titleString"
/*
 *   This function will read a child node to the current XML node, with the
 *   name "string". It must have a title attribute, named titleString, and the body
 *   of the XML node will be read into the valueString output argument.
 *
 *   If the child node is not found then the empty string is returned.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &node;
   std::string valueString;
   std::string typeString;
   std::string nameString = "timeIncrement";
   getString(XML_Node& node, nameString, valueString, valueString, typeString);
 @endverbatim
 *
 *  Reads the following the snippet in the XML file:
 *
 *  *  @verbatim
 <nameString type="typeString">
   valueString
 <\nameString>
 @endverbatim
 *
 *  or alternatively as a retrofit and special case, it also reads the following case
 *
 *  @verbatim
 <string title="nameString" type="typeString">
   valueString
 <\string>
 @endverbatim
 *
 *   @param node          Reference to the XML_Node object of the parent XML element
 *   @param nameString    Name of the XML Node                               input  variable
 *   @param valueString   Value string that is found in the child node.      output variable
 *   @param typeString    String type. This is an optional output variable. It is filled
 *                        with the attribute "type" of the XML entry.         output variable
 */
void getNamedStringValue(const Cantera::XML_Node& node, const std::string& nameString, std::string& valueString,
                         std::string& typeString)
{
    valueString = "";
    typeString = "";
    if (node.hasChild(nameString)) {
        XML_Node& xc = node.child(nameString);
        valueString = xc.value();
        typeString = xc["type"];
    } else {
        XML_Node* s = getByTitle(node, nameString);
        if (s) {
            if (s->name() == "string") {
                valueString = (*s).value();
                typeString = (*s)["type"];
                return;
            }
        }
    }
}
//====================================================================================================================
//  Get a vector of integer values from a child element.
/*
 *  Returns a std::map containing a keyed values for child XML_Nodes
 *  of the current node with the name, "integer".
 *  In the keyed mapping there will be a list of titles vs. values
 *  for all of the XML nodes.
 *  The integer XML_nodes are expected to be in a particular form created
 *  by the function addInteger(). One value per XML_node is expected.
 *
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &State_XMLNode;
   std::map<std::string, integer> v;
   getinteger(State_XMLNode, v);
 @endverbatim
 *
 *  reads the corresponding XML file:
 *
 *  @verbatim
 <state>
   <integer title="i1">   1  <\integer>
   <integer title="i2">   2  <\integer>
   <integer title="i3">   3  <\integer>
 <\state>
 @endverbatim
 *
 *  Will produce the mapping:
 *
 *         v["i1"] = 1
 *         v["i2"] = 2
 *         v["i3"] = 3
 *
 *
 *   @param node     Current XML node to get the values from
 *   @param v        Output map of the results.
 */
void getIntegers(const Cantera::XML_Node& node,
                 std::map<std::string, int>& v)
{
    std::vector<XML_Node*> f;
    node.getChildren("integer",f);
    int n = static_cast<int>(f.size());
    integer x;
    std::string typ, title, vmin, vmax;
    for (int i = 0; i < n; i++) {
        const XML_Node& fi = *(f[i]);
        x = atoi(fi().c_str());
        title = fi["title"];
        vmin = fi["min"];
        vmax = fi["max"];
        if (vmin != "")
            if (fi["max"] != "") {
                v[title] = x;
            }
    }
}

//====================================================================================================================
//  Get a vector of floating-point values from a child element.
/*
 *  Returns a std::map containing a keyed values for child XML_Nodes
 *  of the current node with the name, "float".
 *  In the keyed mapping there will be a list of titles vs. values
 *  for all of the XML nodes.
 *  The float XML_nodes are expected to be in a particular form created
 *  by the function addFloat(). One value per XML_node is expected.
 *
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &State_XMLNode;
   std::map<std::string,double> v;
   bool convert = true;
   getFloats(State_XMLNode, v, convert);
 @endverbatim
 *
 *  reads the corresponding XML file:
 *
 *  @verbatim
 <state>
   <float title="a1" units="m3">   32.4 <\float>
   <float title="a2" units="cm3">   1.  <\float>
   <float title="a3">             100.  <\float>
 <\state>
 @endverbatim
 *
 *  Will produce the mapping:
 *
 *         v["a1"] = 32.4
 *         v["a2"] = 1.0E-6
 *         v["a3"] = 100.
 *
 *
 *   @param node     Current XML node to get the values from
 *   @param v        Output map of the results.
 *   @param convert  Turn on conversion to SI units
 */
void getFloats(const Cantera::XML_Node& node, std::map<std::string, double>& v,
               const bool convert)
{
    std::vector<XML_Node*> f;
    node.getChildren("float",f);
    int n = static_cast<int>(f.size());
    doublereal x, x0, x1, fctr;
    std::string typ, title, units, vmin, vmax;
    for (int i = 0; i < n; i++) {
        const XML_Node& fi = *(f[i]);
        x = atof(fi().c_str());
        x0 = Undef;
        x1 = Undef;
        typ = fi["type"];
        title = fi["title"];
        units = fi["units"];
        vmin = fi["min"];
        vmax = fi["max"];
        if (vmin != "") {
            x0 = atof(vmin.c_str());
            if (x < x0 - Tiny) {
                writelog("\nWarning: value "+fi()+" is below lower limit of "
                         +vmin+".\n");
            }
        }
        if (fi["max"] != "") {
            x1 = atof(vmax.c_str());
            if (x > x1 + Tiny) {
                writelog("\nWarning: value "+fi()+" is above upper limit of "
                         +vmax+".\n");
            }
        }
        fctr = (convert ? toSI(units) : 1.0); // toSI(typ,units);
        v[title] = fctr*x;
    }
}

//====================================================================================================================
//  Get a floating-point value from a child element.
/*
 *  Returns a double value for the child named 'name' of element 'parent'. If
 *  'type' is supplied and matches a known unit type, unit
 *  conversion to SI will be done if the child element has an attribute
 *  'units'.
 *
 *  Note, it's an error for the child element not to exist.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &State_XMLNode;
 doublereal pres = OneAtm;
 if (state_XMLNode.hasChild("pressure")) {
 pres = getFloat(State_XMLNode, "pressure", "toSI");
 }
 @endverbatim
 *
 *  reads the corresponding XML file:
 *  @verbatim
 <state>
 <pressure units="Pa"> 101325.0 </pressure>
 <\state>
 @endverbatim
 *
 *   @param parent reference to the XML_Node object of the parent XML element
 *   @param name   Name of the XML child element
 *   @param type   String type. Currently known types are "toSI" and "actEnergy",
 *                 and "" , for no conversion. The default value is ""
 *                 which implies that no conversion is allowed.
 */
doublereal getFloat(const Cantera::XML_Node& parent,
                    const std::string& name,
                    const std::string type)
{
    if (!parent.hasChild(name))
        throw CanteraError("getFloat (called from XML Node \"" +
                           parent.name() + "\"): ",
                           "no child XML element named \"" + name + "\" exists");
    const XML_Node& node = parent.child(name);
    return getFloatCurrent(node, type);
}

//====================================================================================================================
//  Get a floating-point value from the current XML element
/*
 *  Returns a doublereal value from the current element. If
 *  'type' is supplied and matches a known unit type, unit
 *  conversion to SI will be done if the child element has an attribute  'units'.
 *
 *  Note, it's an error for the child element not to exist.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &State_XMLNode;
 doublereal pres = OneAtm;
 if (state_XMLNode.hasChild("pressure")) {
   XML_Node *pres_XMLNode = State_XMLNode.getChild("pressure");
   pres = getFloatCurrent(pres_XMLNode, "toSI");
 }
 @endverbatim
 *
 *  Rreads the corresponding XML file:
 *  @verbatim
 <state>
   <pressure units="Pa"> 101325.0 </pressure>
 <\state>
 @endverbatim
 *
 *   @param currXML reference to the current XML_Node object
 *   @param type   String type. Currently known types are "toSI" and "actEnergy",
 *                 and "" , for no conversion. The default value is "",
 *                 which implies that no conversion is allowed.
 */
doublereal getFloatCurrent(const Cantera::XML_Node& node,
                           const std::string type)
{
    doublereal x, x0, x1, fctr = 1.0;
    string units, vmin, vmax;
    x = atof(node().c_str());
    x0 = Undef;
    x1 = Undef;
    units = node["units"];
    vmin = node["min"];
    vmax = node["max"];
    if (vmin != "") {
        x0 = atof(vmin.c_str());
        if (x < x0 - Tiny) {
            writelog("\nWarning: value "+node()+" is below lower limit of "
                     +vmin+".\n");
        }
    }
    if (node["max"] != "") {
        x1 = atof(vmax.c_str());
        if (x > x1 + Tiny) {
            writelog("\nWarning: value "+node()+" is above upper limit of "
                     +vmax+".\n");
        }
    }
    // Note, most type's of converters default to toSI() type atm.
    // This may change and become more specific in the future.
    if (type == "actEnergy" && units != "") {
        fctr = actEnergyToSI(units);
    } else if (type == "toSI" && units != "") {
        fctr = toSI(units);
    } else if (type == "temperature" && units != "") {
        fctr = toSI(units);
    } else if (type == "density" && units != "") {
        fctr = toSI(units);
    } else if (type == "pressure" && units != "") {
        fctr = toSI(units);
    } else if (type != "" && units != "") {
        fctr = toSI(units);
#ifdef DEBUG_MODE
        writelog("\nWarning: conversion toSI() was done on node value "  + node.name() +
                 "but wasn't explicity requested. Type was \"" + type + "\"\n");
#endif
    }
    // Note, below currently produces a lot of output due to transport blocks.
    // This needs to be addressed.
#ifdef DEBUG_MODE_MORE
    else if (type == "" && units != "") {
        writelog("\nWarning: XML node "  + node.name() +
                 "has a units attribute, \"" + units + "\","
                 "but no conversion was done because the getFloat() command didn't have a type\n");
    }
#endif
    return fctr*x;
}

//====================================================================================================================
bool getOptionalFloat(const Cantera::XML_Node& parent,
                      const std::string& name,
                      doublereal& fltRtn,
                      const std::string type)
{
    if (parent.hasChild(name)) {
        fltRtn= getFloat(parent, name, type);
        return true;
    }
    return false;
}
//====================================================================================================================
//  Get an optional floating-point value from a child element.
/*
 *  Returns a doublereal value for the child named 'name' of element 'parent'. If
 *  'type' is supplied and matches a known unit type, unit
 *  conversion to SI will be done if the child element has an attribute
 *  'units'.
 *
 *
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &State_XMLNode;
 doublereal pres = OneAtm;
 bool exists = getOptionalFloat(State_XMLNode, "pressure", pres, "toSI");
 @endverbatim
 *
 *  reads the corresponding XML file:
 *  @verbatim
 <state>
   <pressure units="Pa"> 101325.0 </pressure>
 <\state>
 @endverbatim
 *
 *   @param parent reference to the XML_Node object of the parent XML element
 *   @param name   Name of the XML child element
 *   @param fltRtn Float Return. It will be overridden if the XML
 *                 element exists.
 *   @param type   String type. Currently known types are "toSI"
 *                 and "actEnergy",
 *                 and "" , for no conversion. The default value is "",
 *                 which implies that no conversion is allowed.
 *
 * @return returns true if the child element named "name" exists
 */
doublereal getFloatDefaultUnits(const Cantera::XML_Node& parent, std::string name,
                                std::string defaultUnits, std::string type)
{

    doublereal fctr = 1.0;
    if (defaultUnits == "") {
        throw CanteraError("getFloatDefaultUnits",
                           "need to supply an actual value of defaultUnits");
    }
    if (type == "actEnergy") {
        fctr = actEnergyToSI(defaultUnits);
    } else if (type == "toSI") {
        fctr = toSI(defaultUnits);
    } else if (defaultUnits == "temperature") {
        fctr = toSI(defaultUnits);
    } else if (type == "density") {
        fctr = toSI(defaultUnits);
    } else if (type == "pressure") {
        fctr = toSI(defaultUnits);
    } else {
        throw CanteraError("getFloatDefaultUnits",
                           "type of units must be supplied and understood");
    }
    doublereal val = getFloat(parent, name, type);
    val /= fctr;
    return val;
}
//====================================================================================================================
//  Get an optional model name from a named child node.
/*
 *  Returns the model name attribute for the child named 'nodeName' of element 'parent'.
 *  Note, it's optional for the child node to exist
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 std::string modelName = "";
 bool exists = getOptionalModel(transportNode, "compositionDependence",
                  modelName);
 @endverbatim
 *
 *  Reads the corresponding XML file:
 *
 *  @verbatim
  <transport>
    <compositionDependence model="Solvent_Only"/>
  </transport>
 @endverbatim
 *
 *   On return modelName is set to "Solvent_Only".
 *
 *   @param parent     Reference to the XML_Node object of the parent XML element
 *   @param nodeName   Name of the XML child element
 *   @param modelName  On return this contains the contents of the model attribute
 *
 *   @return True if the nodeName XML node exists. False otherwise.
 */
bool getOptionalModel(const Cantera::XML_Node& parent, const std::string nodeName,
                      std::string& modelName)
{
    if (parent.hasChild(nodeName)) {
        const XML_Node& node = parent.child(nodeName);
        modelName = node["model"];
        return true;
    }
    return false;
}
//====================================================================================================================
//  Get an integer value from a child element.
/*
 *  Returns an integer value for the child named 'name' of element 'parent'.
 *
 *  Note, it's an error for the child element not to exist.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
 const XML_Node &State_XMLNode;
 int number = 1;
 if (state_XMLNode.hasChild("NumProcs")) {
 number = getInteger(State_XMLNode, "numProcs");
 }
 @endverbatim
 *
 *  reads the corresponding XML file:
 *  @verbatim
 <state>
 <numProcs> 10 <\numProcs>
 <\state>
 @endverbatim
 *
 *   @param parent reference to the XML_Node object of the parent XML element
 *   @param name   Name of the XML child element
 */
int getInteger(const Cantera::XML_Node& parent, std::string name)
{
    if (!parent.hasChild(name)) {
        throw CanteraError("getInteger (called from XML Node \"" +
                           parent.name() + "\"): ",
                           "no child XML element named " + name);
    }
    const XML_Node& node = parent.child(name);
    int x, x0, x1;
    string units, vmin, vmax;
    x = atoi(node().c_str());
    x0 = -9999999;
    x1 =  9999999;
    vmin = node["min"];
    vmax = node["max"];
    if (vmin != "") {
        x0 = atoi(vmin.c_str());
        if (x < x0) {
            writelog("\nWarning: value "+node()+" is below lower limit of "
                     +vmin+".\n");
        }
    }
    if (node["max"] != "") {
        x1 = atoi(vmax.c_str());
        if (x > x1) {
            writelog("\nWarning: value "+node()+" is above upper limit of "
                     +vmax+".\n");
        }
    }
    return x;
}
//====================================================================================================================
//  This function reads the current node or a  child node of the current node
//  with the default name, "floatArray", with a value field
//  consisting of a comma separated list of floats
/*
 *   This function will read either the current XML node or a  child node
 *   to the current XML node, with the
 *   name "floatArray". It will have a title attribute, and the body
 *   of the XML node will be filled out with a comma separated list of
 *   doublereals.
 *     Get an array of floats from the XML Node. The argument field
 *   is assumed to consist of an arbitrary number of comma
 *   separated floats, with an arbitrary amount of white space
 *   separating each field.
 *      If the node array has an units attribute field, then
 *   the units are used to convert the floats, iff convert is true.
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &State_XMLNode;
   vector_fp v;
   bool convert = true;
   unitsString = "";
   nodeName="floatArray";
   getFloatArray(State_XMLNode, v, convert, unitsString, nodeName);
 @endverbatim
 *
 *  reads the corresponding XML file:
 *
 *  @verbatim
 <state>
   <floatArray  units="m3">   32.4, 1, 100. <\floatArray>
 <\state>
 @endverbatim
 *
 *  Will produce the vector
 *
 *         v[0] = 32.4
 *         v[1] = 1.0
 *         v[2] = 100.
 *
 *
 *   @param  node         XML parent node of the floatArray
 *   @param  v            Output vector of floats containing the floatArray information.
 *   @param  convert      Conversion to SI is carried out if this boolean is
 *                        True. The default is true.
 *   @param  unitsString  String name of the type attribute. This is an optional
 *                        parameter. The default is to have an empty string.
 *                        The only string that is recognized is actEnergy.
 *                        Anything else has no effect. This affects what
 *                        units converter is used.
 *   @param  nodeName     XML Name of the XML node to read.
 *                        The default value for the node name is floatArray
 *
 *   @return              Returns the number of floats read
 *
 *  @note change the v to a std::vector to eliminate a doxygen error. No idea why doxygen needs this.
 */
size_t getFloatArray(const Cantera::XML_Node& node, std::vector<doublereal> & v,
                     const bool convert, const std::string unitsString,
                     const std::string nodeName)
{
    std::string::size_type icom;
    string numstr;
    doublereal dtmp;
    string nn = node.name();
    const Cantera::XML_Node* readNode = &node;
    if (nn != nodeName) {
        vector<Cantera::XML_Node*> ll;
        node.getChildren(nodeName, ll);
        if (ll.size() == 0) {
            throw CanteraError("getFloatArray",
                               "wrong xml element type/name: was expecting "
                               + nodeName + "but accessed " + node.name());
        } else {
            readNode = ll[0];
            ll.clear();
            readNode->getChildren("floatArray", ll);
            if (ll.size() > 0) {
                readNode = ll[0];
            }
        }
    }

    v.clear();
    doublereal vmin = Undef, vmax = Undef;

    doublereal funit = 1.0;
    /*
     * Get the attributes field, units, from the XML node
     */
    std::string units = (*readNode)["units"];
    if (units != "" && convert) {
        if (unitsString == "actEnergy" && units != "") {
            funit = actEnergyToSI(units);
        } else if (unitsString != "" && units != "") {
            funit = toSI(units);
        }
    }

    if ((*readNode)["min"] != "") {
        vmin = atofCheck((*readNode)["min"].c_str());
    }
    if ((*readNode)["max"] != "") {
        vmax = atofCheck((*readNode)["max"].c_str());
    }

    doublereal vv;
    std::string val = readNode->value();
    while (1 > 0) {
        icom = val.find(',');
        if (icom != string::npos) {
            numstr = val.substr(0,icom);
            val = val.substr(icom+1,val.size());
            dtmp = atofCheck(numstr.c_str());
            v.push_back(dtmp);
        } else {
            /*
             * This little bit of code is to allow for the
             * possibility of a comma being the last
             * item in the value text. This was allowed in
             * previous versions of Cantera, even though it
             * would appear to be odd. So, we keep the
             * possibilty in for backwards compatibility.
             */
            if (!val.empty()) {
                dtmp = atofCheck(val.c_str());
                v.push_back(dtmp);
            }
            break;
        }
        vv = v.back();
        if (vmin != Undef && vv < vmin - Tiny) {
            writelog("\nWarning: value "+fp2str(vv)+
                     " is below lower limit of " +fp2str(vmin)+".\n");
        }
        if (vmax != Undef && vv > vmax + Tiny) {
            writelog("\nWarning: value "+fp2str(vv)+
                     " is above upper limit of " +fp2str(vmin)+".\n");
        }
    }
    for (size_t n = 0; n < v.size(); n++) {
        v[n] *= funit;
    }
    return v.size();
}
//====================================================================================================================
size_t getNamedFloatArray(const Cantera::XML_Node& parentNode, const std::string& nodeName, std::vector<doublereal> & v,
                        const bool convert, const std::string unitsString)
{
    std::string::size_type icom;
    std::string numstr;
    doublereal dtmp;
    std::string nn = parentNode.name();
    v.clear();
    const Cantera::XML_Node* readNode = parentNode.findByName(nodeName);
    if (!readNode) {
        return 0;
    }

    doublereal vmin = Undef;
    doublereal vmax = Undef;
    doublereal funit = 1.0;
    /*
     * Get the attributes field, units, from the XML node
     */
    std::string units = (*readNode)["units"];
    if (units != "" && convert) {
        if (unitsString == "actEnergy" && units != "") {
            funit = actEnergyToSI(units);
        } else if (unitsString != "" && units != "") {
            funit = toSI(units);
        }
    }

    if ((*readNode)["min"] != "") {
        vmin = atofCheck((*readNode)["min"].c_str());
    }
    if ((*readNode)["max"] != "") {
        vmax = atofCheck((*readNode)["max"].c_str());
    }

    size_t expectedSize = 0;
    nn = (*readNode)["size"];
    expectedSize = atoi(nn.c_str());

    nn = (*readNode)["vtype"];
    if (nn != "floatArray") {
        throw CanteraError("getNamedFloatArray",
                           "node named " + nodeName + "didn't have correct vtype");
    }

    doublereal vv;
    std::string val = readNode->value();
    while (1 > 0) {
        icom = val.find(',');
        if (icom != string::npos) {
            numstr = val.substr(0,icom);
            val = val.substr(icom+1,val.size());
            dtmp = atofCheck(numstr.c_str());
            v.push_back(dtmp);
        } else {
            /*
             * This little bit of code is to allow for the
             * possibility of a comma being the last
             * item in the value text. This was allowed in
             * previous versions of Cantera, even though it
             * would appear to be odd. So, we keep the
             * possibilty in for backwards compatibility.
             */
            size_t nlen = strlen(val.c_str());
            if (nlen > 0) {
                dtmp = atofCheck(val.c_str());
                v.push_back(dtmp);
            }
            break;
        }
        vv = v.back();
        if (vmin != Undef && vv < vmin - Tiny) {
            writelog("\nWarning: value "+fp2str(vv)+
                     " is below lower limit of " +fp2str(vmin)+".\n");
        }
        if (vmax != Undef && vv > vmax + Tiny) {
            writelog("\nWarning: value "+fp2str(vv)+
                     " is above upper limit of " +fp2str(vmin)+".\n");
        }
    }
    size_t nv = v.size();
    for (size_t n = 0; n < nv; n++) {
        v[n] *= funit;
    }
    if (nv != expectedSize) {
        throw CanteraError("getNamedFloatArray",
                           "node named " + nodeName + "didn't have correct number of floats"
                           + int2str(expectedSize) + " vs " + int2str(nv));
    }
    return nv;
}
//====================================================================================================================
// This routine is used to interpret the value portions of XML
// elements that contain colon separated pairs.
/*
 *  These are used, for example, in describing the element
 *  composition of species.
 *
 *       <atomArray> H:4 C:1 <atomArray\>
 *
 *  The string is first separated into a string vector according
 * to the location of white space. Then each string is again
 * separated into two parts according to the location of a
 * colon in the string. The first part of the string is
 * used as the key, while the second part of the string is
 * used as the value, in the return map.
 * It is an error to not find a colon in each string pair.
 *
 *  @param node Current node
 *  @param m    Output Map containing the pairs of values found
 *              in the XML Node
 */
void getMap(const Cantera::XML_Node& node, std::map<std::string, std::string>& m)
{
    std::vector<std::string> v;
    getStringArray(node, v);
    std::string key, val;
    int n = static_cast<int>(v.size());
    string::size_type icolon;
    for (int i = 0; i < n; i++) {
        icolon = v[i].find(":");
        if (icolon == string::npos) {
            throw CanteraError("getMap","missing colon in map entry ("
                               +v[i]+")");
        }
        key = v[i].substr(0,icolon);
        val = v[i].substr(icolon+1, v[i].size());
        m[key] = val;
    }
}
//====================================================================================================================
// This function interprets the value portion of an XML element
// as a series of "Pairs" separated by white space.
/*
 * Each pair consists of nonwhite-space characters.
 * The first ":" found in the pair string is used to separate
 * the string into two parts. The first part is called the "key"
 * The second part is called the "val".
 * String vectors of key[i] and val[i] are returned in the
 * argument list.
 * Warning: No spaces are allowed in each pair. Quotes get included as part
 *          of the string.
 *   Example: @verbatim
     <xmlNode>
        red:112    blue:34
        green:banana
     </xmlNode>
            @endverbatim
 *
 * Returns:
 *          key       val
 *     0:   "red"     "112"
 *     1:   "blue"    "34"
 *     2:   "green"   "banana"
 *
 *
 *  @param node             XML Node
 *  @param key              Vector of keys for each entry
 *  @param val              Vector of values for each entry
 *
 *  @return Returns the number of pairs found
 */
int getPairs(const Cantera::XML_Node& node, std::vector<std::string>& key,
             std::vector<std::string>& val)
{
    vector<string> v;
    getStringArray(node, v);
    int n = static_cast<int>(v.size());
    string::size_type icolon;
    for (int i = 0; i < n; i++) {
        icolon = v[i].find(":");
        if (icolon == string::npos) {
            throw CanteraError("getPairs","Missing a colon in the Pair entry ("
                               +v[i]+")");
        }
        key.push_back(v[i].substr(0,icolon));
        val.push_back(v[i].substr(icolon+1, v[i].size()));
        //cout << "getPairs: " << key.back() << " " << val.back() << endl;
    }
    return n;
}
//====================================================================================================================
// This function interprets the value portion of an XML element
// as a series of "Matrix ids and entries" separated by white space.
/*
 * Each pair consists of nonwhite-space characters.
 * The first two ":" found in the pair string is used to separate
 * the string into three parts. The first part is called the first
 * key. The second part is the second key. Both parts must match
 * an entry in the keyString1 and keyString2, respectively,
 * in order to provide a location to
 * place the object in the matrix.
 * The third part is called the value. It is expected to be
 * a double. It is translated into a double and placed into the
 * correct location in the matrix.
 *
 * Warning: No spaces are allowed in each triplet. Quotes are part
 *          of the string.
 *   Example
 *         keyString = red, blue, black, green
 *    <xmlNode>
 *        red:green:112
 *        blue:black:3.3E-23
 *
 *    </xmlNode>
 *
 * Returns:
 *     retnValues(0, 3) = 112
 *     retnValues(1, 2) = 3.3E-23
 *
 *
 *  @param node          XML Node containing the information for the matrix
 *  @param keyStringRow  Key string for the row
 *  @param keyStringCol  Key string for the column entries
 *  @param returnValues  Return Matrix.
 *  @param convert       If this is true, and if the node has a units
 *                       attribute, then conversion to si units is carried
 *                       out. Default is true.
 *  @param matrixSymmetric  If true entries are made so that the matrix
 *                       is always symmetric. Default is false.
 */
void getMatrixValues(const Cantera::XML_Node& node,
                     const std::vector<std::string>& keyStringRow,
                     const std::vector<std::string>& keyStringCol,
                     Cantera::Array2D& retnValues, const bool convert,
                     const bool matrixSymmetric)
{
    size_t szKey1 = keyStringRow.size();
    size_t szKey2 = keyStringCol.size();
    size_t nrow   = retnValues.nRows();
    size_t ncol   = retnValues.nColumns();
    if (szKey1 > nrow) {
        throw CanteraError("getMatrixValues",
                           "size of key1 greater than numrows");
    }
    if (szKey2 > ncol) {
        throw CanteraError("getMatrixValues",
                           "size of key2 greater than num cols");
    }
    if (matrixSymmetric) {
        if (nrow != ncol) {
            throw CanteraError("getMatrixValues",
                               "nrow != ncol for a symmetric matrix");
        }
    }

    /*
     * Get the attributes field, units, from the XML node
     * and determine the conversion factor, funit.
     */
    doublereal funit = 1.0;
    string units = node["units"];
    if (units != "" && convert) {
        funit = toSI(units);
    }

    string key1;
    string key2;
    string rmm;
    string val;
    vector<string> v;
    getStringArray(node, v);
    string::size_type icolon;
    for (size_t i = 0; i < v.size(); i++) {
        icolon = v[i].find(":");
        if (icolon == string::npos) {
            throw CanteraError("getMatrixValues","Missing two colons ("
                               +v[i]+")");
        }
        key1 = v[i].substr(0,icolon);
        rmm = v[i].substr(icolon+1, v[i].size());
        icolon = rmm.find(":");
        if (icolon == string::npos) {
            throw CanteraError("getMatrixValues","Missing one colon ("
                               +v[i]+")");
        }
        key2 = rmm.substr(0,icolon);
        val = rmm.substr(icolon+1, rmm.size());

        size_t icol = npos;
        size_t irow = npos;
        for (size_t j = 0; j < szKey1; j++) {
            if (key1 == keyStringRow[j]) {
                irow = j;
                break;
            }
        }
        if (irow == npos) {
            throw CanteraError("getMatrixValues","Row not matched by string: "
                               + key1);
        }
        for (size_t j = 0; j < szKey2; j++) {
            if (key2 == keyStringCol[j]) {
                icol = j;
                break;
            }
        }
        if (icol == npos) {
            throw CanteraError("getMatrixValues","Col not matched by string: "
                               + key2);
        }
        double dval = atofCheck(val.c_str());
        dval *= funit;
        /*
         * Finally, insert the value;
         */
        retnValues(irow, icol) = dval;
        if (matrixSymmetric) {
            retnValues(icol, irow) = dval;
        }
    }
}
//====================================================================================================================
// This function interprets the value portion of an XML element
// as a string. It then separates the string up into tokens
// according to the location of white space.
/*
 * The separate tokens are returned in the string vector
 *
 * @param node   Node to get the value from
 * @param v      Output vector containing the string tokens
 */
void getStringArray(const Cantera::XML_Node& node, std::vector<std::string>& v)
{
    std::string val = node.value();
    tokenizeString(val, v);
}
//====================================================================================================================
//  This function reads a child node with the default name, "floatArray", with a value
//  consisting of a comma separated list of floats
/*
 *   This function will read a child node to the current XML node, with the
 *   name "floatArray". It will have a title attribute, and the body
 *   of the XML node will be filled out with a comma separated list of
 *   doublereals.
 *     Get an array of floats from the XML Node. The argument field
 *   is assumed to consist of an arbitrary number of comma
 *   separated floats, with an arbitrary amount of white space
 *   separating each field.
 *      If the node array has an units attribute field, then
 *   the units are used to convert the floats, iff convert is true.
 *      This function is a wrapper around the function getFloatArray().
 *
 *  Example:
 *
 * Code snippet:
 *       @verbatim
   const XML_Node &State_XMLNode;
   vector_fp v;
   bool convert = true;
   unitsString = "";
   nodeName="floatArray";
   getFloatArray(State_XMLNode, v, convert, unitsString, nodeName);
 @endverbatim
 *
 *  reads the corresponding XML file:
 *
 *  @verbatim
 <state>
   <floatArray  units="m3">   32.4, 1, 100. <\floatArray>
 <\state>
 @endverbatim
 *
 *  Will produce the vector
 *
 *         v[0] = 32.4
 *         v[1] = 1.0
 *         v[2] = 100.
 *
 *
 *   @param  node         XML parent node of the floatArray
 *   @param  typeString   Returns the type attribute of the current node.
 *   @param  xmin         Returns the minimum value attribute of the
 *                        current node.
 *   @param  xmax         Returns the maximum value attribute of the
 *                        current node.
 *   @param  coeffs       Output vector of floats containing the floatArray information.
 */
void getFunction(const Cantera::XML_Node& node, std::string& type, doublereal& xmin,
                 doublereal& xmax, std::vector<doublereal> & coeffs)
{
    const XML_Node& c = node.child("floatArray");
    coeffs.clear();
    getFloatArray(c,coeffs);
    xmin = Cantera::Undef;
    xmin = Cantera::Undef;
    if (node["min"] != "") {
        xmin = fpValue(node["min"]);
    }
    if (node["max"] != "") {
        xmax = fpValue(node["max"]);
    }
    type = node["type"];
}
//====================================================================================================================
}
