source: src/Tesselation/boundary.cpp@ 59fff1

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Added_ParseSaveFragmentResults AddingActions_SaveParseParticleParameters Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_ParticleName_to_Atom Adding_StructOpt_integration_tests AtomFragments Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator CombiningParticlePotentialParsing Combining_Subpackages Debian_Package_split Debian_package_split_molecuildergui_only Disabling_MemDebug Docu_Python_wait EmpiricalPotential_contain_HomologyGraph EmpiricalPotential_contain_HomologyGraph_documentation Enable_parallel_make_install Enhance_userguide Enhanced_StructuralOptimization Enhanced_StructuralOptimization_continued Example_ManyWaysToTranslateAtom Exclude_Hydrogens_annealWithBondGraph FitPartialCharges_GlobalError Fix_BoundInBox_CenterInBox_MoleculeActions Fix_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_PopActions Fix_QtFragmentList_sorted_selection Fix_Restrictedkeyset_FragmentMolecule Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns Fix_fitting_potentials Fixes ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion FragmentAction_writes_AtomFragments FragmentMolecule_checks_bonddegrees GeometryObjects Gui_Fixes Gui_displays_atomic_force_velocity ImplicitCharges IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix MoreRobust_FragmentAutomation ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PdbParser_setsAtomName PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks Rewrite_FitPartialCharges RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching SaturateAtoms_singleDegree StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg Switchable_LogView ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps TremoloParser_setsAtomName Ubuntu_1604_changes stable
Last change on this file since 59fff1 was 59fff1, checked in by Frederik Heber <heber@…>, 13 years ago

Replaced the molecule::const_iterator by a true const version of the transform_iterator.

  • lots of places used const_iterator where it made no sense, atom some others one could have sensibly done so but details prevented it, e.g.
    • FormatParserStorage::save() requires vector<atom*> not const atom *,
    • Boundaries (and most of Tesselation for that) requires atom *, not const atom *.
  • Added new function molecule::isInMolecule() to check whether atomic id is in molecule::atomIds.
  • Added const version of atom::GetTrueFather().
  • Property mode set to 100644
