source: src/Tesselation/boundary.cpp@ d127c8

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

Moved modules related to Tesselation into own source subfolder.

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