/*
* 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;
}