source: src/LinkedCell/LinkedCell_Model.cpp@ f3eb6a

Candidate_v1.7.0 stable
Last change on this file since f3eb6a was f3eb6a, checked in by Frederik Heber <frederik.heber@…>, 5 years ago

FIX: LinkedCell_model executes deletes right away.

  • we use the memory address as key in the queue map. If an atom is removed and another new one happens to land at the same address, this will confuse the queue and end in an assertion failure as the update will be overriden and the atom is already present.
  • Property mode set to 100644
File size: 20.2 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 * LinkedCell_Model.cpp
25 *
26 * Created on: Nov 15, 2011
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35//#include "CodePatterns/MemDebug.hpp"
36
37#include "LinkedCell_Model.hpp"
38
39#include <algorithm>
40#include <boost/bind.hpp>
41#include <boost/multi_array.hpp>
42#include <limits>
43
44#include "Atom/AtomObserver.hpp"
45#include "Atom/TesselPoint.hpp"
46#include "Box.hpp"
47#include "CodePatterns/Assert.hpp"
48#include "CodePatterns/Info.hpp"
49#include "CodePatterns/Log.hpp"
50#include "CodePatterns/Observer/Observer.hpp"
51#include "CodePatterns/Observer/Notification.hpp"
52#include "CodePatterns/toString.hpp"
53#include "LinearAlgebra/RealSpaceMatrix.hpp"
54#include "LinearAlgebra/Vector.hpp"
55#include "LinkedCell/IPointCloud.hpp"
56#include "LinkedCell/LinkedCell.hpp"
57#include "LinkedCell/LinkedCell_Model_changeModel.hpp"
58#include "LinkedCell/LinkedCell_Model_LinkedCellArrayCache.hpp"
59#include "World.hpp"
60
61// initialize static entities
62LinkedCell::tripleIndex LinkedCell::LinkedCell_Model::NearestNeighbors;
63
64
65/** Constructor of LinkedCell_Model.
66 *
67 * @param radius desired maximum neighborhood distance
68 * @param _domain Box instance with domain size and boundary conditions
69 */
70LinkedCell::LinkedCell_Model::LinkedCell_Model(const double radius, const Box &_domain) :
71 ::Observer(std::string("LinkedCell_Model")+std::string("_")+toString(radius)),
72 Changes( new changeModel(radius) ),
73 internal_Sizes(NULL),
74 N(new LinkedCellArrayCache(Changes, boost::bind(&changeModel::performUpdates, Changes), std::string("N_cached"))),
75 domain(_domain)
76{
77 // set default argument
78 NearestNeighbors[0] = NearestNeighbors[1] = NearestNeighbors[2] = 1;
79
80 // get the partition of the domain
81 setPartition(radius);
82
83 // allocate linked cell structure
84 AllocateCells();
85
86 // sign in to AtomObserver
87 startListening();
88}
89
90/** Constructor of LinkedCell_Model.
91 *
92 * @oaram set set of points to place into the linked cell structure
93 * @param radius desired maximum neighborhood distance
94 * @param _domain Box instance with domain size and boundary conditions
95 */
96LinkedCell::LinkedCell_Model::LinkedCell_Model(IPointCloud &set, const double radius, const Box &_domain) :
97 ::Observer(std::string("LinkedCell_Model")+std::string("_")+toString(radius)),
98 Changes( new changeModel(radius) ),
99 internal_Sizes(NULL),
100 N(new LinkedCellArrayCache(Changes, boost::bind(&changeModel::performUpdates, Changes), std::string("N_cached"))),
101 domain(_domain)
102{
103 Info info(__func__);
104
105 // get the partition of the domain
106 setPartition(radius);
107
108 // allocate linked cell structure
109 AllocateCells();
110
111 insertPointCloud(set);
112
113 // sign in to AtomObserver
114 startListening();
115}
116
117/** Destructor of class LinkedCell_Model.
118 *
119 */
120LinkedCell::LinkedCell_Model::~LinkedCell_Model()
121{
122 // sign off from observables
123 stopListening();
124
125 // reset linked cell structure
126 Reset();
127 delete N;
128
129 // delete change queue
130 delete Changes;
131}
132
133/** Signs in to AtomObserver and World to known about all changes.
134 *
135 */
136void LinkedCell::LinkedCell_Model::startListening()
137{
138 World::getInstance().signOn(this, World::AtomInserted);
139 World::getInstance().signOn(this, World::AtomRemoved);
140 AtomObserver::getInstance().signOn(this, AtomObservable::PositionChanged);
141}
142
143/** Signs off from AtomObserver and World.
144 *
145 */
146void LinkedCell::LinkedCell_Model::stopListening()
147{
148 World::getInstance().signOff(this, World::AtomInserted);
149 World::getInstance().signOff(this, World::AtomRemoved);
150 AtomObserver::getInstance().signOff(this, AtomObservable::PositionChanged);
151}
152
153
154/** Allocates as much cells per axis as required by
155 * LinkedCell_Model::BoxPartition.
156 *
157 */
158void LinkedCell::LinkedCell_Model::AllocateCells()
159{
160 // resize array
161 tripleIndex index;
162 for (int i=0;i<NDIM;i++)
163 index[i] = static_cast<LinkedCellArray::index>(Dimensions.at(i,i));
164 N->setN().resize(index);
165 ASSERT(getSize(0)*getSize(1)*getSize(2) < MAX_LINKEDCELLNODES,
166 "LinkedCell_Model::AllocateCells() - Number linked of linked cell nodes exceeded hard-coded limit, use greater edge length!");
167 LOG(1, "INFO: Allocating array ("
168 << getSize(0) << ","
169 << getSize(1) << ","
170 << getSize(2) << ") for a new LinkedCell_Model.");
171
172 // allocate LinkedCell instances
173 for(index[0] = 0; index[0] != static_cast<LinkedCellArray::index>(Dimensions.at(0,0)); ++index[0]) {
174 for(index[1] = 0; index[1] != static_cast<LinkedCellArray::index>(Dimensions.at(1,1)); ++index[1]) {
175 for(index[2] = 0; index[2] != static_cast<LinkedCellArray::index>(Dimensions.at(2,2)); ++index[2]) {
176 LOG(5, "INFO: Creating cell at " << index[0] << " " << index[1] << " " << index[2] << ".");
177 N->setN()(index) = new LinkedCell(index);
178 }
179 }
180 }
181}
182
183/** Frees all Linked Cell instances and sets array dimensions to (0,0,0).
184 *
185 */
186void LinkedCell::LinkedCell_Model::Reset()
187{
188 // free all LinkedCell instances
189 for(iterator3 iter3 = N->setN().begin(); iter3 != N->setN().end(); ++iter3) {
190 for(iterator2 iter2 = (*iter3).begin(); iter2 != (*iter3).end(); ++iter2) {
191 for(iterator1 iter1 = (*iter2).begin(); iter1 != (*iter2).end(); ++iter1) {
192 delete *iter1;
193 }
194 }
195 }
196 // set dimensions to zero
197 N->setN().resize(boost::extents[0][0][0]);
198}
199
200/** Inserts all points contained in \a set.
201 *
202 * @param set set with points to insert into linked cell structure
203 */
204void LinkedCell::LinkedCell_Model::insertPointCloud(IPointCloud &set)
205{
206 if (set.IsEmpty()) {
207 ELOG(1, "set is NULL or contains no linked cell nodes!");
208 return;
209 }
210
211 // put each atom into its respective cell
212 set.GoToFirst();
213 while (!set.IsEnd()) {
214 TesselPoint *Walker = set.GetPoint();
215 addNode(Walker);
216 set.GoToNext();
217 }
218}
219
220/** Calculates the required edge length for the given desired distance.
221 *
222 * We need to make some matrix transformations in order to obtain the required
223 * edge lengths per axis. Goal is guarantee that whatever the shape of the
224 * domain that always all points at least up to \a distance away are contained
225 * in the nearest neighboring cells.
226 *
227 * @param distance distance of this linked cell array
228 */
229void LinkedCell::LinkedCell_Model::setPartition(double distance)
230{
231 // generate box matrix of desired edge length
232 RealSpaceMatrix neighborhood;
233 neighborhood.setIdentity();
234 neighborhood *= distance;
235
236 // obtain refs to both domain matrix transformations
237 //const RealSpaceMatrix &M = domain.getM();
238 const RealSpaceMatrix &Minv = domain.getMinv();
239
240 RealSpaceMatrix output = Minv * neighborhood;
241 std::cout << Minv << " * " << neighborhood << " = " << output << std::endl;
242
243 Dimensions = output.invert();
244 std::cout << "Dimensions are then " << Dimensions << std::endl;
245
246 // now dimensions is floating-point, but we need it to be integer (for allocation)
247 for (size_t col = 0; col < NDIM; ++col) {
248 for (size_t row = 0; row < NDIM; ++row) {
249 ASSERT(fabs(Dimensions.at(row,col) - Dimensions.at(col,row)) < 1.e+3*std::numeric_limits<double>::epsilon(),
250 "LinkedCell_Model::setPartition() - Dimensions is not symmetric by "
251 +toString(fabs(Dimensions.at(row,col) - Dimensions.at(col,row)))+ ".");
252 if (col != row) {
253 ASSERT(fabs(Dimensions.at(row,col)) < 1.e+3*std::numeric_limits<double>::epsilon(),
254 "LinkedCell_Model::setPartition() - Dimensions is not diagonal by "
255 +toString(fabs(Dimensions.at(row,col)))+".");
256 } else {
257 Dimensions.set(row,col, ceil(Dimensions.at(row,col)));
258 }
259 }
260 }
261
262
263 Partition = Minv*Dimensions; //
264
265 std::cout << "Partition matrix is then " << Partition << std::endl;
266}
267
268/** Returns the number of required neighbor-shells to get all neighboring points
269 * in the given \a distance.
270 *
271 * @param distance radius of neighborhood sphere
272 * @return number of LinkedCell's per dimension to get all neighbors
273 */
274const LinkedCell::tripleIndex LinkedCell::LinkedCell_Model::getStep(const double distance) const
275{
276 tripleIndex index;
277 index[0] = index[1] = index[2] = 0;
278
279 if (fabs(distance) < std::numeric_limits<double>::min())
280 return index;
281 // generate box matrix of desired edge length
282 RealSpaceMatrix neighborhood;
283 neighborhood.setIdentity();
284 neighborhood *= distance;
285
286 const RealSpaceMatrix output = Partition * neighborhood;
287
288 //std::cout << "GetSteps: " << Partition << " * " << neighborhood << " = " << output << std::endl;
289
290 const RealSpaceMatrix steps = output;
291 for (size_t i =0; i<NDIM; ++i)
292 index[i] = ceil(steps.at(i,i));
293 LOG(2, "INFO: number of shells are ("+toString(index[0])+","+toString(index[1])+","+toString(index[2])+").");
294
295 return index;
296}
297
298/** Calculates the index of the cell \a position would belong to.
299 *
300 * @param position position whose associated cell to calculate
301 * @return index of the cell
302 */
303const LinkedCell::tripleIndex LinkedCell::LinkedCell_Model::getIndexToVector(const Vector &position) const
304{
305 ASSERT(domain.isValid(position),
306 "LinkedCell::LinkedCell_Model::getIndexToVector() - specified position "+toString(position)
307 +"is not valid in the current domain.");
308 tripleIndex index;
309 Vector x(Partition*domain.enforceBoundaryConditions(position));
310 LOG(2, "INFO: Transformed position is " << x << ".");
311 for (int i=0;i<NDIM;i++) {
312 index[i] = static_cast<LinkedCellArray::index>(floor(x[i]));
313 }
314 return index;
315}
316
317/** Adds an update to the list of lazy changes to add a node.
318 *
319 * @param Walker node to add
320 */
321void LinkedCell::LinkedCell_Model::addNode(const TesselPoint *Walker)
322{
323 LOG(2, "INFO: Requesting update to add node " << *Walker << ".");
324 Changes->addUpdate(Walker, 0, boost::bind(&LinkedCell_Model::addNode_internal, this, _1), "add");
325}
326
327/** Adds an update to the list of lazy changes to add remove a node.
328 *
329 * We do nothing of Walker is not found
330 *
331 * @param Walker node to remove
332 */
333void LinkedCell::LinkedCell_Model::deleteNode(const TesselPoint *Walker)
334{
335 LOG(2, "INFO: Deleting node " << *Walker << ".");
336 deleteNode_internal(Walker);
337 Changes->removeUpdates(Walker);
338}
339
340/** Adds an update to the list of lazy changes to move a node.
341 *
342 * @param Walker node who has moved.
343 */
344void LinkedCell::LinkedCell_Model::moveNode(const TesselPoint *Walker)
345{
346 LOG(2, "INFO: Requesting update to move node " << *Walker << " to position "
347 << Walker->getPosition() << ".");
348 Changes->addUpdate(Walker, 10, boost::bind(&LinkedCell_Model::moveNode_internal, this, _1), "move");
349}
350
351/** Internal function to add a node to the linked cell structure
352 *
353 * @param Walker node to add
354 */
355void LinkedCell::LinkedCell_Model::addNode_internal(const TesselPoint *Walker)
356{
357 // find index
358 tripleIndex index = getIndexToVector(Walker->getPosition());
359 LOG(2, "INFO: " << *Walker << " goes into cell " << index[0] << ", " << index[1] << ", " << index[2] << ".");
360 LOG(2, "INFO: Cell's indices are "
361 << (N->getN())(index)->getIndex(0) << " "
362 << (N->getN())(index)->getIndex(1) << " "
363 << (N->getN())(index)->getIndex(2) << ".");
364 // add to cell
365 (N->setN())(index)->addPoint(Walker);
366 // add to index with check for presence
367 std::pair<MapPointToCell::iterator, bool> inserter = CellLookup.insert( std::make_pair(Walker, (N->setN())(index)) );
368 ASSERT( inserter.second,
369 "LinkedCell_Model::addNode() - Walker "
370 +toString(*Walker)+" is already present in cell "
371 +toString((inserter.first)->second->getIndex(0))+" "
372 +toString((inserter.first)->second->getIndex(1))+" "
373 +toString((inserter.first)->second->getIndex(2))+".");
374}
375
376/** Internal function to remove a node to the linked cell structure
377 *
378 * We do nothing of Walker is not found
379 *
380 * @param Walker node to remove
381 */
382void LinkedCell::LinkedCell_Model::deleteNode_internal(const TesselPoint *Walker)
383{
384 MapPointToCell::iterator iter = CellLookup.find(Walker);
385 // must not ASSERT presence as addNode might have been overrided by this
386 // deleteNode update and hence the node will not be present
387 if (iter != CellLookup.end()) {
388 // remove from lookup
389 CellLookup.erase(iter);
390 // remove from cell
391 iter->second->deletePoint(Walker);
392 }
393}
394
395/** Internal function to move node from current cell to another on position change.
396 *
397 * @param Walker node who has moved.
398 */
399void LinkedCell::LinkedCell_Model::moveNode_internal(const TesselPoint *Walker)
400{
401 MapPointToCell::iterator iter = CellLookup.find(Walker);
402 ASSERT(iter != CellLookup.end(),
403 "LinkedCell_Model::deleteNode() - Walker not present in cell stored under CellLookup.");
404 if (iter != CellLookup.end()) {
405 tripleIndex index = getIndexToVector(Walker->getPosition());
406 if (index != iter->second->getIndices()) {
407 // remove in old cell
408 iter->second->deletePoint(Walker);
409 // add to new cell
410 N->setN()(index)->addPoint(Walker);
411 // update lookup
412 iter->second = N->setN()(index);
413 }
414 }
415}
416
417/** Checks whether cell indicated by \a relative relative to LinkedCell_Model::internal_index
418 * is out of bounds.
419 *
420 * \note We do not check for boundary conditions of LinkedCell_Model::domain,
421 * we only look at the array sizes
422 *
423 * @param relative index relative to LinkedCell_Model::internal_index.
424 * @return true - relative index is still inside bounds, false - outside
425 */
426bool LinkedCell::LinkedCell_Model::checkArrayBounds(const tripleIndex &index) const
427{
428 bool status = true;
429 for (size_t i=0;i<NDIM;++i) {
430 status = status && (
431 (index[i] >= 0) &&
432 (index[i] < getSize(i))
433 );
434 }
435 return status;
436}
437
438/** Corrects \a index according to boundary conditions of LinkedCell_Model::domain .
439 *
440 * @param index index to correct according to boundary conditions
441 */
442void LinkedCell::LinkedCell_Model::applyBoundaryConditions(tripleIndex &index) const
443{
444 for (size_t i=0;i<NDIM;++i) {
445 switch (domain.getConditions()[i]) {
446 case BoundaryConditions::Wrap:
447 if ((index[i] < 0) || (index[i] >= getSize(i)))
448 index[i] = (index[i] % getSize(i));
449 break;
450 case BoundaryConditions::Bounce:
451 if (index[i] < 0)
452 index[i] = 0;
453 if (index[i] >= getSize(i))
454 index[i] = getSize(i)-1;
455 break;
456 case BoundaryConditions::Ignore:
457 if (index[i] < 0)
458 index[i] = 0;
459 if (index[i] >= getSize(i))
460 index[i] = getSize(i)-1;
461 break;
462 default:
463 ASSERT(0, "LinkedCell_Model::checkBounds() - unknown boundary conditions.");
464 break;
465 }
466 }
467}
468
469/** Calculates the interval bounds of the linked cell grid.
470 *
471 * The neighborhood bounds works as follows: We give the lower, left, front
472 * corner of the box and the number of boxes to go in each direction (i.e. the
473 * relative upper, right, behind corner). We assure that the first corner is
474 * within LinkedCell_Model::N, whether the relative second corner is within the
475 * domain must be assured via applying its boundary conditions, see
476 * LinkedCell_Model::applyBoundaryConditions()
477 *
478 * \note we check whether \a index is valid, i.e. within the range of LinkedCell_Model::N.
479 *
480 * \param index index to give relative bounds to
481 * \param step how deep to check the neighbouring cells (i.e. number of layers to check)
482 * \return pair of tripleIndex indicating lower and upper bounds
483 */
484const LinkedCell::LinkedCell_Model::LinkedCellNeighborhoodBounds LinkedCell::LinkedCell_Model::getNeighborhoodBounds(
485 const tripleIndex &index,
486 const tripleIndex &step
487 ) const
488{
489 LinkedCellNeighborhoodBounds neighbors;
490
491 // calculate bounds
492 for (size_t i=0;i<NDIM;++i) {
493 ASSERT(index[i] >= 0,
494 "LinkedCell_Model::getNeighborhoodBounds() - index "
495 +toString(index)+" out of lower bounds.");
496 ASSERT (index[i] < getSize(i),
497 "LinkedCell_Model::getNeighborhoodBounds() - index "
498 +toString(index)+" out of upper bounds.");
499 switch (domain.getConditions()[i]) {
500 case BoundaryConditions::Wrap:
501 if ((index[i] - step[i]) < 0)
502 neighbors.first[i] = getSize(i) + (index[i] - step[i]);
503 else if ((index[i] - step[i]) >= getSize(i))
504 neighbors.first[i] = (index[i] - step[i]) - getSize(i);
505 else
506 neighbors.first[i] = index[i] - step[i];
507 neighbors.second[i] = 2*step[i]+1;
508 break;
509 case BoundaryConditions::Bounce:
510 neighbors.second[i] = 2*step[i]+1;
511 if (index[i] - step[i] >= 0) {
512 neighbors.first[i] = index[i] - step[i];
513 } else {
514 neighbors.first[i] = 0;
515 neighbors.second[i] = index[i] + step[i]+1;
516 }
517 if (index[i] + step[i] >= getSize(i)) {
518 neighbors.second[i] = getSize(i) - (index[i] - step[i]);
519 }
520 break;
521 case BoundaryConditions::Ignore:
522 if (index[i] - step[i] < 0)
523 neighbors.first[i] = 0;
524 else
525 neighbors.first[i] = index[i] - step[i];
526 if ((neighbors.first[i] + 2*step[i]+1) >= getSize(i))
527 neighbors.second[i] = getSize(i) - neighbors.first[i];
528 else
529 neighbors.second[i] = 2*step[i]+1;
530 break;
531 default:
532 ASSERT(0, "LinkedCell_Model::getNeighborhoodBounds() - unknown boundary conditions.");
533 break;
534 }
535 // check afterwards whether we now have correct
536 ASSERT((neighbors.first[i] >= 0) && (neighbors.first[i] < getSize(i)),
537 "LinkedCell_Model::getNeighborhoodBounds() - lower border "
538 +toString(neighbors.first)+" is out of bounds.");
539 }
540 LOG(3, "INFO: Resulting neighborhood bounds are [" << neighbors.first << "]<->[" << neighbors.second << "].");
541
542 return neighbors;
543}
544
545/** Returns a reference to the cell indicated by LinkedCell_Model::internal_index.
546 *
547 * \return LinkedCell ref to current cell
548 */
549const LinkedCell::LinkedCell& LinkedCell::LinkedCell_Model::getCell(const tripleIndex &index) const
550{
551 return *(N->getN()(index));
552}
553
554
555/** Returns size of array for given \a dim.
556 *
557 * @param dim desired dimension
558 * @return size of array along dimension
559 */
560LinkedCell::LinkedCellArray::index LinkedCell::LinkedCell_Model::getSize(const size_t dim) const
561{
562 ASSERT((dim >= 0) && (dim < NDIM),
563 "LinkedCell_Model::getSize() - dimension "
564 +toString(dim)+" is out of bounds.");
565 return N->getN().shape()[dim];
566}
567
568/** Callback function for Observer mechanism.
569 *
570 * @param publisher reference to the Observable that calls
571 */
572void LinkedCell::LinkedCell_Model::update(Observable *publisher)
573{
574 ELOG(2, "LinkedCell_Model received inconclusive general update from "
575 << publisher << ".");
576}
577
578/** Callback function for the Notifications mechanism.
579 *
580 * @param publisher reference to the Observable that calls
581 * @param notification specific notification as cause of the call
582 */
583void LinkedCell::LinkedCell_Model::recieveNotification(Observable *publisher, Notification_ptr notification)
584{
585 // either it's the World or from the atom (through relay) itself
586 if (publisher == World::getPointer()) {
587 switch(notification->getChannelNo()) {
588 case World::AtomInserted:
589 addNode(World::getInstance().lastChanged<atom>());
590 break;
591 case World::AtomRemoved:
592 deleteNode(World::getInstance().lastChanged<atom>());
593 break;
594 }
595 } else {
596 switch(notification->getChannelNo()) {
597 case AtomObservable::PositionChanged:
598 {
599 moveNode(dynamic_cast<const TesselPoint *>(publisher));
600 break;
601 }
602 default:
603 LOG(2, "LinkedCell_Model received unwanted notification from AtomObserver's channel "
604 << notification->getChannelNo() << ".");
605 break;
606 }
607 }
608}
609
610/** Callback function when an Observer dies.
611 *
612 * @param publisher reference to the Observable that calls
613 */
614void LinkedCell::LinkedCell_Model::subjectKilled(Observable *publisher)
615{}
616
Note: See TracBrowser for help on using the repository browser.