source: src/boundary.cpp@ a98603

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

Just a temporary commit

  • Property mode set to 100755
File size: 101.8 KB
Line 
1#include "molecules.hpp"
2#include "boundary.hpp"
3
4#define DEBUG 1
5#define DoTecplotOutput 0
6#define DoRaster3DOutput 1
7#define TecplotSuffix ".dat"
8#define Raster3DSuffix ".r3d"
9
10// ======================================== Points on Boundary =================================
11
12BoundaryPointSet::BoundaryPointSet()
13{
14 LinesCount = 0;
15 Nr = -1;
16}
17;
18
19BoundaryPointSet::BoundaryPointSet(atom *Walker)
20{
21 node = Walker;
22 LinesCount = 0;
23 Nr = Walker->nr;
24}
25;
26
27BoundaryPointSet::~BoundaryPointSet()
28{
29 cout << Verbose(5) << "Erasing point nr. " << Nr << "." << endl;
30 node = NULL;
31 lines.clear();
32}
33;
34
35void
36BoundaryPointSet::AddLine(class BoundaryLineSet *line)
37{
38 cout << Verbose(6) << "Adding " << *this << " to line " << *line << "."
39 << endl;
40 if (line->endpoints[0] == this)
41 {
42 lines.insert(LinePair(line->endpoints[1]->Nr, line));
43 }
44 else
45 {
46 lines.insert(LinePair(line->endpoints[0]->Nr, line));
47 }
48 LinesCount++;
49}
50;
51
52ostream &
53operator <<(ostream &ost, BoundaryPointSet &a)
54{
55 ost << "[" << a.Nr << "|" << a.node->Name << "]";
56 return ost;
57}
58;
59
60// ======================================== Lines on Boundary =================================
61
62BoundaryLineSet::BoundaryLineSet()
63{
64 for (int i = 0; i < 2; i++)
65 endpoints[i] = NULL;
66 TrianglesCount = 0;
67 Nr = -1;
68}
69;
70
71BoundaryLineSet::BoundaryLineSet(class BoundaryPointSet *Point[2], int number)
72{
73 // set number
74 Nr = number;
75 // set endpoints in ascending order
76 SetEndpointsOrdered(endpoints, Point[0], Point[1]);
77 // add this line to the hash maps of both endpoints
78 Point[0]->AddLine(this); //Taken out, to check whether we can avoid unwanted double adding.
79 Point[1]->AddLine(this); //
80 // clear triangles list
81 TrianglesCount = 0;
82 cout << Verbose(5) << "New Line with endpoints " << *this << "." << endl;
83}
84;
85
86BoundaryLineSet::~BoundaryLineSet()
87{
88 for (int i = 0; i < 2; i++) {
89 cout << Verbose(5) << "Erasing Line Nr. " << Nr << " in boundary point " << *endpoints[i] << "." << endl;
90 endpoints[i]->lines.erase(Nr);
91 LineMap::iterator tester = endpoints[i]->lines.begin();
92 tester++;
93 if (tester == endpoints[i]->lines.end()) {
94 cout << Verbose(5) << *endpoints[i] << " has no more lines it's attached to, erasing." << endl;
95 if (endpoints[i] != NULL) {
96 delete(endpoints[i]);
97 endpoints[i] = NULL;
98 } else
99 cerr << "ERROR: Endpoint " << i << " has already been free'd." << endl;
100 } else
101 cout << Verbose(5) << *endpoints[i] << " has still lines it's attached to." << endl;
102 }
103}
104;
105
106void
107BoundaryLineSet::AddTriangle(class BoundaryTriangleSet *triangle)
108{
109 cout << Verbose(6) << "Add " << triangle->Nr << " to line " << *this << "."
110 << endl;
111 triangles.insert(TrianglePair(TrianglesCount, triangle));
112 TrianglesCount++;
113}
114;
115
116ostream &
117operator <<(ostream &ost, BoundaryLineSet &a)
118{
119 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
120 << a.endpoints[1]->node->Name << "]";
121 return ost;
122}
123;
124
125// ======================================== Triangles on Boundary =================================
126
127
128BoundaryTriangleSet::BoundaryTriangleSet()
129{
130 for (int i = 0; i < 3; i++)
131 {
132 endpoints[i] = NULL;
133 lines[i] = NULL;
134 }
135 Nr = -1;
136}
137;
138
139BoundaryTriangleSet::BoundaryTriangleSet(class BoundaryLineSet *line[3],
140 int number)
141{
142 // set number
143 Nr = number;
144 // set lines
145 cout << Verbose(5) << "New triangle " << Nr << ":" << endl;
146 for (int i = 0; i < 3; i++)
147 {
148 lines[i] = line[i];
149 lines[i]->AddTriangle(this);
150 }
151 // get ascending order of endpoints
152 map<int, class BoundaryPointSet *> OrderMap;
153 for (int i = 0; i < 3; i++)
154 // for all three lines
155 for (int j = 0; j < 2; j++)
156 { // for both endpoints
157 OrderMap.insert(pair<int, class BoundaryPointSet *> (
158 line[i]->endpoints[j]->Nr, line[i]->endpoints[j]));
159 // and we don't care whether insertion fails
160 }
161 // set endpoints
162 int Counter = 0;
163 cout << Verbose(6) << " with end points ";
164 for (map<int, class BoundaryPointSet *>::iterator runner = OrderMap.begin(); runner
165 != OrderMap.end(); runner++)
166 {
167 endpoints[Counter] = runner->second;
168 cout << " " << *endpoints[Counter];
169 Counter++;
170 }
171 if (Counter < 3)
172 {
173 cerr << "ERROR! We have a triangle with only two distinct endpoints!"
174 << endl;
175 //exit(1);
176 }
177 cout << "." << endl;
178}
179;
180
181BoundaryTriangleSet::~BoundaryTriangleSet()
182{
183 for (int i = 0; i < 3; i++) {
184 cout << Verbose(5) << "Erasing triangle Nr." << Nr << endl;
185 lines[i]->triangles.erase(Nr);
186 if (lines[i]->triangles.empty()) {
187 cout << Verbose(5) << *lines[i] << " is no more attached to any triangle, erasing." << endl;
188 if (lines[i] != NULL) {
189 delete (lines[i]);
190 lines[i] = NULL;
191 } else
192 cerr << "ERROR: This line " << i << " has already been free'd." << endl;
193 } else
194 cout << Verbose(5) << *lines[i] << " is still attached to a triangle." << endl;
195 }
196}
197;
198
199void
200BoundaryTriangleSet::GetNormalVector(Vector &OtherVector)
201{
202 // get normal vector
203 NormalVector.MakeNormalVector(&endpoints[0]->node->x, &endpoints[1]->node->x,
204 &endpoints[2]->node->x);
205
206 // make it always point inward (any offset vector onto plane projected onto normal vector suffices)
207 if (endpoints[0]->node->x.Projection(&OtherVector) > 0)
208 NormalVector.Scale(-1.);
209}
210;
211
212ostream &
213operator <<(ostream &ost, BoundaryTriangleSet &a)
214{
215 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
216 << a.endpoints[1]->node->Name << "," << a.endpoints[2]->node->Name << "]";
217 return ost;
218}
219;
220
221// ========================================== F U N C T I O N S =================================
222
223/** Finds the endpoint two lines are sharing.
224 * \param *line1 first line
225 * \param *line2 second line
226 * \return point which is shared or NULL if none
227 */
228class BoundaryPointSet *
229GetCommonEndpoint(class BoundaryLineSet * line1, class BoundaryLineSet * line2)
230{
231 class BoundaryLineSet * lines[2] =
232 { line1, line2 };
233 class BoundaryPointSet *node = NULL;
234 map<int, class BoundaryPointSet *> OrderMap;
235 pair<map<int, class BoundaryPointSet *>::iterator, bool> OrderTest;
236 for (int i = 0; i < 2; i++)
237 // for both lines
238 for (int j = 0; j < 2; j++)
239 { // for both endpoints
240 OrderTest = OrderMap.insert(pair<int, class BoundaryPointSet *> (
241 lines[i]->endpoints[j]->Nr, lines[i]->endpoints[j]));
242 if (!OrderTest.second)
243 { // if insertion fails, we have common endpoint
244 node = OrderTest.first->second;
245 cout << Verbose(5) << "Common endpoint of lines " << *line1
246 << " and " << *line2 << " is: " << *node << "." << endl;
247 j = 2;
248 i = 2;
249 break;
250 }
251 }
252 return node;
253}
254;
255
256/** Determines the boundary points of a cluster.
257 * Does a projection per axis onto the orthogonal plane, transforms into spherical coordinates, sorts them by the angle
258 * and looks at triples: if the middle has less a distance than the allowed maximum height of the triangle formed by the plane's
259 * center and first and last point in the triple, it is thrown out.
260 * \param *out output stream for debugging
261 * \param *mol molecule structure representing the cluster
262 */
263Boundaries *
264GetBoundaryPoints(ofstream *out, molecule *mol)
265{
266 atom *Walker = NULL;
267 PointMap PointsOnBoundary;
268 LineMap LinesOnBoundary;
269 TriangleMap TrianglesOnBoundary;
270
271 *out << Verbose(1) << "Finding all boundary points." << endl;
272 Boundaries *BoundaryPoints = new Boundaries[NDIM]; // first is alpha, second is (r, nr)
273 BoundariesTestPair BoundaryTestPair;
274 Vector AxisVector, AngleReferenceVector, AngleReferenceNormalVector;
275 double radius, angle;
276 // 3a. Go through every axis
277 for (int axis = 0; axis < NDIM; axis++)
278 {
279 AxisVector.Zero();
280 AngleReferenceVector.Zero();
281 AngleReferenceNormalVector.Zero();
282 AxisVector.x[axis] = 1.;
283 AngleReferenceVector.x[(axis + 1) % NDIM] = 1.;
284 AngleReferenceNormalVector.x[(axis + 2) % NDIM] = 1.;
285 // *out << Verbose(1) << "Axisvector is ";
286 // AxisVector.Output(out);
287 // *out << " and AngleReferenceVector is ";
288 // AngleReferenceVector.Output(out);
289 // *out << "." << endl;
290 // *out << " and AngleReferenceNormalVector is ";
291 // AngleReferenceNormalVector.Output(out);
292 // *out << "." << endl;
293 // 3b. construct set of all points, transformed into cylindrical system and with left and right neighbours
294 Walker = mol->start;
295 while (Walker->next != mol->end)
296 {
297 Walker = Walker->next;
298 Vector ProjectedVector;
299 ProjectedVector.CopyVector(&Walker->x);
300 ProjectedVector.ProjectOntoPlane(&AxisVector);
301 // correct for negative side
302 //if (Projection(y) < 0)
303 //angle = 2.*M_PI - angle;
304 radius = ProjectedVector.Norm();
305 if (fabs(radius) > MYEPSILON)
306 angle = ProjectedVector.Angle(&AngleReferenceVector);
307 else
308 angle = 0.; // otherwise it's a vector in Axis Direction and unimportant for boundary issues
309
310 //*out << "Checking sign in quadrant : " << ProjectedVector.Projection(&AngleReferenceNormalVector) << "." << endl;
311 if (ProjectedVector.Projection(&AngleReferenceNormalVector) > 0)
312 {
313 angle = 2. * M_PI - angle;
314 }
315 //*out << Verbose(2) << "Inserting " << *Walker << ": (r, alpha) = (" << radius << "," << angle << "): ";
316 //ProjectedVector.Output(out);
317 //*out << endl;
318 BoundaryTestPair = BoundaryPoints[axis].insert(BoundariesPair(angle,
319 DistancePair (radius, Walker)));
320 if (BoundaryTestPair.second)
321 { // successfully inserted
322 }
323 else
324 { // same point exists, check first r, then distance of original vectors to center of gravity
325 *out << Verbose(2)
326 << "Encountered two vectors whose projection onto axis "
327 << axis << " is equal: " << endl;
328 *out << Verbose(2) << "Present vector: ";
329 BoundaryTestPair.first->second.second->x.Output(out);
330 *out << endl;
331 *out << Verbose(2) << "New vector: ";
332 Walker->x.Output(out);
333 *out << endl;
334 double tmp = ProjectedVector.Norm();
335 if (tmp > BoundaryTestPair.first->second.first)
336 {
337 BoundaryTestPair.first->second.first = tmp;
338 BoundaryTestPair.first->second.second = Walker;
339 *out << Verbose(2) << "Keeping new vector." << endl;
340 }
341 else if (tmp == BoundaryTestPair.first->second.first)
342 {
343 if (BoundaryTestPair.first->second.second->x.ScalarProduct(
344 &BoundaryTestPair.first->second.second->x)
345 < Walker->x.ScalarProduct(&Walker->x))
346 { // Norm() does a sqrt, which makes it a lot slower
347 BoundaryTestPair.first->second.second = Walker;
348 *out << Verbose(2) << "Keeping new vector." << endl;
349 }
350 else
351 {
352 *out << Verbose(2) << "Keeping present vector." << endl;
353 }
354 }
355 else
356 {
357 *out << Verbose(2) << "Keeping present vector." << endl;
358 }
359 }
360 }
361 // printing all inserted for debugging
362 // {
363 // *out << Verbose(2) << "Printing list of candidates for axis " << axis << " which we have inserted so far." << endl;
364 // int i=0;
365 // for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
366 // if (runner != BoundaryPoints[axis].begin())
367 // *out << ", " << i << ": " << *runner->second.second;
368 // else
369 // *out << i << ": " << *runner->second.second;
370 // i++;
371 // }
372 // *out << endl;
373 // }
374 // 3c. throw out points whose distance is less than the mean of left and right neighbours
375 bool flag = false;
376 do
377 { // do as long as we still throw one out per round
378 *out << Verbose(1)
379 << "Looking for candidates to kick out by convex condition ... "
380 << endl;
381 flag = false;
382 Boundaries::iterator left = BoundaryPoints[axis].end();
383 Boundaries::iterator right = BoundaryPoints[axis].end();
384 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
385 != BoundaryPoints[axis].end(); runner++)
386 {
387 // set neighbours correctly
388 if (runner == BoundaryPoints[axis].begin())
389 {
390 left = BoundaryPoints[axis].end();
391 }
392 else
393 {
394 left = runner;
395 }
396 left--;
397 right = runner;
398 right++;
399 if (right == BoundaryPoints[axis].end())
400 {
401 right = BoundaryPoints[axis].begin();
402 }
403 // check distance
404
405 // construct the vector of each side of the triangle on the projected plane (defined by normal vector AxisVector)
406 {
407 Vector SideA, SideB, SideC, SideH;
408 SideA.CopyVector(&left->second.second->x);
409 SideA.ProjectOntoPlane(&AxisVector);
410 // *out << "SideA: ";
411 // SideA.Output(out);
412 // *out << endl;
413
414 SideB.CopyVector(&right->second.second->x);
415 SideB.ProjectOntoPlane(&AxisVector);
416 // *out << "SideB: ";
417 // SideB.Output(out);
418 // *out << endl;
419
420 SideC.CopyVector(&left->second.second->x);
421 SideC.SubtractVector(&right->second.second->x);
422 SideC.ProjectOntoPlane(&AxisVector);
423 // *out << "SideC: ";
424 // SideC.Output(out);
425 // *out << endl;
426
427 SideH.CopyVector(&runner->second.second->x);
428 SideH.ProjectOntoPlane(&AxisVector);
429 // *out << "SideH: ";
430 // SideH.Output(out);
431 // *out << endl;
432
433 // calculate each length
434 double a = SideA.Norm();
435 //double b = SideB.Norm();
436 //double c = SideC.Norm();
437 double h = SideH.Norm();
438 // calculate the angles
439 double alpha = SideA.Angle(&SideH);
440 double beta = SideA.Angle(&SideC);
441 double gamma = SideB.Angle(&SideH);
442 double delta = SideC.Angle(&SideH);
443 double MinDistance = a * sin(beta) / (sin(delta)) * (((alpha
444 < M_PI / 2.) || (gamma < M_PI / 2.)) ? 1. : -1.);
445 // *out << Verbose(2) << " 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;
446 //*out << 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;
447 if ((fabs(h / fabs(h) - MinDistance / fabs(MinDistance))
448 < MYEPSILON) && (h < MinDistance))
449 {
450 // throw out point
451 //*out << Verbose(1) << "Throwing out " << *runner->second.second << "." << endl;
452 BoundaryPoints[axis].erase(runner);
453 flag = true;
454 }
455 }
456 }
457 }
458 while (flag);
459 }
460 return BoundaryPoints;
461}
462;
463
464/** Determines greatest diameters of a cluster defined by its convex envelope.
465 * Looks at lines parallel to one axis and where they intersect on the projected planes
466 * \param *out output stream for debugging
467 * \param *BoundaryPoints NDIM set of boundary points defining the convex envelope on each projected plane
468 * \param *mol molecule structure representing the cluster
469 * \param IsAngstroem whether we have angstroem or atomic units
470 * \return NDIM array of the diameters
471 */
472double *
473GetDiametersOfCluster(ofstream *out, Boundaries *BoundaryPtr, molecule *mol,
474 bool IsAngstroem)
475{
476 // get points on boundary of NULL was given as parameter
477 bool BoundaryFreeFlag = false;
478 Boundaries *BoundaryPoints = BoundaryPtr;
479 if (BoundaryPoints == NULL)
480 {
481 BoundaryFreeFlag = true;
482 BoundaryPoints = GetBoundaryPoints(out, mol);
483 }
484 else
485 {
486 *out << Verbose(1) << "Using given boundary points set." << endl;
487 }
488 // determine biggest "diameter" of cluster for each axis
489 Boundaries::iterator Neighbour, OtherNeighbour;
490 double *GreatestDiameter = new double[NDIM];
491 for (int i = 0; i < NDIM; i++)
492 GreatestDiameter[i] = 0.;
493 double OldComponent, tmp, w1, w2;
494 Vector DistanceVector, OtherVector;
495 int component, Othercomponent;
496 for (int axis = 0; axis < NDIM; axis++)
497 { // regard each projected plane
498 //*out << Verbose(1) << "Current axis is " << axis << "." << endl;
499 for (int j = 0; j < 2; j++)
500 { // and for both axis on the current plane
501 component = (axis + j + 1) % NDIM;
502 Othercomponent = (axis + 1 + ((j + 1) & 1)) % NDIM;
503 //*out << Verbose(1) << "Current component is " << component << ", Othercomponent is " << Othercomponent << "." << endl;
504 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
505 != BoundaryPoints[axis].end(); runner++)
506 {
507 //*out << Verbose(2) << "Current runner is " << *(runner->second.second) << "." << endl;
508 // seek for the neighbours pair where the Othercomponent sign flips
509 Neighbour = runner;
510 Neighbour++;
511 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
512 Neighbour = BoundaryPoints[axis].begin();
513 DistanceVector.CopyVector(&runner->second.second->x);
514 DistanceVector.SubtractVector(&Neighbour->second.second->x);
515 do
516 { // seek for neighbour pair where it flips
517 OldComponent = DistanceVector.x[Othercomponent];
518 Neighbour++;
519 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
520 Neighbour = BoundaryPoints[axis].begin();
521 DistanceVector.CopyVector(&runner->second.second->x);
522 DistanceVector.SubtractVector(&Neighbour->second.second->x);
523 //*out << Verbose(3) << "OldComponent is " << OldComponent << ", new one is " << DistanceVector.x[Othercomponent] << "." << endl;
524 }
525 while ((runner != Neighbour) && (fabs(OldComponent / fabs(
526 OldComponent) - DistanceVector.x[Othercomponent] / fabs(
527 DistanceVector.x[Othercomponent])) < MYEPSILON)); // as long as sign does not flip
528 if (runner != Neighbour)
529 {
530 OtherNeighbour = Neighbour;
531 if (OtherNeighbour == BoundaryPoints[axis].begin()) // make it wrap around
532 OtherNeighbour = BoundaryPoints[axis].end();
533 OtherNeighbour--;
534 //*out << Verbose(2) << "The pair, where the sign of OtherComponent flips, is: " << *(Neighbour->second.second) << " and " << *(OtherNeighbour->second.second) << "." << endl;
535 // now we have found the pair: Neighbour and OtherNeighbour
536 OtherVector.CopyVector(&runner->second.second->x);
537 OtherVector.SubtractVector(&OtherNeighbour->second.second->x);
538 //*out << Verbose(2) << "Distances to Neighbour and OtherNeighbour are " << DistanceVector.x[component] << " and " << OtherVector.x[component] << "." << endl;
539 //*out << Verbose(2) << "OtherComponents to Neighbour and OtherNeighbour are " << DistanceVector.x[Othercomponent] << " and " << OtherVector.x[Othercomponent] << "." << endl;
540 // do linear interpolation between points (is exact) to extract exact intersection between Neighbour and OtherNeighbour
541 w1 = fabs(OtherVector.x[Othercomponent]);
542 w2 = fabs(DistanceVector.x[Othercomponent]);
543 tmp = fabs((w1 * DistanceVector.x[component] + w2
544 * OtherVector.x[component]) / (w1 + w2));
545 // mark if it has greater diameter
546 //*out << Verbose(2) << "Comparing current greatest " << GreatestDiameter[component] << " to new " << tmp << "." << endl;
547 GreatestDiameter[component] = (GreatestDiameter[component]
548 > tmp) ? GreatestDiameter[component] : tmp;
549 } //else
550 //*out << Verbose(2) << "Saw no sign flip, probably top or bottom node." << endl;
551 }
552 }
553 }
554 *out << Verbose(0) << "RESULT: The biggest diameters are "
555 << GreatestDiameter[0] << " and " << GreatestDiameter[1] << " and "
556 << GreatestDiameter[2] << " " << (IsAngstroem ? "angstrom"
557 : "atomiclength") << "." << endl;
558
559 // free reference lists
560 if (BoundaryFreeFlag)
561 delete[] (BoundaryPoints);
562
563 return GreatestDiameter;
564}
565;
566
567/** Creates the objects in a raster3d file (renderable with a header.r3d)
568 * \param *out output stream for debugging
569 * \param *tecplot output stream for tecplot data
570 * \param *Tess Tesselation structure with constructed triangles
571 * \param *mol molecule structure with atom positions
572 */
573void write_raster3d_file(ofstream *out, ofstream *rasterfile, class Tesselation *Tess, class molecule *mol)
574{
575 atom *Walker = mol->start;
576 bond *Binder = mol->first;
577 int i;
578 Vector *center = mol->DetermineCenterOfAll(out);
579 if (rasterfile != NULL) {
580 //cout << Verbose(1) << "Writing Raster3D file ... ";
581 *rasterfile << "# Raster3D object description, created by MoleCuilder" << endl;
582 *rasterfile << "@header.r3d" << endl;
583 *rasterfile << "# All atoms as spheres" << endl;
584 while (Walker->next != mol->end) {
585 Walker = Walker->next;
586 *rasterfile << "2" << endl << " "; // 2 is sphere type
587 for (i=0;i<NDIM;i++)
588 *rasterfile << Walker->x.x[i]+center->x[i] << " ";
589 *rasterfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
590 }
591
592 *rasterfile << "# All bonds as vertices" << endl;
593 while (Binder->next != mol->last) {
594 Binder = Binder->next;
595 *rasterfile << "3" << endl << " "; // 2 is round-ended cylinder type
596 for (i=0;i<NDIM;i++)
597 *rasterfile << Binder->leftatom->x.x[i]+center->x[i] << " ";
598 *rasterfile << "\t0.03\t";
599 for (i=0;i<NDIM;i++)
600 *rasterfile << Binder->rightatom->x.x[i]+center->x[i] << " ";
601 *rasterfile << "\t0.03\t0. 0. 1." << endl; // radius 0.05 and blue as colour
602 }
603
604 *rasterfile << "# All tesselation triangles" << endl;
605 for (TriangleMap::iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
606 *rasterfile << "1" << endl << " "; // 1 is triangle type
607 for (i=0;i<3;i++) { // print each node
608 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
609 *rasterfile << TriangleRunner->second->endpoints[i]->node->x.x[j]+center->x[j] << " ";
610 *rasterfile << "\t";
611 }
612 *rasterfile << "1. 0. 0." << endl; // red as colour
613 *rasterfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
614 }
615 } else {
616 cerr << "ERROR: Given rasterfile is " << rasterfile << "." << endl;
617 }
618 delete(center);
619};
620
621/** This function creates the tecplot file, displaying the tesselation of the hull.
622 * \param *out output stream for debugging
623 * \param *tecplot output stream for tecplot data
624 * \param N arbitrary number to differentiate various zones in the tecplot format
625 */
626void
627write_tecplot_file(ofstream *out, ofstream *tecplot,
628 class Tesselation *TesselStruct, class molecule *mol, int N)
629{
630 if (tecplot != NULL)
631 {
632 *tecplot << "TITLE = \"3D CONVEX SHELL\"" << endl;
633 *tecplot << "VARIABLES = \"X\" \"Y\" \"Z\"" << endl;
634 *tecplot << "ZONE T=\"TRIANGLES" << N << "\", N="
635 << TesselStruct->PointsOnBoundaryCount << ", E="
636 << TesselStruct->TrianglesOnBoundaryCount
637 << ", DATAPACKING=POINT, ZONETYPE=FETRIANGLE" << endl;
638 int *LookupList = new int[mol->AtomCount];
639 for (int i = 0; i < mol->AtomCount; i++)
640 LookupList[i] = -1;
641
642 // print atom coordinates
643 *out << Verbose(2) << "The following triangles were created:";
644 int Counter = 1;
645 atom *Walker = NULL;
646 for (PointMap::iterator target = TesselStruct->PointsOnBoundary.begin(); target
647 != TesselStruct->PointsOnBoundary.end(); target++)
648 {
649 Walker = target->second->node;
650 LookupList[Walker->nr] = Counter++;
651 *tecplot << Walker->x.x[0] << " " << Walker->x.x[1] << " "
652 << Walker->x.x[2] << " " << endl;
653 }
654 *tecplot << endl;
655 // print connectivity
656 for (TriangleMap::iterator runner =
657 TesselStruct->TrianglesOnBoundary.begin(); runner
658 != TesselStruct->TrianglesOnBoundary.end(); runner++)
659 {
660 *out << " " << runner->second->endpoints[0]->node->Name << "<->"
661 << runner->second->endpoints[1]->node->Name << "<->"
662 << runner->second->endpoints[2]->node->Name;
663 *tecplot << LookupList[runner->second->endpoints[0]->node->nr] << " "
664 << LookupList[runner->second->endpoints[1]->node->nr] << " "
665 << LookupList[runner->second->endpoints[2]->node->nr] << endl;
666 }
667 delete[] (LookupList);
668 *out << endl;
669 }
670}
671
672/** Determines the volume of a cluster.
673 * Determines first the convex envelope, then tesselates it and calculates its volume.
674 * \param *out output stream for debugging
675 * \param *tecplot output stream for tecplot data
676 * \param *configuration needed for path to store convex envelope file
677 * \param *BoundaryPoints NDIM set of boundary points on the projected plane per axis, on return if desired
678 * \param *mol molecule structure representing the cluster
679 * \return determined volume of the cluster in cubed config:GetIsAngstroem()
680 */
681double
682VolumeOfConvexEnvelope(ofstream *out, ofstream *tecplot, config *configuration,
683 Boundaries *BoundaryPtr, molecule *mol)
684{
685 bool IsAngstroem = configuration->GetIsAngstroem();
686 atom *Walker = NULL;
687 struct Tesselation *TesselStruct = new Tesselation;
688 bool BoundaryFreeFlag = false;
689 Boundaries *BoundaryPoints = BoundaryPtr;
690 double volume = 0.;
691 double PyramidVolume = 0.;
692 double G, h;
693 Vector x, y;
694 double a, b, c;
695
696 //Find_non_convex_border(out, tecplot, *TesselStruct, mol); // Is now called from command line.
697
698 // 1. calculate center of gravity
699 *out << endl;
700 Vector *CenterOfGravity = mol->DetermineCenterOfGravity(out);
701
702 // 2. translate all points into CoG
703 *out << Verbose(1) << "Translating system to Center of Gravity." << endl;
704 Walker = mol->start;
705 while (Walker->next != mol->end)
706 {
707 Walker = Walker->next;
708 Walker->x.Translate(CenterOfGravity);
709 }
710
711 // 3. Find all points on the boundary
712 if (BoundaryPoints == NULL)
713 {
714 BoundaryFreeFlag = true;
715 BoundaryPoints = GetBoundaryPoints(out, mol);
716 }
717 else
718 {
719 *out << Verbose(1) << "Using given boundary points set." << endl;
720 }
721
722 // 4. fill the boundary point list
723 for (int axis = 0; axis < NDIM; axis++)
724 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
725 != BoundaryPoints[axis].end(); runner++)
726 {
727 TesselStruct->AddPoint(runner->second.second);
728 }
729
730 *out << Verbose(2) << "I found " << TesselStruct->PointsOnBoundaryCount
731 << " points on the convex boundary." << endl;
732 // now we have the whole set of edge points in the BoundaryList
733
734 // listing for debugging
735 // *out << Verbose(1) << "Listing PointsOnBoundary:";
736 // for(PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
737 // *out << " " << *runner->second;
738 // }
739 // *out << endl;
740
741 // 5a. guess starting triangle
742 TesselStruct->GuessStartingTriangle(out);
743
744 // 5b. go through all lines, that are not yet part of two triangles (only of one so far)
745 TesselStruct->TesselateOnBoundary(out, configuration, mol);
746
747 *out << Verbose(2) << "I created " << TesselStruct->TrianglesOnBoundaryCount
748 << " triangles with " << TesselStruct->LinesOnBoundaryCount
749 << " lines and " << TesselStruct->PointsOnBoundaryCount << " points."
750 << endl;
751
752 // 6a. Every triangle forms a pyramid with the center of gravity as its peak, sum up the volumes
753 *out << Verbose(1)
754 << "Calculating the volume of the pyramids formed out of triangles and center of gravity."
755 << endl;
756 for (TriangleMap::iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner
757 != TesselStruct->TrianglesOnBoundary.end(); runner++)
758 { // go through every triangle, calculate volume of its pyramid with CoG as peak
759 x.CopyVector(&runner->second->endpoints[0]->node->x);
760 x.SubtractVector(&runner->second->endpoints[1]->node->x);
761 y.CopyVector(&runner->second->endpoints[0]->node->x);
762 y.SubtractVector(&runner->second->endpoints[2]->node->x);
763 a = sqrt(runner->second->endpoints[0]->node->x.Distance(
764 &runner->second->endpoints[1]->node->x));
765 b = sqrt(runner->second->endpoints[0]->node->x.Distance(
766 &runner->second->endpoints[2]->node->x));
767 c = sqrt(runner->second->endpoints[2]->node->x.Distance(
768 &runner->second->endpoints[1]->node->x));
769 G = sqrt(((a * a + b * b + c * c) * (a * a + b * b + c * c) - 2 * (a * a
770 * a * a + b * b * b * b + c * c * c * c)) / 16.); // area of tesselated triangle
771 x.MakeNormalVector(&runner->second->endpoints[0]->node->x,
772 &runner->second->endpoints[1]->node->x,
773 &runner->second->endpoints[2]->node->x);
774 x.Scale(runner->second->endpoints[1]->node->x.Projection(&x));
775 h = x.Norm(); // distance of CoG to triangle
776 PyramidVolume = (1. / 3.) * G * h; // this formula holds for _all_ pyramids (independent of n-edge base or (not) centered peak)
777 *out << Verbose(2) << "Area of triangle is " << G << " "
778 << (IsAngstroem ? "angstrom" : "atomiclength") << "^2, height is "
779 << h << " and the volume is " << PyramidVolume << " "
780 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
781 volume += PyramidVolume;
782 }
783 *out << Verbose(0) << "RESULT: The summed volume is " << setprecision(10)
784 << volume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3."
785 << endl;
786
787 // 7. translate all points back from CoG
788 *out << Verbose(1) << "Translating system back from Center of Gravity."
789 << endl;
790 CenterOfGravity->Scale(-1);
791 Walker = mol->start;
792 while (Walker->next != mol->end)
793 {
794 Walker = Walker->next;
795 Walker->x.Translate(CenterOfGravity);
796 }
797
798 // 8. Store triangles in tecplot file
799 write_tecplot_file(out, tecplot, TesselStruct, mol, 0);
800
801 // free reference lists
802 if (BoundaryFreeFlag)
803 delete[] (BoundaryPoints);
804
805 return volume;
806}
807;
808
809/** Creates multiples of the by \a *mol given cluster and suspends them in water with a given final density.
810 * We get cluster volume by VolumeOfConvexEnvelope() and its diameters by GetDiametersOfCluster()
811 * \param *out output stream for debugging
812 * \param *configuration needed for path to store convex envelope file
813 * \param *mol molecule structure representing the cluster
814 * \param ClusterVolume guesstimated cluster volume, if equal 0 we used VolumeOfConvexEnvelope() instead.
815 * \param celldensity desired average density in final cell
816 */
817void
818PrepareClustersinWater(ofstream *out, config *configuration, molecule *mol,
819 double ClusterVolume, double celldensity)
820{
821 // transform to PAS
822 mol->PrincipalAxisSystem(out, true);
823
824 // some preparations beforehand
825 bool IsAngstroem = configuration->GetIsAngstroem();
826 Boundaries *BoundaryPoints = GetBoundaryPoints(out, mol);
827 double clustervolume;
828 if (ClusterVolume == 0)
829 clustervolume = VolumeOfConvexEnvelope(out, NULL, configuration,
830 BoundaryPoints, mol);
831 else
832 clustervolume = ClusterVolume;
833 double *GreatestDiameter = GetDiametersOfCluster(out, BoundaryPoints, mol,
834 IsAngstroem);
835 Vector BoxLengths;
836 int repetition[NDIM] =
837 { 1, 1, 1 };
838 int TotalNoClusters = 1;
839 for (int i = 0; i < NDIM; i++)
840 TotalNoClusters *= repetition[i];
841
842 // sum up the atomic masses
843 double totalmass = 0.;
844 atom *Walker = mol->start;
845 while (Walker->next != mol->end)
846 {
847 Walker = Walker->next;
848 totalmass += Walker->type->mass;
849 }
850 *out << Verbose(0) << "RESULT: The summed mass is " << setprecision(10)
851 << totalmass << " atomicmassunit." << endl;
852
853 *out << Verbose(0) << "RESULT: The average density is " << setprecision(10)
854 << totalmass / clustervolume << " atomicmassunit/"
855 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
856
857 // solve cubic polynomial
858 *out << Verbose(1) << "Solving equidistant suspension in water problem ..."
859 << endl;
860 double cellvolume;
861 if (IsAngstroem)
862 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_A - (totalmass
863 / clustervolume)) / (celldensity - 1);
864 else
865 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_a0 - (totalmass
866 / clustervolume)) / (celldensity - 1);
867 *out << Verbose(1) << "Cellvolume needed for a density of " << celldensity
868 << " g/cm^3 is " << cellvolume << " " << (IsAngstroem ? "angstrom"
869 : "atomiclength") << "^3." << endl;
870
871 double minimumvolume = TotalNoClusters * (GreatestDiameter[0]
872 * GreatestDiameter[1] * GreatestDiameter[2]);
873 *out << Verbose(1)
874 << "Minimum volume of the convex envelope contained in a rectangular box is "
875 << minimumvolume << " atomicmassunit/" << (IsAngstroem ? "angstrom"
876 : "atomiclength") << "^3." << endl;
877 if (minimumvolume > cellvolume)
878 {
879 cerr << Verbose(0)
880 << "ERROR: the containing box already has a greater volume than the envisaged cell volume!"
881 << endl;
882 cout << Verbose(0)
883 << "Setting Box dimensions to minimum possible, the greatest diameters."
884 << endl;
885 for (int i = 0; i < NDIM; i++)
886 BoxLengths.x[i] = GreatestDiameter[i];
887 mol->CenterEdge(out, &BoxLengths);
888 }
889 else
890 {
891 BoxLengths.x[0] = (repetition[0] * GreatestDiameter[0] + repetition[1]
892 * GreatestDiameter[1] + repetition[2] * GreatestDiameter[2]);
893 BoxLengths.x[1] = (repetition[0] * repetition[1] * GreatestDiameter[0]
894 * GreatestDiameter[1] + repetition[0] * repetition[2]
895 * GreatestDiameter[0] * GreatestDiameter[2] + repetition[1]
896 * repetition[2] * GreatestDiameter[1] * GreatestDiameter[2]);
897 BoxLengths.x[2] = minimumvolume - cellvolume;
898 double x0 = 0., x1 = 0., x2 = 0.;
899 if (gsl_poly_solve_cubic(BoxLengths.x[0], BoxLengths.x[1],
900 BoxLengths.x[2], &x0, &x1, &x2) == 1) // either 1 or 3 on return
901 *out << Verbose(0) << "RESULT: The resulting spacing is: " << x0
902 << " ." << endl;
903 else
904 {
905 *out << Verbose(0) << "RESULT: The resulting spacings are: " << x0
906 << " and " << x1 << " and " << x2 << " ." << endl;
907 x0 = x2; // sorted in ascending order
908 }
909
910 cellvolume = 1;
911 for (int i = 0; i < NDIM; i++)
912 {
913 BoxLengths.x[i] = repetition[i] * (x0 + GreatestDiameter[i]);
914 cellvolume *= BoxLengths.x[i];
915 }
916
917 // set new box dimensions
918 *out << Verbose(0) << "Translating to box with these boundaries." << endl;
919 mol->CenterInBox((ofstream *) &cout, &BoxLengths);
920 }
921 // update Box of atoms by boundary
922 mol->SetBoxDimension(&BoxLengths);
923 *out << Verbose(0) << "RESULT: The resulting cell dimensions are: "
924 << BoxLengths.x[0] << " and " << BoxLengths.x[1] << " and "
925 << BoxLengths.x[2] << " with total volume of " << cellvolume << " "
926 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
927}
928;
929
930// =========================================================== class TESSELATION ===========================================
931
932/** Constructor of class Tesselation.
933 */
934Tesselation::Tesselation()
935{
936 PointsOnBoundaryCount = 0;
937 LinesOnBoundaryCount = 0;
938 TrianglesOnBoundaryCount = 0;
939 TriangleFilesWritten = 0;
940}
941;
942
943/** Constructor of class Tesselation.
944 * We have to free all points, lines and triangles.
945 */
946Tesselation::~Tesselation()
947{
948 cout << Verbose(1) << "Free'ing TesselStruct ... " << endl;
949 for (TriangleMap::iterator runner = TrianglesOnBoundary.begin(); runner != TrianglesOnBoundary.end(); runner++) {
950 if (runner->second != NULL) {
951 delete (runner->second);
952 runner->second = NULL;
953 } else
954 cerr << "ERROR: The triangle " << runner->first << " has already been free'd." << endl;
955 }
956 for (LineMap::iterator runner = LinesOnBoundary.begin(); runner != LinesOnBoundary.end(); runner++) {
957 if (runner->second != NULL) {
958 delete (runner->second);
959 runner->second = NULL;
960 } else
961 cerr << "ERROR: The line " << runner->first << " has already been free'd." << endl;
962 }
963 for (PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
964 if (runner->second != NULL) {
965 delete (runner->second);
966 runner->second = NULL;
967 } else
968 cerr << "ERROR: The point " << runner->first << " has already been free'd." << endl;
969 }
970}
971;
972
973/** Gueses first starting triangle of the convex envelope.
974 * We guess the starting triangle by taking the smallest distance between two points and looking for a fitting third.
975 * \param *out output stream for debugging
976 * \param PointsOnBoundary set of boundary points defining the convex envelope of the cluster
977 */
978void
979Tesselation::GuessStartingTriangle(ofstream *out)
980{
981 // 4b. create a starting triangle
982 // 4b1. create all distances
983 DistanceMultiMap DistanceMMap;
984 double distance, tmp;
985 Vector PlaneVector, TrialVector;
986 PointMap::iterator A, B, C; // three nodes of the first triangle
987 A = PointsOnBoundary.begin(); // the first may be chosen arbitrarily
988
989 // with A chosen, take each pair B,C and sort
990 if (A != PointsOnBoundary.end())
991 {
992 B = A;
993 B++;
994 for (; B != PointsOnBoundary.end(); B++)
995 {
996 C = B;
997 C++;
998 for (; C != PointsOnBoundary.end(); C++)
999 {
1000 tmp = A->second->node->x.Distance(&B->second->node->x);
1001 distance = tmp * tmp;
1002 tmp = A->second->node->x.Distance(&C->second->node->x);
1003 distance += tmp * tmp;
1004 tmp = B->second->node->x.Distance(&C->second->node->x);
1005 distance += tmp * tmp;
1006 DistanceMMap.insert(DistanceMultiMapPair(distance, pair<
1007 PointMap::iterator, PointMap::iterator> (B, C)));
1008 }
1009 }
1010 }
1011 // // listing distances
1012 // *out << Verbose(1) << "Listing DistanceMMap:";
1013 // for(DistanceMultiMap::iterator runner = DistanceMMap.begin(); runner != DistanceMMap.end(); runner++) {
1014 // *out << " " << runner->first << "(" << *runner->second.first->second << ", " << *runner->second.second->second << ")";
1015 // }
1016 // *out << endl;
1017 // 4b2. pick three baselines forming a triangle
1018 // 1. we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
1019 DistanceMultiMap::iterator baseline = DistanceMMap.begin();
1020 for (; baseline != DistanceMMap.end(); baseline++)
1021 {
1022 // we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
1023 // 2. next, we have to check whether all points reside on only one side of the triangle
1024 // 3. construct plane vector
1025 PlaneVector.MakeNormalVector(&A->second->node->x,
1026 &baseline->second.first->second->node->x,
1027 &baseline->second.second->second->node->x);
1028 *out << Verbose(2) << "Plane vector of candidate triangle is ";
1029 PlaneVector.Output(out);
1030 *out << endl;
1031 // 4. loop over all points
1032 double sign = 0.;
1033 PointMap::iterator checker = PointsOnBoundary.begin();
1034 for (; checker != PointsOnBoundary.end(); checker++)
1035 {
1036 // (neglecting A,B,C)
1037 if ((checker == A) || (checker == baseline->second.first) || (checker
1038 == baseline->second.second))
1039 continue;
1040 // 4a. project onto plane vector
1041 TrialVector.CopyVector(&checker->second->node->x);
1042 TrialVector.SubtractVector(&A->second->node->x);
1043 distance = TrialVector.Projection(&PlaneVector);
1044 if (fabs(distance) < 1e-4) // we need to have a small epsilon around 0 which is still ok
1045 continue;
1046 *out << Verbose(3) << "Projection of " << checker->second->node->Name
1047 << " yields distance of " << distance << "." << endl;
1048 tmp = distance / fabs(distance);
1049 // 4b. Any have different sign to than before? (i.e. would lie outside convex hull with this starting triangle)
1050 if ((sign != 0) && (tmp != sign))
1051 {
1052 // 4c. If so, break 4. loop and continue with next candidate in 1. loop
1053 *out << Verbose(2) << "Current candidates: "
1054 << A->second->node->Name << ","
1055 << baseline->second.first->second->node->Name << ","
1056 << baseline->second.second->second->node->Name << " leave "
1057 << checker->second->node->Name << " outside the convex hull."
1058 << endl;
1059 break;
1060 }
1061 else
1062 { // note the sign for later
1063 *out << Verbose(2) << "Current candidates: "
1064 << A->second->node->Name << ","
1065 << baseline->second.first->second->node->Name << ","
1066 << baseline->second.second->second->node->Name << " leave "
1067 << checker->second->node->Name << " inside the convex hull."
1068 << endl;
1069 sign = tmp;
1070 }
1071 // 4d. Check whether the point is inside the triangle (check distance to each node
1072 tmp = checker->second->node->x.Distance(&A->second->node->x);
1073 int innerpoint = 0;
1074 if ((tmp < A->second->node->x.Distance(
1075 &baseline->second.first->second->node->x)) && (tmp
1076 < A->second->node->x.Distance(
1077 &baseline->second.second->second->node->x)))
1078 innerpoint++;
1079 tmp = checker->second->node->x.Distance(
1080 &baseline->second.first->second->node->x);
1081 if ((tmp < baseline->second.first->second->node->x.Distance(
1082 &A->second->node->x)) && (tmp
1083 < baseline->second.first->second->node->x.Distance(
1084 &baseline->second.second->second->node->x)))
1085 innerpoint++;
1086 tmp = checker->second->node->x.Distance(
1087 &baseline->second.second->second->node->x);
1088 if ((tmp < baseline->second.second->second->node->x.Distance(
1089 &baseline->second.first->second->node->x)) && (tmp
1090 < baseline->second.second->second->node->x.Distance(
1091 &A->second->node->x)))
1092 innerpoint++;
1093 // 4e. If so, break 4. loop and continue with next candidate in 1. loop
1094 if (innerpoint == 3)
1095 break;
1096 }
1097 // 5. come this far, all on same side? Then break 1. loop and construct triangle
1098 if (checker == PointsOnBoundary.end())
1099 {
1100 *out << "Looks like we have a candidate!" << endl;
1101 break;
1102 }
1103 }
1104 if (baseline != DistanceMMap.end())
1105 {
1106 BPS[0] = baseline->second.first->second;
1107 BPS[1] = baseline->second.second->second;
1108 BLS[0] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1109 BPS[0] = A->second;
1110 BPS[1] = baseline->second.second->second;
1111 BLS[1] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1112 BPS[0] = baseline->second.first->second;
1113 BPS[1] = A->second;
1114 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1115
1116 // 4b3. insert created triangle
1117 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1118 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1119 TrianglesOnBoundaryCount++;
1120 for (int i = 0; i < NDIM; i++)
1121 {
1122 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BTS->lines[i]));
1123 LinesOnBoundaryCount++;
1124 }
1125
1126 *out << Verbose(1) << "Starting triangle is " << *BTS << "." << endl;
1127 }
1128 else
1129 {
1130 *out << Verbose(1) << "No starting triangle found." << endl;
1131 exit(255);
1132 }
1133}
1134;
1135
1136/** Tesselates the convex envelope of a cluster from a single starting triangle.
1137 * The starting triangle is made out of three baselines. Each line in the final tesselated cluster may belong to at most
1138 * 2 triangles. Hence, we go through all current lines:
1139 * -# if the lines contains to only one triangle
1140 * -# We search all points in the boundary
1141 * -# if the triangle with the baseline and the current point has the smallest of angles (comparison between normal vectors
1142 * -# if the triangle is in forward direction of the baseline (at most 90 degrees angle between vector orthogonal to
1143 * baseline in triangle plane pointing out of the triangle and normal vector of new triangle)
1144 * -# then we have a new triangle, whose baselines we again add (or increase their TriangleCount)
1145 * \param *out output stream for debugging
1146 * \param *configuration for IsAngstroem
1147 * \param *mol the cluster as a molecule structure
1148 */
1149void
1150Tesselation::TesselateOnBoundary(ofstream *out, config *configuration,
1151 molecule *mol)
1152{
1153 bool flag;
1154 PointMap::iterator winner;
1155 class BoundaryPointSet *peak = NULL;
1156 double SmallestAngle, TempAngle;
1157 Vector NormalVector, VirtualNormalVector, CenterVector, TempVector,
1158 PropagationVector;
1159 LineMap::iterator LineChecker[2];
1160 do
1161 {
1162 flag = false;
1163 for (LineMap::iterator baseline = LinesOnBoundary.begin(); baseline
1164 != LinesOnBoundary.end(); baseline++)
1165 if (baseline->second->TrianglesCount == 1)
1166 {
1167 *out << Verbose(2) << "Current baseline is between "
1168 << *(baseline->second) << "." << endl;
1169 // 5a. go through each boundary point if not _both_ edges between either endpoint of the current line and this point exist (and belong to 2 triangles)
1170 SmallestAngle = M_PI;
1171 BTS = baseline->second->triangles.begin()->second; // there is only one triangle so far
1172 // get peak point with respect to this base line's only triangle
1173 for (int i = 0; i < 3; i++)
1174 if ((BTS->endpoints[i] != baseline->second->endpoints[0])
1175 && (BTS->endpoints[i] != baseline->second->endpoints[1]))
1176 peak = BTS->endpoints[i];
1177 *out << Verbose(3) << " and has peak " << *peak << "." << endl;
1178 // normal vector of triangle
1179 BTS->GetNormalVector(NormalVector);
1180 *out << Verbose(4) << "NormalVector of base triangle is ";
1181 NormalVector.Output(out);
1182 *out << endl;
1183 // offset to center of triangle
1184 CenterVector.Zero();
1185 for (int i = 0; i < 3; i++)
1186 CenterVector.AddVector(&BTS->endpoints[i]->node->x);
1187 CenterVector.Scale(1. / 3.);
1188 *out << Verbose(4) << "CenterVector of base triangle is ";
1189 CenterVector.Output(out);
1190 *out << endl;
1191 // vector in propagation direction (out of triangle)
1192 // project center vector onto triangle plane (points from intersection plane-NormalVector to plane-CenterVector intersection)
1193 TempVector.CopyVector(&baseline->second->endpoints[0]->node->x);
1194 TempVector.SubtractVector(&baseline->second->endpoints[1]->node->x);
1195 PropagationVector.MakeNormalVector(&TempVector, &NormalVector);
1196 TempVector.CopyVector(&CenterVector);
1197 TempVector.SubtractVector(&baseline->second->endpoints[0]->node->x); // TempVector is vector on triangle plane pointing from one baseline egde towards center!
1198 //*out << Verbose(2) << "Projection of propagation onto temp: " << PropagationVector.Projection(&TempVector) << "." << endl;
1199 if (PropagationVector.Projection(&TempVector) > 0) // make sure normal propagation vector points outward from baseline
1200 PropagationVector.Scale(-1.);
1201 *out << Verbose(4) << "PropagationVector of base triangle is ";
1202 PropagationVector.Output(out);
1203 *out << endl;
1204 winner = PointsOnBoundary.end();
1205 for (PointMap::iterator target = PointsOnBoundary.begin(); target
1206 != PointsOnBoundary.end(); target++)
1207 if ((target->second != baseline->second->endpoints[0])
1208 && (target->second != baseline->second->endpoints[1]))
1209 { // don't take the same endpoints
1210 *out << Verbose(3) << "Target point is " << *(target->second)
1211 << ":";
1212 bool continueflag = true;
1213
1214 VirtualNormalVector.CopyVector(
1215 &baseline->second->endpoints[0]->node->x);
1216 VirtualNormalVector.AddVector(
1217 &baseline->second->endpoints[0]->node->x);
1218 VirtualNormalVector.Scale(-1. / 2.); // points now to center of base line
1219 VirtualNormalVector.AddVector(&target->second->node->x); // points from center of base line to target
1220 TempAngle = VirtualNormalVector.Angle(&PropagationVector);
1221 continueflag = continueflag && (TempAngle < (M_PI/2.)); // no bends bigger than Pi/2 (90 degrees)
1222 if (!continueflag)
1223 {
1224 *out << Verbose(4)
1225 << "Angle between propagation direction and base line to "
1226 << *(target->second) << " is " << TempAngle
1227 << ", bad direction!" << endl;
1228 continue;
1229 }
1230 else
1231 *out << Verbose(4)
1232 << "Angle between propagation direction and base line to "
1233 << *(target->second) << " is " << TempAngle
1234 << ", good direction!" << endl;
1235 LineChecker[0] = baseline->second->endpoints[0]->lines.find(
1236 target->first);
1237 LineChecker[1] = baseline->second->endpoints[1]->lines.find(
1238 target->first);
1239 // if (LineChecker[0] != baseline->second->endpoints[0]->lines.end())
1240 // *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has line " << *(LineChecker[0]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[0]->second->TrianglesCount << " triangles." << endl;
1241 // else
1242 // *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has no line to " << *(target->second) << " as endpoint." << endl;
1243 // if (LineChecker[1] != baseline->second->endpoints[1]->lines.end())
1244 // *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has line " << *(LineChecker[1]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[1]->second->TrianglesCount << " triangles." << endl;
1245 // else
1246 // *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has no line to " << *(target->second) << " as endpoint." << endl;
1247 // check first endpoint (if any connecting line goes to target or at least not more than 1)
1248 continueflag = continueflag && (((LineChecker[0]
1249 == baseline->second->endpoints[0]->lines.end())
1250 || (LineChecker[0]->second->TrianglesCount == 1)));
1251 if (!continueflag)
1252 {
1253 *out << Verbose(4) << *(baseline->second->endpoints[0])
1254 << " has line " << *(LineChecker[0]->second)
1255 << " to " << *(target->second)
1256 << " as endpoint with "
1257 << LineChecker[0]->second->TrianglesCount
1258 << " triangles." << endl;
1259 continue;
1260 }
1261 // check second endpoint (if any connecting line goes to target or at least not more than 1)
1262 continueflag = continueflag && (((LineChecker[1]
1263 == baseline->second->endpoints[1]->lines.end())
1264 || (LineChecker[1]->second->TrianglesCount == 1)));
1265 if (!continueflag)
1266 {
1267 *out << Verbose(4) << *(baseline->second->endpoints[1])
1268 << " has line " << *(LineChecker[1]->second)
1269 << " to " << *(target->second)
1270 << " as endpoint with "
1271 << LineChecker[1]->second->TrianglesCount
1272 << " triangles." << endl;
1273 continue;
1274 }
1275 // check whether the envisaged triangle does not already exist (if both lines exist and have same endpoint)
1276 continueflag = continueflag && (!(((LineChecker[0]
1277 != baseline->second->endpoints[0]->lines.end())
1278 && (LineChecker[1]
1279 != baseline->second->endpoints[1]->lines.end())
1280 && (GetCommonEndpoint(LineChecker[0]->second,
1281 LineChecker[1]->second) == peak))));
1282 if (!continueflag)
1283 {
1284 *out << Verbose(4) << "Current target is peak!" << endl;
1285 continue;
1286 }
1287 // in case NOT both were found
1288 if (continueflag)
1289 { // create virtually this triangle, get its normal vector, calculate angle
1290 flag = true;
1291 VirtualNormalVector.MakeNormalVector(
1292 &baseline->second->endpoints[0]->node->x,
1293 &baseline->second->endpoints[1]->node->x,
1294 &target->second->node->x);
1295 // make it always point inward
1296 if (baseline->second->endpoints[0]->node->x.Projection(
1297 &VirtualNormalVector) > 0)
1298 VirtualNormalVector.Scale(-1.);
1299 // calculate angle
1300 TempAngle = NormalVector.Angle(&VirtualNormalVector);
1301 *out << Verbose(4) << "NormalVector is ";
1302 VirtualNormalVector.Output(out);
1303 *out << " and the angle is " << TempAngle << "." << endl;
1304 if (SmallestAngle > TempAngle)
1305 { // set to new possible winner
1306 SmallestAngle = TempAngle;
1307 winner = target;
1308 }
1309 }
1310 }
1311 // 5b. The point of the above whose triangle has the greatest angle with the triangle the current line belongs to (it only belongs to one, remember!): New triangle
1312 if (winner != PointsOnBoundary.end())
1313 {
1314 *out << Verbose(2) << "Winning target point is "
1315 << *(winner->second) << " with angle " << SmallestAngle
1316 << "." << endl;
1317 // create the lins of not yet present
1318 BLS[0] = baseline->second;
1319 // 5c. add lines to the line set if those were new (not yet part of a triangle), delete lines that belong to two triangles)
1320 LineChecker[0] = baseline->second->endpoints[0]->lines.find(
1321 winner->first);
1322 LineChecker[1] = baseline->second->endpoints[1]->lines.find(
1323 winner->first);
1324 if (LineChecker[0]
1325 == baseline->second->endpoints[0]->lines.end())
1326 { // create
1327 BPS[0] = baseline->second->endpoints[0];
1328 BPS[1] = winner->second;
1329 BLS[1] = new class BoundaryLineSet(BPS,
1330 LinesOnBoundaryCount);
1331 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount,
1332 BLS[1]));
1333 LinesOnBoundaryCount++;
1334 }
1335 else
1336 BLS[1] = LineChecker[0]->second;
1337 if (LineChecker[1]
1338 == baseline->second->endpoints[1]->lines.end())
1339 { // create
1340 BPS[0] = baseline->second->endpoints[1];
1341 BPS[1] = winner->second;
1342 BLS[2] = new class BoundaryLineSet(BPS,
1343 LinesOnBoundaryCount);
1344 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount,
1345 BLS[2]));
1346 LinesOnBoundaryCount++;
1347 }
1348 else
1349 BLS[2] = LineChecker[1]->second;
1350 BTS = new class BoundaryTriangleSet(BLS,
1351 TrianglesOnBoundaryCount);
1352 TrianglesOnBoundary.insert(TrianglePair(
1353 TrianglesOnBoundaryCount, BTS));
1354 TrianglesOnBoundaryCount++;
1355 }
1356 else
1357 {
1358 *out << Verbose(1)
1359 << "I could not determine a winner for this baseline "
1360 << *(baseline->second) << "." << endl;
1361 }
1362
1363 // 5d. If the set of lines is not yet empty, go to 5. and continue
1364 }
1365 else
1366 *out << Verbose(2) << "Baseline candidate " << *(baseline->second)
1367 << " has a triangle count of "
1368 << baseline->second->TrianglesCount << "." << endl;
1369 }
1370 while (flag);
1371
1372}
1373;
1374
1375/** Adds an atom to the tesselation::PointsOnBoundary list.
1376 * \param *Walker atom to add
1377 */
1378void
1379Tesselation::AddPoint(atom *Walker)
1380{
1381 PointTestPair InsertUnique;
1382 BPS[0] = new class BoundaryPointSet(Walker);
1383 InsertUnique = PointsOnBoundary.insert(PointPair(Walker->nr, BPS[0]));
1384 if (InsertUnique.second) // if new point was not present before, increase counter
1385 PointsOnBoundaryCount++;
1386}
1387;
1388
1389/** Adds point to Tesselation::PointsOnBoundary if not yet present.
1390 * Tesselation::TPS is set to either this new BoundaryPointSet or to the existing one of not unique.
1391 * @param Candidate point to add
1392 * @param n index for this point in Tesselation::TPS array
1393 */
1394void
1395Tesselation::AddTrianglePoint(atom* Candidate, int n)
1396{
1397 PointTestPair InsertUnique;
1398 TPS[n] = new class BoundaryPointSet(Candidate);
1399 InsertUnique = PointsOnBoundary.insert(PointPair(Candidate->nr, TPS[n]));
1400 if (InsertUnique.second) // if new point was not present before, increase counter
1401 {
1402 PointsOnBoundaryCount++;
1403 }
1404 else
1405 {
1406 delete TPS[n];
1407 cout << Verbose(2) << "Atom " << *((InsertUnique.first)->second->node)
1408 << " gibt's schon in der PointMap." << endl;
1409 TPS[n] = (InsertUnique.first)->second;
1410 }
1411}
1412;
1413
1414/** Function tries to add line from current Points in BPS to BoundaryLineSet.
1415 * If succesful it raises the line count and inserts the new line into the BLS,
1416 * if unsuccesful, it writes the line which had been present into the BLS, deleting the new constructed one.
1417 * @param *a first endpoint
1418 * @param *b second endpoint
1419 * @param n index of Tesselation::BLS giving the line with both endpoints
1420 */
1421void
1422Tesselation::AddTriangleLine(class BoundaryPointSet *a,
1423 class BoundaryPointSet *b, int n)
1424{
1425 LineMap::iterator LineWalker;
1426 //cout << "Manually checking endpoints for line." << endl;
1427 if ((a->lines.find(b->node->nr)) != a->lines.end()) // ->first == b->node->nr)
1428 //If a line is there, how do I recognize that beyond a shadow of a doubt?
1429 {
1430 //cout << Verbose(2) << "Line exists already, retrieving it from LinesOnBoundarySet" << endl;
1431
1432 LineWalker = LinesOnBoundary.end();
1433 LineWalker--;
1434
1435 while (LineWalker->second->endpoints[0]->node->nr != min(a->node->nr,
1436 b->node->nr) or LineWalker->second->endpoints[1]->node->nr != max(
1437 a->node->nr, b->node->nr))
1438 {
1439 //cout << Verbose(1) << "Looking for line which already exists"<< endl;
1440 LineWalker--;
1441 }
1442 BPS[0] = LineWalker->second->endpoints[0];
1443 BPS[1] = LineWalker->second->endpoints[1];
1444 BLS[n] = LineWalker->second;
1445
1446 }
1447 else
1448 {
1449 cout << Verbose(2)
1450 << "Adding line which has not been used before between "
1451 << *(a->node) << " and " << *(b->node) << "." << endl;
1452 BPS[0] = a;
1453 BPS[1] = b;
1454 BLS[n] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1455
1456 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[n]));
1457 LinesOnBoundaryCount++;
1458
1459 }
1460}
1461;
1462
1463/** Function tries to add Triangle just created to Triangle and remarks if already existent (Failure of algorithm).
1464 * Furthermore it adds the triangle to all of its lines, in order to recognize those which are saturated later.
1465 */
1466void
1467Tesselation::AddTriangleToLines()
1468{
1469
1470 cout << Verbose(1) << "Adding triangle to its lines" << endl;
1471 int i = 0;
1472 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1473 TrianglesOnBoundaryCount++;
1474
1475 /*
1476 * this is apparently done when constructing triangle
1477
1478 for (i=0; i<3; i++)
1479 {
1480 BLS[i]->AddTriangle(BTS);
1481 }
1482 */
1483}
1484;
1485
1486/**
1487 * Function returns center of sphere with RADIUS, which rests on points a, b, c
1488 * @param Center this vector will be used for return
1489 * @param a vector first point of triangle
1490 * @param b vector second point of triangle
1491 * @param c vector third point of triangle
1492 * @param *Umkreismittelpunkt new cneter point of circumference
1493 * @param Direction vector indicates up/down
1494 * @param AlternativeDirection vecotr, needed in case the triangles have 90 deg angle
1495 * @param Halfplaneindicator double indicates whether Direction is up or down
1496 * @param AlternativeIndicator doube indicates in case of orthogonal triangles which direction of AlternativeDirection is suitable
1497 * @param alpha double angle at a
1498 * @param beta double, angle at b
1499 * @param gamma, double, angle at c
1500 * @param Radius, double
1501 * @param Umkreisradius double radius of circumscribing circle
1502 */
1503
1504 void Get_center_of_sphere(Vector* Center, Vector a, Vector b, Vector c, Vector *NewUmkreismittelpunkt, Vector* Direction, Vector* AlternativeDirection,
1505 double HalfplaneIndicator, double AlternativeIndicator, double alpha, double beta, double gamma, double RADIUS, double Umkreisradius)
1506 {
1507 Vector TempNormal, helper;
1508 double Restradius;
1509 cout << Verbose(3) << "Begin of Get_center_of_sphere.\n";
1510 Center->Zero();
1511 helper.CopyVector(&a);
1512 helper.Scale(sin(2.*alpha));
1513 Center->AddVector(&helper);
1514 helper.CopyVector(&b);
1515 helper.Scale(sin(2.*beta));
1516 Center->AddVector(&helper);
1517 helper.CopyVector(&c);
1518 helper.Scale(sin(2.*gamma));
1519 Center->AddVector(&helper);
1520 //*Center = a * sin(2.*alpha) + b * sin(2.*beta) + c * sin(2.*gamma) ;
1521 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
1522 NewUmkreismittelpunkt->CopyVector(Center);
1523 cout << Verbose(4) << "Center of new circumference is " << *NewUmkreismittelpunkt << ".\n";
1524 // Here we calculated center of circumscribing circle, using barycentric coordinates
1525 cout << Verbose(4) << "Center of circumference is " << *Center << " in direction " << *Direction << ".\n";
1526
1527 TempNormal.CopyVector(&a);
1528 TempNormal.SubtractVector(&b);
1529 helper.CopyVector(&a);
1530 helper.SubtractVector(&c);
1531 TempNormal.VectorProduct(&helper);
1532 if (fabs(HalfplaneIndicator) < MYEPSILON)
1533 {
1534 if ((TempNormal.ScalarProduct(AlternativeDirection) <0 and AlternativeIndicator >0) or (TempNormal.ScalarProduct(AlternativeDirection) >0 and AlternativeIndicator <0))
1535 {
1536 TempNormal.Scale(-1);
1537 }
1538 }
1539 else
1540 {
1541 if (TempNormal.ScalarProduct(Direction)<0 && HalfplaneIndicator >0 || TempNormal.ScalarProduct(Direction)>0 && HalfplaneIndicator<0)
1542 {
1543 TempNormal.Scale(-1);
1544 }
1545 }
1546
1547 TempNormal.Normalize();
1548 Restradius = sqrt(RADIUS*RADIUS - Umkreisradius*Umkreisradius);
1549 cout << Verbose(4) << "Height of center of circumference to center of sphere is " << Restradius << ".\n";
1550 TempNormal.Scale(Restradius);
1551 cout << Verbose(4) << "Shift vector to sphere of circumference is " << TempNormal << ".\n";
1552
1553 Center->AddVector(&TempNormal);
1554 cout << Verbose(4) << "Center of sphere of circumference is " << *Center << ".\n";
1555 cout << Verbose(3) << "End of Get_center_of_sphere.\n";
1556 }
1557 ;
1558
1559
1560/** This recursive function finds a third point, to form a triangle with two given ones.
1561 * Two atoms are fixed, a candidate is supplied, additionally two vectors for direction distinction, a Storage area to \
1562 * supply results to the calling function, the radius of the sphere which the triangle shall support and the molecule \
1563 * upon which we operate.
1564 * If the candidate is more fitting to support the sphere than the already stored atom is, then we write its general \
1565 * direction and angle into Storage.
1566 * We the determine the recursive level we have reached and if this is not on the threshold yet, call this function again, \
1567 * with all neighbours of the candidate.
1568 * @param a first point
1569 * @param b second point
1570 * *param c atom old third point of old triangle
1571 * @param Candidate base point along whose bonds to start looking from
1572 * @param Parent point to avoid during search as its wrong direction
1573 * @param RecursionLevel contains current recursion depth
1574 * @param Chord baseline vector of first and second point
1575 * @param direction1 second in plane vector (along with \a Chord) of the triangle the baseline belongs to
1576 * @param OldNormal normal of the triangle which the baseline belongs to
1577 * @param ReferencePoint Vector of center of circumscribing circle from which we look towards center of sphere
1578 * @param Opt_Candidate candidate reference to return
1579 * @param Storage array containing two angles of current Opt_Candidate
1580 * @param RADIUS radius of ball
1581 * @param mol molecule structure with atoms and bonds
1582 */
1583
1584void Tesselation::Find_next_suitable_point_via_Angle_of_Sphere(atom* a, atom* b, atom* c, atom* Candidate, atom* Parent,
1585 int RecursionLevel, Vector *Chord, Vector *direction1, Vector *OldNormal, Vector ReferencePoint,
1586 atom*& Opt_Candidate, double *Storage, const double RADIUS, molecule* mol)
1587{
1588 cout << Verbose(2) << "Begin of Find_next_suitable_point_via_Angle_of_Sphere, recursion level " << RecursionLevel << ".\n";
1589 cout << Verbose(3) << "Candidate is "<< *Candidate << endl;
1590 cout << Verbose(4) << "Baseline vector is " << *Chord << "." << endl;
1591 cout << Verbose(4) << "ReferencePoint is " << ReferencePoint << "." << endl;
1592 cout << Verbose(4) << "Normal of base triangle is " << *OldNormal << "." << endl;
1593 cout << Verbose(4) << "Search direction is " << *direction1 << "." << endl;
1594 /* OldNormal is normal vector on the old triangle
1595 * direction1 is normal on the triangle line, from which we come, as well as on OldNormal.
1596 * Chord points from b to a!!!
1597 */
1598 Vector dif_a; //Vector from a to candidate
1599 Vector dif_b; //Vector from b to candidate
1600 Vector AngleCheck;
1601 Vector TempNormal, Umkreismittelpunkt;
1602 Vector Mittelpunkt;
1603
1604 double CurrentEpsilon = 0.1;
1605 double alpha, beta, gamma, SideA, SideB, SideC, sign, Umkreisradius, Restradius, Distance;
1606 double BallAngle, AlternativeSign;
1607 atom *Walker; // variable atom point
1608
1609 Vector NewUmkreismittelpunkt;
1610
1611 if (a != Candidate and b != Candidate and c != Candidate) {
1612 cout << Verbose(3) << "We have a unique candidate!" << endl;
1613 dif_a.CopyVector(&(a->x));
1614 dif_a.SubtractVector(&(Candidate->x));
1615 dif_b.CopyVector(&(b->x));
1616 dif_b.SubtractVector(&(Candidate->x));
1617 AngleCheck.CopyVector(&(Candidate->x));
1618 AngleCheck.SubtractVector(&(a->x));
1619 AngleCheck.ProjectOntoPlane(Chord);
1620
1621 SideA = dif_b.Norm();
1622 SideB = dif_a.Norm();
1623 SideC = Chord->Norm();
1624 //Chord->Scale(-1);
1625
1626 alpha = Chord->Angle(&dif_a);
1627 beta = M_PI - Chord->Angle(&dif_b);
1628 gamma = dif_a.Angle(&dif_b);
1629
1630 cout << Verbose(2) << "Base triangle has sides " << dif_a << ", " << dif_b << ", " << *Chord << " with angles " << alpha/M_PI*180. << ", " << beta/M_PI*180. << ", " << gamma/M_PI*180. << "." << endl;
1631
1632 if (fabs(M_PI - alpha - beta - gamma) > MYEPSILON) {
1633 cerr << Verbose(0) << "WARNING: sum of angles for base triangle " << (alpha + beta + gamma)/M_PI*180. << " != 180.\n";
1634 cout << Verbose(1) << "Base triangle has sides " << dif_a << ", " << dif_b << ", " << *Chord << " with angles " << alpha/M_PI*180. << ", " << beta/M_PI*180. << ", " << gamma/M_PI*180. << "." << endl;
1635 }
1636
1637 if ((M_PI*4. > alpha*5.) && (M_PI*4. > beta*5.) && (M_PI*4 > gamma*5.)) {
1638 Umkreisradius = SideA / 2.0 / sin(alpha);
1639 //cout << Umkreisradius << endl;
1640 //cout << SideB / 2.0 / sin(beta) << endl;
1641 //cout << SideC / 2.0 / sin(gamma) << endl;
1642
1643 if (Umkreisradius < RADIUS) { //Checking whether ball will at least rest on points.
1644 cout << Verbose(3) << "Circle of circumference would fit: " << Umkreisradius << " < " << RADIUS << "." << endl;
1645 cout << Verbose(2) << "Candidate is "<< *Candidate << endl;
1646 sign = AngleCheck.ScalarProduct(direction1);
1647 if (fabs(sign)<MYEPSILON) {
1648 if (AngleCheck.ScalarProduct(OldNormal)<0) {
1649 sign =0;
1650 AlternativeSign=1;
1651 } else {
1652 sign =0;
1653 AlternativeSign=-1;
1654 }
1655 } else {
1656 sign /= fabs(sign);
1657 cout << Verbose(3) << "Candidate is in search direction: " << sign << "." << endl;
1658 }
1659
1660 Get_center_of_sphere(&Mittelpunkt, (a->x), (b->x), (Candidate->x), &NewUmkreismittelpunkt, OldNormal, direction1, sign, AlternativeSign, alpha, beta, gamma, RADIUS, Umkreisradius);
1661
1662 AngleCheck.CopyVector(&ReferencePoint);
1663 AngleCheck.Scale(-1);
1664 //cout << "AngleCheck is " << AngleCheck.x[0] << " "<< AngleCheck.x[1] << " "<< AngleCheck.x[2] << " "<< endl;
1665 AngleCheck.AddVector(&Mittelpunkt);
1666 //cout << "AngleCheck is " << AngleCheck.x[0] << " "<< AngleCheck.x[1] << " "<< AngleCheck.x[2] << " "<< endl;
1667 cout << Verbose(4) << "Reference vector to sphere's center is " << AngleCheck << "." << endl;
1668
1669 BallAngle = AngleCheck.Angle(OldNormal);
1670 cout << Verbose(3) << "Angle between normal of base triangle and center of ball sphere is :" << BallAngle << "." << endl;
1671
1672 //cout << "direction1 is " << direction1->x[0] <<" "<< direction1->x[1] <<" "<< direction1->x[2] <<" " << endl;
1673 //cout << "AngleCheck is " << AngleCheck.x[0] << " "<< AngleCheck.x[1] << " "<< AngleCheck.x[2] << " "<< endl;
1674
1675 cout << Verbose(3) << "BallAngle is " << BallAngle << " Sign is " << sign << endl;
1676
1677 NewUmkreismittelpunkt.SubtractVector(&ReferencePoint);
1678
1679 if ((AngleCheck.ScalarProduct(direction1) >=0) || (fabs(NewUmkreismittelpunkt.Norm()) < MYEPSILON)) {
1680 if (Storage[0]< -1.5) { // first Candidate at all
1681 if (1) {//if (CheckPresenceOfTriangle((ofstream *)&cout,a,b,Candidate)) {
1682 cout << Verbose(2) << "First good candidate is " << *Candidate << " with ";
1683 Opt_Candidate = Candidate;
1684 Storage[0] = sign;
1685 Storage[1] = AlternativeSign;
1686 Storage[2] = BallAngle;
1687 cout << " angle " << Storage[2] << " and Up/Down "
1688 << Storage[0] << endl;
1689 } else
1690 cout << "Candidate " << *Candidate << " does not belong to a valid triangle." << endl;
1691 } else {
1692 if ( Storage[2] > BallAngle) {
1693 if (1) { //if (CheckPresenceOfTriangle((ofstream *)&cout,a,b,Candidate)) {
1694 cout << Verbose(2) << "Next better candidate is " << *Candidate << " with ";
1695 Opt_Candidate = Candidate;
1696 Storage[0] = sign;
1697 Storage[1] = AlternativeSign;
1698 Storage[2] = BallAngle;
1699 cout << " angle " << Storage[2] << " and Up/Down "
1700 << Storage[0] << endl;
1701 } else
1702 cout << "Candidate " << *Candidate << " does not belong to a valid triangle." << endl;
1703 } else {
1704 if (DEBUG) {
1705 cout << Verbose(3) << *Candidate << " looses against better candidate " << *Opt_Candidate << "." << endl;
1706 }
1707 }
1708 }
1709 } else {
1710 if (DEBUG) {
1711 cout << Verbose(3) << *Candidate << " refused due to Up/Down sign which is " << sign << endl;
1712 }
1713 }
1714 } else {
1715 if (DEBUG) {
1716 cout << Verbose(3) << *Candidate << " would have circumference of " << Umkreisradius << " bigger than ball's radius " << RADIUS << "." << endl;
1717 }
1718 }
1719 } else {
1720 if (DEBUG) {
1721 cout << Verbose(0) << "Triangle consisting of " << *Candidate << ", " << *a << " and " << *b << " has an angle >150!" << endl;
1722 }
1723 }
1724 } else {
1725 if (DEBUG) {
1726 cout << Verbose(3) << *Candidate << " is either " << *a << " or " << *b << "." << endl;
1727 }
1728 }
1729
1730 if (RecursionLevel < 5) { // Seven is the recursion level threshold.
1731 for (int i = 0; i < mol->NumberOfBondsPerAtom[Candidate->nr]; i++) { // go through all bond
1732 Walker = mol->ListOfBondsPerAtom[Candidate->nr][i]->GetOtherAtom(Candidate);
1733 if (Walker == Parent) { // don't go back the same bond
1734 continue;
1735 } else {
1736 Find_next_suitable_point_via_Angle_of_Sphere(a, b, c, Walker, Candidate, RecursionLevel+1, Chord, direction1, OldNormal, ReferencePoint, Opt_Candidate, Storage, RADIUS, mol); //call function again
1737 }
1738 }
1739 }
1740 cout << Verbose(2) << "End of Find_next_suitable_point_via_Angle_of_Sphere, recursion level " << RecursionLevel << ".\n";
1741}
1742;
1743
1744
1745 /** This recursive function finds a third point, to form a triangle with two given ones.
1746 * Two atoms are fixed, a candidate is supplied, additionally two vectors for direction distinction, a Storage area to \
1747 * supply results to the calling function, the radius of the sphere which the triangle shall support and the molecule \
1748 * upon which we operate.
1749 * If the candidate is more fitting to support the sphere than the already stored atom is, then we write its general \
1750 * direction and angle into Storage.
1751 * We the determine the recursive level we have reached and if this is not on the threshold yet, call this function again, \
1752 * with all neighbours of the candidate.
1753 * @param a first point
1754 * @param b second point
1755 * @param Candidate base point along whose bonds to start looking from
1756 * @param Parent point to avoid during search as its wrong direction
1757 * @param RecursionLevel contains current recursion depth
1758 * @param Chord baseline vector of first and second point
1759 * @param d1 second in plane vector (along with \a Chord) of the triangle the baseline belongs to
1760 * @param OldNormal normal of the triangle which the baseline belongs to
1761 * @param Opt_Candidate candidate reference to return
1762 * @param Opt_Mittelpunkt Centerpoint of ball, when resting on Opt_Candidate
1763 * @param Storage array containing two angles of current Opt_Candidate
1764 * @param RADIUS radius of ball
1765 * @param mol molecule structure with atoms and bonds
1766 */
1767void Find_next_suitable_point(atom* a, atom* b, atom* Candidate, atom* Parent,
1768 int RecursionLevel, Vector *Chord, Vector *d1, Vector *OldNormal,
1769 atom*& Opt_Candidate, Vector *Opt_Mittelpunkt, double *Storage, const double RADIUS, molecule* mol)
1770{
1771 /* OldNormal is normal vector on the old triangle
1772 * d1 is normal on the triangle line, from which we come, as well as on OldNormal.
1773 * Chord points from b to a!!!
1774 */
1775 Vector dif_a; //Vector from a to candidate
1776 Vector dif_b; //Vector from b to candidate
1777 Vector AngleCheck, AngleCheckReference, DirectionCheckPoint;
1778 Vector TempNormal, Umkreismittelpunkt, Mittelpunkt, helper;
1779
1780 double CurrentEpsilon = 0.1;
1781 double alpha, beta, gamma, SideA, SideB, SideC, sign, Umkreisradius, Restradius, Distance;
1782 double BallAngle;
1783 atom *Walker; // variable atom point
1784
1785
1786 dif_a.CopyVector(&(a->x));
1787 dif_a.SubtractVector(&(Candidate->x));
1788 dif_b.CopyVector(&(b->x));
1789 dif_b.SubtractVector(&(Candidate->x));
1790 DirectionCheckPoint.CopyVector(&dif_a);
1791 DirectionCheckPoint.Scale(-1);
1792 DirectionCheckPoint.ProjectOntoPlane(Chord);
1793
1794 SideA = dif_b.Norm();
1795 SideB = dif_a.Norm();
1796 SideC = Chord->Norm();
1797 //Chord->Scale(-1);
1798
1799 alpha = Chord->Angle(&dif_a);
1800 beta = M_PI - Chord->Angle(&dif_b);
1801 gamma = dif_a.Angle(&dif_b);
1802
1803
1804 if (DEBUG) {
1805 cout << "Atom number" << Candidate->nr << endl;
1806 Candidate->x.Output((ofstream *) &cout);
1807 cout << "number of bonds " << mol->NumberOfBondsPerAtom[Candidate->nr] << endl;
1808 }
1809
1810 if (a != Candidate and b != Candidate) {
1811 // alpha = dif_a.Angle(&dif_b) / 2.;
1812 // SideA = Chord->Norm() / 2.;// (Chord->Norm()/2.) / sin(0.5*alpha);
1813 // SideB = dif_a.Norm();
1814 // centerline = SideA * SideA + SideB * SideB - 2. * SideA * SideB * cos(
1815 // alpha); // note this is squared of center line length
1816 // centerline = (Chord->Norm()/2.) / sin(0.5*alpha);
1817 // Those are remains from Freddie. Needed?
1818
1819 Umkreisradius = SideA / 2.0 / sin(alpha);
1820 //cout << Umkreisradius << endl;
1821 //cout << SideB / 2.0 / sin(beta) << endl;
1822 //cout << SideC / 2.0 / sin(gamma) << endl;
1823
1824 if (Umkreisradius < RADIUS && DirectionCheckPoint.ScalarProduct(&(Candidate->x))>0) { //Checking whether ball will at least rest o points.
1825 // intermediate calculations to aquire centre of sphere, called Mittelpunkt:
1826 Umkreismittelpunkt.Zero();
1827 helper.CopyVector(&a->x);
1828 helper.Scale(sin(2.*alpha));
1829 Umkreismittelpunkt.AddVector(&helper);
1830 helper.CopyVector(&b->x);
1831 helper.Scale(sin(2.*beta));
1832 Umkreismittelpunkt.AddVector(&helper);
1833 helper.CopyVector(&Candidate->x);
1834 helper.Scale(sin(2.*gamma));
1835 Umkreismittelpunkt.AddVector(&helper);
1836 //Umkreismittelpunkt = (a->x) * sin(2.*alpha) + b->x * sin(2.*beta) + (Candidate->x) * sin(2.*gamma) ;
1837 Umkreismittelpunkt.Scale(1/(sin(2*alpha) + sin(2*beta) + sin(2*gamma)));
1838
1839 TempNormal.CopyVector(&dif_a);
1840 TempNormal.VectorProduct(&dif_b);
1841 if (TempNormal.ScalarProduct(OldNormal)<0 && sign>0 || TempNormal.ScalarProduct(OldNormal)>0 && sign<0) {
1842 TempNormal.Scale(-1);
1843 }
1844 TempNormal.Normalize();
1845 Restradius = sqrt(RADIUS*RADIUS - Umkreisradius*Umkreisradius);
1846 TempNormal.Scale(Restradius);
1847
1848 Mittelpunkt.CopyVector(&Umkreismittelpunkt);
1849 Mittelpunkt.AddVector(&TempNormal); //this is center of sphere supported by a, b and Candidate
1850
1851 AngleCheck.CopyVector(Chord);
1852 AngleCheck.Scale(-0.5);
1853 AngleCheck.SubtractVector(&(b->x));
1854 AngleCheckReference.CopyVector(&AngleCheck);
1855 AngleCheckReference.AddVector(Opt_Mittelpunkt);
1856 AngleCheck.AddVector(&Mittelpunkt);
1857
1858 BallAngle = AngleCheck.Angle(&AngleCheckReference);
1859
1860 d1->ProjectOntoPlane(&AngleCheckReference);
1861 sign = AngleCheck.ScalarProduct(d1);
1862 if (fabs(sign) < MYEPSILON)
1863 sign = 0;
1864 else
1865 sign /= fabs(sign); // +1 if in direction of triangle plane, -1 if in other direction...
1866
1867
1868 if (Storage[0]< -1.5) { // first Candidate at all
1869 cout << "Next better candidate is " << *Candidate << " with ";
1870 Opt_Candidate = Candidate;
1871 Storage[0] = sign;
1872 Storage[1] = BallAngle;
1873 Opt_Mittelpunkt->CopyVector(&Mittelpunkt);
1874 cout << "Angle is " << Storage[1] << ", Halbraum ist " << Storage[0] << endl;
1875 } else {
1876 /*
1877 * removed due to change in criterium, now checking angle of ball to old normal.
1878 //We will now check for non interference, that is if the new candidate would have the Opt_Candidate
1879 //within the ball.
1880
1881 Distance = Opt_Candidate->x.Distance(&Mittelpunkt);
1882 //cout << "Opt_Candidate " << Opt_Candidate << " has distance " << Distance << " to Center of Candidate " << endl;
1883
1884
1885 if (Distance >RADIUS) { // We have no interference and may now check whether the new point is better.
1886 */
1887 //cout << "Atom " << Candidate << " has distance " << Candidate->x.Distance(Opt_Mittelpunkt) << " to Center of Candidate " << endl;
1888
1889 if (((Storage[0] < 0 && fabs(sign - Storage[0]) > CurrentEpsilon))) { //This will give absolute preference to those in "right-hand" quadrants
1890 //(Candidate->x.Distance(Opt_Mittelpunkt) < RADIUS)) //and those where Candidate would be within old Sphere.
1891 cout << "Next better candidate is " << *Candidate << " with ";
1892 Opt_Candidate = Candidate;
1893 Storage[0] = sign;
1894 Storage[1] = BallAngle;
1895 Opt_Mittelpunkt->CopyVector(&Mittelpunkt);
1896 cout << "Angle is " << Storage[1] << ", Halbraum ist " << Storage[0] << endl;
1897 } else {
1898 if ((fabs(sign - Storage[0]) < CurrentEpsilon && sign > 0 && Storage[1] > BallAngle) || (fabs(sign - Storage[0]) < CurrentEpsilon && sign < 0 && Storage[1] < BallAngle)) {
1899 //Depending on quadrant we prefer higher or lower atom with respect to Triangle normal first.
1900 cout << "Next better candidate is " << *Candidate << " with ";
1901 Opt_Candidate = Candidate;
1902 Storage[0] = sign;
1903 Storage[1] = BallAngle;
1904 Opt_Mittelpunkt->CopyVector(&Mittelpunkt);
1905 cout << "Angle is " << Storage[1] << ", Halbraum ist " << Storage[0] << endl;
1906 }
1907 }
1908 }
1909/*
1910 * This is for checking point-angle and presence of Candidates in Ball, currently we are checking the ball Angle.
1911 *
1912 else
1913 {
1914 if (sign>0 && BallAngle>0 && Storage[0]<0)
1915 {
1916 cout << "Next better candidate is " << *Candidate << " with ";
1917 Opt_Candidate = Candidate;
1918 Storage[0] = sign;
1919 Storage[1] = BallAngle;
1920 Opt_Mittelpunkt->CopyVector(&Mittelpunkt);
1921 cout << "Angle is " << Storage[1] << ", Halbraum ist "
1922 << Storage[0] << endl;
1923
1924//Debugging purposes only
1925 cout << "Umkreismittelpunkt has coordinates" << Umkreismittelpunkt.x[0] << " "<< Umkreismittelpunkt.x[1] <<" "<<Umkreismittelpunkt.x[2] << endl;
1926 cout << "Candidate has coordinates" << Candidate->x.x[0]<< " " << Candidate->x.x[1] << " " << Candidate->x.x[2] << endl;
1927 cout << "a has coordinates" << a->x.x[0]<< " " << a->x.x[1] << " " << a->x.x[2] << endl;
1928 cout << "b has coordinates" << b->x.x[0]<< " " << b->x.x[1] << " " << b->x.x[2] << endl;
1929 cout << "Mittelpunkt has coordinates" << Mittelpunkt.x[0] << " " << Mittelpunkt.x[1]<< " " <<Mittelpunkt.x[2] << endl;
1930 cout << "Umkreisradius ist " << Umkreisradius << endl;
1931 cout << "Restradius ist " << Restradius << endl;
1932 cout << "TempNormal has coordinates " << TempNormal.x[0] << " " << TempNormal.x[1] << " " << TempNormal.x[2] << " " << endl;
1933 cout << "OldNormal has coordinates " << OldNormal->x[0] << " " << OldNormal->x[1] << " " << OldNormal->x[2] << " " << endl;
1934 cout << "Dist a to UmkreisMittelpunkt " << a->x.Distance(&Umkreismittelpunkt) << endl;
1935 cout << "Dist b to UmkreisMittelpunkt " << b->x.Distance(&Umkreismittelpunkt) << endl;
1936 cout << "Dist Candidate to UmkreisMittelpunkt " << Candidate->x.Distance(&Umkreismittelpunkt) << endl;
1937 cout << "Dist a to Mittelpunkt " << a->x.Distance(&Mittelpunkt) << endl;
1938 cout << "Dist b to Mittelpunkt " << b->x.Distance(&Mittelpunkt) << endl;
1939 cout << "Dist Candidate to Mittelpunkt " << Candidate->x.Distance(&Mittelpunkt) << endl;
1940
1941
1942
1943 }
1944 else
1945 {
1946 if (DEBUG)
1947 cout << "Looses to better candidate" << endl;
1948 }
1949 }
1950 */
1951 } else {
1952 if (DEBUG) {
1953 cout << "Doesn't satisfy requirements for circumscribing circle" << endl;
1954 }
1955 }
1956 } else {
1957 if (DEBUG) {
1958 cout << "identisch mit Ursprungslinie" << endl;
1959 }
1960 }
1961
1962 if (RecursionLevel < 9) { // Five is the recursion level threshold.
1963 for (int i = 0; i < mol->NumberOfBondsPerAtom[Candidate->nr]; i++) { // go through all bond
1964 Walker = mol->ListOfBondsPerAtom[Candidate->nr][i]->GetOtherAtom(Candidate);
1965 if (Walker == Parent) { // don't go back the same bond
1966 continue;
1967 } else {
1968 Find_next_suitable_point(a, b, Walker, Candidate, RecursionLevel+1, Chord, d1, OldNormal, Opt_Candidate, Opt_Mittelpunkt, Storage, RADIUS, mol); //call function again
1969 }
1970 }
1971 }
1972};
1973
1974/** This function finds a triangle to a line, adjacent to an existing one.
1975 * @param out output stream for debugging
1976 * @param tecplot output stream for writing found triangles in TecPlot format
1977 * @param mol molecule structure with all atoms and bonds
1978 * @param Line current baseline to search from
1979 * @param T current triangle which \a Line is edge of
1980 * @param RADIUS radius of the rolling ball
1981 * @param N number of found triangles
1982 * @param *filename filename base for intermediate envelopes
1983 */
1984bool Tesselation::Find_next_suitable_triangle(ofstream *out, ofstream *tecplot,
1985 molecule* mol, BoundaryLineSet &Line, BoundaryTriangleSet &T,
1986 const double& RADIUS, int N, const char *tempbasename)
1987{
1988 cout << Verbose(1) << "Begin of Find_next_suitable_triangle\n";
1989 Vector direction1;
1990 Vector helper;
1991 Vector Chord;
1992 ofstream *tempstream = NULL;
1993 char NumberName[255];
1994 double tmp;
1995 //atom* Walker;
1996 atom* OldThirdPoint;
1997
1998 double Storage[3];
1999 Storage[0] = -2.; // This direction is either +1 or -1 one, so any result will take precedence over initial values
2000 Storage[1] = -2.; // This is also lower then any value produced by an eligible atom, which are all positive
2001 Storage[2] = 9999999.;
2002 atom* Opt_Candidate = NULL;
2003 Vector Opt_Mittelpunkt;
2004
2005 cout << Verbose(1) << "Current baseline is " << Line << " of triangle " << T << "." << endl;
2006
2007 helper.CopyVector(&(Line.endpoints[0]->node->x));
2008 for (int i = 0; i < 3; i++) {
2009 if (T.endpoints[i]->node->nr != Line.endpoints[0]->node->nr && T.endpoints[i]->node->nr != Line.endpoints[1]->node->nr) {
2010 OldThirdPoint = T.endpoints[i]->node;
2011 helper.SubtractVector(&T.endpoints[i]->node->x);
2012 break;
2013 }
2014 }
2015
2016 direction1.CopyVector(&Line.endpoints[0]->node->x);
2017 direction1.SubtractVector(&Line.endpoints[1]->node->x);
2018 direction1.VectorProduct(&(T.NormalVector));
2019
2020 if (direction1.ScalarProduct(&helper) < 0) {
2021 direction1.Scale(-1);
2022 }
2023 cout << Verbose(2) << "Looking in direction " << direction1 << " for candidates.\n";
2024
2025 Chord.CopyVector(&(Line.endpoints[0]->node->x)); // bring into calling function
2026 Chord.SubtractVector(&(Line.endpoints[1]->node->x));
2027 cout << Verbose(2) << "Baseline vector is " << Chord << ".\n";
2028
2029
2030 Vector Umkreismittelpunkt, a, b, c;
2031 double alpha, beta, gamma;
2032 a.CopyVector(&(T.endpoints[0]->node->x));
2033 b.CopyVector(&(T.endpoints[1]->node->x));
2034 c.CopyVector(&(T.endpoints[2]->node->x));
2035 a.SubtractVector(&(T.endpoints[1]->node->x));
2036 b.SubtractVector(&(T.endpoints[2]->node->x));
2037 c.SubtractVector(&(T.endpoints[0]->node->x));
2038
2039 alpha = M_PI - a.Angle(&c);
2040 beta = M_PI - b.Angle(&a);
2041 gamma = M_PI - c.Angle(&b);
2042 if (fabs(M_PI - alpha - beta - gamma) > MYEPSILON)
2043 cerr << Verbose(0) << "WARNING: sum of angles for candidate triangle " << (alpha + beta + gamma)/M_PI*180. << " != 180.\n";
2044
2045 Umkreismittelpunkt.Zero();
2046 helper.CopyVector(&T.endpoints[0]->node->x);
2047 helper.Scale(sin(2.*alpha));
2048 Umkreismittelpunkt.AddVector(&helper);
2049 helper.CopyVector(&T.endpoints[1]->node->x);
2050 helper.Scale(sin(2.*beta));
2051 Umkreismittelpunkt.AddVector(&helper);
2052 helper.CopyVector(&T.endpoints[2]->node->x);
2053 helper.Scale(sin(2.*gamma));
2054 Umkreismittelpunkt.AddVector(&helper);
2055 //Umkreismittelpunkt = (T.endpoints[0]->node->x) * sin(2.*alpha) + T.endpoints[1]->node->x * sin(2.*beta) + (T.endpoints[2]->node->x) * sin(2.*gamma) ;
2056 //cout << "UmkreisMittelpunkt is " << Umkreismittelpunkt.x[0] << " "<< Umkreismittelpunkt.x[1] << " "<< Umkreismittelpunkt.x[2] << " "<< endl;
2057 Umkreismittelpunkt.Scale(1/(sin(2*alpha) + sin(2*beta) + sin(2*gamma)));
2058 //cout << "UmkreisMittelpunkt is " << Umkreismittelpunkt.x[0] << " "<< Umkreismittelpunkt.x[1] << " "<< Umkreismittelpunkt.x[2] << " "<< endl;
2059 cout << " We look over line " << Line << " in direction " << direction1.x[0] << " " << direction1.x[1] << " " << direction1.x[2] << " " << endl;
2060 cout << " Old Normal is " << (T.NormalVector.x)[0] << " " << T.NormalVector.x[1] << " " << (T.NormalVector.x)[2] << " " << endl;
2061
2062 cout << Verbose(2) << "Base triangle has sides " << a << ", " << b << ", " << c << " with angles " << alpha/M_PI*180. << ", " << beta/M_PI*180. << ", " << gamma/M_PI*180. << "." << endl;
2063 cout << Verbose(2) << "Center of circumference is " << Umkreismittelpunkt << "." << endl;
2064 if (DEBUG)
2065 cout << Verbose(3) << "Check of relative endpoints (same distance, equally spreaded): "<< endl;
2066 tmp = 0;
2067 for (int i=0;i<NDIM;i++) {
2068 helper.CopyVector(&T.endpoints[i]->node->x);
2069 helper.SubtractVector(&Umkreismittelpunkt);
2070 if (DEBUG)
2071 cout << Verbose(3) << "Endpoint[" << i << "]: " << helper << " with length " << helper.Norm() << "." << endl;
2072 if (tmp == 0) // set on first time for comparison against next ones
2073 tmp = helper.Norm();
2074 if (fabs(helper.Norm() - tmp) > MYEPSILON)
2075 cerr << Verbose(1) << "WARNING: center of circumference is wrong!" << endl;
2076 }
2077
2078 cout << Verbose(1) << "Looking for third point candidates for triangle ... " << endl;
2079
2080 Find_next_suitable_point_via_Angle_of_Sphere(Line.endpoints[0]->node, Line.endpoints[1]->node, OldThirdPoint, Line.endpoints[0]->node, Line.endpoints[1]->node, 0, &Chord, &direction1, &(T.NormalVector), Umkreismittelpunkt, Opt_Candidate, Storage, RADIUS, mol);
2081 Find_next_suitable_point_via_Angle_of_Sphere(Line.endpoints[0]->node, Line.endpoints[1]->node, OldThirdPoint, Line.endpoints[1]->node, Line.endpoints[0]->node, 0, &Chord, &direction1, &(T.NormalVector), Umkreismittelpunkt, Opt_Candidate, Storage, RADIUS, mol);
2082 if (Opt_Candidate == NULL) {
2083 cerr << "WARNING: Could not find a suitable candidate." << endl;
2084 return false;
2085 }
2086 cout << Verbose(1) << " Optimal candidate is " << *Opt_Candidate << endl;
2087
2088 // check whether all edges of the new triangle still have space for one more triangle (i.e. TriangleCount <2)
2089 bool flag = CheckPresenceOfTriangle(out, Opt_Candidate, Line.endpoints[0]->node, Line.endpoints[1]->node);
2090
2091 if (flag) { // if so, add
2092 AddTrianglePoint(Opt_Candidate, 0);
2093 AddTrianglePoint(Line.endpoints[0]->node, 1);
2094 AddTrianglePoint(Line.endpoints[1]->node, 2);
2095
2096 AddTriangleLine(TPS[0], TPS[1], 0);
2097 AddTriangleLine(TPS[0], TPS[2], 1);
2098 AddTriangleLine(TPS[1], TPS[2], 2);
2099
2100 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
2101 AddTriangleToLines();
2102
2103 BTS->GetNormalVector(BTS->NormalVector);
2104
2105 if ((BTS->NormalVector.ScalarProduct(&(T.NormalVector)) < 0 && Storage[0] > 0) || (BTS->NormalVector.ScalarProduct(&(T.NormalVector)) > 0 && Storage[0] < 0) || (fabs(Storage[0]) < MYEPSILON && Storage[1]*BTS->NormalVector.ScalarProduct(&direction1) < 0) ) {
2106 BTS->NormalVector.Scale(-1);
2107 };
2108 cout << Verbose(1) << "New triangle with " << *BTS << " and normal vector " << BTS->NormalVector << " for this triangle ... " << endl;
2109 cout << Verbose(1) << "We have "<< TrianglesOnBoundaryCount << " for line " << Line << "." << endl;
2110 } else { // else, yell and do nothing
2111 cout << Verbose(1) << "This triangle consisting of ";
2112 cout << *Opt_Candidate << ", ";
2113 cout << *Line.endpoints[0]->node << " and ";
2114 cout << *Line.endpoints[1]->node << " ";
2115 cout << "is invalid!" << endl;
2116 return false;
2117 }
2118
2119 if ((TrianglesOnBoundaryCount % 10) == 0) {
2120 sprintf(NumberName, "-%d", TriangleFilesWritten);
2121 if (DoTecplotOutput) {
2122 string NameofTempFile(tempbasename);
2123 NameofTempFile.append(NumberName);
2124 NameofTempFile.append(TecplotSuffix);
2125 cout << Verbose(1) << "Writing temporary non convex hull to file " << NameofTempFile << ".\n";
2126 tempstream = new ofstream(NameofTempFile.c_str(), ios::trunc);
2127 write_tecplot_file(out, tempstream, this, mol, TriangleFilesWritten);
2128 tempstream->close();
2129 tempstream->flush();
2130 delete(tempstream);
2131 }
2132
2133 if (DoRaster3DOutput) {
2134 string NameofTempFile(tempbasename);
2135 NameofTempFile.append(NumberName);
2136 NameofTempFile.append(Raster3DSuffix);
2137 cout << Verbose(1) << "Writing temporary non convex hull to file " << NameofTempFile << ".\n";
2138 tempstream = new ofstream(NameofTempFile.c_str(), ios::trunc);
2139 write_raster3d_file(out, tempstream, this, mol);
2140 tempstream->close();
2141 tempstream->flush();
2142 delete(tempstream);
2143 }
2144 if (DoTecplotOutput || DoRaster3DOutput)
2145 TriangleFilesWritten++;
2146 }
2147
2148 cout << Verbose(1) << "End of Find_next_suitable_triangle\n";
2149 return true;
2150};
2151
2152/** Checks whether the triangle consisting of the three atoms is already present.
2153 * Searches for the points in Tesselation::PointsOnBoundary and checks their
2154 * lines. If any of the three edges already has two triangles attached, false is
2155 * returned.
2156 * \param *out output stream for debugging
2157 * \param *a first endpoint
2158 * \param *b second endpoint
2159 * \param *c third endpoint
2160 * \return false - triangle invalid due to edge criteria, true - triangle may be added.
2161 */
2162bool Tesselation::CheckPresenceOfTriangle(ofstream *out, atom *a, atom *b, atom *c) {
2163 LineMap::iterator TryAndError;
2164 PointTestPair InsertPoint;
2165 bool Present[3];
2166
2167 *out << Verbose(2) << "Begin of CheckPresenceOfTriangle" << endl;
2168 TPS[0] = new class BoundaryPointSet(a);
2169 TPS[1] = new class BoundaryPointSet(b);
2170 TPS[2] = new class BoundaryPointSet(c);
2171 for (int i=0;i<3;i++) { // check through all endpoints
2172 InsertPoint = PointsOnBoundary.insert(PointPair(TPS[i]->node->nr, TPS[i]));
2173 Present[i] = !InsertPoint.second;
2174 if (Present[i]) { // if new point was not present before, increase counter
2175 delete TPS[i];
2176 *out << Verbose(2) << "Atom " << *((InsertPoint.first)->second->node) << " gibt's schon in der PointMap." << endl;
2177 TPS[i] = (InsertPoint.first)->second;
2178 }
2179 }
2180
2181 // check lines
2182 for (int i=0;i<3;i++)
2183 if (Present[i])
2184 for (int j=i;j<3;j++)
2185 if (Present[j]) {
2186 TryAndError = TPS[i]->lines.find(TPS[j]->node->nr);
2187 if ((TryAndError != TPS[i]->lines.end()) && (TryAndError->second->TrianglesCount > 1)) {
2188 *out << "WARNING: Line " << *TryAndError->second << " already present with " << TryAndError->second->TrianglesCount << " triangles attached." << endl;
2189 *out << Verbose(2) << "End of CheckPresenceOfTriangle" << endl;
2190 return false;
2191 }
2192 }
2193 *out << Verbose(2) << "End of CheckPresenceOfTriangle" << endl;
2194 return true;
2195};
2196
2197
2198void Find_second_point_for_Tesselation(atom* a, atom* Candidate, atom* Parent,
2199 int RecursionLevel, Vector Oben, atom*& Opt_Candidate, double Storage[3],
2200 molecule* mol, double RADIUS)
2201{
2202 cout << Verbose(2) << "Begin of Find_second_point_for_Tesselation, recursive level " << RecursionLevel << endl;
2203 int i;
2204 Vector AngleCheck;
2205 atom* Walker;
2206 double norm = -1., angle;
2207
2208 // check if we only have one unique point yet ...
2209 if (a != Candidate) {
2210 cout << Verbose(3) << "Current candidate is " << *Candidate << ": ";
2211 AngleCheck.CopyVector(&(Candidate->x));
2212 AngleCheck.SubtractVector(&(a->x));
2213 norm = AngleCheck.Norm();
2214 // second point shall have smallest angle with respect to Oben vector
2215 if (norm < RADIUS) {
2216 angle = AngleCheck.Angle(&Oben);
2217 if (angle < Storage[0]) {
2218 //cout << Verbose(1) << "Old values of Storage: %lf %lf \n", Storage[0], Storage[1]);
2219 cout << "Is a better candidate with distance " << norm << " and " << angle << ".\n";
2220 Opt_Candidate = Candidate;
2221 Storage[0] = AngleCheck.Angle(&Oben);
2222 //cout << Verbose(1) << "Changing something in Storage: %lf %lf. \n", Storage[0], Storage[2]);
2223 } else {
2224 cout << "Looses with angle " << angle << " to a better candidate " << *Opt_Candidate << endl;
2225 }
2226 } else {
2227 cout << "Refused due to Radius " << norm << endl;
2228 }
2229 }
2230
2231 // if not recursed to deeply, look at all its bonds
2232 if (RecursionLevel < 7) {
2233 for (i = 0; i < mol->NumberOfBondsPerAtom[Candidate->nr]; i++) {
2234 Walker = mol->ListOfBondsPerAtom[Candidate->nr][i]->GetOtherAtom(Candidate);
2235 if (Walker == Parent) // don't go back along the bond we came from
2236 continue;
2237 else
2238 Find_second_point_for_Tesselation(a, Walker, Candidate, RecursionLevel + 1, Oben, Opt_Candidate, Storage, mol, RADIUS);
2239 }
2240 }
2241 cout << Verbose(2) << "End of Find_second_point_for_Tesselation, recursive level " << RecursionLevel << endl;
2242};
2243
2244void Tesselation::Find_starting_triangle(molecule* mol, const double RADIUS)
2245{
2246 cout << Verbose(1) << "Begin of Find_starting_triangle\n";
2247 int i = 0;
2248 atom* Walker;
2249 atom* FirstPoint;
2250 atom* SecondPoint;
2251 atom* max_index[NDIM];
2252 double max_coordinate[NDIM];
2253 Vector Oben;
2254 Vector helper;
2255 Vector Chord;
2256 Vector CenterOfFirstLine;
2257
2258 Oben.Zero();
2259
2260 for (i = 0; i < 3; i++) {
2261 max_index[i] = NULL;
2262 max_coordinate[i] = -1;
2263 }
2264 cout << Verbose(2) << "Molecule mol is there and has " << mol->AtomCount << " Atoms \n";
2265
2266 // 1. searching topmost atom with respect to each axis
2267 Walker = mol->start;
2268 while (Walker->next != mol->end) {
2269 Walker = Walker->next;
2270 for (i = 0; i < 3; i++) {
2271 if (Walker->x.x[i] > max_coordinate[i]) {
2272 max_coordinate[i] = Walker->x.x[i];
2273 max_index[i] = Walker;
2274 }
2275 }
2276 }
2277
2278 cout << Verbose(2) << "Found maximum coordinates: ";
2279 for (int i=0;i<NDIM;i++)
2280 cout << i << ": " << *max_index[i] << "\t";
2281 cout << endl;
2282 //Koennen dies fuer alle Richtungen, legen hier erstmal Richtung auf k=0
2283 const int k = 1;
2284 Oben.x[k] = 1.;
2285 FirstPoint = max_index[k];
2286
2287 cout << Verbose(1) << "Coordinates of start atom " << *FirstPoint << " at " << FirstPoint->x << " with " << mol->NumberOfBondsPerAtom[FirstPoint->nr] << " bonds." << endl;
2288 double Storage[3];
2289 atom* Opt_Candidate = NULL;
2290 Storage[0] = 999999.; // This will contain the angle, which will be always positive (when looking for second point), when looking for third point this will be the quadrant.
2291 Storage[1] = 999999.; // This will be an angle looking for the third point.
2292 Storage[2] = 999999.;
2293
2294 Find_second_point_for_Tesselation(FirstPoint, FirstPoint, FirstPoint, 0, Oben, Opt_Candidate, Storage, mol, RADIUS); // we give same point as next candidate as its bonds are looked into in find_second_...
2295 SecondPoint = Opt_Candidate;
2296 cout << Verbose(1) << "Found second point is " << *SecondPoint << " at " << SecondPoint->x << ".\n";
2297
2298 helper.CopyVector(&(FirstPoint->x));
2299 helper.SubtractVector(&(SecondPoint->x));
2300 helper.Normalize();
2301 Oben.ProjectOntoPlane(&helper);
2302 Oben.Normalize();
2303 helper.VectorProduct(&Oben);
2304 Storage[0] = -2.; // This will indicate the quadrant.
2305 Storage[1] = 9999999.; // This will be an angle looking for the third point.
2306 Storage[2] = 9999999.;
2307
2308 Chord.CopyVector(&(FirstPoint->x)); // bring into calling function
2309 Chord.SubtractVector(&(SecondPoint->x));
2310 // Now, oben and helper are two orthonormalized vectors in the plane defined by Chord (not normalized)
2311
2312 cout << Verbose(2) << "Looking for third point candidates \n";
2313 // look in one direction of baseline for initial candidate
2314 Opt_Candidate = NULL;
2315 CenterOfFirstLine.CopyVector(&Chord);
2316 CenterOfFirstLine.Scale(0.5);
2317 CenterOfFirstLine.AddVector(&(SecondPoint->x));
2318
2319 cout << Verbose(1) << "Looking for third point candidates from " << *FirstPoint << " onward ...\n";
2320 Find_next_suitable_point_via_Angle_of_Sphere(FirstPoint, SecondPoint, SecondPoint, SecondPoint, FirstPoint, 0, &Chord, &helper, &Oben, CenterOfFirstLine, Opt_Candidate, Storage, RADIUS, mol);
2321 // look in other direction of baseline for possible better candidate
2322 cout << Verbose(1) << "Looking for third point candidates from " << *SecondPoint << " onward ...\n";
2323 Find_next_suitable_point_via_Angle_of_Sphere(FirstPoint, SecondPoint, SecondPoint, FirstPoint, SecondPoint, 0, &Chord, &helper, &Oben, CenterOfFirstLine, Opt_Candidate, Storage, RADIUS, mol);
2324 cout << Verbose(1) << "Third Point is " << *Opt_Candidate << endl;
2325
2326 // FOUND Starting Triangle: FirstPoint, SecondPoint, Opt_Candidate
2327
2328 // Finally, we only have to add the found points
2329 AddTrianglePoint(FirstPoint, 0);
2330 AddTrianglePoint(SecondPoint, 1);
2331 AddTrianglePoint(Opt_Candidate, 2);
2332 // ... and respective lines
2333 AddTriangleLine(TPS[0], TPS[1], 0);
2334 AddTriangleLine(TPS[1], TPS[2], 1);
2335 AddTriangleLine(TPS[0], TPS[2], 2);
2336 // ... and triangles to the Maps of the Tesselation class
2337 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
2338 AddTriangleToLines();
2339 // ... and calculate its normal vector (with correct orientation)
2340 Oben.Scale(-1.);
2341 BTS->GetNormalVector(Oben);
2342 cout << Verbose(0) << "==> The found starting triangle consists of " << *FirstPoint << ", " << *SecondPoint << " and " << *Opt_Candidate << " with normal vector " << BTS->NormalVector << ".\n";
2343 cout << Verbose(1) << "End of Find_starting_triangle\n";
2344};
2345
2346void Find_non_convex_border(ofstream *out, ofstream *tecplot, molecule* mol, const char *filename, const double RADIUS)
2347{
2348 int N = 0;
2349 struct Tesselation *Tess = new Tesselation;
2350 cout << Verbose(1) << "Entering search for non convex hull. " << endl;
2351 cout << flush;
2352 LineMap::iterator baseline;
2353 cout << Verbose(0) << "Begin of Find_non_convex_border\n";
2354 bool flag = false; // marks whether we went once through all baselines without finding any without two triangles
2355 bool failflag = false;
2356 if ((mol->first->next == mol->last) || (mol->last->previous == mol->first))
2357 mol->CreateAdjacencyList((ofstream *)&cout, 1.6, true);
2358
2359 Tess->Find_starting_triangle(mol, RADIUS);
2360
2361 baseline = Tess->LinesOnBoundary.begin();
2362 while ((baseline != Tess->LinesOnBoundary.end()) || (flag)) {
2363 if (baseline->second->TrianglesCount == 1) {
2364 failflag = Tess->Find_next_suitable_triangle(out, tecplot, mol, *(baseline->second), *(((baseline->second->triangles.begin()))->second), RADIUS, N, filename); //the line is there, so there is a triangle, but only one.
2365 flag = flag || failflag;
2366 if (!failflag)
2367 cerr << "WARNING: Find_next_suitable_triangle failed." << endl;
2368 } else {
2369 cout << Verbose(1) << "Line " << *baseline->second << " has " << baseline->second->TrianglesCount << " triangles adjacent" << endl;
2370 }
2371 N++;
2372 baseline++;
2373 if ((baseline == Tess->LinesOnBoundary.end()) && (flag)) {
2374 baseline = Tess->LinesOnBoundary.begin(); // restart if we reach end due to newly inserted lines
2375 flag = false;
2376 }
2377 }
2378 if (failflag) {
2379 cout << Verbose(1) << "Writing final tecplot file\n";
2380 if (DoTecplotOutput)
2381 write_tecplot_file(out, tecplot, Tess, mol, -1);
2382 if (DoRaster3DOutput)
2383 write_raster3d_file(out, tecplot, Tess, mol);
2384 } else {
2385 cerr << "ERROR: Could definately not find all necessary triangles!" << endl;
2386 }
2387
2388 cout << Verbose(0) << "End of Find_non_convex_border\n";
2389 delete(Tess);
2390};
2391
Note: See TracBrowser for help on using the repository browser.