source: src/Tesselation/boundary.cpp@ 41a467

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 41a467 was 47d041, checked in by Frederik Heber <heber@…>, 13 years ago

HUGE: Removed all calls to Log(), eLog(), replaced by LOG() and ELOG().

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