/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2012 University of Bonn. All rights reserved. * Copyright (C) 2013 Frederik Heber. All rights reserved. * * * This file is part of MoleCuilder. * * MoleCuilder is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * MoleCuilder is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MoleCuilder. If not, see . */ /* * XmlParser.cpp * * Created on: Mar 23, 2012 * Author: heber */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include #include #include "CodePatterns/Log.hpp" #include "CodePatterns/Verbose.hpp" #include "XmlParser.hpp" #include "Atom/atom.hpp" #include "Box.hpp" #include "Element/element.hpp" #include "Element/periodentafel.hpp" #include "molecule.hpp" #include "World.hpp" #include "Parser/pugixml/pugixml.hpp" // static instances FormatParser< xml >::additionalAtomInfo FormatParser< xml >::defaultAtomInfo; // declare specialized static variables const std::string FormatParserTrait::name = "xml"; const std::string FormatParserTrait::suffix = "xml"; const ParserTypes FormatParserTrait::type = xml; const char *box_axis[NDIM] = { "box_a", "box_b", "box_c" }; /** * Constructor. */ FormatParser< xml >::FormatParser() : FormatParser_common(NULL) {} /** * Destructor. */ FormatParser< xml >::~FormatParser() {} Vector toVector(const std::string &value) { std::stringstream inputstream(value); Vector returnVector; for (size_t i=0;i> std::setprecision(16) >> returnVector[i]; return returnVector; } double toDouble(const std::string &value) { std::stringstream inputstream(value); double returnDouble; inputstream >> std::setprecision(16) >> returnDouble; return returnDouble; } std::string fromVector(const Vector&a) { std::stringstream returnstring; for (size_t i=0;i::load(std::istream* file) { // create the molecule molecule * const newmol = World::getInstance().createMolecule(); newmol->ActiveFlag = true; // load file into xml tree pugi::xml_document doc; doc.load(*file); // header pugi::xml_node scafacos_test = doc.root().child("scafacos_test"); data.name = toString(scafacos_test.attribute("name").value()); data.description = toString(scafacos_test.attribute("description").value()); data.reference_method = toString(scafacos_test.attribute("reference_method").value()); data.error_potential = scafacos_test.attribute("error_potential").as_double(); data.error_field = scafacos_test.attribute("error_field").as_double(); LOG(1, "INFO: scafacos_test.name is '" << data.name << "'."); newmol->setName(data.name); // configuration information pugi::xml_node configuration = scafacos_test.child("configuration"); data.config.offset = toVector(configuration.attribute("offset").value()); for (size_t i=0; i::load() - periodicity attribute has less than 3 entries."); inputstream >> data.config.periodicity[i]; } } data.config.epsilon = toString(configuration.attribute("epsilon").value()); // use a map to at least give each charge a different element typedef std::map charge_map_t; charge_map_t charge_map; size_t last_atomicnumber = 1; // particles for(pugi::xml_node::iterator iter = configuration.begin(); iter != configuration.end(); ++iter) { struct scafacos::configuration::particle p; p.position = toVector((*iter).attribute("position").value()); p.q = toDouble((*iter).attribute("q").value()); p.potential = toDouble((*iter).attribute("potential").value()); p.field = toVector((*iter).attribute("field").value()); data.config.p.push_back(p); LOG(2, "DEBUG: Parsing particle at " << p.position << "."); atom * const newAtom = World::getInstance().createAtom(); charge_map_t::const_iterator chargeiter = charge_map.find(p.q); if (chargeiter == charge_map.end()) { // create new entry and set iter pointing to it std::pair inserter = charge_map.insert( std::make_pair( p.q, last_atomicnumber++) ); chargeiter = inserter.first; } newAtom->setType(World::getInstance().getPeriode()->FindElement(chargeiter->second)); newAtom->setPosition(p.position); newAtom->setCharge(p.q); newmol->AddAtom(newAtom); additionalAtomInfo atomInfo(p.q, p.potential, p.field); #ifndef NDEBUG std::pair inserter = #endif additionalAtomData.insert( std::make_pair(newAtom->getId(), atomInfo) ); ASSERT( inserter.second, "FormatParser< xml >::load() - atomInfo entry for atom "+toString(newAtom->getId()) +" already present."); } BOOST_FOREACH(const atom *_atom, const_cast(World::getInstance()).getAllAtoms()) LOG(3, "INFO: Atom " << _atom->getName() << " " << *dynamic_cast(_atom) << "."); // refresh atom::nr and atom::name newmol->getAtomCount(); } /** * Saves the \a atoms into as a XYZ file. * * \param file where to save the state * \param atoms atoms to store */ void FormatParser< xml >::save( std::ostream* file, const std::vector &atoms) { LOG(2, "DEBUG: Saving changes to xml."); // fill the structure with updated information const Box &domain = World::getInstance().getDomain(); data.config.box = domain.getM(); for (size_t i=0;i::const_iterator it = atoms.begin(); it != atoms.end(); it++) { struct scafacos::configuration::particle p; const additionalAtomInfo &atomInfo = getAtomData(*(*it)); p.position = (*it)->getPosition(); p.q = (*it)->getCharge(); p.potential = atomInfo.potential; p.field = atomInfo.field; data.config.p.push_back(p); } // create the xml tree pugi::xml_document doc; pugi::xml_attribute attr; // header pugi::xml_node xml_scafacos_test = doc.root().append_child(); xml_scafacos_test.set_name("scafacos_test"); xml_scafacos_test.append_attribute("name").set_value(data.name.c_str()); xml_scafacos_test.append_attribute("description").set_value(data.description.c_str()); xml_scafacos_test.append_attribute("reference_method").set_value(data.reference_method.c_str()); xml_scafacos_test.append_attribute("error_potential").set_value(data.error_potential); xml_scafacos_test.append_attribute("error_field").set_value(data.error_field); // configuration pugi::xml_node xml_configuration = xml_scafacos_test.append_child(); xml_configuration.set_name("configuration"); xml_configuration.append_attribute("offset").set_value(fromVector(data.config.offset).c_str()); for (size_t i=0; i::const_iterator iter = data.config.p.begin(); iter != data.config.p.end();++iter) { pugi::xml_node particle = xml_configuration.append_child(); particle.set_name("particle"); particle.append_attribute("position").set_value(fromVector((*iter).position).c_str()); particle.append_attribute("q").set_value(fromDouble((*iter).q).c_str()); particle.append_attribute("potential").set_value(fromDouble((*iter).potential).c_str()); particle.append_attribute("field").set_value(fromVector((*iter).field).c_str()); } // print standard header and save without declaration *file << "\n"; *file << "\n"; doc.save(*file, "\t", pugi::format_no_declaration, pugi::encoding_utf8); } /** Observer callback when new atom is added to World. * * @param id of atom */ void FormatParser< xml >::AtomInserted(atomId_t id) { std::map::iterator iter = additionalAtomData.find(id); ASSERT(iter == additionalAtomData.end(), "FormatParser< xml >::AtomInserted() - additionalAtomData already present for newly added atom " +toString(id)+"."); // additionalAtomData.insert( std::make_pair(id, additionalAtomInfo()) ); } /** Remove additional AtomData info, when atom has been removed from World. * * @param id of atom */ void FormatParser< xml >::AtomRemoved(atomId_t id) { std::map::iterator iter = additionalAtomData.find(id); // as we do not insert AtomData on AtomInserted, we cannot be assured of its presence // ASSERT(iter != additionalAtomData.end(), // "FormatParser< tremolo >::AtomRemoved() - additionalAtomData is not present for atom " // +toString(id)+" to remove."); if (iter != additionalAtomData.end()) additionalAtomData.erase(iter); } const FormatParser< xml >::additionalAtomInfo& FormatParser< xml >::getAtomData(const atom &_atom) const { { // has its own entry? AtomInfoMap_t::const_iterator iter = additionalAtomData.find(_atom.getId()); if (iter != additionalAtomData.end()) { return iter->second; } } { // father has an entry? AtomInfoMap_t::const_iterator iter = additionalAtomData.find(_atom.GetTrueFather()->getId()); if (iter != additionalAtomData.end()) { return iter->second; } } return defaultAtomInfo; } #define comparator(x,y) if (x != y) { LOG(2, "DEBUG: Mismatch in " << #x << ": " << x << " != " << y); return false; } #define num_comparator(x,y) if (fabs(x - y) > MYEPSILON) { LOG(2, "DEBUG: Numeric mismatch in " << #x << ": " << x << " != " << y << " by " << fabs(x - y) << "."); return false; } bool FormatParser< xml >::scafacos::configuration::particle::operator==(const particle &p) const { comparator(position, p.position) num_comparator(q, p.q) num_comparator(potential, p.potential) comparator(field, p.field) return true; } bool FormatParser< xml >::scafacos::configuration::operator==(const configuration &c) const { comparator(offset, c.offset) comparator(box, c.box) for (size_t i=0;i::const_iterator iter = p.begin(); std::vector::const_iterator citer = c.p.begin(); for (;iter != p.end(); ++iter, ++citer) { if ((*iter) != (*citer)) return false; } return true; } bool FormatParser< xml >::scafacos::operator==(const scafacos &s) const { comparator(name, s.name) comparator(description, s.description) comparator(reference_method, s.reference_method) num_comparator(error_potential, s.error_potential) num_comparator(error_field, s.error_field) if (config != s.config) { return false; } return true; }