source: src/Actions/MoleculeAction/StretchBondAction.cpp@ 6afd46

ForceAnnealing_oldresults IndependentFragmentGrids_IntegrationTest
Last change on this file since 6afd46 was 6afd46, checked in by Frederik Heber <frederik.heber@…>, 8 years ago

Extracted conversion to boost::graph into distinct struct.

  • BoostGraphCreator allows to convert sets of atoms into a boost::graph while using an additional predicate.
  • Property mode set to 100644
File size: 8.9 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2012 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * StretchBondAction.cpp
25 *
26 * Created on: Sep 26, 2012
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35#include <boost/graph/adjacency_list.hpp>
36#include <boost/graph/breadth_first_search.hpp>
37
38//#include "CodePatterns/MemDebug.hpp"
39
40#include "Actions/MoleculeAction/StretchBondAction.hpp"
41
42#include <boost/bind.hpp>
43
44#include "CodePatterns/Assert.hpp"
45#include "CodePatterns/Log.hpp"
46#include "CodePatterns/Verbose.hpp"
47
48#include "LinearAlgebra/Plane.hpp"
49
50#include "Atom/atom.hpp"
51#include "Bond/bond.hpp"
52#include "Descriptors/AtomIdDescriptor.hpp"
53#include "Graph/BoostGraphCreator.hpp"
54#include "molecule.hpp"
55#include "World.hpp"
56
57using namespace MoleCuilder;
58
59// and construct the stuff
60#include "StretchBondAction.def"
61#include "Action_impl_pre.hpp"
62
63
64/**
65 * I have no idea why this is so complicated with BGL ...
66 *
67 * This is taken from the book "The Boost Graph Library: User Guide and Reference Manual, Portable Documents",
68 * chapter "Basic Graph Algorithms", example on calculating the bacon number.
69 */
70template <typename DistanceMap>
71class distance_recorder : public boost::default_bfs_visitor
72{
73public:
74 distance_recorder(DistanceMap dist) : d(dist) {}
75
76 template <typename Edge, typename Graph>
77 void tree_edge(Edge e, const Graph &g) const {
78 typename boost::graph_traits<Graph>::vertex_descriptor u = source(e,g), v = target(e,g);
79 d[v] = d[u] + 1;
80 }
81
82private:
83 DistanceMap d;
84};
85
86template <typename DistanceMap>
87distance_recorder<DistanceMap> record_distance(DistanceMap d)
88{
89 return distance_recorder<DistanceMap>(d);
90}
91
92static bool addEdgePredicate(
93 const bond &_bond,
94 const std::vector<atomId_t> &_atomids)
95{
96 ASSERT(_atomids.size() == (size_t)2,
97 "addEdgePredicate() - atomids must contain exactly two ids.");
98 // do not add selected edge
99 return ((_bond.leftatom->getId() != _atomids[0])
100 || (_bond.rightatom->getId() != _atomids[1]));
101}
102
103/** =========== define the function ====================== */
104ActionState::ptr MoleculeStretchBondAction::performCall()
105{
106 // check preconditions
107 const std::vector< atom *> atoms = World::getInstance().getSelectedAtoms();
108 if (atoms.size() != 2) {
109 STATUS("Exactly two atoms must be selected.");
110 return Action::failure;
111 }
112 std::vector<atomId_t> atomids(2);
113 atomids[0] = atoms[0]->getId();
114 atomids[1] = atoms[1]->getId();
115 std::sort(atomids.begin(), atomids.end());
116 LOG(1, "DEBUG: Selected nodes are " << atomids);
117 molecule *mol = World::getInstance().
118 getMolecule(MoleculeById(atoms[0]->getMolecule()->getId()));
119 if (mol != atoms[1]->getMolecule()) {
120 STATUS("The two selected atoms must belong to the same molecule.");
121 return Action::failure;
122 }
123 // gather undo information
124 const double olddistance = atoms[0]->getPosition().distance(atoms[1]->getPosition());
125 const double newdistance = params.bonddistance.get();
126 LOG(1, "INFO: Old bond distance is " << olddistance << ", stretching to " << newdistance << ".");
127
128 // Assume the selected bond splits the molecule into two parts, each one on
129 // either side of the bond. We need to perform a BFS from each bond partner
130 // not using the selected bond. Therefrom, we obtain two sets of atoms/nodes.
131 // If both are disjoint, the bond is not contained in a cycle and we simply
132 // shift either set as desired. If not, then we simply shift each atom,
133 // leaving the other positions untouched.
134
135 // convert BondGraph into boost::graph
136 BoostGraphCreator BGcreator;
137 BGcreator.createFromMolecule(*mol,
138 boost::bind(addEdgePredicate, _1, boost::ref(atomids)));
139 const BoostGraphCreator::UndirectedGraph &molgraph = BGcreator.get();
140
141 const size_t num_vertices = BGcreator.getNumVertices();
142 std::vector< std::vector<size_t> > distances;
143 for (size_t i=0;i<2;++i) {
144 distances.push_back(std::vector<size_t>(num_vertices, num_vertices+1)); // set distance to num+1
145 distances[i][atomids[i]] = 0;
146 boost::breadth_first_search(
147 molgraph,
148 boost::vertex(atomids[i], molgraph),
149 boost::visitor(record_distance(&(distances[i][0]))));
150 LOG(3, "DEBUG: From atom #" << atomids[i]
151 << " BFS discovered the following distances " << distances[i]);
152 }
153
154 const Vector NormalVector = (atoms[0]->getPosition() - atoms[1]->getPosition())* (1./olddistance);
155 const double shift = 0.5*(newdistance - olddistance);
156 std::vector<Vector> Shift(2);
157 Shift[0] = shift * NormalVector;
158 Shift[1] = -shift * NormalVector;
159 Box &domain = World::getInstance().getDomain();
160 std::vector< std::vector<size_t> > bondside_sets(2);
161
162 // Check whether there are common nodes in each set of distances
163 for (size_t i=0;i<num_vertices;++i)
164 if ((distances[0][i] != (num_vertices+1)) && (distances[1][i] != (num_vertices+1))) {
165 ELOG(2, "Node #" << i << " is reachable from either side of bond, hence must by cyclic."
166 << " Shifting only bond partners.");
167 for(size_t j=0;j<2;++j) {
168 bondside_sets[j].push_back(atoms[j]->getId());
169 const Vector &position = atoms[j]->getPosition();
170 atoms[j]->setPosition( domain.enforceBoundaryConditions(position+Shift[j]) );
171 }
172 break;
173 }
174
175 // go through the molecule and stretch each atom in either set of nodes
176 if (bondside_sets[0].empty()) {
177 const BoostGraphCreator::index_map_t index_map = BGcreator.getIndexMap();
178 for (molecule::iterator iter = mol->begin(); iter != mol->end(); ++iter) {
179 const Vector &position = (*iter)->getPosition();
180 // for each atom determine in which set of nodes it is and shift accordingly
181 size_t i=0;
182 const size_t nodeindex = boost::get(index_map, boost::vertex((*iter)->getId(), molgraph));
183 for (;i<2;++i) {
184 if (distances[i][nodeindex] != (num_vertices+1)) {
185 (*iter)->setPosition( domain.enforceBoundaryConditions(position+Shift[i]) );
186 bondside_sets[i].push_back((*iter)->getId());
187 break;
188 }
189 }
190 if (i==2) {
191 ELOG(1, "Atom " << *iter << " is not contained on either side of bond? Undoing done shifts");
192 // Have to undo shifts
193 for (i=0;i<2;++i) {
194 for (std::vector<size_t>::const_iterator iter = bondside_sets[i].begin();
195 iter != bondside_sets[i].end(); ++iter) {
196 atom &walker = *World::getInstance().getAtom(AtomById(*iter));
197 const Vector &position = walker.getPosition();
198 walker.setPosition( domain.enforceBoundaryConditions(position-Shift[i]) );
199 }
200 }
201 return Action::failure;
202 }
203 }
204 }
205
206 MoleculeStretchBondState *UndoState = new MoleculeStretchBondState(Shift, bondside_sets, mol, params);
207 return ActionState::ptr(UndoState);
208}
209
210ActionState::ptr MoleculeStretchBondAction::performUndo(ActionState::ptr _state) {
211 MoleculeStretchBondState *state = assert_cast<MoleculeStretchBondState*>(_state.get());
212
213 // use given plane to undo
214 Box &domain = World::getInstance().getDomain();
215 for (size_t i=0;i<2;++i) {
216 for (std::vector<size_t>::const_iterator iter = state->bondside_sets[i].begin();
217 iter != state->bondside_sets[i].end(); ++iter) {
218 atom &walker = *World::getInstance().getAtom(AtomById(*iter));
219 const Vector &position = walker.getPosition();
220 walker.setPosition( domain.enforceBoundaryConditions(position-state->Shift[i]) );
221 }
222 }
223
224 return ActionState::ptr(_state);
225}
226
227ActionState::ptr MoleculeStretchBondAction::performRedo(ActionState::ptr _state){
228 MoleculeStretchBondState *state = assert_cast<MoleculeStretchBondState*>(_state.get());
229
230 Box &domain = World::getInstance().getDomain();
231 for (size_t i=0;i<2;++i) {
232 for (std::vector<size_t>::const_iterator iter = state->bondside_sets[i].begin();
233 iter != state->bondside_sets[i].end(); ++iter) {
234 atom &walker = *World::getInstance().getAtom(AtomById(*iter));
235 const Vector &position = walker.getPosition();
236 walker.setPosition( domain.enforceBoundaryConditions(position+state->Shift[i]) );
237 }
238 }
239
240 return ActionState::ptr(_state);
241}
242
243bool MoleculeStretchBondAction::canUndo() {
244 return true;
245}
246
247bool MoleculeStretchBondAction::shouldUndo() {
248 return true;
249}
250/** =========== end of function ====================== */
Note: See TracBrowser for help on using the repository browser.