File size: 60.1 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2011 University of Bonn. All rights reserved.
5 * Please see the LICENSE file or "Copyright notice" in builder.cpp for details.
6 */
7
8/** \file boundary.cpp
9 *
10 * Implementations and super-function for envelopes
11 */
12
13// include config.h
14#ifdef HAVE_CONFIG_H
15#include <config.h>
16#endif
17
18#include "CodePatterns/MemDebug.hpp"
19
20#include "atom.hpp"
21#include "Bond/bond.hpp"
22#include "boundary.hpp"
23#include "BoundaryLineSet.hpp"
24#include "BoundaryPointSet.hpp"
25#include "BoundaryTriangleSet.hpp"
26#include "Box.hpp"
27#include "CandidateForTesselation.hpp"
28#include "CodePatterns/Info.hpp"
29#include "CodePatterns/Log.hpp"
30#include "CodePatterns/Verbose.hpp"
31#include "config.hpp"
32#include "Element/element.hpp"
33#include "LinearAlgebra/Plane.hpp"
34#include "LinearAlgebra/RealSpaceMatrix.hpp"
35#include "linkedcell.hpp"
36#include "molecule.hpp"
37#include "MoleculeListClass.hpp"
38#include "PointCloudAdaptor.hpp"
39#include "RandomNumbers/RandomNumberGeneratorFactory.hpp"
40#include "RandomNumbers/RandomNumberGenerator.hpp"
41#include "tesselation.hpp"
42#include "tesselationhelpers.hpp"
43#include "World.hpp"
44
45#include <iostream>
46#include <iomanip>
47
48#include<gsl/gsl_poly.h>
49#include<time.h>
50
51// ========================================== F U N C T I O N S =================================
52
53
54/** Determines greatest diameters of a cluster defined by its convex envelope.
55 * Looks at lines parallel to one axis and where they intersect on the projected planes
56 * \param *out output stream for debugging
57 * \param *BoundaryPoints NDIM set of boundary points defining the convex envelope on each projected plane
58 * \param *mol molecule structure representing the cluster
59 * \param *&TesselStruct Tesselation structure with triangles
60 * \param IsAngstroem whether we have angstroem or atomic units
61 * \return NDIM array of the diameters
62 */
63double *GetDiametersOfCluster(const Boundaries *BoundaryPtr, const molecule *mol, Tesselation *&TesselStruct, const bool IsAngstroem)
64{
65 Info FunctionInfo(__func__);
66 // get points on boundary of NULL was given as parameter
67 bool BoundaryFreeFlag = false;
68 double OldComponent = 0.;
69 double tmp = 0.;
70 double w1 = 0.;
71 double w2 = 0.;
72 Vector DistanceVector;
73 Vector OtherVector;
74 int component = 0;
75 int Othercomponent = 0;
76 Boundaries::const_iterator Neighbour;
77 Boundaries::const_iterator OtherNeighbour;
78 double *GreatestDiameter = new double[NDIM];
79
80 const Boundaries *BoundaryPoints;
81 if (BoundaryPtr == NULL) {
82 BoundaryFreeFlag = true;
83 BoundaryPoints = GetBoundaryPoints(mol, TesselStruct);
84 } else {
85 BoundaryPoints = BoundaryPtr;
86 LOG(0, "Using given boundary points set.");
87 }
88 // determine biggest "diameter" of cluster for each axis
89 for (int i = 0; i < NDIM; i++)
90 GreatestDiameter[i] = 0.;
91 for (int axis = 0; axis < NDIM; axis++)
92 { // regard each projected plane
93 //LOG(1, "Current axis is " << axis << ".");
94 for (int j = 0; j < 2; j++)
95 { // and for both axis on the current plane
96 component = (axis + j + 1) % NDIM;
97 Othercomponent = (axis + 1 + ((j + 1) & 1)) % NDIM;
98 //LOG(1, "Current component is " << component << ", Othercomponent is " << Othercomponent << ".");
99 for (Boundaries::const_iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
100 //LOG(1, "Current runner is " << *(runner->second.second) << ".");
101 // seek for the neighbours pair where the Othercomponent sign flips
102 Neighbour = runner;
103 Neighbour++;
104 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
105 Neighbour = BoundaryPoints[axis].begin();
106 DistanceVector = (runner->second.second->getPosition()) - (Neighbour->second.second->getPosition());
107 do { // seek for neighbour pair where it flips
108 OldComponent = DistanceVector[Othercomponent];
109 Neighbour++;
110 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
111 Neighbour = BoundaryPoints[axis].begin();
112 DistanceVector = (runner->second.second->getPosition()) - (Neighbour->second.second->getPosition());
113 //LOG(2, "OldComponent is " << OldComponent << ", new one is " << DistanceVector.x[Othercomponent] << ".");
114 } while ((runner != Neighbour) && (fabs(OldComponent / fabs(
115 OldComponent) - DistanceVector[Othercomponent] / fabs(
116 DistanceVector[Othercomponent])) < MYEPSILON)); // as long as sign does not flip
117 if (runner != Neighbour) {
118 OtherNeighbour = Neighbour;
119 if (OtherNeighbour == BoundaryPoints[axis].begin()) // make it wrap around
120 OtherNeighbour = BoundaryPoints[axis].end();
121 OtherNeighbour--;
122 //LOG(1, "The pair, where the sign of OtherComponent flips, is: " << *(Neighbour->second.second) << " and " << *(OtherNeighbour->second.second) << ".");
123 // now we have found the pair: Neighbour and OtherNeighbour
124 OtherVector = (runner->second.second->getPosition()) - (OtherNeighbour->second.second->getPosition());
125 //LOG(1, "Distances to Neighbour and OtherNeighbour are " << DistanceVector.x[component] << " and " << OtherVector.x[component] << ".");
126 //LOG(1, "OtherComponents to Neighbour and OtherNeighbour are " << DistanceVector.x[Othercomponent] << " and " << OtherVector.x[Othercomponent] << ".");
127 // do linear interpolation between points (is exact) to extract exact intersection between Neighbour and OtherNeighbour
128 w1 = fabs(OtherVector[Othercomponent]);
129 w2 = fabs(DistanceVector[Othercomponent]);
130 tmp = fabs((w1 * DistanceVector[component] + w2
131 * OtherVector[component]) / (w1 + w2));
132 // mark if it has greater diameter
133 //LOG(1, "Comparing current greatest " << GreatestDiameter[component] << " to new " << tmp << ".");
134 GreatestDiameter[component] = (GreatestDiameter[component]
135 > tmp) ? GreatestDiameter[component] : tmp;
136 } //else
137 //LOG(1, "Saw no sign flip, probably top or bottom node.");
138 }
139 }
140 }
141 LOG(0, "RESULT: The biggest diameters are "
142 << GreatestDiameter[0] << " and " << GreatestDiameter[1] << " and "
143 << GreatestDiameter[2] << " " << (IsAngstroem ? "angstrom"
144 : "atomiclength") << ".");
145
146 // free reference lists
147 if (BoundaryFreeFlag)
148 delete[] (BoundaryPoints);
149
150 return GreatestDiameter;
151}
152;
153
154
155/** Determines the boundary points of a cluster.
156 * Does a projection per axis onto the orthogonal plane, transforms into spherical coordinates, sorts them by the angle
157 * and looks at triples: if the middle has less a distance than the allowed maximum height of the triangle formed by the plane's
158 * center and first and last point in the triple, it is thrown out.
159 * \param *out output stream for debugging
160 * \param *mol molecule structure representing the cluster
161 * \param *&TesselStruct pointer to Tesselation structure
162 */
163Boundaries *GetBoundaryPoints(const molecule *mol, Tesselation *&TesselStruct)
164{
165 Info FunctionInfo(__func__);
166 PointMap PointsOnBoundary;
167 LineMap LinesOnBoundary;
168 TriangleMap TrianglesOnBoundary;
169 Vector *MolCenter = mol->DetermineCenterOfAll();
170 Vector helper;
171 BoundariesTestPair BoundaryTestPair;
172 Vector AxisVector;
173 Vector AngleReferenceVector;
174 Vector AngleReferenceNormalVector;
175 Vector ProjectedVector;
176 Boundaries *BoundaryPoints = new Boundaries[NDIM]; // first is alpha, second is (r, Nr)
177 double angle = 0.;
178
179 // 3a. Go through every axis
180 for (int axis = 0; axis < NDIM; axis++) {
181 AxisVector.Zero();
182 AngleReferenceVector.Zero();
183 AngleReferenceNormalVector.Zero();
184 AxisVector[axis] = 1.;
185 AngleReferenceVector[(axis + 1) % NDIM] = 1.;
186 AngleReferenceNormalVector[(axis + 2) % NDIM] = 1.;
187
188 LOG(1, "Axisvector is " << AxisVector << " and AngleReferenceVector is " << AngleReferenceVector << ", and AngleReferenceNormalVector is " << AngleReferenceNormalVector << ".");
189
190 // 3b. construct set of all points, transformed into cylindrical system and with left and right neighbours
191 // Boundaries stores non-const TesselPoint ref, hence we need iterator here
192 for (molecule::iterator iter = mol->begin(); iter != mol->end(); ++iter) {
193 ProjectedVector = (*iter)->getPosition() - (*MolCenter);
194 ProjectedVector.ProjectOntoPlane(AxisVector);
195
196 // correct for negative side
197 const double radius = ProjectedVector.NormSquared();
198 if (fabs(radius) > MYEPSILON)
199 angle = ProjectedVector.Angle(AngleReferenceVector);
200 else
201 angle = 0.; // otherwise it's a vector in Axis Direction and unimportant for boundary issues
202
203 //LOG(1, "Checking sign in quadrant : " << ProjectedVector.Projection(&AngleReferenceNormalVector) << ".");
204 if (ProjectedVector.ScalarProduct(AngleReferenceNormalVector) > 0) {
205 angle = 2. * M_PI - angle;
206 }
207 LOG(1, "Inserting " << **iter << ": (r, alpha) = (" << radius << "," << angle << "): " << ProjectedVector);
208 BoundaryTestPair = BoundaryPoints[axis].insert(BoundariesPair(angle, TesselPointDistancePair (radius, (*iter))));
209 if (!BoundaryTestPair.second) { // same point exists, check first r, then distance of original vectors to center of gravity
210 LOG(2, "Encountered two vectors whose projection onto axis " << axis << " is equal: ");
211 LOG(2, "Present vector: " << *BoundaryTestPair.first->second.second);
212 LOG(2, "New vector: " << **iter);
213 const double ProjectedVectorNorm = ProjectedVector.NormSquared();
214 if ((ProjectedVectorNorm - BoundaryTestPair.first->second.first) > MYEPSILON) {
215 BoundaryTestPair.first->second.first = ProjectedVectorNorm;
216 BoundaryTestPair.first->second.second = (*iter);
217 LOG(2, "Keeping new vector due to larger projected distance " << ProjectedVectorNorm << ".");
218 } else if (fabs(ProjectedVectorNorm - BoundaryTestPair.first->second.first) < MYEPSILON) {
219 helper = (*iter)->getPosition() - (*MolCenter);
220 const double oldhelperNorm = helper.NormSquared();
221 helper = BoundaryTestPair.first->second.second->getPosition() - (*MolCenter);
222 if (helper.NormSquared() < oldhelperNorm) {
223 BoundaryTestPair.first->second.second = (*iter);
224 LOG(2, "Keeping new vector due to larger distance to molecule center " << helper.NormSquared() << ".");
225 } else {
226 LOG(2, "Keeping present vector due to larger distance to molecule center " << oldhelperNorm << ".");
227 }
228 } else {
229 LOG(2, "Keeping present vector due to larger projected distance " << ProjectedVectorNorm << ".");
230 }
231 }
232 }
233 // printing all inserted for debugging
234 // {
235 // std::stringstream output;
236 // output << "Printing list of candidates for axis " << axis << " which we have inserted so far: ";
237 // int i=0;
238 // for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
239 // if (runner != BoundaryPoints[axis].begin())
240 // output << ", " << i << ": " << *runner->second.second;
241 // else
242 // output << i << ": " << *runner->second.second;
243 // i++;
244 // }
245 // LOG(1, output.str());
246 // }
247 // 3c. throw out points whose distance is less than the mean of left and right neighbours
248 bool flag = false;
249 LOG(1, "Looking for candidates to kick out by convex condition ... ");
250 do { // do as long as we still throw one out per round
251 flag = false;
252 Boundaries::iterator left = BoundaryPoints[axis].begin();
253 Boundaries::iterator right = BoundaryPoints[axis].begin();
254 Boundaries::iterator runner = BoundaryPoints[axis].begin();
255 bool LoopOnceDone = false;
256 while (!LoopOnceDone) {
257 runner = right;
258 right++;
259 // set neighbours correctly
260 if (runner == BoundaryPoints[axis].begin()) {
261 left = BoundaryPoints[axis].end();
262 } else {
263 left = runner;
264 }
265 left--;
266 if (right == BoundaryPoints[axis].end()) {
267 right = BoundaryPoints[axis].begin();
268 LoopOnceDone = true;
269 }
270 // check distance
271
272 // construct the vector of each side of the triangle on the projected plane (defined by normal vector AxisVector)
273 {
274 Vector SideA, SideB, SideC, SideH;
275 SideA = left->second.second->getPosition() - (*MolCenter);
276 SideA.ProjectOntoPlane(AxisVector);
277 // LOG(1, "SideA: " << SideA);
278
279 SideB = right->second.second->getPosition() -(*MolCenter);
280 SideB.ProjectOntoPlane(AxisVector);
281 // LOG(1, "SideB: " << SideB);
282
283 SideC = left->second.second->getPosition() - right->second.second->getPosition();
284 SideC.ProjectOntoPlane(AxisVector);
285 // LOG(1, "SideC: " << SideC);
286
287 SideH = runner->second.second->getPosition() -(*MolCenter);
288 SideH.ProjectOntoPlane(AxisVector);
289 // LOG(1, "SideH: " << SideH);
290
291 // calculate each length
292 const double a = SideA.Norm();
293 //const double b = SideB.Norm();
294 //const double c = SideC.Norm();
295 const double h = SideH.Norm();
296 // calculate the angles
297 const double alpha = SideA.Angle(SideH);
298 const double beta = SideA.Angle(SideC);
299 const double gamma = SideB.Angle(SideH);
300 const double delta = SideC.Angle(SideH);
301 const double MinDistance = a * sin(beta) / (sin(delta)) * (((alpha < M_PI / 2.) || (gamma < M_PI / 2.)) ? 1. : -1.);
302 //LOG(1, " I calculated: a = " << a << ", h = " << h << ", beta(" << left->second.second->Name << "," << left->second.second->Name << "-" << right->second.second->Name << ") = " << beta << ", delta(" << left->second.second->Name << "," << runner->second.second->Name << ") = " << delta << ", Min = " << MinDistance << ".");
303 LOG(1, "Checking CoG distance of runner " << *runner->second.second << " " << h << " against triangle's side length spanned by (" << *left->second.second << "," << *right->second.second << ") of " << MinDistance << ".");
304 if ((fabs(h / fabs(h) - MinDistance / fabs(MinDistance)) < MYEPSILON) && ((h - MinDistance)) < -MYEPSILON) {
305 // throw out point
306 LOG(1, "Throwing out " << *runner->second.second << ".");
307 BoundaryPoints[axis].erase(runner);
308 runner = right;
309 flag = true;
310 }
311 }
312 }
313 } while (flag);
314 }
315 delete(MolCenter);
316 return BoundaryPoints;
317};
318
319/** Tesselates the convex boundary by finding all boundary points.
320 * \param *out output stream for debugging
321 * \param *mol molecule structure with Atom's and Bond's.
322 * \param *BoundaryPts set of boundary points to use or NULL
323 * \param *TesselStruct Tesselation filled with points, lines and triangles on boundary on return
324 * \param *LCList atoms in LinkedCell list
325 * \param *filename filename prefix for output of vertex data
326 * \return *TesselStruct is filled with convex boundary and tesselation is stored under \a *filename.
327 */
328void FindConvexBorder(const molecule* mol, Boundaries *BoundaryPts, Tesselation *&TesselStruct, const LinkedCell *LCList, const char *filename)
329{
330 Info FunctionInfo(__func__);
331 bool BoundaryFreeFlag = false;
332 Boundaries *BoundaryPoints = NULL;
333
334 if (TesselStruct != NULL) // free if allocated
335 delete(TesselStruct);
336 TesselStruct = new class Tesselation;
337
338 // 1. Find all points on the boundary
339 if (BoundaryPts == NULL) {
340 BoundaryFreeFlag = true;
341 BoundaryPoints = GetBoundaryPoints(mol, TesselStruct);
342 } else {
343 BoundaryPoints = BoundaryPts;
344 LOG(0, "Using given boundary points set.");
345 }
346
347// printing all inserted for debugging
348 if (DoLog(1)) {
349 for (int axis=0; axis < NDIM; axis++) {
350 std::stringstream output;
351 output << "Printing list of candidates for axis " << axis << " which we have inserted so far: ";
352 int i=0;
353 for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
354 if (runner != BoundaryPoints[axis].begin())
355 output << ", " << i << ": " << *runner->second.second;
356 else
357 output << i << ": " << *runner->second.second;
358 i++;
359 }
360 LOG(1, output.str());
361 }
362 }
363
364 // 2. fill the boundary point list
365 for (int axis = 0; axis < NDIM; axis++)
366 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++)
367 if (!TesselStruct->AddBoundaryPoint(runner->second.second, 0))
368 LOG(2, "Point " << *(runner->second.second) << " is already present.");
369
370 LOG(0, "I found " << TesselStruct->PointsOnBoundaryCount << " points on the convex boundary.");
371 // now we have the whole set of edge points in the BoundaryList
372
373 // listing for debugging
374 //if (DoLog(1)) {
375 // std::stringstream output;
376 // output << "Listing PointsOnBoundary:";
377 // for(PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
378 // output << " " << *runner->second;
379 // }
380 // LOG(1, output.str());
381 //}
382
383 // 3a. guess starting triangle
384 TesselStruct->GuessStartingTriangle();
385
386 // 3b. go through all lines, that are not yet part of two triangles (only of one so far)
387 PointCloudAdaptor< molecule > cloud(const_cast<molecule *>(mol), mol->name);
388 TesselStruct->TesselateOnBoundary(cloud);
389
390 // 3c. check whether all atoms lay inside the boundary, if not, add to boundary points, segment triangle into three with the new point
391 if (!TesselStruct->InsertStraddlingPoints(cloud, LCList))
392 ELOG(1, "Insertion of straddling points failed!");
393
394 LOG(0, "I created " << TesselStruct->TrianglesOnBoundary.size() << " intermediate triangles with " << TesselStruct->LinesOnBoundary.size() << " lines and " << TesselStruct->PointsOnBoundary.size() << " points.");
395
396 // 4. Store triangles in tecplot file
397 StoreTrianglesinFile(mol, TesselStruct, filename, "_intermed");
398
399 // 3d. check all baselines whether the peaks of the two adjacent triangles with respect to center of baseline are convex, if not, make the baseline between the two peaks and baseline endpoints become the new peaks
400 bool AllConvex = true;
401 class BoundaryLineSet *line = NULL;
402 do {
403 AllConvex = true;
404 for (LineMap::iterator LineRunner = TesselStruct->LinesOnBoundary.begin(); LineRunner != TesselStruct->LinesOnBoundary.end(); LineRunner++) {
405 line = LineRunner->second;
406 LOG(1, "INFO: Current line is " << *line << ".");
407 if (!line->CheckConvexityCriterion()) {
408 LOG(1, "... line " << *line << " is concave, flipping it.");
409
410 // flip the line
411 if (TesselStruct->PickFarthestofTwoBaselines(line) == 0.)
412 ELOG(1, "Correction of concave baselines failed!");
413 else {
414 TesselStruct->FlipBaseline(line);
415 LOG(1, "INFO: Correction of concave baselines worked.");
416 LineRunner = TesselStruct->LinesOnBoundary.begin(); // LineRunner may have been erase if line was deleted from LinesOnBoundary
417 }
418 }
419 }
420 } while (!AllConvex);
421
422 // 3e. we need another correction here, for TesselPoints that are below the surface (i.e. have an odd number of concave triangles surrounding it)
423// if (!TesselStruct->CorrectConcaveTesselPoints(out))
424// ELOG(1, "Correction of concave tesselpoints failed!");
425
426 LOG(0, "I created " << TesselStruct->TrianglesOnBoundary.size() << " triangles with " << TesselStruct->LinesOnBoundary.size() << " lines and " << TesselStruct->PointsOnBoundary.size() << " points.");
427
428 // 4. Store triangles in tecplot file
429 StoreTrianglesinFile(mol, TesselStruct, filename, "");
430
431 // free reference lists
432 if (BoundaryFreeFlag)
433 delete[] (BoundaryPoints);
434};
435
436/** For testing removes one boundary point after another to check for leaks.
437 * \param *out output stream for debugging
438 * \param *TesselStruct Tesselation containing envelope with boundary points
439 * \param *mol molecule
440 * \param *filename name of file
441 * \return true - all removed, false - something went wrong
442 */
443bool RemoveAllBoundaryPoints(class Tesselation *&TesselStruct, const molecule * const mol, const char * const filename)
444{
445 Info FunctionInfo(__func__);
446 int i=0;
447 char number[MAXSTRINGSIZE];
448
449 if ((TesselStruct == NULL) || (TesselStruct->PointsOnBoundary.empty())) {
450 ELOG(1, "TesselStruct is empty.");
451 return false;
452 }
453
454 PointMap::iterator PointRunner;
455 while (!TesselStruct->PointsOnBoundary.empty()) {
456 if (DoLog(1)) {
457 std::stringstream output;
458 output << "Remaining points are: ";
459 for (PointMap::iterator PointSprinter = TesselStruct->PointsOnBoundary.begin(); PointSprinter != TesselStruct->PointsOnBoundary.end(); PointSprinter++)
460 output << *(PointSprinter->second) << "\t";
461 LOG(1, output.str());
462 }
463
464 PointRunner = TesselStruct->PointsOnBoundary.begin();
465 // remove point
466 TesselStruct->RemovePointFromTesselatedSurface(PointRunner->second);
467
468 // store envelope
469 sprintf(number, "-%04d", i++);
470 StoreTrianglesinFile(mol, (const Tesselation *&)TesselStruct, filename, number);
471 }
472
473 return true;
474};
475
476/** Creates a convex envelope from a given non-convex one.
477 * -# First step, remove concave spots, i.e. singular "dents"
478 * -# We go through all PointsOnBoundary.
479 * -# We CheckConvexityCriterion() for all its lines.
480 * -# If all its lines are concave, it cannot be on the convex envelope.
481 * -# Hence, we remove it and re-create all its triangles from its getCircleOfConnectedPoints()
482 * -# We calculate the additional volume.
483 * -# We go over all lines until none yields a concavity anymore.
484 * -# Second step, remove concave lines, i.e. line-shape "dents"
485 * -# We go through all LinesOnBoundary
486 * -# We CheckConvexityCriterion()
487 * -# If it returns concave, we flip the line in this quadrupel of points (abusing the degeneracy of the tesselation)
488 * -# We CheckConvexityCriterion(),
489 * -# if it's concave, we continue
490 * -# if not, we mark an error and stop
491 * Note: This routine - for free - calculates the difference in volume between convex and
492 * non-convex envelope, as the former is easy to calculate - VolumeOfConvexEnvelope() - it
493 * can be used to compute volumes of arbitrary shapes.
494 * \param *out output stream for debugging
495 * \param *TesselStruct non-convex envelope, is changed in return!
496 * \param *mol molecule
497 * \param *filename name of file
498 * \return volume difference between the non- and the created convex envelope
499 */
500double ConvexizeNonconvexEnvelope(class Tesselation *&TesselStruct, const molecule * const mol, const char * const filename)
501{
502 Info FunctionInfo(__func__);
503 double volume = 0;
504 class BoundaryPointSet *point = NULL;
505 class BoundaryLineSet *line = NULL;
506 bool Concavity = false;
507 char dummy[MAXSTRINGSIZE];
508 PointMap::iterator PointRunner;
509 PointMap::iterator PointAdvance;
510 LineMap::iterator LineRunner;
511 LineMap::iterator LineAdvance;
512 TriangleMap::iterator TriangleRunner;
513 TriangleMap::iterator TriangleAdvance;
514 int run = 0;
515
516 // check whether there is something to work on
517 if (TesselStruct == NULL) {
518 ELOG(1, "TesselStruct is empty!");
519 return volume;
520 }
521
522 // First step: RemovePointFromTesselatedSurface
523 do {
524 Concavity = false;
525 sprintf(dummy, "-first-%d", run);
526 //CalculateConcavityPerBoundaryPoint(TesselStruct);
527 StoreTrianglesinFile(mol, (const Tesselation *&)TesselStruct, filename, dummy);
528
529 PointRunner = TesselStruct->PointsOnBoundary.begin();
530 PointAdvance = PointRunner; // we need an advanced point, as the PointRunner might get removed
531 while (PointRunner != TesselStruct->PointsOnBoundary.end()) {
532 PointAdvance++;
533 point = PointRunner->second;
534 LOG(1, "INFO: Current point is " << *point << ".");
535 for (LineMap::iterator LineRunner = point->lines.begin(); LineRunner != point->lines.end(); LineRunner++) {
536 line = LineRunner->second;
537 LOG(1, "INFO: Current line of point " << *point << " is " << *line << ".");
538 if (!line->CheckConvexityCriterion()) {
539 // remove the point if needed
540 LOG(1, "... point " << *point << " cannot be on convex envelope.");
541 volume += TesselStruct->RemovePointFromTesselatedSurface(point);
542 sprintf(dummy, "-first-%d", ++run);
543 StoreTrianglesinFile(mol, (const Tesselation *&)TesselStruct, filename, dummy);
544 Concavity = true;
545 break;
546 }
547 }
548 PointRunner = PointAdvance;
549 }
550
551 sprintf(dummy, "-second-%d", run);
552 //CalculateConcavityPerBoundaryPoint(TesselStruct);
553 StoreTrianglesinFile(mol, (const Tesselation *&)TesselStruct, filename, dummy);
554
555 // second step: PickFarthestofTwoBaselines
556 LineRunner = TesselStruct->LinesOnBoundary.begin();
557 LineAdvance = LineRunner; // we need an advanced line, as the LineRunner might get removed
558 while (LineRunner != TesselStruct->LinesOnBoundary.end()) {
559 LineAdvance++;
560 line = LineRunner->second;
561 LOG(1, "INFO: Picking farthest baseline for line is " << *line << ".");
562 // take highest of both lines
563 if (TesselStruct->IsConvexRectangle(line) == NULL) {
564 const double tmp = TesselStruct->PickFarthestofTwoBaselines(line);
565 volume += tmp;
566 if (tmp != 0.) {
567 TesselStruct->FlipBaseline(line);
568 Concavity = true;
569 }
570 }
571 LineRunner = LineAdvance;
572 }
573 run++;
574 } while (Concavity);
575 //CalculateConcavityPerBoundaryPoint(TesselStruct);
576 //StoreTrianglesinFile(mol, filename, "-third");
577
578 // third step: IsConvexRectangle
579// LineRunner = TesselStruct->LinesOnBoundary.begin();
580// LineAdvance = LineRunner; // we need an advanced line, as the LineRunner might get removed
581// while (LineRunner != TesselStruct->LinesOnBoundary.end()) {
582// LineAdvance++;
583// line = LineRunner->second;
584// LOG(1, "INFO: Current line is " << *line << ".");
585// //if (LineAdvance != TesselStruct->LinesOnBoundary.end())
586// //LOG(1, "INFO: Next line will be " << *(LineAdvance->second) << ".");
587// if (!line->CheckConvexityCriterion(out)) {
588// LOG(1, "INFO: ... line " << *line << " is concave, flipping it.");
589//
590// // take highest of both lines
591// point = TesselStruct->IsConvexRectangle(line);
592// if (point != NULL)
593// volume += TesselStruct->RemovePointFromTesselatedSurface(point);
594// }
595// LineRunner = LineAdvance;
596// }
597
598 CalculateConcavityPerBoundaryPoint(TesselStruct);
599 StoreTrianglesinFile(mol, (const Tesselation *&)TesselStruct, filename, "");
600
601 // end
602 LOG(0, "Volume is " << volume << ".");
603 return volume;
604};
605
606
607/** Determines the volume of a cluster.
608 * Determines first the convex envelope, then tesselates it and calculates its volume.
609 * \param *out output stream for debugging
610 * \param *TesselStruct Tesselation filled with points, lines and triangles on boundary on return
611 * \param *configuration needed for path to store convex envelope file
612 * \return determined volume of the cluster in cubed config:GetIsAngstroem()
613 */
614double VolumeOfConvexEnvelope(class Tesselation *TesselStruct, class config *configuration)
615{
616 Info FunctionInfo(__func__);
617 bool IsAngstroem = configuration->GetIsAngstroem();
618 double volume = 0.;
619 Vector x;
620 Vector y;
621
622 // 6a. Every triangle forms a pyramid with the center of gravity as its peak, sum up the volumes
623 for (TriangleMap::iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner != TesselStruct->TrianglesOnBoundary.end(); runner++)
624 { // go through every triangle, calculate volume of its pyramid with CoG as peak
625 x = runner->second->getEndpoint(0) - runner->second->getEndpoint(1);
626 y = runner->second->getEndpoint(0) - runner->second->getEndpoint(2);
627 const double a = x.Norm();
628 const double b = y.Norm();
629 const double c = runner->second->getEndpoint(2).distance(runner->second->getEndpoint(1));
630 const double G = sqrt(((a + b + c) * (a + b + c) - 2 * (a * a + b * b + c * c)) / 16.); // area of tesselated triangle
631 x = runner->second->getPlane().getNormal();
632 x.Scale(runner->second->getEndpoint(1).ScalarProduct(x));
633 const double h = x.Norm(); // distance of CoG to triangle
634 const double PyramidVolume = (1. / 3.) * G * h; // this formula holds for _all_ pyramids (independent of n-edge base or (not) centered peak)
635 LOG(1, "INFO: Area of triangle is " << setprecision(10) << G << " "
636 << (IsAngstroem ? "angstrom" : "atomiclength") << "^2, height is "
637 << h << " and the volume is " << PyramidVolume << " "
638 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3.");
639 volume += PyramidVolume;
640 }
641 LOG(0, "RESULT: The summed volume is " << setprecision(6)
642 << volume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3.");
643
644 return volume;
645};
646
647/** Stores triangles to file.
648 * \param *out output stream for debugging
649 * \param *mol molecule with atoms and bonds
650 * \param *TesselStruct Tesselation with boundary triangles
651 * \param *filename prefix of filename
652 * \param *extraSuffix intermediate suffix
653 */
654void StoreTrianglesinFile(const molecule * const mol, const Tesselation * const TesselStruct, const char *filename, const char *extraSuffix)
655{
656 Info FunctionInfo(__func__);
657 PointCloudAdaptor< molecule > cloud(const_cast<molecule *>(mol), mol->name);
658 // 4. Store triangles in tecplot file
659 if (filename != NULL) {
660 if (DoTecplotOutput) {
661 string OutputName(filename);
662 OutputName.append(extraSuffix);
663 OutputName.append(TecplotSuffix);
664 ofstream *tecplot = new ofstream(OutputName.c_str());
665 WriteTecplotFile(tecplot, TesselStruct, cloud, -1);
666 tecplot->close();
667 delete(tecplot);
668 }
669 if (DoRaster3DOutput) {
670 string OutputName(filename);
671 OutputName.append(extraSuffix);
672 OutputName.append(Raster3DSuffix);
673 ofstream *rasterplot = new ofstream(OutputName.c_str());
674 WriteRaster3dFile(rasterplot, TesselStruct, cloud);
675 rasterplot->close();
676 delete(rasterplot);
677 }
678 }
679};
680
681/** Creates multiples of the by \a *mol given cluster and suspends them in water with a given final density.
682 * We get cluster volume by VolumeOfConvexEnvelope() and its diameters by GetDiametersOfCluster()
683 * TODO: Here, we need a VolumeOfGeneralEnvelope (i.e. non-convex one)
684 * \param *out output stream for debugging
685 * \param *configuration needed for path to store convex envelope file
686 * \param *mol molecule structure representing the cluster
687 * \param *&TesselStruct Tesselation structure with triangles on return
688 * \param ClusterVolume guesstimated cluster volume, if equal 0 we used VolumeOfConvexEnvelope() instead.
689 * \param celldensity desired average density in final cell
690 */
691void PrepareClustersinWater(config *configuration, molecule *mol, double ClusterVolume, double celldensity)
692{
693 Info FunctionInfo(__func__);
694 bool IsAngstroem = true;
695 double *GreatestDiameter = NULL;
696 Boundaries *BoundaryPoints = NULL;
697 class Tesselation *TesselStruct = NULL;
698 Vector BoxLengths;
699 int repetition[NDIM] = { 1, 1, 1 };
700 int TotalNoClusters = 1;
701 double totalmass = 0.;
702 double clustervolume = 0.;
703 double cellvolume = 0.;
704
705 // transform to PAS by Action
706 Vector MainAxis(0.,0.,1.);
707 mol->RotateToPrincipalAxisSystem(MainAxis);
708
709 IsAngstroem = configuration->GetIsAngstroem();
710 BoundaryPoints = GetBoundaryPoints(mol, TesselStruct);
711 GreatestDiameter = GetDiametersOfCluster(BoundaryPoints, mol, TesselStruct, IsAngstroem);
712 PointCloudAdaptor< molecule > cloud(mol, mol->name);
713 LinkedCell *LCList = new LinkedCell(cloud, 10.);
714 FindConvexBorder(mol, BoundaryPoints, TesselStruct, (const LinkedCell *&)LCList, NULL);
715 delete (LCList);
716 delete[] BoundaryPoints;
717
718
719 // some preparations beforehand
720 if (ClusterVolume == 0)
721 clustervolume = VolumeOfConvexEnvelope(TesselStruct, configuration);
722 else
723 clustervolume = ClusterVolume;
724
725 delete TesselStruct;
726
727 for (int i = 0; i < NDIM; i++)
728 TotalNoClusters *= repetition[i];
729
730 // sum up the atomic masses
731 for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
732 totalmass += (*iter)->getType()->getMass();
733 }
734 LOG(0, "RESULT: The summed mass is " << setprecision(10) << totalmass << " atomicmassunit.");
735 LOG(0, "RESULT: The average density is " << setprecision(10) << totalmass / clustervolume << " atomicmassunit/" << (IsAngstroem ? "angstrom" : "atomiclength") << "^3.");
736
737 // solve cubic polynomial
738 LOG(1, "Solving equidistant suspension in water problem ...");
739 if (IsAngstroem)
740 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_A - (totalmass / clustervolume)) / (celldensity - 1);
741 else
742 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_a0 - (totalmass / clustervolume)) / (celldensity - 1);
743 LOG(1, "Cellvolume needed for a density of " << celldensity << " g/cm^3 is " << cellvolume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3.");
744
745 double minimumvolume = TotalNoClusters * (GreatestDiameter[0] * GreatestDiameter[1] * GreatestDiameter[2]);
746 LOG(1, "Minimum volume of the convex envelope contained in a rectangular box is " << minimumvolume << " atomicmassunit/" << (IsAngstroem ? "angstrom" : "atomiclength") << "^3.");
747 if (minimumvolume > cellvolume) {
748 ELOG(1, "the containing box already has a greater volume than the envisaged cell volume!");
749 LOG(0, "Setting Box dimensions to minimum possible, the greatest diameters.");
750 for (int i = 0; i < NDIM; i++)
751 BoxLengths[i] = GreatestDiameter[i];
752 mol->CenterEdge(&BoxLengths);
753 } else {
754 BoxLengths[0] = (repetition[0] * GreatestDiameter[0] + repetition[1] * GreatestDiameter[1] + repetition[2] * GreatestDiameter[2]);
755 BoxLengths[1] = (repetition[0] * repetition[1] * GreatestDiameter[0] * GreatestDiameter[1] + repetition[0] * repetition[2] * GreatestDiameter[0] * GreatestDiameter[2] + repetition[1] * repetition[2] * GreatestDiameter[1] * GreatestDiameter[2]);
756 BoxLengths[2] = minimumvolume - cellvolume;
757 double x0 = 0.;
758 double x1 = 0.;
759 double x2 = 0.;
760 if (gsl_poly_solve_cubic(BoxLengths[0], BoxLengths[1], BoxLengths[2], &x0, &x1, &x2) == 1) // either 1 or 3 on return
761 LOG(0, "RESULT: The resulting spacing is: " << x0 << " .");
762 else {
763 LOG(0, "RESULT: The resulting spacings are: " << x0 << " and " << x1 << " and " << x2 << " .");
764 x0 = x2; // sorted in ascending order
765 }
766
767 cellvolume = 1.;
768 for (int i = 0; i < NDIM; i++) {
769 BoxLengths[i] = repetition[i] * (x0 + GreatestDiameter[i]);
770 cellvolume *= BoxLengths[i];
771 }
772
773 // set new box dimensions
774 LOG(0, "Translating to box with these boundaries.");
775 mol->SetBoxDimension(&BoxLengths);
776 mol->CenterInBox();
777 }
778 delete GreatestDiameter;
779 // update Box of atoms by boundary
780 mol->SetBoxDimension(&BoxLengths);
781 LOG(0, "RESULT: The resulting cell dimensions are: " << BoxLengths[0] << " and " << BoxLengths[1] << " and " << BoxLengths[2] << " with total volume of " << cellvolume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3.");
782};
783
784
785/** Fills the empty space around other molecules' surface of the simulation box with a filler.
786 * \param *out output stream for debugging
787 * \param *List list of molecules already present in box
788 * \param *TesselStruct contains tesselated surface
789 * \param *filler molecule which the box is to be filled with
790 * \param configuration contains box dimensions
791 * \param MaxDistance fills in molecules only up to this distance (set to -1 if whole of the domain)
792 * \param distance[NDIM] distance between filling molecules in each direction
793 * \param boundary length of boundary zone between molecule and filling mollecules
794 * \param epsilon distance to surface which is not filled
795 * \param RandAtomDisplacement maximum distance for random displacement per atom
796 * \param RandMolDisplacement maximum distance for random displacement per filler molecule
797 * \param DoRandomRotation true - do random rotiations, false - don't
798 */
799void FillBoxWithMolecule(MoleculeListClass *List, molecule *filler, config &configuration, const double MaxDistance, const double distance[NDIM], const double boundary, const double RandomAtomDisplacement, const double RandomMolDisplacement, const bool DoRandomRotation)
800{
801 Info FunctionInfo(__func__);
802 molecule *Filling = World::getInstance().createMolecule();
803 Vector CurrentPosition;
804 int N[NDIM];
805 int n[NDIM];
806 const RealSpaceMatrix &M = World::getInstance().getDomain().getM();
807 RealSpaceMatrix Rotations;
808 const RealSpaceMatrix &MInverse = World::getInstance().getDomain().getMinv();
809 Vector AtomTranslations;
810 Vector FillerTranslations;
811 Vector FillerDistance;
812 Vector Inserter;
813 double FillIt = false;
814 bond *Binder = NULL;
815 double phi[NDIM];
816 map<molecule *, Tesselation *> TesselStruct;
817 map<molecule *, LinkedCell *> LCList;
818
819 for (MoleculeList::iterator ListRunner = List->ListOfMolecules.begin(); ListRunner != List->ListOfMolecules.end(); ListRunner++)
820 if ((*ListRunner)->getAtomCount() > 0) {
821 LOG(1, "Pre-creating linked cell lists for molecule " << *ListRunner << ".");
822 PointCloudAdaptor< molecule > cloud(*ListRunner, (*ListRunner)->name);
823 LCList[(*ListRunner)] = new LinkedCell(cloud, 10.); // get linked cell list
824 LOG(1, "Pre-creating tesselation for molecule " << *ListRunner << ".");
825 TesselStruct[(*ListRunner)] = NULL;
826 FindNonConvexBorder((*ListRunner), TesselStruct[(*ListRunner)], (const LinkedCell *&)LCList[(*ListRunner)], 5., NULL);
827 }
828
829 // Center filler at origin
830 filler->CenterEdge(&Inserter);
831 const int FillerCount = filler->getAtomCount();
832 LOG(2, "INFO: Filler molecule has the following bonds:");
833 for(molecule::iterator AtomRunner = filler->begin(); AtomRunner != filler->end(); ++AtomRunner) {
834 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
835 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
836 BondRunner != ListOfBonds.end();
837 ++BondRunner) {
838 if ((*BondRunner)->leftatom == *AtomRunner)
839 LOG(2, " " << *(*BondRunner));
840 }
841 }
842
843 atom * CopyAtoms[FillerCount];
844
845 // calculate filler grid in [0,1]^3
846 FillerDistance = MInverse * Vector(distance[0], distance[1], distance[2]);
847 for(int i=0;i<NDIM;i++)
848 N[i] = (int) ceil(1./FillerDistance[i]);
849 LOG(1, "INFO: Grid steps are " << N[0] << ", " << N[1] << ", " << N[2] << ".");
850
851 // initialize seed of random number generator to current time
852 RandomNumberGenerator &random = RandomNumberGeneratorFactory::getInstance().makeRandomNumberGenerator();
853 const double rng_min = random.min();
854 const double rng_max = random.max();
855 //srand ( time(NULL) );
856
857 // go over [0,1]^3 filler grid
858 for (n[0] = 0; n[0] < N[0]; n[0]++)
859 for (n[1] = 0; n[1] < N[1]; n[1]++)
860 for (n[2] = 0; n[2] < N[2]; n[2]++) {
861 // calculate position of current grid vector in untransformed box
862 CurrentPosition = M * Vector((double)n[0]/(double)N[0], (double)n[1]/(double)N[1], (double)n[2]/(double)N[2]);
863 // create molecule random translation vector ...
864 for (int i=0;i<NDIM;i++)
865 FillerTranslations[i] = RandomMolDisplacement*(random()/((rng_max-rng_min)/2.) - 1.);
866 LOG(2, "INFO: Current Position is " << CurrentPosition << "+" << FillerTranslations << ".");
867
868 // go through all atoms
869 for (int i=0;i<FillerCount;i++)
870 CopyAtoms[i] = NULL;
871
872 // have same rotation angles for all molecule's atoms
873 if (DoRandomRotation)
874 for (int i=0;i<NDIM;i++)
875 phi[i] = (random()/(rng_max-rng_min))*(2.*M_PI);
876
877 // atom::clone is not const member function, hence we need iterator here
878 for(molecule::iterator iter = filler->begin(); iter !=filler->end();++iter){
879
880 // create atomic random translation vector ...
881 for (int i=0;i<NDIM;i++)
882 AtomTranslations[i] = RandomAtomDisplacement*(random()/((rng_max-rng_min)/2.) - 1.);
883
884 // ... and rotation matrix
885 if (DoRandomRotation) {
886 Rotations.set(0,0, cos(phi[0]) *cos(phi[2]) + (sin(phi[0])*sin(phi[1])*sin(phi[2])));
887 Rotations.set(0,1, sin(phi[0]) *cos(phi[2]) - (cos(phi[0])*sin(phi[1])*sin(phi[2])));
888 Rotations.set(0,2, cos(phi[1])*sin(phi[2]) );
889 Rotations.set(1,0, -sin(phi[0])*cos(phi[1]) );
890 Rotations.set(1,1, cos(phi[0])*cos(phi[1]) );
891 Rotations.set(1,2, sin(phi[1]) );
892 Rotations.set(2,0, -cos(phi[0]) *sin(phi[2]) + (sin(phi[0])*sin(phi[1])*cos(phi[2])));
893 Rotations.set(2,1, -sin(phi[0]) *sin(phi[2]) - (cos(phi[0])*sin(phi[1])*cos(phi[2])));
894 Rotations.set(2,2, cos(phi[1])*cos(phi[2]) );
895 }
896
897 // ... and put at new position
898 Inserter = (*iter)->getPosition();
899 if (DoRandomRotation)
900 Inserter *= Rotations;
901 Inserter += AtomTranslations + FillerTranslations + CurrentPosition;
902
903 // check whether inserter is inside box
904 Inserter *= MInverse;
905 FillIt = true;
906 for (int i=0;i<NDIM;i++)
907 FillIt = FillIt && (Inserter[i] >= -MYEPSILON) && ((Inserter[i]-1.) <= MYEPSILON);
908 Inserter *= M;
909
910 // Check whether point is in- or outside
911 for (MoleculeList::iterator ListRunner = List->ListOfMolecules.begin(); ListRunner != List->ListOfMolecules.end(); ListRunner++) {
912 // get linked cell list
913 if (TesselStruct[(*ListRunner)] != NULL) {
914 const double distance = (TesselStruct[(*ListRunner)]->GetDistanceToSurface(Inserter, LCList[(*ListRunner)]));
915 FillIt = FillIt && (distance > boundary) && ((MaxDistance < 0) || (MaxDistance > distance));
916 }
917 }
918 // insert into Filling
919 if (FillIt) {
920 LOG(1, "INFO: Position at " << Inserter << " is outer point.");
921 // copy atom ...
922 CopyAtoms[(*iter)->getNr()] = (*iter)->clone();
923 (*CopyAtoms[(*iter)->getNr()]).setPosition(Inserter);
924 Filling->AddAtom(CopyAtoms[(*iter)->getNr()]);
925 LOG(1, "Filling atom " << **iter << ", translated to " << AtomTranslations << ", at final position is " << (CopyAtoms[(*iter)->getNr()]->getPosition()) << ".");
926 } else {
927 LOG(1, "INFO: Position at " << Inserter << " is inner point, within boundary or outside of MaxDistance.");
928 CopyAtoms[(*iter)->getNr()] = NULL;
929 continue;
930 }
931 }
932 // go through all bonds and add as well
933 for(molecule::iterator AtomRunner = filler->begin(); AtomRunner != filler->end(); ++AtomRunner) {
934 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
935 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
936 BondRunner != ListOfBonds.end();
937 ++BondRunner)
938 if ((*BondRunner)->leftatom == *AtomRunner) {
939 Binder = (*BondRunner);
940 if ((CopyAtoms[Binder->leftatom->getNr()] != NULL) && (CopyAtoms[Binder->rightatom->getNr()] != NULL)) {
941 LOG(3, "Adding Bond between " << *CopyAtoms[Binder->leftatom->getNr()] << " and " << *CopyAtoms[Binder->rightatom->getNr()]<< ".");
942 Filling->AddBond(CopyAtoms[Binder->leftatom->getNr()], CopyAtoms[Binder->rightatom->getNr()], Binder->BondDegree);
943 }
944 }
945 }
946 }
947 for (MoleculeList::iterator ListRunner = List->ListOfMolecules.begin(); ListRunner != List->ListOfMolecules.end(); ListRunner++) {
948 delete LCList[*ListRunner];
949 delete TesselStruct[(*ListRunner)];
950 }
951};
952
953/** Rotates given molecule \a Filling and moves its atoms according to given
954 * \a RandomAtomDisplacement.
955 *
956 * Note that for rotation to be sensible, the molecule should be centered at
957 * the origin. This is not done here!
958 *
959 * \param &Filling molecule whose atoms to displace
960 * \param RandomAtomDisplacement magnitude of random displacement
961 * \param &Rotations 3D rotation matrix (or unity if no rotation desired)
962 */
963void RandomizeMoleculePositions(
964 molecule *&Filling,
965 double RandomAtomDisplacement,
966 RealSpaceMatrix &Rotations,
967 RandomNumberGenerator &random
968 )
969{
970 const double rng_min = random.min();
971 const double rng_max = random.max();
972
973 Vector AtomTranslations;
974 for(molecule::iterator miter = Filling->begin(); miter != Filling->end(); ++miter) {
975 Vector temp = (*miter)->getPosition();
976 temp *= Rotations;
977 (*miter)->setPosition(temp);
978 // create atomic random translation vector ...
979 for (int i=0;i<NDIM;i++)
980 AtomTranslations[i] = RandomAtomDisplacement*(random()/((rng_max-rng_min)/2.) - 1.);
981 (*miter)->setPosition((*miter)->getPosition() + AtomTranslations);
982 }
983}
984
985/** Removes all atoms of a molecule outside.
986 *
987 * If the molecule is empty, it is removed as well.
988 *
989 * @param Filling molecule whose atoms to check, removed if eventually left
990 * empty.
991 * @return true - atoms had to be removed, false - no atoms have been removed
992 */
993bool RemoveAtomsOutsideDomain(molecule *&Filling)
994{
995 bool status = false;
996 Box &Domain = World::getInstance().getDomain();
997 // check if all is still inside domain
998 for(molecule::iterator miter = Filling->begin(); miter != Filling->end(); ) {
999 // check whether each atom is inside box
1000 if (!Domain.isInside((*miter)->getPosition())) {
1001 status = true;
1002 atom *Walker = *miter;
1003 ++miter;
1004 World::getInstance().destroyAtom(Walker);
1005 } else {
1006 ++miter;
1007 }
1008 }
1009 if (Filling->empty()) {
1010 LOG(0, "Removing molecule " << Filling->getName() << ", all atoms have been removed.");
1011 World::getInstance().destroyMolecule(Filling);
1012 Filling = NULL;
1013 }
1014 return status;
1015}
1016
1017/** Checks whether there are no atoms inside a sphere around \a CurrentPosition
1018 * except those atoms present in \a *filler.
1019 * If filler is NULL, then we just call LinkedCell::GetPointsInsideSphere() and
1020 * check whether the return list is empty.
1021 * @param *filler
1022 * @param boundary
1023 * @param CurrentPosition
1024 */
1025bool isSpaceAroundPointVoid(
1026 LinkedCell *LC,
1027 molecule *filler,
1028 const double boundary,
1029 Vector &CurrentPosition)
1030{
1031 size_t compareTo = 0;
1032 TesselPointSTLList* liste = LC->GetPointsInsideSphere(boundary == 0. ? MYEPSILON : boundary, &CurrentPosition);
1033 if (filler != NULL) {
1034 for (TesselPointSTLList::const_iterator iter = liste->begin();
1035 iter != liste->end();
1036 ++iter) {
1037 for (molecule::iterator miter = filler->begin();
1038 miter != filler->end();
1039 ++miter) {
1040 if (*iter == *miter)
1041 ++compareTo;
1042 }
1043 }
1044 }
1045 const bool result = (liste->size() == compareTo);
1046 if (!result) {
1047 LOG(0, "Skipping because of the following atoms:");
1048 for (TesselPointSTLList::const_iterator iter = liste->begin();
1049 iter != liste->end();
1050 ++iter) {
1051 LOG(0, **iter);
1052 }
1053 }
1054 delete(liste);
1055 return result;
1056}
1057
1058/** Sets given 3x3 matrix to a random rotation matrix.
1059 *
1060 * @param a matrix to set
1061 */
1062inline void setRandomRotation(RealSpaceMatrix &a)
1063{
1064 double phi[NDIM];
1065 RandomNumberGenerator &random = RandomNumberGeneratorFactory::getInstance().makeRandomNumberGenerator();
1066 const double rng_min = random.min();
1067 const double rng_max = random.max();
1068
1069 for (int i=0;i<NDIM;i++) {
1070 phi[i] = (random()/(rng_max-rng_min))*(2.*M_PI);
1071 std::cout << "Random angle is " << phi[i] << std::endl;
1072 }
1073
1074 a.setRotation(phi);
1075}
1076
1077/** Fills the empty space of the simulation box with water.
1078 * \param *filler molecule which the box is to be filled with
1079 * \param configuration contains box dimensions
1080 * \param distance[NDIM] distance between filling molecules in each direction
1081 * \param boundary length of boundary zone between molecule and filling molecules
1082 * \param RandAtomDisplacement maximum distance for random displacement per atom
1083 * \param RandMolDisplacement maximum distance for random displacement per filler molecule
1084 * \param MinDistance minimum distance to boundary of domain and present molecules
1085 * \param DoRandomRotation true - do random rotations, false - don't
1086 */
1087void FillVoidWithMolecule(
1088 molecule *&filler,
1089 config &configuration,
1090 const double distance[NDIM],
1091 const double boundary,
1092 const double RandomAtomDisplacement,
1093 const double RandomMolDisplacement,
1094 const double MinDistance,
1095 const bool DoRandomRotation
1096 )
1097{
1098 Info FunctionInfo(__func__);
1099 molecule *Filling = NULL;
1100 Vector CurrentPosition;
1101 int N[NDIM];
1102 int n[NDIM];
1103 const RealSpaceMatrix &M = World::getInstance().getDomain().getM();
1104 RealSpaceMatrix Rotations;
1105 const RealSpaceMatrix &MInverse = World::getInstance().getDomain().getMinv();
1106 Vector FillerTranslations;
1107 Vector FillerDistance;
1108 Vector Inserter;
1109 double FillIt = false;
1110 Vector firstInserter;
1111 bool firstInsertion = true;
1112 const Box &Domain = World::getInstance().getDomain();
1113 map<molecule *, LinkedCell *> LCList;
1114 std::vector<molecule *> List = World::getInstance().getAllMolecules();
1115 MoleculeListClass *MolList = World::getInstance().getMolecules();
1116
1117 for (std::vector<molecule *>::iterator ListRunner = List.begin(); ListRunner != List.end(); ListRunner++)
1118 if ((*ListRunner)->getAtomCount() > 0) {
1119 LOG(1, "Pre-creating linked cell lists for molecule " << *ListRunner << ".");
1120 PointCloudAdaptor< molecule > cloud(*ListRunner, (*ListRunner)->name);
1121 LCList[(*ListRunner)] = new LinkedCell(cloud, 10.); // get linked cell list
1122 }
1123
1124 // Center filler at its center of gravity
1125 Vector *gravity = filler->DetermineCenterOfGravity();
1126 filler->CenterAtVector(gravity);
1127 delete gravity;
1128 //const int FillerCount = filler->getAtomCount();
1129 LOG(2, "INFO: Filler molecule has the following bonds:");
1130 for(molecule::iterator AtomRunner = filler->begin(); AtomRunner != filler->end(); ++AtomRunner) {
1131 const BondList& ListOfBonds = (*AtomRunner)->getListOfBonds();
1132 for(BondList::const_iterator BondRunner = ListOfBonds.begin();
1133 BondRunner != ListOfBonds.end();
1134 ++BondRunner)
1135 if ((*BondRunner)->leftatom == *AtomRunner)
1136 LOG(2, " " << *(*BondRunner));
1137 }
1138
1139 // calculate filler grid in [0,1]^3
1140 FillerDistance = MInverse * Vector(distance[0], distance[1], distance[2]);
1141 for(int i=0;i<NDIM;i++)
1142 N[i] = (int) ceil(1./FillerDistance[i]);
1143 LOG(1, "INFO: Grid steps are " << N[0] << ", " << N[1] << ", " << N[2] << ".");
1144
1145 // initialize seed of random number generator to current time
1146 RandomNumberGenerator &random = RandomNumberGeneratorFactory::getInstance().makeRandomNumberGenerator();
1147 const double rng_min = random.min();
1148 const double rng_max = random.max();
1149 //srand ( time(NULL) );
1150
1151 // go over [0,1]^3 filler grid
1152 for (n[0] = 0; n[0] < N[0]; n[0]++)
1153 for (n[1] = 0; n[1] < N[1]; n[1]++)
1154 for (n[2] = 0; n[2] < N[2]; n[2]++) {
1155 // calculate position of current grid vector in untransformed box
1156 CurrentPosition = M * Vector((double)n[0]/(double)N[0], (double)n[1]/(double)N[1], (double)n[2]/(double)N[2]);
1157 // create molecule random translation vector ...
1158 for (int i=0;i<NDIM;i++) // have the random values [-1,1]*RandomMolDisplacement
1159 FillerTranslations[i] = RandomMolDisplacement*(random()/((rng_max-rng_min)/2.) - 1.);
1160 LOG(2, "INFO: Current Position is " << CurrentPosition << "+" << FillerTranslations << ".");
1161
1162 // ... and rotation matrix
1163 if (DoRandomRotation)
1164 setRandomRotation(Rotations);
1165 else
1166 Rotations.setIdentity();
1167
1168
1169 // Check whether there is anything too close by and whether atom is outside of domain
1170 FillIt = true;
1171 for (std::map<molecule *, LinkedCell *>::iterator ListRunner = LCList.begin(); ListRunner != LCList.end(); ++ListRunner) {
1172 FillIt = FillIt && isSpaceAroundPointVoid(
1173 ListRunner->second,
1174 (firstInsertion ? filler : NULL),
1175 boundary,
1176 CurrentPosition);
1177 FillIt = FillIt && (Domain.isInside(CurrentPosition))
1178 && ((Domain.DistanceToBoundary(CurrentPosition) - MinDistance) > -MYEPSILON);
1179 if (!FillIt)
1180 break;
1181 }
1182
1183 // insert into Filling
1184 if (FillIt) {
1185 Inserter = CurrentPosition + FillerTranslations;
1186 LOG(1, "INFO: Position at " << Inserter << " is void point.");
1187 // fill!
1188 Filling = filler->CopyMolecule();
1189 RandomizeMoleculePositions(Filling, RandomAtomDisplacement, Rotations, random);
1190 // translation
1191 Filling->Translate(&Inserter);
1192 // remove out-of-bounds atoms
1193 const bool status = RemoveAtomsOutsideDomain(Filling);
1194 if ((firstInsertion) && (!status)) { // no atom has been removed
1195 // remove copied atoms and molecule again
1196 Filling->removeAtomsinMolecule();
1197 World::getInstance().destroyMolecule(Filling);
1198 // and mark is final filler position
1199 Filling = filler;
1200 firstInsertion = false;
1201 firstInserter = Inserter;
1202 } else {
1203 // TODO: Remove when World has no MoleculeListClass anymore
1204 if (Filling)
1205 MolList->insert(Filling);
1206 }
1207 } else {
1208 LOG(1, "INFO: Position at " << Inserter << " is non-void point, within boundary or outside of MaxDistance.");
1209 continue;
1210 }
1211 }
1212
1213 // have we inserted any molecules?
1214 if (firstInsertion) {
1215 // If not remove filler
1216 for(molecule::iterator miter = filler->begin(); !filler->empty(); miter = filler->begin()) {
1217 atom *Walker = *miter;
1218 World::getInstance().destroyAtom(Walker);
1219 }
1220 World::getInstance().destroyMolecule(filler);
1221 } else {
1222 // otherwise translate and randomize to final position
1223 if (DoRandomRotation)
1224 setRandomRotation(Rotations);
1225 else
1226 Rotations.setIdentity();
1227 RandomizeMoleculePositions(filler, RandomAtomDisplacement, Rotations, random);
1228 // translation
1229 filler->Translate(&firstInserter);
1230 // remove out-of-bounds atoms
1231 RemoveAtomsOutsideDomain(filler);
1232 }
1233
1234 LOG(0, MolList->ListOfMolecules.size() << " molecules have been inserted.");
1235
1236 for (std::map<molecule *, LinkedCell *>::iterator ListRunner = LCList.begin(); !LCList.empty(); ListRunner = LCList.begin()) {
1237 delete ListRunner->second;
1238 LCList.erase(ListRunner);
1239 }
1240};
1241
1242/** Tesselates the non convex boundary by rolling a virtual sphere along the surface of the molecule.
1243 * \param *out output stream for debugging
1244 * \param *mol molecule structure with Atom's and Bond's
1245 * \param *&TesselStruct Tesselation filled with points, lines and triangles on boundary on return
1246 * \param *&LCList atoms in LinkedCell list
1247 * \param RADIUS radius of the virtual sphere
1248 * \param *filename filename prefix for output of vertex data
1249 * \return true - tesselation successful, false - tesselation failed
1250 */
1251bool FindNonConvexBorder(molecule* const mol, Tesselation *&TesselStruct, const LinkedCell *&LCList, const double RADIUS, const char *filename = NULL)
1252{
1253 Info FunctionInfo(__func__);
1254 bool freeLC = false;
1255 bool status = false;
1256 CandidateForTesselation *baseline = NULL;
1257 bool OneLoopWithoutSuccessFlag = true; // marks whether we went once through all baselines without finding any without two triangles
1258 bool TesselationFailFlag = false;
1259
1260 mol->getAtomCount();
1261
1262 if (TesselStruct == NULL) {
1263 LOG(1, "Allocating Tesselation struct ...");
1264 TesselStruct= new Tesselation;
1265 } else {
1266 delete(TesselStruct);
1267 LOG(1, "Re-Allocating Tesselation struct ...");
1268 TesselStruct = new Tesselation;
1269 }
1270
1271 // initialise Linked Cell
1272 PointCloudAdaptor< molecule > cloud(mol, mol->name);
1273 if (LCList == NULL) {
1274 LCList = new LinkedCell(cloud, 2.*RADIUS);
1275 freeLC = true;
1276 }
1277
1278 // 1. get starting triangle
1279 if (!TesselStruct->FindStartingTriangle(RADIUS, LCList)) {
1280 ELOG(0, "No valid starting triangle found.");
1281 //performCriticalExit();
1282 }
1283 if (filename != NULL) {
1284 if ((DoSingleStepOutput && ((TesselStruct->TrianglesOnBoundary.size() % SingleStepWidth == 0)))) { // if we have a new triangle and want to output each new triangle configuration
1285 TesselStruct->Output(filename, cloud);
1286 }
1287 }
1288
1289 // 2. expand from there
1290 while ((!TesselStruct->OpenLines.empty()) && (OneLoopWithoutSuccessFlag)) {
1291 (cerr << "There are " << TesselStruct->TrianglesOnBoundary.size() << " triangles and " << TesselStruct->OpenLines.size() << " open lines to scan for candidates." << endl);
1292 // 2a. print OpenLines without candidates
1293 LOG(1, "There are the following open lines to scan for a candidates:");
1294 for (CandidateMap::iterator Runner = TesselStruct->OpenLines.begin(); Runner != TesselStruct->OpenLines.end(); Runner++)
1295 if (Runner->second->pointlist.empty())
1296 LOG(1, " " << *(Runner->second));
1297
1298 // 2b. find best candidate for each OpenLine
1299 TesselationFailFlag = TesselStruct->FindCandidatesforOpenLines(RADIUS, LCList);
1300
1301 // 2c. print OpenLines with candidates again
1302 LOG(1, "There are " << TesselStruct->OpenLines.size() << " open lines to scan for the best candidates:");
1303 for (CandidateMap::iterator Runner = TesselStruct->OpenLines.begin(); Runner != TesselStruct->OpenLines.end(); Runner++)
1304 LOG(1, " " << *(Runner->second));
1305
1306 // 2d. search for smallest ShortestAngle among all candidates
1307 double ShortestAngle = 4.*M_PI;
1308 for (CandidateMap::iterator Runner = TesselStruct->OpenLines.begin(); Runner != TesselStruct->OpenLines.end(); Runner++) {
1309 if (Runner->second->ShortestAngle < ShortestAngle) {
1310 baseline = Runner->second;
1311 ShortestAngle = baseline->ShortestAngle;
1312 LOG(1, "New best candidate is " << *baseline->BaseLine << " with point " << *(*baseline->pointlist.begin()) << " and angle " << baseline->ShortestAngle);
1313 }
1314 }
1315 // 2e. if we found one, add candidate
1316 if ((ShortestAngle == 4.*M_PI) || (baseline->pointlist.empty()))
1317 OneLoopWithoutSuccessFlag = false;
1318 else {
1319 TesselStruct->AddCandidatePolygon(*baseline, RADIUS, LCList);
1320 }
1321
1322 // 2f. write temporary envelope
1323 if (filename != NULL) {
1324 if ((DoSingleStepOutput && ((TesselStruct->TrianglesOnBoundary.size() % SingleStepWidth == 0)))) { // if we have a new triangle and want to output each new triangle configuration
1325 TesselStruct->Output(filename, cloud);
1326 }
1327 }
1328 }
1329// // check envelope for consistency
1330// status = CheckListOfBaselines(TesselStruct);
1331//
1332// // look whether all points are inside of the convex envelope, otherwise add them via degenerated triangles
1333// //->InsertStraddlingPoints(mol, LCList);
1334// for (molecule::const_iterator iter = mol->begin(); iter != mol->end(); ++iter) {
1335// class TesselPoint *Runner = NULL;
1336// Runner = *iter;
1337// LOG(1, "Checking on " << Runner->Name << " ... ");
1338// if (!->IsInnerPoint(Runner, LCList)) {
1339// LOG(2, Runner->Name << " is outside of envelope, adding via degenerated triangles.");
1340// ->AddBoundaryPointByDegeneratedTriangle(Runner, LCList);
1341// } else {
1342// LOG(2, Runner->Name << " is inside of or on envelope.");
1343// }
1344// }
1345
1346// // Purges surplus triangles.
1347// TesselStruct->RemoveDegeneratedTriangles();
1348//
1349// // check envelope for consistency
1350// status = CheckListOfBaselines(TesselStruct);
1351
1352 cout << "before correction" << endl;
1353
1354 // store before correction
1355 StoreTrianglesinFile(mol, TesselStruct, filename, "");
1356
1357// // correct degenerated polygons
1358// TesselStruct->CorrectAllDegeneratedPolygons();
1359//
1360 // check envelope for consistency
1361 status = CheckListOfBaselines(TesselStruct);
1362
1363 // write final envelope
1364 CalculateConcavityPerBoundaryPoint(TesselStruct);
1365 cout << "after correction" << endl;
1366 StoreTrianglesinFile(mol, TesselStruct, filename, "");
1367
1368 if (freeLC)
1369 delete(LCList);
1370
1371 return status;
1372};
1373
1374
1375/** Finds a hole of sufficient size in \a *mols to embed \a *srcmol into it.
1376 * \param *out output stream for debugging
1377 * \param *mols molecules in the domain to embed in between
1378 * \param *srcmol embedding molecule
1379 * \return *Vector new center of \a *srcmol for embedding relative to \a this
1380 */
1381Vector* FindEmbeddingHole(MoleculeListClass *mols, molecule *srcmol)
1382{
1383 Info FunctionInfo(__func__);
1384 Vector *Center = new Vector;
1385 Center->Zero();
1386 // calculate volume/shape of \a *srcmol
1387
1388 // find embedding holes
1389
1390 // if more than one, let user choose
1391
1392 // return embedding center
1393 return Center;
1394};
1395
Note: See TracBrowser for help on using the repository browser.