/*
* 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 .
*/
/*
* bondgraph.cpp
*
* Created on: Oct 29, 2009
* Author: heber
*/
// include config.h
#ifdef HAVE_CONFIG_H
#include
#endif
// boost::graph uses placement new
#include
#include "CodePatterns/MemDebug.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include "Atom/atom.hpp"
#include "Bond/bond.hpp"
#include "Graph/BondGraph.hpp"
#include "Box.hpp"
#include "Element/element.hpp"
#include "CodePatterns/Info.hpp"
#include "CodePatterns/Log.hpp"
#include "CodePatterns/Range.hpp"
#include "CodePatterns/Verbose.hpp"
#include "molecule.hpp"
#include "Element/periodentafel.hpp"
#include "Fragmentation/MatrixContainer.hpp"
#include "Graph/DepthFirstSearchAnalysis.hpp"
#include "LinearAlgebra/Vector.hpp"
#include "World.hpp"
#include "WorldTime.hpp"
const double BondGraph::BondThreshold = 0.4; //!< CSD threshold in bond check which is the width of the interval whose center is the sum of the covalent radii
BondGraph::BondGraph() :
BondLengthMatrix(NULL),
IsAngstroem(true)
{}
BondGraph::BondGraph(bool IsA) :
BondLengthMatrix(NULL),
IsAngstroem(IsA)
{}
BondGraph::~BondGraph()
{
CleanupBondLengthTable();
}
void BondGraph::CleanupBondLengthTable()
{
if (BondLengthMatrix != NULL) {
delete(BondLengthMatrix);
}
}
bool BondGraph::LoadBondLengthTable(
std::istream &input)
{
Info FunctionInfo(__func__);
bool status = true;
MatrixContainer *TempContainer = NULL;
// allocate MatrixContainer
if (BondLengthMatrix != NULL) {
LOG(1, "MatrixContainer for Bond length already present, removing.");
delete(BondLengthMatrix);
BondLengthMatrix = NULL;
}
TempContainer = new MatrixContainer;
// parse in matrix
if ((input.good()) && (status = TempContainer->ParseMatrix(input, 0, 1, 0))) {
LOG(1, "Parsing bond length matrix successful.");
} else {
ELOG(1, "Parsing bond length matrix failed.");
status = false;
}
if (status) // set to not NULL only if matrix was parsed
BondLengthMatrix = TempContainer;
else {
BondLengthMatrix = NULL;
delete(TempContainer);
}
return status;
}
double BondGraph::GetBondLength(
int firstZ,
int secondZ) const
{
double return_length;
if ((firstZ < 0) || (firstZ >= (int)BondLengthMatrix->Matrix[0].size()))
return -1.;
if ((secondZ < 0) || (secondZ >= (int)BondLengthMatrix->Matrix[0][firstZ].size()))
return -1.;
if (BondLengthMatrix == NULL) {
return_length = -1.;
} else {
return_length = BondLengthMatrix->Matrix[0][firstZ][secondZ];
}
LOG(4, "INFO: Request for length between " << firstZ << " and "
<< secondZ << ": " << return_length << ".");
return return_length;
}
range BondGraph::CovalentMinMaxDistance(
const element * const Walker,
const element * const OtherWalker) const
{
range MinMaxDistance(0.,0.);
MinMaxDistance.first = OtherWalker->getCovalentRadius() + Walker->getCovalentRadius();
MinMaxDistance.first *= (IsAngstroem) ? 1. : 1. / AtomicLengthToAngstroem;
MinMaxDistance.last = MinMaxDistance.first + BondThreshold;
MinMaxDistance.first -= BondThreshold;
return MinMaxDistance;
}
range BondGraph::BondLengthMatrixMinMaxDistance(
const element * const Walker,
const element * const OtherWalker) const
{
ASSERT(BondLengthMatrix, "BondGraph::BondLengthMatrixMinMaxDistance() called without NULL BondLengthMatrix.");
ASSERT(Walker, "BondGraph::BondLengthMatrixMinMaxDistance() - illegal element given.");
ASSERT(OtherWalker, "BondGraph::BondLengthMatrixMinMaxDistance() - illegal other element given.");
range MinMaxDistance(0.,0.);
MinMaxDistance.first = GetBondLength(Walker->getAtomicNumber()-1, OtherWalker->getAtomicNumber()-1);
MinMaxDistance.first *= (IsAngstroem) ? 1. : 1. / AtomicLengthToAngstroem;
MinMaxDistance.last = MinMaxDistance.first + BondThreshold;
MinMaxDistance.first -= BondThreshold;
return MinMaxDistance;
}
range BondGraph::getMinMaxDistance(
const element * const Walker,
const element * const OtherWalker) const
{
range MinMaxDistance(0.,0.);
if (BondLengthMatrix == NULL) {// safety measure if no matrix has been parsed yet
LOG(5, "DEBUG: Using Covalent radii criterion for [min,max) distances.");
MinMaxDistance = CovalentMinMaxDistance(Walker, OtherWalker);
} else {
LOG(5, "DEBUG: Using tabulated bond table criterion for [min,max) distances.");
MinMaxDistance = BondLengthMatrixMinMaxDistance(Walker, OtherWalker);
}
return MinMaxDistance;
}
range BondGraph::getMinMaxDistance(
const BondedParticle * const Walker,
const BondedParticle * const OtherWalker) const
{
return getMinMaxDistance(Walker->getType(), OtherWalker->getType());
}
range BondGraph::getMinMaxDistanceSquared(
const BondedParticle * const Walker,
const BondedParticle * const OtherWalker) const
{
// use non-squared version
range MinMaxDistance(getMinMaxDistance(Walker, OtherWalker));
// and square
MinMaxDistance.first *= MinMaxDistance.first;
MinMaxDistance.last *= MinMaxDistance.last;
return MinMaxDistance;
}
LinkedCell::LinkedCell_View BondGraph::getLinkedCell(const double max_distance) const
{
return World::getInstance().getLinkedCell(max_distance);
}
std::set< const element *> BondGraph::getElementSetFromNumbers(const std::set &Set) const
{
std::set< const element *> PresentElements;
for(std::set::const_iterator iter = Set.begin(); iter != Set.end(); ++iter)
PresentElements.insert( World::getInstance().getPeriode()->FindElement(*iter) );
return PresentElements;
}
Box &BondGraph::getDomain() const
{
return World::getInstance().getDomain();
}
unsigned int BondGraph::getTime() const
{
return WorldTime::getTime();
}
bool BondGraph::operator==(const BondGraph &other) const
{
if (IsAngstroem != other.IsAngstroem)
return false;
if (((BondLengthMatrix != NULL) && (other.BondLengthMatrix == NULL))
|| ((BondLengthMatrix == NULL) && (other.BondLengthMatrix != NULL)))
return false;
if ((BondLengthMatrix != NULL) && (other.BondLengthMatrix != NULL))
if (*BondLengthMatrix != *other.BondLengthMatrix)
return false;
return true;
}
/** Corrects the bond degree by one at most if necessary.
*
* We are constraint to bonds in \a egdes, if the candidate bond is in edges, we
* just don't increment its bond degree. We do not choose an alternative for this
* atom.
*
* \param edges only these edges can be updated
* \return number of corrections done
*/
int BondGraph::CorrectBondDegree(atom *_atom, const std::set& edges) const
{
int NoBonds = 0;
int OtherNoBonds = 0;
int FalseBondDegree = 0;
int CandidateDeltaNoBonds = -1;
atom *OtherWalker = NULL;
bond::ptr CandidateBond;
NoBonds = _atom->CountBonds();
LOG(3, "Walker " << *_atom << ": " << (int)_atom->getType()->getNoValenceOrbitals() << " > " << NoBonds << "?");
const int DeltaNoBonds = (int)(_atom->getType()->getNoValenceOrbitals()) - NoBonds;
if (DeltaNoBonds > 0) { // we have a mismatch, check all bonding partners for mismatch
const BondList& ListOfBonds = _atom->getListOfBonds();
for (BondList::const_iterator Runner = ListOfBonds.begin(); Runner != ListOfBonds.end(); (++Runner)) {
OtherWalker = (*Runner)->GetOtherAtom(_atom);
OtherNoBonds = OtherWalker->CountBonds();
const int OtherDeltaNoBonds = (int)(OtherWalker->getType()->getNoValenceOrbitals()) - OtherNoBonds;
LOG(3, "OtherWalker " << *OtherWalker << ": " << (int)OtherWalker->getType()->getNoValenceOrbitals() << " > " << OtherNoBonds << "?");
if (OtherDeltaNoBonds > 0) { // check if possible candidate
const BondList& OtherListOfBonds = OtherWalker->getListOfBonds();
if ((CandidateBond == NULL)
|| (ListOfBonds.size() > OtherListOfBonds.size()) // pick the one with fewer number of bonds first
|| ((CandidateDeltaNoBonds < 0) || (CandidateDeltaNoBonds > OtherDeltaNoBonds)) ) // pick the one closer to fulfilling its valence first
{
CandidateDeltaNoBonds = OtherDeltaNoBonds;
CandidateBond = (*Runner);
LOG(3, "New candidate is " << *CandidateBond << ".");
}
}
}
if ((CandidateBond != NULL)) {
if (edges.find(CandidateBond) != edges.end()) {
CandidateBond->setDegree(CandidateBond->getDegree()+1);
LOG(2, "Increased bond degree for bond " << *CandidateBond << ".");
} else
LOG(2, "Bond " << *CandidateBond << " is not in edges.");
} else {
ELOG(2, "Could not find correct degree for atom " << *_atom << ".");
FalseBondDegree++;
}
}
return FalseBondDegree;
}
/** Sets the weight of all connected bonds to one.
*/
void BondGraph::resetBondDegree(atom *_atom) const
{
const BondList &ListOfBonds = _atom->getListOfBonds();
for (BondList::const_iterator BondRunner = ListOfBonds.begin();
BondRunner != ListOfBonds.end();
++BondRunner)
(*BondRunner)->setDegree(1);
}
std::set BondGraph::getBackEdges() const
{
DepthFirstSearchAnalysis DFS;
DFS();
const std::deque& backedgestack = DFS.getBackEdgeStack();
std::set backedges(backedgestack.begin(), backedgestack.end());
return backedges;
}
std::set BondGraph::getTreeEdges() const
{
const std::set backedges = getBackEdges();
std::set alledges;
const World& world = World::getInstance();
for(World::AtomConstIterator iter = world.getAtomIter();
iter != world.atomEnd(); ++iter) {
const BondList &ListOfBonds = (*iter)->getListOfBonds();
alledges.insert(ListOfBonds.begin(), ListOfBonds.end());
}
std::set treeedges;
std::set_difference(
alledges.begin(), alledges.end(),
backedges.begin(), backedges.end(),
std::inserter(treeedges, treeedges.begin()));
return treeedges;
}
struct hasDelta {
bool operator()(atom *_atom) {
const double delta =
_atom->getType()->getNoValenceOrbitals() - _atom->CountBonds();
return (fabs(delta) > 0);
}
};
typedef std::set deltaatoms_t;
typedef std::set deltaedges_t;
static int gatherAllDeltaAtoms(
const deltaatoms_t &allatoms,
deltaatoms_t &deltaatoms)
{
static hasDelta delta;
deltaatoms.clear();
for (deltaatoms_t::const_iterator iter = allatoms.begin();
iter != allatoms.end(); ++iter) {
if (delta(*iter))
deltaatoms.insert(*iter);
}
return deltaatoms.size();
}
typedef boost::bimap AtomIndexLookup_t;
static AtomIndexLookup_t createAtomIndexLookup(
const deltaedges_t &edges)
{
AtomIndexLookup_t AtomIndexLookup;
size_t index = 0;
for (deltaedges_t::const_iterator edgeiter = edges.begin();
edgeiter != edges.end(); ++edgeiter) {
const bond::ptr & _bond = *edgeiter;
// insert left into lookup
std::pair< AtomIndexLookup_t::iterator, bool > inserter =
AtomIndexLookup.insert( AtomIndexLookup_t::value_type(index, _bond->leftatom) );
if (inserter.second)
++index;
// insert right into lookup
inserter = AtomIndexLookup.insert( AtomIndexLookup_t::value_type(index, _bond->rightatom) );
if (inserter.second)
++index;
}
return AtomIndexLookup;
}
typedef std::map< std::pair, bond::ptr> BondLookup_t;
static BondLookup_t createBondLookup(
const deltaedges_t &edges)
{
BondLookup_t BondLookup;
for (deltaedges_t::const_iterator edgeiter = edges.begin();
edgeiter != edges.end(); ++edgeiter) {
const bond::ptr & _bond = *edgeiter;
// insert bond into pair lookup
BondLookup.insert(
std::make_pair(
std::make_pair(_bond->leftatom, _bond->rightatom),
_bond)
);
}
return BondLookup;
}
typedef boost::adjacency_list graph_t;
typedef std::vector::vertex_descriptor> mate_t;
static void fillEdgesIntoMatching(
graph_t &g,
mate_t &mate,
const AtomIndexLookup_t &AtomIndexLookup,
const BondLookup_t &BondLookup,
deltaedges_t &matching
)
{
matching.clear();
boost::graph_traits::vertex_iterator vi, vi_end;
for(tie(vi,vi_end) = boost::vertices(g); vi != vi_end; ++vi)
if (mate[*vi] != boost::graph_traits::null_vertex() && *vi < mate[*vi]) {
atom * leftatom = AtomIndexLookup.left.at(*vi);
atom * rightatom = AtomIndexLookup.left.at(mate[*vi]);
std::pair AtomPair(leftatom, rightatom);
std::pair reverseAtomPair(rightatom, leftatom);
BondLookup_t::const_iterator iter = BondLookup.find(AtomPair);
if (iter != BondLookup.end()) {
matching.insert(iter->second);
} else {
iter = BondLookup.find(reverseAtomPair);
if (iter != BondLookup.end()) {
matching.insert(iter->second);
} else
ASSERT(0, "fillEdgesIntoMatching() - cannot find ("+toString(*vi)+","
+toString(mate[*vi])+") in BondLookup.");
}
}
}
static bool createMatching(
deltaedges_t &deltaedges,
deltaedges_t &matching
)
{
// gather all vertices for graph in a lookup structure
// to translate the graphs indices to atoms and also to associated bonds
AtomIndexLookup_t AtomIndexLookup = createAtomIndexLookup(deltaedges);
BondLookup_t BondLookup = createBondLookup(deltaedges);
const int n_vertices = AtomIndexLookup.size();
// construct graph
graph_t g(n_vertices);
// add edges to graph
for (deltaedges_t::const_iterator edgeiter = deltaedges.begin();
edgeiter != deltaedges.end(); ++edgeiter) {
const bond::ptr & _bond = *edgeiter;
boost::add_edge(
AtomIndexLookup.right.at(_bond->leftatom),
AtomIndexLookup.right.at(_bond->rightatom),
g);
}
// mate structure contains unique edge partner to every vertex in matching
mate_t mate(n_vertices);
// get maximum matching over given edges
bool success = boost::checked_edmonds_maximum_cardinality_matching(g, &mate[0]);
if (success) {
LOG(1, "STATUS: Found a matching of size " << matching_size(g, &mate[0]) << ".");
fillEdgesIntoMatching(g, mate, AtomIndexLookup, BondLookup, matching);
} else {
ELOG(2, "Failed to find maximum matching.");
}
return success;
}
int BondGraph::calculateBondDegree(const deltaatoms_t &allatoms) const
{
deltaatoms_t deltaatoms;
deltaedges_t deltaedges;
deltaedges_t matching;
LOG(1, "STATUS: Calculating bond degrees.");
if (DoLog(2)) {
std::stringstream output;
output << "INFO: All atoms are: ";
BOOST_FOREACH( atom *_atom, allatoms) {
output << *_atom << " ";
}
LOG(2, output.str());
}
size_t IterCount = 0;
// 1. gather all atoms with delta > 0
while ((gatherAllDeltaAtoms(allatoms, deltaatoms) != 0) && (IterCount < 100)) {
if (DoLog(3)) {
std::stringstream output;
output << "DEBUG: Current delta atoms are: ";
BOOST_FOREACH( atom *_atom, deltaatoms) {
output << *_atom << " ";
}
LOG(3, output.str());
}
// 2. gather all edges that connect atoms with both(!) having delta > 0
deltaedges.clear();
for (deltaatoms_t::const_iterator atomiter = deltaatoms.begin();
atomiter != deltaatoms.end(); ++atomiter) {
const atom * const _atom = *atomiter;
const BondList& ListOfBonds = (*atomiter)->getListOfBonds();
for (BondList::const_iterator bonditer = ListOfBonds.begin();
bonditer != ListOfBonds.end();++bonditer) {
const bond::ptr _bond = *bonditer;
if ((_bond->leftatom == _atom) && (deltaatoms.find(_bond->rightatom) != deltaatoms.end()))
deltaedges.insert(_bond);
else if ((_bond->rightatom == _atom) && (deltaatoms.find(_bond->leftatom) != deltaatoms.end()))
deltaedges.insert(_bond);
}
}
if (DoLog(3)) {
std::stringstream output;
output << "DEBUG: Current delta bonds are: ";
BOOST_FOREACH( bond::ptr _bond, deltaedges) {
output << *_bond << " ";
}
LOG(3, output.str());
}
if (deltaedges.empty())
break;
// 3. build matching over these edges
if (!createMatching(deltaedges, matching))
break;
if (DoLog(2)) {
std::stringstream output;
output << "DEBUG: Resulting matching is: ";
BOOST_FOREACH( bond::ptr _bond, matching) {
output << *_bond << " ";
}
LOG(2, output.str());
}
// 4. increase degree for each edge of the matching
LOG(2, "DEBUG: Increasing bond degrees by one.");
for (deltaedges_t::iterator edgeiter = matching.begin();
edgeiter != matching.end(); ++edgeiter) {
(*edgeiter)->setDegree( (*edgeiter)->getDegree()+1 );
}
// 6. stop after a given number of iterations or when all atoms are happy.
++IterCount;
};
// check whether we still have deltaatoms
{
hasDelta delta;
for (deltaatoms_t::const_iterator iter = allatoms.begin();
iter != allatoms.end(); ++iter)
if (delta(*iter))
ELOG(2, "Could not find satisfy charge neutrality for atom " << **iter << ".");
}
return deltaedges.size();
}