/* * Project: MoleCuilder * Description: creates and alters molecular systems * Copyright (C) 2010-2012 University of Bonn. 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 . */ /** \file periodentafel.cpp * * Function implementations for the class periodentafel. * */ // include config.h #ifdef HAVE_CONFIG_H #include #endif #include "CodePatterns/MemDebug.hpp" #include #include #include #include #include #include "CodePatterns/Assert.hpp" #include "CodePatterns/Log.hpp" #include "element.hpp" #include "elements_db.hpp" #include "Helpers/defs.hpp" #include "ion.hpp" #include "periodentafel.hpp" using namespace std; /************************************* Functions for class periodentafel ***************************/ /** constructor for class periodentafel * Initialises start and end of list and resets periodentafel::checkliste to false. */ periodentafel::periodentafel(const bool DoLoad) { if (DoLoad) { ScanPeriodentafel(); } }; /** destructor for class periodentafel * Removes every element and afterwards deletes start and end of list. * TODO: Handle when elements have changed and store databases then */ periodentafel::~periodentafel() { CleanupPeriodtable(); }; /** Adds element to period table list * \param *pointer element to be added * \return iterator to added element */ periodentafel::iterator periodentafel::AddElement(element * pointer) { atomicNumber_t Z = pointer->getAtomicNumber(); ASSERT(!elements.count(Z), "Element is already present."); if (pointer->getAtomicNumber() < 1 && pointer->getAtomicNumber() >= MAX_ELEMENTS) ELOG(0, "Invalid Z number!"); pair res = elements.insert(pair(Z,pointer)); return res.first; }; /** Removes element from list. * \param *pointer element to be removed */ size_t periodentafel::RemoveElement(const element * pointer) { return RemoveElement(pointer->getAtomicNumber()); }; /** Removes element from list. * \param Z element to be removed */ size_t periodentafel::RemoveElement(atomicNumber_t Z) { return elements.erase(Z); }; /** Removes every element from the period table. */ void periodentafel::CleanupPeriodtable() { for(iterator iter=elements.begin();iter!=elements.end();++iter){ delete(*iter).second; } elements.clear(); }; /** Finds an element by its atomic number. * If element is not yet in list, returns NULL. * \param Z atomic number * \return pointer to element or NULL if not found */ const element * periodentafel::FindElement(atomicNumber_t Z) const { const_iterator res = elements.find(Z); return res!=elements.end()?((*res).second):0; }; /** Returns the desired ion to a specific element. * * If the respective element is not in the list, we return * NULL. * \return pointer to an element or NULL if not found */ const element * periodentafel::FindElement(atomicNumber_t Z, const int ionization) { // if not ionization given, fall back to other function if (ionization == 0) { return FindElement(Z); } // element present? const_iterator elementiter = elements.find(Z); if (elementiter == elements.end()) return NULL; const element & element_base = *(elementiter->second); // element has already got ions? IonsPerElement::iterator setiter = ions.find(Z); if (setiter != ions.end()) { // yes, found ion list ionSet::const_iterator res = setiter->second.find(ionization); if (res != setiter->second.end()) { // ion present already element * const _ion = res->second; return _ion; } else { // ion not present yet ion * const _ion = new ion(element_base, ionization); // insert ion setiter->second.insert( std::make_pair( ionization, _ion) ); return _ion; } } else { // no ions yet, create map ion * const _ion = new ion(element_base, ionization); std::pair inserter = ions.insert( std::make_pair(Z, ionSet()) ); // insert ion ASSERT( inserter.second, "periodentafel::FindElement() - could not insert new ionSet to element."); inserter.first->second.insert( std::make_pair( ionization, _ion) ); return _ion; } return NULL; } /** Finds an element by its atomic number. * If element is not yet in list, datas are asked and stored in database. * \param shorthand chemical symbol of the element, e.g. H for hydrogene * \return pointer to element */ const element * periodentafel::FindElement(const string &shorthand) const { element *res = 0; for(const_iterator iter=elements.begin();iter!=elements.end();++iter) { if((*iter).second->getSymbol() == shorthand){ res = (*iter).second; break; } } return res; }; /** Asks for element number and returns pointer to element * \return desired element or NULL */ const element * periodentafel::AskElement() const { const element * walker = NULL; int Z; do { std::cout << "Atomic number Z: "; std::cin >> Z; walker = this->FindElement(Z); // give type } while (walker == NULL); return walker; }; /** Asks for element and if not found, presents mask to enter info. * \return pointer to either present or newly created element */ const element * periodentafel::EnterElement() { atomicNumber_t Z = 0; std::cout << "Atomic number: " << Z; cin >> Z; const element *res = FindElement(Z); if (!res) { // TODO: make this using the constructor std::cout << "Element not found in database, please enter." << std::endl; element *tmp = new element; tmp->Z = Z; std::cout << "Mass: "; cin >> tmp->mass; std::cout << "Name [max 64 chars]: "; cin >> tmp->name; std::cout << "Short form [max 3 chars]: "; cin >> tmp->symbol; AddElement(tmp); return tmp; } return res; }; /******************** Access to iterators ****************************/ periodentafel::const_iterator periodentafel::begin() const{ return elements.begin(); } periodentafel::const_iterator periodentafel::end() const{ return elements.end(); } periodentafel::reverse_iterator periodentafel::rbegin() const{ return reverse_iterator(elements.end()); } periodentafel::reverse_iterator periodentafel::rend() const{ return reverse_iterator(elements.begin()); } /** Prints element data to \a *out. * \param *out outstream */ void periodentafel::OutputElement(ostream * const out, const element *elem) const { *out << elem->getName() << "\t"; *out << elem->getSymbol() << "\t"; *out << elem->getAtomicNumber() << "\t"; *out << elem->getMass() << "\t"; *out << elem->getCovalentRadius() << "\t"; *out << elem->getVanDerWaalsRadius() << std::endl; //*out << elem->getSymbol() << "\t" << fixed << setprecision(11) << showpoint << elem->getMass() << "g/mol\t" << elem->getName() << "\t" << elem->getSymbol() << "\t" << endl; }; /** Prints period table to given stream. * \param output stream */ bool periodentafel::Output(ostream * const output) const { if (output != NULL) { for(elementSet::const_iterator iter = elements.begin(); iter != elements.end(); ++iter) { OutputElement(output, iter->second); } return true; } return false; } /** Scan periodentafel contents from internal databases. * */ void periodentafel::ScanPeriodentafel() { { stringstream input(elementsDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadElementsDatabase(input); ASSERT(status, "General element initialization failed"); } { stringstream input(ElectronegativitiesDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadElectronegativityDatabase(input); ASSERT(status, "Electronegativities entry of element initialization failed"); } { stringstream input(valenceDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadValenceDatabase(input); ASSERT(status, "Valence entry of element initialization failed"); } { stringstream input(orbitalsDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadOrbitalsDatabase(input); ASSERT(status, "Orbitals entry of element initialization failed"); } { stringstream input(HbondangleDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadHBondAngleDatabase(input); ASSERT(status, "HBond angle entry of element initialization failed"); } { stringstream input(HbonddistanceDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadHBondLengthsDatabase(input); ASSERT(status, "HBond distance entry of element initialization failed"); } { stringstream input(ColorDB,ios_base::in); #ifndef NDEBUG bool status = #endif LoadColorDatabase(input); ASSERT(status, "color entry of element initialization failed"); } } /** Loads element list from file. * \param *path to to standard file names */ bool periodentafel::LoadPeriodentafel(const char *path) { ifstream input; bool status = true; bool otherstatus = true; char filename[MAXSTRINGSIZE]; // fill elements DB if (strlen(path)+1+strlen(STANDARDELEMENTSDB) > MAXSTRINGSIZE-1) ELOG(2, "Generated path '" << path << "' will be too long."); filename[0] = '\0'; strncat(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDELEMENTSDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as elements database."); status = status && LoadElementsDatabase(input); input.close(); input.clear(); // fill valence DB per element strncpy(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDELECTRONEGATIVITYDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as electronegativity database."); otherstatus = otherstatus && LoadElectronegativityDatabase(input); input.close(); input.clear(); // fill valence DB per element strncpy(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDVALENCEDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as valence database."); otherstatus = otherstatus && LoadValenceDatabase(input); input.close(); input.clear(); // fill orbitals DB per element strncpy(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDORBITALDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as orbitals database."); otherstatus = otherstatus && LoadOrbitalsDatabase(input); input.close(); input.clear(); // fill H-BondAngle DB per element strncpy(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDHBONDANGLEDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as H bond angle database."); otherstatus = otherstatus && LoadHBondAngleDatabase(input); input.close(); input.clear(); // fill H-BondDistance DB per element strncpy(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDHBONDDISTANCEDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as H bond length database."); otherstatus = otherstatus && LoadHBondLengthsDatabase(input); input.close(); input.clear(); // fill color DB per element strncpy(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDCOLORDB, MAXSTRINGSIZE-strlen(filename)); input.open(filename); if (!input.fail()) LOG(0, "Using " << filename << " as color database."); otherstatus = otherstatus && LoadColorDatabase(input); input.close(); input.clear(); if (!otherstatus){ ELOG(2, "Something went wrong while parsing the other databases!"); } return status; }; /** load the element info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadElementsDatabase(istream &input) { bool status = true; string header1tmp,header2tmp; // std::stringstream parsedelements; // first parse into a map, so we can revert to old status in case something goes wront map parsedElements; if (!input.fail()) { getline(input,header1tmp); getline(input,header2tmp); // skip first two header lines //cout << "First header: " << header1tmp << endl; //cout << "Second header: " << header2tmp << endl; // parsedelements << "Parsed elements:"); while (!input.eof()) { element *neues = new element; input >> neues->name; //input >> ws; input >> neues->symbol; //input >> ws; input >> neues->period; //input >> ws; input >> neues->group; //input >> ws; input >> neues->block; //input >> ws; input >> neues->Z; //input >> ws; input >> neues->mass; //input >> ws; input >> neues->CovalentRadius; //input >> ws; input >> neues->VanDerWaalsRadius; //input >> ws; input >> ws; //neues->Output((ofstream *)&cout); if ((neues->getAtomicNumber() > 0) && (neues->getAtomicNumber() < MAX_ELEMENTS)) { parsedElements[neues->Z] = neues; // parsedelements << " " << *neues); } else { ELOG(2, "Detected empty line or invalid element in elements db, discarding."); // parsedelements << " "); delete(neues); } // when the input is in failed state, we most likely just read garbage if(input.fail()) { ELOG(2, "Error parsing elements db."); status = false; break; } } } else { ELOG(1, "Could not open the database."); status = false; } //LOG(0, parsedElements.str()); if (!parsedElements.size()) status = false; if(status){ for(map::iterator iter=parsedElements.begin(); iter!=parsedElements.end(); ++iter){ if (elements.count(iter->first)) { // if element already present, replace the old one // pointer to old element might still be in use, so we have to replace into the old element *(elements[iter->first])=*iter->second; delete(iter->second); } else { // no such element in periodentafel... we can just insert elements[iter->first] = iter->second; } } // all went well.. we now copy the header strncpy(header1,header1tmp.c_str(),MAXSTRINGSIZE); header1[MAXSTRINGSIZE-1]=0; strncpy(header2,header2tmp.c_str(),MAXSTRINGSIZE); header2[MAXSTRINGSIZE-1]=0; } return status; } /** load the electronegativity info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadElectronegativityDatabase(std::istream &input) { char dummy[MAXSTRINGSIZE]; if (!input.fail()) { input.getline(dummy, MAXSTRINGSIZE); while (!input.eof()) { atomicNumber_t Z; input >> Z; ASSERT(elements.count(Z), "Element not present"); input >> ws; input >> elements[Z]->Electronegativity; input >> ws; //LOG(1, "INFO: Element " << Z << " has " << FindElement(Z)->Electronegativity << " valence electrons."); } return true; } else return false; } /** load the valence info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadValenceDatabase(istream &input) { char dummy[MAXSTRINGSIZE]; if (!input.fail()) { input.getline(dummy, MAXSTRINGSIZE); while (!input.eof()) { atomicNumber_t Z; input >> Z; ASSERT(elements.count(Z), "Element not present"); input >> ws; input >> elements[Z]->Valence; input >> ws; //LOG(3, "INFO: Element " << Z << " has " << FindElement(Z)->Valence << " valence electrons."); } return true; } else return false; } /** load the orbitals info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadOrbitalsDatabase(istream &input) { char dummy[MAXSTRINGSIZE]; if (!input.fail()) { input.getline(dummy, MAXSTRINGSIZE); while (!input.eof()) { atomicNumber_t Z; input >> Z; ASSERT(elements.count(Z), "Element not present"); input >> ws; input >> elements[Z]->NoValenceOrbitals; input >> ws; //LOG(3, "Element " << Z << " has " << FindElement(Z)->NoValenceOrbitals << " number of singly occupied valence orbitals."); } return true; } else return false; } /** load the hbond angles info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadHBondAngleDatabase(istream &input) { char dummy[MAXSTRINGSIZE]; if (!input.fail()) { input.getline(dummy, MAXSTRINGSIZE); while (!input.eof()) { atomicNumber_t Z; input >> Z; ASSERT(elements.count(Z), "Element not present"); input >> ws; input >> elements[Z]->HBondAngle[0]; input >> elements[Z]->HBondAngle[1]; input >> elements[Z]->HBondAngle[2]; input >> ws; //LOG(3, "Element " << (int)tmp << " has " << FindElement((int)tmp)->HBondAngle[0] << ", " << FindElement((int)tmp)->HBondAngle[1] << ", " << FindElement((int)tmp)->HBondAngle[2] << " degrees bond angle for one, two, three connected hydrogens."); } return true; } else return false; } /** load the hbond lengths info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadHBondLengthsDatabase(istream &input) { char dummy[MAXSTRINGSIZE]; if (!input.fail()) { input.getline(dummy, MAXSTRINGSIZE); while (!input.eof()) { atomicNumber_t Z; input >> Z; ASSERT(elements.count(Z), "Element not present"); input >> ws; input >> elements[Z]->HBondDistance[0]; input >> elements[Z]->HBondDistance[1]; input >> elements[Z]->HBondDistance[2]; input >> ws; //LOG(3, "Element " << (int)tmp << " has " << FindElement((int)tmp)->HBondDistance[0] << " Angstrom typical distance to hydrogen."); } return true; } else return false; } /** load the color info. * \param *input stream to parse from * \return true - parsing successful, false - something went wrong */ bool periodentafel::LoadColorDatabase(istream &input) { char dummy[MAXSTRINGSIZE]; if (!input.fail()) { input.getline(dummy, MAXSTRINGSIZE); while (!input.eof()) { atomicNumber_t Z; input >> Z; ASSERT(elements.count(Z), "Element not present"); input >> ws; input >> dummy; { int tmpcolor; // char here will only parse a single char (i.e. only "2" out of "255") for (int i=0;i<3;++i) { input >> ws; input >> tmpcolor; elements[Z]->color[i] = (unsigned char)tmpcolor; } } input >> ws; // { // const element * tmp = FindElement(Z); // LOG(0, "Element " << tmp->getName() << " has (" // << (int)tmp->color[0] << "," << (int)tmp->color[1] << "," << (int)tmp->color[2] // << ") colors."); // } } return true; } else return false; } /** Stores element list to file. */ bool periodentafel::StorePeriodentafel(const char *path) const { bool result = true; ofstream f; char filename[MAXSTRINGSIZE]; if (strlen(path)+1+strlen(STANDARDELEMENTSDB) > MAXSTRINGSIZE-1) ELOG(2, "Generated path '" << path << "' will be too long."); filename[0] = '\0'; strncat(filename, path, MAXSTRINGSIZE); strncat(filename, "/", MAXSTRINGSIZE-strlen(filename)); strncat(filename, STANDARDELEMENTSDB, MAXSTRINGSIZE-strlen(filename)); f.open(filename); if (f != NULL) { f << header1 << endl; f << header2 << endl; for(const_iterator iter=elements.begin();iter!=elements.end();++iter){ OutputElement(&f, iter->second); } f.close(); return true; } else return result; }; /** Comparison operator for periodentafel. * * @param other other instance to compare to * @return true when both contain same elements */ bool periodentafel::operator==(const periodentafel &other) const { // there are only pointers in the elementSet, hence we have to compare ourselves if (elements.size() != other.elements.size()) return false; const_iterator iter = elements.begin(); const_iterator otheriter = other.elements.begin(); for (;(iter != elements.end()) && (otheriter != other.elements.end()); ++iter, ++otheriter) { bool status = true; status = status && (iter->first == otheriter->first); status = status && (*(iter->second) == *(otheriter->second)); if (!status) { std::cout << *(iter->second) << " not equal to " << *(otheriter->second) << "." << std::endl; return false; } // else // std::cout << (iter->second)->getName() << " are equal to " << (otheriter->second)->getName() << "." << std::endl; } if (strncmp(header1, other.header1, MAXSTRINGSIZE) != 0) return false; if (strncmp(header2, other.header2, MAXSTRINGSIZE) != 0) return false; return true; }