source: src/boundary.cpp@ 5347cd

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 5347cd was 1f91f4, checked in by Frederik Heber <heber@…>, 14 years ago

factored functionality in PrincipalAxisSystemAction() and RotateToPrincipalAxisSystemAction() into class molecule:

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