/*************************************************************************************
* *
* ***** OpenThermo ***** *
* Calculation of thermodynamic functions from molecular data *
* Copyright 2008 Konstantin Tokarev <annulen@users.sourceforge.net> *
* and others *
* *
*************************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License. See COPYING for *
* more details *
* *
*************************************************************************************
* Module name : atom.hpp *
* Author : Tokarev. K *
* Last modification : 2008/08/05 *
* Description : This module contains definitions of class 'XMLInputDocument', *
* which is used for structured storage of input data, and *
* useful functions to work with XMLNodes *
* *
*************************************************************************************/
#include "exception.hpp"
#include <iostream>
using namespace std;
#include "xml.hpp"
using namespace xmlpp;
namespace xml_cpp
{
int k;
}
// Something like DTD of input file:
string ValidInputNames[] = { "info",
"units",
// "control",
"thresh",
"job",
"geometry",
"elevels",
"rotors",
"freq" };
string ValidUnitsNames[] = { "inp", "out" };
string ValidInpUnitsAttr[] = { "energy", "length", "p", "t" };
string ValidOutUnitsAttr[] = { "energy" };
string ValidControlAttr[] = { "" };
string ValidThreshAttr[] = { "symmetry_primary", "symmetry_final", "symmetry_maxit",
"q_cutoff", "q_print", "newton_maxstep", "newton_maxit",
"rotation_grid", "balanced_rotor" };
string ValidJobAttr[] = { "type", "p", "t", "tset", "tunediff", "diffdt", "diff2dt", "spin", "classic_rotation" };
string ValidGeometryAttr[] = { "sym", "monoisotopic" };
string ValidElevelNames[] = { "level" };
string ValidLevelAttr[] = { "degen", "energy" };
string ValidRotorsAttr[] = { "theory", "sdet" };
string ValidRotorsNames[] = { "rotor" };
string ValidRotorAttr[] = { "atoms", "bond", "sigma", "theory", "barrier" };
string ValidRotorNames[] = { "pes" };
string ValidPesAttr[] = { "type", "degree" };
string ValidFreqAttr[] = { "type", "style", "scale"};
int ValidInputNamesN = sizeof(ValidInputNames)/sizeof(string);
int ValidUnitsNamesN = sizeof(ValidUnitsNames)/sizeof(string);
int ValidInpUnitsAttrN = sizeof(ValidInpUnitsAttr)/sizeof(string);
int ValidOutUnitsAttrN = sizeof(ValidOutUnitsAttr)/sizeof(string);
int ValidControlAttrN = sizeof(ValidControlAttr)/sizeof(string);
int ValidThreshAttrN = sizeof(ValidThreshAttr)/sizeof(string);
int ValidJobAttrN = sizeof(ValidJobAttr)/sizeof(string);
int ValidGeometryAttrN = sizeof(ValidGeometryAttr)/sizeof(string);
int ValidElevelNamesN = sizeof(ValidElevelNames)/sizeof(string);
int ValidLevelAttrN = sizeof(ValidLevelAttr)/sizeof(string);
int ValidRotorsAttrN = sizeof(ValidRotorsAttr)/sizeof(string);
int ValidRotorsNamesN = sizeof(ValidRotorsNames)/sizeof(string);
int ValidRotorAttrN = sizeof(ValidRotorAttr)/sizeof(string);
int ValidRotorNamesN = sizeof(ValidRotorNames)/sizeof(string);
int ValidPesAttrN = sizeof(ValidPesAttr)/sizeof(string);
int ValidFreqAttrN = sizeof(ValidFreqAttr)/sizeof(string);
// Useful functions to work with XML
string GetCdata (XMLNode * node)
{
string CDataSum("");
XMLNodeList nl;
XMLNodeListIterator it;
try
{
nl = node->children("cdata");
}
catch (xmlerror e)
{
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
cerr << "Error: " << e.get_string() << endl;
return CDataSum;
}
for(it=nl.begin(); it!=nl.end(); it++)
CDataSum += (*it)->data();
return CDataSum;
}
/*string GetAttr(XMLNode * node, string name)
{
XMLAttributes pia=node->get_attrmap();
XMLAttributes::const_iterator aIt;
for(aIt=pia.begin(); aIt!=pia.end(); aIt++)
{
XMLAttributes::value_type attr = *aIt;
if (attr.first == name)
return attr.second;
}
return "";
}*/
int CountNodes (XMLNode * node, string name)
{
int N=0;
XMLNodeList nl;
XMLNodeListIterator it;
try
{
nl = node->children(name);
}
catch (xmlerror e)
{
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
cerr << "Error: " << e.get_string() << endl;
return 0;
}
for(it=nl.begin(); it!=nl.end(); it++)
N++;
return N;
}
bool XML_True (string s)
{
if ((s == "true") || (s == "yes") || (s == "y") || (s == ".true.") || (s == ".t."))
return true;
else if ((s == "false") || (s == "no") || (s == "n") || (s == ".false.") || (s == ".f."))
return false;
else
throw GenericError("Invalid logical value " + s);
return false;
}
void XML_ShowError (xmlerror e, XMLContextPtr ctx, string FileName)
{
cerr << "Error: " << e.get_string() << endl;
if(e.get_info().size())
{
cerr << "File: " << e.get_info().c_str() << endl;
}
if(e.get_error()!=xml_filename_invalid &&
e.get_error()!=xml_file_access)
{
e.show_error(ctx);
e.show_line(ctx, FileName);
}
}
// Internal methods
bool CheckNode (XMLNode * node, string * ValidNames, int ValidNamesN, bool noattr=true);
bool CheckAttr (XMLNode * node, string * ValidAttr, int ValidAttrN, bool nosubnodes=true);
inline bool CheckNodeAttr (XMLNode * node, string * ValidNames, int ValidNamesN, string * ValidAttr, int ValidAttrN);
inline bool CheckOnce (XMLNodeList & nl);
inline void PrintList (string * Names, int N);
// Definitions of XMLInputFile
XMLInputFile::XMLInputFile()
{
}
XMLInputFile::XMLInputFile(XMLContextPtr pctx)
{
contextptr=pctx;
}
XMLInputFile::~XMLInputFile()
{
}
void XMLInputFile::load_file(string filename)
{
XMLDocument::load_file(filename);
// this variables are set false on exception
hasUnits = true;
hasUnitsInp = true;
hasUnitsOut = true;
hasControl = true;
hasThresh = true;
hasJob = true;
hasElectronicLevels = true;
hasRotors = true;
// this variables aren't set on exception
hasFreqHarm = false;
hasFreqAnh = false;
hasFreqExp = false;
isValid = false;
Validate();
}
bool XMLInputFile::is_valid()
{
return isValid;
}
bool XMLInputFile::has_units_inp()
{
return hasUnitsInp;
}
bool XMLInputFile::has_units_out()
{
return hasUnitsOut;
}
bool XMLInputFile::has_control()
{
return hasControl;
}
bool XMLInputFile::has_thresh()
{
return hasThresh;
}
bool XMLInputFile::has_job()
{
return hasJob;
}
bool XMLInputFile::has_electronic_levels()
{
return hasElectronicLevels;
}
bool XMLInputFile::has_rotors()
{
return hasRotors;
}
bool XMLInputFile::has_freq_anh()
{
return hasFreqAnh;
}
bool XMLInputFile::has_freq_exp()
{
return hasFreqExp;
}
void XMLInputFile::Validate()
{
using namespace xml_cpp;
XMLNodeList nl;
XMLNodeListIterator it;
// Validate Root
try
{
nl = children("input");
}
catch (xmlerror e)
{
if ((e.get_error() == xml_childlist_empty) || (e.get_error() == xml_name_not_found))
cerr << "File reading error: <input> group not found!" << endl;
else
cerr << e.get_string() << endl;
return;
}
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <input> groups found!" << endl;
return;
}
if (!CheckNode(firstchild("input"), ValidInputNames, ValidInputNamesN))
return;
// Validate SubNodes
// <info> ?
// <units>
try
{
nl=firstchild("input")->children("units");
}
catch (xmlerror e)
{
// it's ok if no units group
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
{
cerr << e.get_string() << endl;
return;
}
else
hasUnits = false;
}
if (hasUnits)
{
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <units> groups found!" << endl;
return;
}
if (!CheckNode(firstchild("input")->firstchild("units"), ValidUnitsNames, ValidUnitsNamesN))
return;
try
{
nl=firstchild("input")->firstchild("units")->children("inp");
}
catch (xmlerror e)
{
if (e.get_error() != xml_name_not_found)
{
cerr << e.get_string() << endl;
return;
}
else
hasUnitsInp = false;
}
if (hasUnitsInp)
{
if (!CheckOnce(nl))
{
cerr << "Error in group <units>: " << k << " <inp> subgroups found!" << endl;
return;
}
if (!CheckAttr(firstchild("input")->firstchild("units")->firstchild("inp"), ValidInpUnitsAttr, ValidInpUnitsAttrN))
return;
}
try
{
nl=firstchild("input")->firstchild("units")->children("out");
}
catch (xmlerror e)
{
if (e.get_error() != xml_name_not_found)
{
cerr << e.get_string() << endl;
return;
}
else
hasUnitsOut = false;
}
if (hasUnitsOut)
{
if (!CheckOnce(nl))
{
cerr << "Error in group <units>: " << k << " <out> subgroups found!" << endl;
return;
}
if (!CheckAttr(firstchild("input")->firstchild("units")->firstchild("out"), ValidOutUnitsAttr, ValidOutUnitsAttrN))
return;
}
}
else // hasUnits
hasUnitsInp = hasUnitsOut = false;
// <conrol>
// cerr << "control ok" << endl;
// <thresh>
try
{
nl=firstchild("input")->children("thresh");
}
catch (xmlerror e)
{
// it's ok if no job group
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
{
cerr << e.get_string() << endl;
return;
}
else
hasThresh = false;
}
if (hasThresh)
{
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <thresh> groups found!" << endl;
return;
}
if (!CheckAttr(firstchild("input")->firstchild("thresh"), ValidThreshAttr, ValidThreshAttrN))
return;
}
// <job>
try
{
nl=firstchild("input")->children("job");
}
catch (xmlerror e)
{
// it's ok if no job group
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
{
cerr << e.get_string() << endl;
return;
}
else
hasJob = false;
}
if (hasJob)
{
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <job> groups found!" << endl;
return;
}
if (!CheckAttr(firstchild("input")->firstchild("job"), ValidJobAttr, ValidJobAttrN))
return;
}
// cerr << "job ok" << endl;
// <geometry>
try
{
nl=firstchild("input")->children("geometry");
}
catch (xmlerror e)
{
if ((e.get_error() == xml_childlist_empty) || (e.get_error() == xml_name_not_found))
cerr << "Fatal error: <geometry> group not found!" << endl;
else
cerr << e.get_string() << endl;
return;
}
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <geometry> groups found!" << endl;
return;
}
if (!CheckAttr(firstchild("input")->firstchild("geometry"), ValidGeometryAttr, ValidGeometryAttrN, false))
return;
// cerr << "geometry ok" << endl;
// <elevels>
try
{
nl=firstchild("input")->children("elevels");
}
catch (xmlerror e)
{
// it's ok if no elevels group
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
{
cerr << e.get_string() << endl;
return;
}
else
hasElectronicLevels = false;
}
if (hasElectronicLevels)
{
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <elevels> groups found!" << endl;
return;
}
try
{
nl=firstchild("input")->firstchild("elevels")->children();
}
catch (xmlerror e)
{
if (e.get_error() == xml_childlist_empty)
cerr << "File reading error: <elevels> group must contain at least one <level> subgroup!" << endl;
else
cerr << e.get_string() << endl;
return;
}
if (!CheckNode(firstchild("input")->firstchild("elevels"), ValidElevelNames, ValidElevelNamesN))
return;
try
{
nl=firstchild("input")->firstchild("elevels")->children("level");
}
catch (xmlerror e)
{
if ((e.get_error() == xml_childlist_empty) || (e.get_error() == xml_name_not_found))
cerr << "File reading error: <elevels> group must contain at least one <level> subgroup!" << endl;
else
cerr << e.get_string() << endl;
return;
}
for(it=nl.begin(); it!=nl.end(); it++)
if (!CheckAttr(*it, ValidLevelAttr, ValidLevelAttrN))
return;
}
// cerr << "elevels ok" << endl;
// <rotors>
try
{
nl=firstchild("input")->children("rotors");
}
catch (xmlerror e)
{
// it's ok if no rotors group
if ((e.get_error() != xml_childlist_empty) && (e.get_error() != xml_name_not_found))
{
cerr << e.get_string() << endl;
return;
}
else
hasRotors = false;
}
if (hasRotors)
{
if (!CheckOnce(nl))
{
cerr << "File reading error: " << k << " <rotors> groups found!" << endl;
return;
}
try
{
nl=firstchild("input")->firstchild("rotors")->children();
}
catch (xmlerror e)
{
if (e.get_error() == xml_childlist_empty)
cerr << "File reading error: <rotors> group must contain at least one <rotor> subgroup!" << endl;
else
cerr << e.get_string() << endl;
return;
}
if (!CheckNodeAttr(firstchild("input")->firstchild("rotors"),
ValidRotorsNames, ValidRotorsNamesN, ValidRotorsAttr, ValidRotorsAttrN))
return;
try
{
nl=firstchild("input")->firstchild("rotors")->children("rotor");
}
catch (xmlerror e)
{
if (e.get_error() == xml_name_not_found)
cerr << "File reading error: <rotors> group must contain at least one <rotor> subgroup!" << endl;
else
cerr << e.get_string() << endl;
return;
}
for(it=nl.begin(); it!=nl.end(); it++)
if (!CheckNodeAttr(*it, ValidRotorNames, ValidRotorNamesN, ValidRotorAttr, ValidRotorAttrN))
return;
}
// cerr << "rotors ok" << endl;
// <freq>
try
{
nl=firstchild("input")->children("freq");
}
catch (xmlerror e)
{
if ((e.get_error() == xml_childlist_empty) || (e.get_error() == xml_name_not_found))
cerr << "File reading error: <freq> group(s) not found!" << endl;
else
cerr << e.get_string() << endl;
return;
}
int harm=0, anharm=0, exper=0;
for(it=nl.begin(); it!=nl.end(); it++)
{
if (!CheckAttr(*it, ValidFreqAttr, ValidFreqAttrN, false)) // can contain subnode "cdata"
return;
string s("");
s =(*it)->get_attr("type");
if ((s=="") || (s=="harm"))
{
hasFreqHarm = true;
harm++;
}
else if ((*it)->get_attr("scale") != "")
{
cerr << "File reading error: scale can be specified only for \"harmonic\" <freq> group!\n";
return;
}
if (s=="anharm")
{
hasFreqAnh = true;
anharm++;
}
if (s=="exp")
{
hasFreqExp = true;
exper++;
}
if ((harm==0) && ( (anharm>0) || (exper>0) ))
{
cerr << "File reading error: \"harmonic\" <freq> group must be first of <freq> groups!\n";
return;
}
}
if (harm == 0)
{
cerr << "File reading error: no \"harmonic\" <freq> group found!" << endl;
return;
}
if (harm > 1)
{
cerr << "File reading error: " << harm << " \"harmonic\" <freq> groups found!" << endl;
return;
}
if (anharm > 1)
{
cerr << "File reading error: " << anharm << " \"anharmonic\" <freq> groups found!" << endl;
return;
}
if (exper > 1)
{
cerr << "File reading error: " << exper << " \"experimental\" <freq> groups found!" << endl;
return;
}
if ((anharm > 0) && (exper > 0))
{
cerr << "File reading error: both \"anharmonic\" and \"experimental\" <freq> groups found!" << endl;
return;
}
// cerr << "freq ok" << endl;
// Let's assume all is OK
isValid = true;
}
bool CheckNode (XMLNode * node, string * Names, int N, bool noattr)
{
XMLNodeList nl;
XMLNodeListIterator it;
try
{
nl = node->children();
}
catch (xmlerror e)
{
if (e.get_error() == xml_childlist_empty)
{
cerr << "File reading error: <" << node->name().c_str() << "> group must contain subgroups!\n";
cerr << "Valid group names are: ";
PrintList(Names, N);
cerr << endl;
}
else
cerr << "File reading error: " << e.get_string() << endl;
return false;
}
for(it=nl.begin(); it!=nl.end(); it++)
{
int match=0;
for (int i=0; i<N; i++)
match += ((*it)->name() == Names[i]);
if (match == 0)
{
if ((*it)->name()=="cdata")
//cerr << "Error in group <" << node->name().c_str() << ">: Character data is not allowed!" << endl;
cerr << "Error in group <" << node->name().c_str() << ">: Unknown expression\n" << (*it)->data();
else
cerr << "Error in group <" << node->name().c_str() << ">: illegal subgroup <" << (*it)->name().c_str() << ">\n";
cerr << "Valid keywords are: ";
PrintList(Names, N);
cerr << endl;
return false;
}
}
if (noattr)
{
XMLAttributes pia=node->get_attrmap();
if (pia.begin()!=pia.end())
{
cerr << "Error in group <" << node->name().c_str() << ">: illegal subgroup <" << (*it)->name().c_str() << ">\n";
return false;
}
}
return true;
}
bool CheckAttr (XMLNode * node, string * Names, int N, bool nosubnodes)
{
XMLAttributes pia=node->get_attrmap();
XMLAttributes::const_iterator aIt;
for(aIt=pia.begin(); aIt!=pia.end(); aIt++)
{
int match=0;
XMLAttributes::value_type attr = *aIt;
for (int i=0; i<N; i++)
match += (attr.first == Names[i]);
if (match == 0)
{
cerr << "Error in group <" << node->name().c_str() << ">: illegal keyword " << attr.first.c_str() << "\n";
cerr << "Valid keywords are: ";
PrintList(Names, N);
cerr << endl;
return false;
}
}
if (nosubnodes)
{
try
{
XMLNodeList nl = node->children();
}
catch (xmlerror e)
{
if (e.get_error() == xml_childlist_empty)
return true; // that's good!
else
cerr << "File reading error: " << e.get_string() << endl;
}
cerr << "File reading error: group <" << node->name().c_str() << "> must not have subgroups" << endl;
return false;
}
return true;
}
bool CheckNodeAttr (XMLNode * node, string * ValidNames, int ValidNamesN, string * ValidAttr, int ValidAttrN)
{
XMLNodeList nl;
XMLNodeListIterator it;
XMLAttributes pia=node->get_attrmap();
XMLAttributes::const_iterator aIt;
bool hasChildren = true;
try
{
nl = node->children();
}
catch (xmlerror e)
{
if (e.get_error() == xml_childlist_empty)
hasChildren = false; // it's ok
else
{
cerr << "File reading error: " << e.get_string() << endl;
return false;
}
}
if (hasChildren)
{
for(it=nl.begin(); it!=nl.end(); it++)
{
int match=0;
for (int i=0; i<ValidNamesN; i++)
match += ((*it)->name().c_str() == ValidNames[i]);
if (match == 0)
{
if ((*it)->name()=="cdata")
cerr << "Error in group <" << node->name().c_str() << ">: Character data is not allowed!" << endl;
else
cerr << "Error in group <" << node->name().c_str() << ">: illegal subgroup <" << (*it)->name().c_str() << ">\n";
cerr << "Valid keywords are: ";
PrintList(ValidNames, ValidNamesN);
cerr << endl;
return false;
}
}
}
for(aIt=pia.begin(); aIt!=pia.end(); aIt++)
{
int match=0;
XMLAttributes::value_type attr = *aIt;
for (int i=0; i<ValidAttrN; i++)
match += (attr.first == ValidAttr[i]);
if (match == 0)
{
cerr << "Error in group <" << node->name().c_str() << ">: illegal keyword " << attr.first.c_str() << "\n";
cerr << "Valid keywords are: ";
PrintList(ValidAttr, ValidAttrN);
cerr << endl;
return false;
}
}
return true;
}
bool CheckOnce (XMLNodeList & nl)
{
using namespace xml_cpp;
XMLNodeListIterator it;
k = 0;
for(it=nl.begin(); it!=nl.end(); it++)
k++;
if (k>1)
{
//cerr << "File reading error: " << k << " <""> groups found!" << endl;
return false;
}
return true;
}
void PrintList (string * Names, int N)
{
for (int i=0; i<N; i++)
{
cerr << "\n";
for (int j=0; j<4; j++, i++)
cerr << " " << Names[i].c_str();
}
}