source: src/boundary.cpp@ 2319ed

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

We are one step further in fixing the convex hull: There are two functions of Saskia Metzler missing, but then we may proceed with testing whether the simple correction scheme of the convex envelope works, but one thing: Right now we cannot associate a Tesselation to its molecule as the snake bites it's one tail. Hence, the next commit will consist of fixing this bad-OOP issue.

  • Makefile.am: Just some alphabetical resorting.
  • atom::atom() new copy constructor
  • builder.cpp: some output for cluster volume, molecule::AddCopyAtom() uses new copy constructor
  • FillBoxWithMolecule() - new function to fill the remainder of the simulation box with some given filler molecules. Makes explicit use of the tesselated surfaces
  • find_convex_border() - InsertStraddlingPoints() and CorrectConcaveBaselines() is called to correct for atoms outside the envelope and caused-by concave points
  • Tesselation::InsertStraddlingPoints() enlarges the envelope for all atoms found outside, Tesselation::CorrectConcaveBaselines() corrects all found baselines if the adjacent triangles are concave by flipping.
  • boundary.cpp: Lots of helper routines for stuff further below:
  • The following routines are needed to check whether point is in- or outside:
  • FIX: Tesselation::AddPoint() - newly created BoundaryPoint is removed if already present.

Problem: We want to associate a Tesselation class with each molecule class. However, so far we have to know about atoms and bond and molecules inside the Tesselation. We have to remove this dependency and create some intermediate class which enables/encapsulates the access to Vectors, e.g. hidden inside the atom class. This is also good OOP! The Tesselation also only needs a set of Vectors, not more!

  • Property mode set to 100755
File size: 141.9 KB
Line 
1#include "boundary.hpp"
2#include "linkedcell.hpp"
3#include "molecules.hpp"
4#include <gsl/gsl_matrix.h>
5#include <gsl/gsl_linalg.h>
6#include <gsl/gsl_multimin.h>
7#include <gsl/gsl_permutation.h>
8
9#define DEBUG 1
10#define DoSingleStepOutput 0
11#define DoTecplotOutput 1
12#define DoRaster3DOutput 1
13#define DoVRMLOutput 1
14#define TecplotSuffix ".dat"
15#define Raster3DSuffix ".r3d"
16#define VRMLSUffix ".wrl"
17#define HULLEPSILON 1e-7
18
19// ======================================== Points on Boundary =================================
20
21BoundaryPointSet::BoundaryPointSet()
22{
23 LinesCount = 0;
24 Nr = -1;
25}
26;
27
28BoundaryPointSet::BoundaryPointSet(atom *Walker)
29{
30 node = Walker;
31 LinesCount = 0;
32 Nr = Walker->nr;
33}
34;
35
36BoundaryPointSet::~BoundaryPointSet()
37{
38 cout << Verbose(5) << "Erasing point nr. " << Nr << "." << endl;
39 if (!lines.empty())
40 cerr << "WARNING: Memory Leak! I " << *this << " am still connected to some lines." << endl;
41 node = NULL;
42}
43;
44
45void BoundaryPointSet::AddLine(class BoundaryLineSet *line)
46{
47 cout << Verbose(6) << "Adding " << *this << " to line " << *line << "."
48 << endl;
49 if (line->endpoints[0] == this)
50 {
51 lines.insert(LinePair(line->endpoints[1]->Nr, line));
52 }
53 else
54 {
55 lines.insert(LinePair(line->endpoints[0]->Nr, line));
56 }
57 LinesCount++;
58}
59;
60
61ostream &
62operator <<(ostream &ost, BoundaryPointSet &a)
63{
64 ost << "[" << a.Nr << "|" << a.node->Name << "]";
65 return ost;
66}
67;
68
69// ======================================== Lines on Boundary =================================
70
71BoundaryLineSet::BoundaryLineSet()
72{
73 for (int i = 0; i < 2; i++)
74 endpoints[i] = NULL;
75 TrianglesCount = 0;
76 Nr = -1;
77}
78;
79
80BoundaryLineSet::BoundaryLineSet(class BoundaryPointSet *Point[2], int number)
81{
82 // set number
83 Nr = number;
84 // set endpoints in ascending order
85 SetEndpointsOrdered(endpoints, Point[0], Point[1]);
86 // add this line to the hash maps of both endpoints
87 Point[0]->AddLine(this); //Taken out, to check whether we can avoid unwanted double adding.
88 Point[1]->AddLine(this); //
89 // clear triangles list
90 TrianglesCount = 0;
91 cout << Verbose(5) << "New Line with endpoints " << *this << "." << endl;
92}
93;
94
95BoundaryLineSet::~BoundaryLineSet()
96{
97 int Numbers[2];
98 Numbers[0] = endpoints[1]->Nr;
99 Numbers[1] = endpoints[0]->Nr;
100 for (int i = 0; i < 2; i++) {
101 cout << Verbose(5) << "Erasing Line Nr. " << Nr << " in boundary point " << *endpoints[i] << "." << endl;
102 // as there may be multiple lines with same endpoints, we have to go through each and find in the endpoint's line list this line set
103 pair<LineMap::iterator, LineMap::iterator> erasor = endpoints[i]->lines.equal_range(Numbers[i]);
104 for (LineMap::iterator Runner = erasor.first; Runner != erasor.second; Runner++)
105 if ((*Runner).second == this) {
106 endpoints[i]->lines.erase(Runner);
107 break;
108 }
109 if (endpoints[i]->lines.empty()) {
110 cout << Verbose(5) << *endpoints[i] << " has no more lines it's attached to, erasing." << endl;
111 if (endpoints[i] != NULL) {
112 delete(endpoints[i]);
113 endpoints[i] = NULL;
114 } else
115 cerr << "ERROR: Endpoint " << i << " has already been free'd." << endl;
116 } else
117 cout << Verbose(5) << *endpoints[i] << " has still lines it's attached to." << endl;
118 }
119 if (!triangles.empty())
120 cerr << "WARNING: Memory Leak! I " << *this << " am still connected to some triangles." << endl;
121}
122;
123
124void
125BoundaryLineSet::AddTriangle(class BoundaryTriangleSet *triangle)
126{
127 cout << Verbose(6) << "Add " << triangle->Nr << " to line " << *this << "."
128 << endl;
129 triangles.insert(TrianglePair(triangle->Nr, triangle));
130 TrianglesCount++;
131}
132;
133
134/** Checks whether we have a common endpoint with given \a *line.
135 * \param *line other line to test
136 * \return true - common endpoint present, false - not connected
137 */
138bool BoundaryLineSet::IsConnectedTo(class BoundaryLineSet *line)
139{
140 if ((endpoints[0] == line->endpoints[0]) || (endpoints[1] == line->endpoints[0]) || (endpoints[0] == line->endpoints[1]) || (endpoints[1] == line->endpoints[1]))
141 return true;
142 else
143 return false;
144};
145
146/** Checks whether the adjacent triangles of a baseline are convex or not.
147 * We sum the two angles of each normal vector with a ficticious normnal vector from this baselinbe pointing outwards.
148 * If greater/equal M_PI than we are convex.
149 * \param *out output stream for debugging
150 * \return true - triangles are convex, false - concave or less than two triangles connected
151 */
152bool BoundaryLineSet::CheckConvexityCriterion(ofstream *out)
153{
154 Vector BaseLineNormal;
155 double angle = 0;
156 // get the two triangles
157 if (TrianglesCount != 2) {
158 *out << Verbose(1) << "ERROR: Baseline " << this << " is connect to less than two triangles, Tesselation incomplete!" << endl;
159 return false;
160 }
161 // have a normal vector on the base line pointing outwards
162 BaseLineNormal.Zero();
163 for(TriangleMap::iterator runner = triangles.begin(); runner != triangles.end(); runner++)
164 BaseLineNormal.AddVector(&runner->second->NormalVector);
165 BaseLineNormal.Normalize();
166 // and calculate the sum of the angles with this normal vector and each of the triangle ones'
167 for(TriangleMap::iterator runner = triangles.begin(); runner != triangles.end(); runner++)
168 angle += BaseLineNormal.Angle(&runner->second->NormalVector);
169
170 if ((angle - M_PI) > -MYEPSILON)
171 return true;
172 else
173 return false;
174}
175
176/** Checks whether point is any of the two endpoints this line contains.
177 * \param *point point to test
178 * \return true - point is of the line, false - is not
179 */
180bool BoundaryLineSet::ContainsBoundaryPoint(class BoundaryPointSet *point)
181{
182 for(int i=0;i<2;i++)
183 if (point == endpoints[i])
184 return true;
185 return false;
186};
187
188ostream &
189operator <<(ostream &ost, BoundaryLineSet &a)
190{
191 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
192 << a.endpoints[1]->node->Name << "]";
193 return ost;
194}
195;
196
197// ======================================== Triangles on Boundary =================================
198
199
200BoundaryTriangleSet::BoundaryTriangleSet()
201{
202 for (int i = 0; i < 3; i++)
203 {
204 endpoints[i] = NULL;
205 lines[i] = NULL;
206 }
207 Nr = -1;
208}
209;
210
211BoundaryTriangleSet::BoundaryTriangleSet(class BoundaryLineSet *line[3], int number)
212{
213 // set number
214 Nr = number;
215 // set lines
216 cout << Verbose(5) << "New triangle " << Nr << ":" << endl;
217 for (int i = 0; i < 3; i++)
218 {
219 lines[i] = line[i];
220 lines[i]->AddTriangle(this);
221 }
222 // get ascending order of endpoints
223 map<int, class BoundaryPointSet *> OrderMap;
224 for (int i = 0; i < 3; i++)
225 // for all three lines
226 for (int j = 0; j < 2; j++)
227 { // for both endpoints
228 OrderMap.insert(pair<int, class BoundaryPointSet *> (
229 line[i]->endpoints[j]->Nr, line[i]->endpoints[j]));
230 // and we don't care whether insertion fails
231 }
232 // set endpoints
233 int Counter = 0;
234 cout << Verbose(6) << " with end points ";
235 for (map<int, class BoundaryPointSet *>::iterator runner = OrderMap.begin(); runner
236 != OrderMap.end(); runner++)
237 {
238 endpoints[Counter] = runner->second;
239 cout << " " << *endpoints[Counter];
240 Counter++;
241 }
242 if (Counter < 3)
243 {
244 cerr << "ERROR! We have a triangle with only two distinct endpoints!"
245 << endl;
246 //exit(1);
247 }
248 cout << "." << endl;
249}
250;
251
252BoundaryTriangleSet::~BoundaryTriangleSet()
253{
254 for (int i = 0; i < 3; i++) {
255 cout << Verbose(5) << "Erasing triangle Nr." << Nr << endl;
256 lines[i]->triangles.erase(Nr);
257 if (lines[i]->triangles.empty()) {
258 if (lines[i] != NULL) {
259 cout << Verbose(5) << *lines[i] << " is no more attached to any triangle, erasing." << endl;
260 delete (lines[i]);
261 lines[i] = NULL;
262 } else
263 cerr << "ERROR: This line " << i << " has already been free'd." << endl;
264 } else
265 cout << Verbose(5) << *lines[i] << " is still attached to another triangle." << endl;
266 }
267}
268;
269
270/** Calculates the normal vector for this triangle.
271 * Is made unique by comparison with \a OtherVector to point in the other direction.
272 * \param &OtherVector direction vector to make normal vector unique.
273 */
274void BoundaryTriangleSet::GetNormalVector(Vector &OtherVector)
275{
276 // get normal vector
277 NormalVector.MakeNormalVector(&endpoints[0]->node->x, &endpoints[1]->node->x,
278 &endpoints[2]->node->x);
279
280 // make it always point inward (any offset vector onto plane projected onto normal vector suffices)
281 if (NormalVector.Projection(&OtherVector) > 0)
282 NormalVector.Scale(-1.);
283};
284
285/** Finds the point on the triangle \a *BTS the line defined by \a *MolCenter and \a *x crosses through.
286 * We call Vector::GetIntersectionWithPlane() to receive the intersection point with the plane
287 * This we test if it's really on the plane and whether it's inside the triangle on the plane or not.
288 * The latter is done as follows: if it's really outside, then for any endpoint of the triangle and it's opposite
289 * base line, the intersection between the line from endpoint to intersection and the base line will have a Vector::NormSquared()
290 * smaller than the first line.
291 * \param *out output stream for debugging
292 * \param *MolCenter offset vector of line
293 * \param *x second endpoint of line, minus \a *MolCenter is directional vector of line
294 * \param *Intersection intersection on plane on return
295 * \return true - \a *Intersection contains intersection on plane defined by triangle, false - zero vector if outside of triangle.
296 */
297bool BoundaryTriangleSet::GetIntersectionInsideTriangle(ofstream *out, Vector *MolCenter, Vector *x, Vector *Intersection)
298{
299 Vector CrossPoint;
300 Vector helper;
301 int i=0;
302
303 if (Intersection->GetIntersectionWithPlane(out, &NormalVector, &endpoints[0]->node->x, MolCenter, x)) {
304 *out << Verbose(1) << "Alas! [Bronstein] failed - at least numerically - the intersection is not on the plane!" << endl;
305 return false;
306 }
307
308 // Calculate cross point between one baseline and the line from the third endpoint to intersection
309 do {
310 CrossPoint.GetIntersectionOfTwoLinesOnPlane(out, &endpoints[i%3]->node->x, &endpoints[(i+1)%3]->node->x, &endpoints[(i+2)%3]->node->x, Intersection);
311 helper.CopyVector(&endpoints[(i+1)%3]->node->x);
312 helper.SubtractVector(&endpoints[i%3]->node->x);
313 i++;
314 if (i>3)
315 break;
316 } while (CrossPoint.NormSquared() < MYEPSILON);
317 if (i>3) {
318 *out << Verbose(1) << "ERROR: Could not find any cross points, something's utterly wrong here!" << endl;
319 exit(255);
320 }
321 CrossPoint.SubtractVector(&endpoints[i%3]->node->x);
322
323 // check whether intersection is inside or not by comparing length of intersection and length of cross point
324 if ((CrossPoint.NormSquared() - helper.NormSquared()) > -MYEPSILON) { // inside
325 return true;
326 } else { // outside!
327 Intersection->Zero();
328 return false;
329 }
330};
331
332/** Checks whether lines is any of the three boundary lines this triangle contains.
333 * \param *line line to test
334 * \return true - line is of the triangle, false - is not
335 */
336bool BoundaryTriangleSet::ContainsBoundaryLine(class BoundaryLineSet *line)
337{
338 for(int i=0;i<3;i++)
339 if (line == lines[i])
340 return true;
341 return false;
342};
343
344/** Checks whether point is any of the three endpoints this triangle contains.
345 * \param *point point to test
346 * \return true - point is of the triangle, false - is not
347 */
348bool BoundaryTriangleSet::ContainsBoundaryPoint(class BoundaryPointSet *point)
349{
350 for(int i=0;i<3;i++)
351 if (point == endpoints[i])
352 return true;
353 return false;
354};
355
356ostream &
357operator <<(ostream &ost, BoundaryTriangleSet &a)
358{
359 ost << "[" << a.Nr << "|" << a.endpoints[0]->node->Name << ","
360 << a.endpoints[1]->node->Name << "," << a.endpoints[2]->node->Name << "]";
361 return ost;
362}
363;
364
365
366// ============================ CandidateForTesselation =============================
367
368CandidateForTesselation::CandidateForTesselation(
369 atom *candidate, BoundaryLineSet* line, Vector OptCandidateCenter, Vector OtherOptCandidateCenter
370) {
371 point = candidate;
372 BaseLine = line;
373 OptCenter.CopyVector(&OptCandidateCenter);
374 OtherOptCenter.CopyVector(&OtherOptCandidateCenter);
375}
376
377CandidateForTesselation::~CandidateForTesselation() {
378 point = NULL;
379 BaseLine = NULL;
380}
381
382// ========================================== F U N C T I O N S =================================
383
384/** Finds the endpoint two lines are sharing.
385 * \param *line1 first line
386 * \param *line2 second line
387 * \return point which is shared or NULL if none
388 */
389class BoundaryPointSet *
390GetCommonEndpoint(class BoundaryLineSet * line1, class BoundaryLineSet * line2)
391{
392 class BoundaryLineSet * lines[2] =
393 { line1, line2 };
394 class BoundaryPointSet *node = NULL;
395 map<int, class BoundaryPointSet *> OrderMap;
396 pair<map<int, class BoundaryPointSet *>::iterator, bool> OrderTest;
397 for (int i = 0; i < 2; i++)
398 // for both lines
399 for (int j = 0; j < 2; j++)
400 { // for both endpoints
401 OrderTest = OrderMap.insert(pair<int, class BoundaryPointSet *> (
402 lines[i]->endpoints[j]->Nr, lines[i]->endpoints[j]));
403 if (!OrderTest.second)
404 { // if insertion fails, we have common endpoint
405 node = OrderTest.first->second;
406 cout << Verbose(5) << "Common endpoint of lines " << *line1
407 << " and " << *line2 << " is: " << *node << "." << endl;
408 j = 2;
409 i = 2;
410 break;
411 }
412 }
413 return node;
414}
415;
416
417/** Determines the boundary points of a cluster.
418 * Does a projection per axis onto the orthogonal plane, transforms into spherical coordinates, sorts them by the angle
419 * and looks at triples: if the middle has less a distance than the allowed maximum height of the triangle formed by the plane's
420 * center and first and last point in the triple, it is thrown out.
421 * \param *out output stream for debugging
422 * \param *mol molecule structure representing the cluster
423 */
424Boundaries *
425GetBoundaryPoints(ofstream *out, molecule *mol)
426{
427 atom *Walker = NULL;
428 PointMap PointsOnBoundary;
429 LineMap LinesOnBoundary;
430 TriangleMap TrianglesOnBoundary;
431 Vector *MolCenter = mol->DetermineCenterOfAll(out);
432 Vector helper;
433
434 *out << Verbose(1) << "Finding all boundary points." << endl;
435 Boundaries *BoundaryPoints = new Boundaries[NDIM]; // first is alpha, second is (r, nr)
436 BoundariesTestPair BoundaryTestPair;
437 Vector AxisVector, AngleReferenceVector, AngleReferenceNormalVector;
438 double radius, angle;
439 // 3a. Go through every axis
440 for (int axis = 0; axis < NDIM; axis++) {
441 AxisVector.Zero();
442 AngleReferenceVector.Zero();
443 AngleReferenceNormalVector.Zero();
444 AxisVector.x[axis] = 1.;
445 AngleReferenceVector.x[(axis + 1) % NDIM] = 1.;
446 AngleReferenceNormalVector.x[(axis + 2) % NDIM] = 1.;
447
448 *out << Verbose(1) << "Axisvector is " << AxisVector << " and AngleReferenceVector is " << AngleReferenceVector << ", and AngleReferenceNormalVector is " << AngleReferenceNormalVector << "." << endl;
449
450 // 3b. construct set of all points, transformed into cylindrical system and with left and right neighbours
451 Walker = mol->start;
452 while (Walker->next != mol->end) {
453 Walker = Walker->next;
454 Vector ProjectedVector;
455 ProjectedVector.CopyVector(&Walker->x);
456 ProjectedVector.SubtractVector(MolCenter);
457 ProjectedVector.ProjectOntoPlane(&AxisVector);
458
459 // correct for negative side
460 radius = ProjectedVector.NormSquared();
461 if (fabs(radius) > MYEPSILON)
462 angle = ProjectedVector.Angle(&AngleReferenceVector);
463 else
464 angle = 0.; // otherwise it's a vector in Axis Direction and unimportant for boundary issues
465
466 //*out << "Checking sign in quadrant : " << ProjectedVector.Projection(&AngleReferenceNormalVector) << "." << endl;
467 if (ProjectedVector.Projection(&AngleReferenceNormalVector) > 0) {
468 angle = 2. * M_PI - angle;
469 }
470 *out << Verbose(2) << "Inserting " << *Walker << ": (r, alpha) = (" << radius << "," << angle << "): " << ProjectedVector << endl;
471 BoundaryTestPair = BoundaryPoints[axis].insert(BoundariesPair(angle, DistancePair (radius, Walker)));
472 if (!BoundaryTestPair.second) { // same point exists, check first r, then distance of original vectors to center of gravity
473 *out << Verbose(2) << "Encountered two vectors whose projection onto axis " << axis << " is equal: " << endl;
474 *out << Verbose(2) << "Present vector: " << *BoundaryTestPair.first->second.second << endl;
475 *out << Verbose(2) << "New vector: " << *Walker << endl;
476 double tmp = ProjectedVector.NormSquared();
477 if ((tmp - BoundaryTestPair.first->second.first) > MYEPSILON) {
478 BoundaryTestPair.first->second.first = tmp;
479 BoundaryTestPair.first->second.second = Walker;
480 *out << Verbose(2) << "Keeping new vector due to larger projected distance " << tmp << "." << endl;
481 } else if (fabs(tmp - BoundaryTestPair.first->second.first) < MYEPSILON) {
482 helper.CopyVector(&Walker->x);
483 helper.SubtractVector(MolCenter);
484 tmp = helper.NormSquared();
485 helper.CopyVector(&BoundaryTestPair.first->second.second->x);
486 helper.SubtractVector(MolCenter);
487 if (helper.NormSquared() < tmp) {
488 BoundaryTestPair.first->second.second = Walker;
489 *out << Verbose(2) << "Keeping new vector due to larger distance to molecule center " << helper.NormSquared() << "." << endl;
490 } else {
491 *out << Verbose(2) << "Keeping present vector due to larger distance to molecule center " << tmp << "." << endl;
492 }
493 } else {
494 *out << Verbose(2) << "Keeping present vector due to larger projected distance " << tmp << "." << endl;
495 }
496 }
497 }
498 // printing all inserted for debugging
499 // {
500 // *out << Verbose(2) << "Printing list of candidates for axis " << axis << " which we have inserted so far." << endl;
501 // int i=0;
502 // for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
503 // if (runner != BoundaryPoints[axis].begin())
504 // *out << ", " << i << ": " << *runner->second.second;
505 // else
506 // *out << i << ": " << *runner->second.second;
507 // i++;
508 // }
509 // *out << endl;
510 // }
511 // 3c. throw out points whose distance is less than the mean of left and right neighbours
512 bool flag = false;
513 *out << Verbose(1) << "Looking for candidates to kick out by convex condition ... " << endl;
514 do { // do as long as we still throw one out per round
515 flag = false;
516 Boundaries::iterator left = BoundaryPoints[axis].end();
517 Boundaries::iterator right = BoundaryPoints[axis].end();
518 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
519 // set neighbours correctly
520 if (runner == BoundaryPoints[axis].begin()) {
521 left = BoundaryPoints[axis].end();
522 } else {
523 left = runner;
524 }
525 left--;
526 right = runner;
527 right++;
528 if (right == BoundaryPoints[axis].end()) {
529 right = BoundaryPoints[axis].begin();
530 }
531 // check distance
532
533 // construct the vector of each side of the triangle on the projected plane (defined by normal vector AxisVector)
534 {
535 Vector SideA, SideB, SideC, SideH;
536 SideA.CopyVector(&left->second.second->x);
537 SideA.SubtractVector(MolCenter);
538 SideA.ProjectOntoPlane(&AxisVector);
539 // *out << "SideA: ";
540 // SideA.Output(out);
541 // *out << endl;
542
543 SideB.CopyVector(&right->second.second->x);
544 SideB.SubtractVector(MolCenter);
545 SideB.ProjectOntoPlane(&AxisVector);
546 // *out << "SideB: ";
547 // SideB.Output(out);
548 // *out << endl;
549
550 SideC.CopyVector(&left->second.second->x);
551 SideC.SubtractVector(&right->second.second->x);
552 SideC.ProjectOntoPlane(&AxisVector);
553 // *out << "SideC: ";
554 // SideC.Output(out);
555 // *out << endl;
556
557 SideH.CopyVector(&runner->second.second->x);
558 SideH.SubtractVector(MolCenter);
559 SideH.ProjectOntoPlane(&AxisVector);
560 // *out << "SideH: ";
561 // SideH.Output(out);
562 // *out << endl;
563
564 // calculate each length
565 double a = SideA.Norm();
566 //double b = SideB.Norm();
567 //double c = SideC.Norm();
568 double h = SideH.Norm();
569 // calculate the angles
570 double alpha = SideA.Angle(&SideH);
571 double beta = SideA.Angle(&SideC);
572 double gamma = SideB.Angle(&SideH);
573 double delta = SideC.Angle(&SideH);
574 double MinDistance = a * sin(beta) / (sin(delta)) * (((alpha < M_PI / 2.) || (gamma < M_PI / 2.)) ? 1. : -1.);
575 //*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;
576 *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;
577 if ((fabs(h / fabs(h) - MinDistance / fabs(MinDistance)) < MYEPSILON) && ((h - MinDistance)) < -MYEPSILON) {
578 // throw out point
579 *out << Verbose(1) << "Throwing out " << *runner->second.second << "." << endl;
580 BoundaryPoints[axis].erase(runner);
581 flag = true;
582 }
583 }
584 }
585 } while (flag);
586 }
587 delete(MolCenter);
588 return BoundaryPoints;
589}
590;
591
592/** Determines greatest diameters of a cluster defined by its convex envelope.
593 * Looks at lines parallel to one axis and where they intersect on the projected planes
594 * \param *out output stream for debugging
595 * \param *BoundaryPoints NDIM set of boundary points defining the convex envelope on each projected plane
596 * \param *mol molecule structure representing the cluster
597 * \param IsAngstroem whether we have angstroem or atomic units
598 * \return NDIM array of the diameters
599 */
600double *
601GetDiametersOfCluster(ofstream *out, Boundaries *BoundaryPtr, molecule *mol,
602 bool IsAngstroem)
603{
604 // get points on boundary of NULL was given as parameter
605 bool BoundaryFreeFlag = false;
606 Boundaries *BoundaryPoints = BoundaryPtr;
607 if (BoundaryPoints == NULL)
608 {
609 BoundaryFreeFlag = true;
610 BoundaryPoints = GetBoundaryPoints(out, mol);
611 }
612 else
613 {
614 *out << Verbose(1) << "Using given boundary points set." << endl;
615 }
616 // determine biggest "diameter" of cluster for each axis
617 Boundaries::iterator Neighbour, OtherNeighbour;
618 double *GreatestDiameter = new double[NDIM];
619 for (int i = 0; i < NDIM; i++)
620 GreatestDiameter[i] = 0.;
621 double OldComponent, tmp, w1, w2;
622 Vector DistanceVector, OtherVector;
623 int component, Othercomponent;
624 for (int axis = 0; axis < NDIM; axis++)
625 { // regard each projected plane
626 //*out << Verbose(1) << "Current axis is " << axis << "." << endl;
627 for (int j = 0; j < 2; j++)
628 { // and for both axis on the current plane
629 component = (axis + j + 1) % NDIM;
630 Othercomponent = (axis + 1 + ((j + 1) & 1)) % NDIM;
631 //*out << Verbose(1) << "Current component is " << component << ", Othercomponent is " << Othercomponent << "." << endl;
632 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner
633 != BoundaryPoints[axis].end(); runner++)
634 {
635 //*out << Verbose(2) << "Current runner is " << *(runner->second.second) << "." << endl;
636 // seek for the neighbours pair where the Othercomponent sign flips
637 Neighbour = runner;
638 Neighbour++;
639 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
640 Neighbour = BoundaryPoints[axis].begin();
641 DistanceVector.CopyVector(&runner->second.second->x);
642 DistanceVector.SubtractVector(&Neighbour->second.second->x);
643 do
644 { // seek for neighbour pair where it flips
645 OldComponent = DistanceVector.x[Othercomponent];
646 Neighbour++;
647 if (Neighbour == BoundaryPoints[axis].end()) // make it wrap around
648 Neighbour = BoundaryPoints[axis].begin();
649 DistanceVector.CopyVector(&runner->second.second->x);
650 DistanceVector.SubtractVector(&Neighbour->second.second->x);
651 //*out << Verbose(3) << "OldComponent is " << OldComponent << ", new one is " << DistanceVector.x[Othercomponent] << "." << endl;
652 }
653 while ((runner != Neighbour) && (fabs(OldComponent / fabs(
654 OldComponent) - DistanceVector.x[Othercomponent] / fabs(
655 DistanceVector.x[Othercomponent])) < MYEPSILON)); // as long as sign does not flip
656 if (runner != Neighbour)
657 {
658 OtherNeighbour = Neighbour;
659 if (OtherNeighbour == BoundaryPoints[axis].begin()) // make it wrap around
660 OtherNeighbour = BoundaryPoints[axis].end();
661 OtherNeighbour--;
662 //*out << Verbose(2) << "The pair, where the sign of OtherComponent flips, is: " << *(Neighbour->second.second) << " and " << *(OtherNeighbour->second.second) << "." << endl;
663 // now we have found the pair: Neighbour and OtherNeighbour
664 OtherVector.CopyVector(&runner->second.second->x);
665 OtherVector.SubtractVector(&OtherNeighbour->second.second->x);
666 //*out << Verbose(2) << "Distances to Neighbour and OtherNeighbour are " << DistanceVector.x[component] << " and " << OtherVector.x[component] << "." << endl;
667 //*out << Verbose(2) << "OtherComponents to Neighbour and OtherNeighbour are " << DistanceVector.x[Othercomponent] << " and " << OtherVector.x[Othercomponent] << "." << endl;
668 // do linear interpolation between points (is exact) to extract exact intersection between Neighbour and OtherNeighbour
669 w1 = fabs(OtherVector.x[Othercomponent]);
670 w2 = fabs(DistanceVector.x[Othercomponent]);
671 tmp = fabs((w1 * DistanceVector.x[component] + w2
672 * OtherVector.x[component]) / (w1 + w2));
673 // mark if it has greater diameter
674 //*out << Verbose(2) << "Comparing current greatest " << GreatestDiameter[component] << " to new " << tmp << "." << endl;
675 GreatestDiameter[component] = (GreatestDiameter[component]
676 > tmp) ? GreatestDiameter[component] : tmp;
677 } //else
678 //*out << Verbose(2) << "Saw no sign flip, probably top or bottom node." << endl;
679 }
680 }
681 }
682 *out << Verbose(0) << "RESULT: The biggest diameters are "
683 << GreatestDiameter[0] << " and " << GreatestDiameter[1] << " and "
684 << GreatestDiameter[2] << " " << (IsAngstroem ? "angstrom"
685 : "atomiclength") << "." << endl;
686
687 // free reference lists
688 if (BoundaryFreeFlag)
689 delete[] (BoundaryPoints);
690
691 return GreatestDiameter;
692}
693;
694
695/** Creates the objects in a VRML file.
696 * \param *out output stream for debugging
697 * \param *vrmlfile output stream for tecplot data
698 * \param *Tess Tesselation structure with constructed triangles
699 * \param *mol molecule structure with atom positions
700 */
701void write_vrml_file(ofstream *out, ofstream *vrmlfile, class Tesselation *Tess, class molecule *mol)
702{
703 atom *Walker = mol->start;
704 bond *Binder = mol->first;
705 int i;
706 Vector *center = mol->DetermineCenterOfAll(out);
707 if (vrmlfile != NULL) {
708 //cout << Verbose(1) << "Writing Raster3D file ... ";
709 *vrmlfile << "#VRML V2.0 utf8" << endl;
710 *vrmlfile << "#Created by molecuilder" << endl;
711 *vrmlfile << "#All atoms as spheres" << endl;
712 while (Walker->next != mol->end) {
713 Walker = Walker->next;
714 *vrmlfile << "Sphere {" << endl << " "; // 2 is sphere type
715 for (i=0;i<NDIM;i++)
716 *vrmlfile << Walker->x.x[i]-center->x[i] << " ";
717 *vrmlfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
718 }
719
720 *vrmlfile << "# All bonds as vertices" << endl;
721 while (Binder->next != mol->last) {
722 Binder = Binder->next;
723 *vrmlfile << "3" << endl << " "; // 2 is round-ended cylinder type
724 for (i=0;i<NDIM;i++)
725 *vrmlfile << Binder->leftatom->x.x[i]-center->x[i] << " ";
726 *vrmlfile << "\t0.03\t";
727 for (i=0;i<NDIM;i++)
728 *vrmlfile << Binder->rightatom->x.x[i]-center->x[i] << " ";
729 *vrmlfile << "\t0.03\t0. 0. 1." << endl; // radius 0.05 and blue as colour
730 }
731
732 *vrmlfile << "# All tesselation triangles" << endl;
733 for (TriangleMap::iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
734 *vrmlfile << "1" << endl << " "; // 1 is triangle type
735 for (i=0;i<3;i++) { // print each node
736 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
737 *vrmlfile << TriangleRunner->second->endpoints[i]->node->x.x[j]-center->x[j] << " ";
738 *vrmlfile << "\t";
739 }
740 *vrmlfile << "1. 0. 0." << endl; // red as colour
741 *vrmlfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
742 }
743 } else {
744 cerr << "ERROR: Given vrmlfile is " << vrmlfile << "." << endl;
745 }
746 delete(center);
747};
748
749/** Creates the objects in a raster3d file (renderable with a header.r3d).
750 * \param *out output stream for debugging
751 * \param *rasterfile output stream for tecplot data
752 * \param *Tess Tesselation structure with constructed triangles
753 * \param *mol molecule structure with atom positions
754 */
755void write_raster3d_file(ofstream *out, ofstream *rasterfile, class Tesselation *Tess, class molecule *mol)
756{
757 atom *Walker = mol->start;
758 bond *Binder = mol->first;
759 int i;
760 Vector *center = mol->DetermineCenterOfAll(out);
761 if (rasterfile != NULL) {
762 //cout << Verbose(1) << "Writing Raster3D file ... ";
763 *rasterfile << "# Raster3D object description, created by MoleCuilder" << endl;
764 *rasterfile << "@header.r3d" << endl;
765 *rasterfile << "# All atoms as spheres" << endl;
766 while (Walker->next != mol->end) {
767 Walker = Walker->next;
768 *rasterfile << "2" << endl << " "; // 2 is sphere type
769 for (i=0;i<NDIM;i++)
770 *rasterfile << Walker->x.x[i]-center->x[i] << " ";
771 *rasterfile << "\t0.1\t1. 1. 1." << endl; // radius 0.05 and white as colour
772 }
773
774 *rasterfile << "# All bonds as vertices" << endl;
775 while (Binder->next != mol->last) {
776 Binder = Binder->next;
777 *rasterfile << "3" << endl << " "; // 2 is round-ended cylinder type
778 for (i=0;i<NDIM;i++)
779 *rasterfile << Binder->leftatom->x.x[i]-center->x[i] << " ";
780 *rasterfile << "\t0.03\t";
781 for (i=0;i<NDIM;i++)
782 *rasterfile << Binder->rightatom->x.x[i]-center->x[i] << " ";
783 *rasterfile << "\t0.03\t0. 0. 1." << endl; // radius 0.05 and blue as colour
784 }
785
786 *rasterfile << "# All tesselation triangles" << endl;
787 *rasterfile << "8\n 25. -1. 1. 1. 1. 0.0 0 0 0 2\n SOLID 1.0 0.0 0.0\n BACKFACE 0.3 0.3 1.0 0 0\n";
788 for (TriangleMap::iterator TriangleRunner = Tess->TrianglesOnBoundary.begin(); TriangleRunner != Tess->TrianglesOnBoundary.end(); TriangleRunner++) {
789 *rasterfile << "1" << endl << " "; // 1 is triangle type
790 for (i=0;i<3;i++) { // print each node
791 for (int j=0;j<NDIM;j++) // and for each node all NDIM coordinates
792 *rasterfile << TriangleRunner->second->endpoints[i]->node->x.x[j]-center->x[j] << " ";
793 *rasterfile << "\t";
794 }
795 *rasterfile << "1. 0. 0." << endl; // red as colour
796 //*rasterfile << "18" << endl << " 0.5 0.5 0.5" << endl; // 18 is transparency type for previous object
797 }
798 *rasterfile << "9\n terminating special property\n";
799 } else {
800 cerr << "ERROR: Given rasterfile is " << rasterfile << "." << endl;
801 }
802 delete(center);
803};
804
805/** This function creates the tecplot file, displaying the tesselation of the hull.
806 * \param *out output stream for debugging
807 * \param *tecplot output stream for tecplot data
808 * \param N arbitrary number to differentiate various zones in the tecplot format
809 */
810void write_tecplot_file(ofstream *out, ofstream *tecplot, class Tesselation *TesselStruct, class molecule *mol, int N)
811{
812 if (tecplot != NULL) {
813 *tecplot << "TITLE = \"3D CONVEX SHELL\"" << endl;
814 *tecplot << "VARIABLES = \"X\" \"Y\" \"Z\"" << endl;
815 *tecplot << "ZONE T=\"TRIANGLES" << N << "\", N=" << TesselStruct->PointsOnBoundaryCount << ", E=" << TesselStruct->TrianglesOnBoundaryCount << ", DATAPACKING=POINT, ZONETYPE=FETRIANGLE" << endl;
816 int *LookupList = new int[mol->AtomCount];
817 for (int i = 0; i < mol->AtomCount; i++)
818 LookupList[i] = -1;
819
820 // print atom coordinates
821 *out << Verbose(2) << "The following triangles were created:";
822 int Counter = 1;
823 atom *Walker = NULL;
824 for (PointMap::iterator target = TesselStruct->PointsOnBoundary.begin(); target != TesselStruct->PointsOnBoundary.end(); target++) {
825 Walker = target->second->node;
826 LookupList[Walker->nr] = Counter++;
827 *tecplot << Walker->x.x[0] << " " << Walker->x.x[1] << " " << Walker->x.x[2] << " " << endl;
828 }
829 *tecplot << endl;
830 // print connectivity
831 for (TriangleMap::iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner != TesselStruct->TrianglesOnBoundary.end(); runner++) {
832 *out << " " << runner->second->endpoints[0]->node->Name << "<->" << runner->second->endpoints[1]->node->Name << "<->" << runner->second->endpoints[2]->node->Name;
833 *tecplot << LookupList[runner->second->endpoints[0]->node->nr] << " " << LookupList[runner->second->endpoints[1]->node->nr] << " " << LookupList[runner->second->endpoints[2]->node->nr] << endl;
834 }
835 delete[] (LookupList);
836 *out << endl;
837 }
838}
839
840/** Tesselates the convex boundary by finding all boundary points.
841 * \param *out output stream for debugging
842 * \param *mol molecule structure with Atom's and Bond's
843 * \param *TesselStruct Tesselation filled with points, lines and triangles on boundary on return
844 * \param *LCList atoms in LinkedCell list
845 * \param *filename filename prefix for output of vertex data
846 * \return *TesselStruct is filled with convex boundary and tesselation is stored under \a *filename.
847 */
848void Find_convex_border(ofstream *out, molecule* mol, class Tesselation *&TesselStruct, class LinkedCell *LCList, const char *filename)
849{
850 bool BoundaryFreeFlag = false;
851 Boundaries *BoundaryPoints = NULL;
852
853 cout << Verbose(1) << "Begin of find_convex_border" << endl;
854
855 if (TesselStruct != NULL) // free if allocated
856 delete(TesselStruct);
857 TesselStruct = new class Tesselation;
858
859 // 1. Find all points on the boundary
860 if (BoundaryPoints == NULL) {
861 BoundaryFreeFlag = true;
862 BoundaryPoints = GetBoundaryPoints(out, mol);
863 } else {
864 *out << Verbose(1) << "Using given boundary points set." << endl;
865 }
866
867// printing all inserted for debugging
868 for (int axis=0; axis < NDIM; axis++)
869 {
870 *out << Verbose(2) << "Printing list of candidates for axis " << axis << " which we have inserted so far." << endl;
871 int i=0;
872 for(Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++) {
873 if (runner != BoundaryPoints[axis].begin())
874 *out << ", " << i << ": " << *runner->second.second;
875 else
876 *out << i << ": " << *runner->second.second;
877 i++;
878 }
879 *out << endl;
880 }
881
882 // 2. fill the boundary point list
883 for (int axis = 0; axis < NDIM; axis++)
884 for (Boundaries::iterator runner = BoundaryPoints[axis].begin(); runner != BoundaryPoints[axis].end(); runner++)
885 TesselStruct->AddPoint(runner->second.second);
886
887 *out << Verbose(2) << "I found " << TesselStruct->PointsOnBoundaryCount << " points on the convex boundary." << endl;
888 // now we have the whole set of edge points in the BoundaryList
889
890 // listing for debugging
891 // *out << Verbose(1) << "Listing PointsOnBoundary:";
892 // for(PointMap::iterator runner = PointsOnBoundary.begin(); runner != PointsOnBoundary.end(); runner++) {
893 // *out << " " << *runner->second;
894 // }
895 // *out << endl;
896
897 // 3a. guess starting triangle
898 TesselStruct->GuessStartingTriangle(out);
899
900 // 3b. go through all lines, that are not yet part of two triangles (only of one so far)
901 TesselStruct->TesselateOnBoundary(out, mol);
902
903 // 3c. check whether all atoms lay inside the boundary, if not, add to boundary points, segment triangle into three with the new point
904 if (!TesselStruct->InsertStraddlingPoints(out, mol))
905 *out << Verbose(1) << "Insertion of straddling points failed!" << endl;
906
907 // 3d. check all baselines whether the peaks of the two adjacent triangles with respect to center of baseline are convex, if not, make the baseline between the two peaks and baseline endpoints become the new peaks
908 if (!TesselStruct->CorrectConcaveBaselines(out))
909 *out << Verbose(1) << "Correction of concave baselines failed!" << endl;
910
911 *out << Verbose(2) << "I created " << TesselStruct->TrianglesOnBoundary.size() << " triangles with " << TesselStruct->LinesOnBoundary.size() << " lines and " << TesselStruct->PointsOnBoundary.size() << " points." << endl;
912
913 // 4. Store triangles in tecplot file
914 if (filename != NULL) {
915 if (DoTecplotOutput) {
916 string OutputName(filename);
917 OutputName.append(TecplotSuffix);
918 ofstream *tecplot = new ofstream(OutputName.c_str());
919 write_tecplot_file(out, tecplot, TesselStruct, mol, 0);
920 tecplot->close();
921 delete(tecplot);
922 }
923 if (DoRaster3DOutput) {
924 string OutputName(filename);
925 OutputName.append(Raster3DSuffix);
926 ofstream *rasterplot = new ofstream(OutputName.c_str());
927 write_raster3d_file(out, rasterplot, TesselStruct, mol);
928 rasterplot->close();
929 delete(rasterplot);
930 }
931 }
932
933 // free reference lists
934 if (BoundaryFreeFlag)
935 delete[] (BoundaryPoints);
936
937 cout << Verbose(1) << "End of find_convex_border" << endl;
938};
939
940
941/** Determines the volume of a cluster.
942 * Determines first the convex envelope, then tesselates it and calculates its volume.
943 * \param *out output stream for debugging
944 * \param *TesselStruct Tesselation filled with points, lines and triangles on boundary on return
945 * \param *configuration needed for path to store convex envelope file
946 * \return determined volume of the cluster in cubed config:GetIsAngstroem()
947 */
948double VolumeOfConvexEnvelope(ofstream *out, class Tesselation *TesselStruct, class config *configuration)
949{
950 bool IsAngstroem = configuration->GetIsAngstroem();
951 double volume = 0.;
952 double PyramidVolume = 0.;
953 double G, h;
954 Vector x, y;
955 double a, b, c;
956
957 // 6a. Every triangle forms a pyramid with the center of gravity as its peak, sum up the volumes
958 *out << Verbose(1)
959 << "Calculating the volume of the pyramids formed out of triangles and center of gravity."
960 << endl;
961 for (TriangleMap::iterator runner = TesselStruct->TrianglesOnBoundary.begin(); runner
962 != TesselStruct->TrianglesOnBoundary.end(); runner++)
963 { // go through every triangle, calculate volume of its pyramid with CoG as peak
964 x.CopyVector(&runner->second->endpoints[0]->node->x);
965 x.SubtractVector(&runner->second->endpoints[1]->node->x);
966 y.CopyVector(&runner->second->endpoints[0]->node->x);
967 y.SubtractVector(&runner->second->endpoints[2]->node->x);
968 a = sqrt(runner->second->endpoints[0]->node->x.DistanceSquared(
969 &runner->second->endpoints[1]->node->x));
970 b = sqrt(runner->second->endpoints[0]->node->x.DistanceSquared(
971 &runner->second->endpoints[2]->node->x));
972 c = sqrt(runner->second->endpoints[2]->node->x.DistanceSquared(
973 &runner->second->endpoints[1]->node->x));
974 G = sqrt(((a + b + c) * (a + b + c) - 2 * (a * a + b * b + c * c)) / 16.); // area of tesselated triangle
975 x.MakeNormalVector(&runner->second->endpoints[0]->node->x,
976 &runner->second->endpoints[1]->node->x,
977 &runner->second->endpoints[2]->node->x);
978 x.Scale(runner->second->endpoints[1]->node->x.Projection(&x));
979 h = x.Norm(); // distance of CoG to triangle
980 PyramidVolume = (1. / 3.) * G * h; // this formula holds for _all_ pyramids (independent of n-edge base or (not) centered peak)
981 *out << Verbose(2) << "Area of triangle is " << G << " "
982 << (IsAngstroem ? "angstrom" : "atomiclength") << "^2, height is "
983 << h << " and the volume is " << PyramidVolume << " "
984 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
985 volume += PyramidVolume;
986 }
987 *out << Verbose(0) << "RESULT: The summed volume is " << setprecision(10)
988 << volume << " " << (IsAngstroem ? "angstrom" : "atomiclength") << "^3."
989 << endl;
990
991 return volume;
992}
993;
994
995/** Creates multiples of the by \a *mol given cluster and suspends them in water with a given final density.
996 * We get cluster volume by VolumeOfConvexEnvelope() and its diameters by GetDiametersOfCluster()
997 * \param *out output stream for debugging
998 * \param *configuration needed for path to store convex envelope file
999 * \param *mol molecule structure representing the cluster
1000 * \param ClusterVolume guesstimated cluster volume, if equal 0 we used VolumeOfConvexEnvelope() instead.
1001 * \param celldensity desired average density in final cell
1002 */
1003void
1004PrepareClustersinWater(ofstream *out, config *configuration, molecule *mol,
1005 double ClusterVolume, double celldensity)
1006{
1007 // transform to PAS
1008 mol->PrincipalAxisSystem(out, true);
1009
1010 // some preparations beforehand
1011 bool IsAngstroem = configuration->GetIsAngstroem();
1012 Boundaries *BoundaryPoints = GetBoundaryPoints(out, mol);
1013 class Tesselation *TesselStruct = NULL;
1014 LinkedCell LCList(mol, 10.);
1015 Find_convex_border(out, mol, TesselStruct, &LCList, NULL);
1016 double clustervolume;
1017 if (ClusterVolume == 0)
1018 clustervolume = VolumeOfConvexEnvelope(out, TesselStruct, configuration);
1019 else
1020 clustervolume = ClusterVolume;
1021 double *GreatestDiameter = GetDiametersOfCluster(out, BoundaryPoints, mol, IsAngstroem);
1022 Vector BoxLengths;
1023 int repetition[NDIM] =
1024 { 1, 1, 1 };
1025 int TotalNoClusters = 1;
1026 for (int i = 0; i < NDIM; i++)
1027 TotalNoClusters *= repetition[i];
1028
1029 // sum up the atomic masses
1030 double totalmass = 0.;
1031 atom *Walker = mol->start;
1032 while (Walker->next != mol->end)
1033 {
1034 Walker = Walker->next;
1035 totalmass += Walker->type->mass;
1036 }
1037 *out << Verbose(0) << "RESULT: The summed mass is " << setprecision(10)
1038 << totalmass << " atomicmassunit." << endl;
1039
1040 *out << Verbose(0) << "RESULT: The average density is " << setprecision(10)
1041 << totalmass / clustervolume << " atomicmassunit/"
1042 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
1043
1044 // solve cubic polynomial
1045 *out << Verbose(1) << "Solving equidistant suspension in water problem ..."
1046 << endl;
1047 double cellvolume;
1048 if (IsAngstroem)
1049 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_A - (totalmass
1050 / clustervolume)) / (celldensity - 1);
1051 else
1052 cellvolume = (TotalNoClusters * totalmass / SOLVENTDENSITY_a0 - (totalmass
1053 / clustervolume)) / (celldensity - 1);
1054 *out << Verbose(1) << "Cellvolume needed for a density of " << celldensity
1055 << " g/cm^3 is " << cellvolume << " " << (IsAngstroem ? "angstrom"
1056 : "atomiclength") << "^3." << endl;
1057
1058 double minimumvolume = TotalNoClusters * (GreatestDiameter[0]
1059 * GreatestDiameter[1] * GreatestDiameter[2]);
1060 *out << Verbose(1)
1061 << "Minimum volume of the convex envelope contained in a rectangular box is "
1062 << minimumvolume << " atomicmassunit/" << (IsAngstroem ? "angstrom"
1063 : "atomiclength") << "^3." << endl;
1064 if (minimumvolume > cellvolume)
1065 {
1066 cerr << Verbose(0)
1067 << "ERROR: the containing box already has a greater volume than the envisaged cell volume!"
1068 << endl;
1069 cout << Verbose(0)
1070 << "Setting Box dimensions to minimum possible, the greatest diameters."
1071 << endl;
1072 for (int i = 0; i < NDIM; i++)
1073 BoxLengths.x[i] = GreatestDiameter[i];
1074 mol->CenterEdge(out, &BoxLengths);
1075 }
1076 else
1077 {
1078 BoxLengths.x[0] = (repetition[0] * GreatestDiameter[0] + repetition[1]
1079 * GreatestDiameter[1] + repetition[2] * GreatestDiameter[2]);
1080 BoxLengths.x[1] = (repetition[0] * repetition[1] * GreatestDiameter[0]
1081 * GreatestDiameter[1] + repetition[0] * repetition[2]
1082 * GreatestDiameter[0] * GreatestDiameter[2] + repetition[1]
1083 * repetition[2] * GreatestDiameter[1] * GreatestDiameter[2]);
1084 BoxLengths.x[2] = minimumvolume - cellvolume;
1085 double x0 = 0., x1 = 0., x2 = 0.;
1086 if (gsl_poly_solve_cubic(BoxLengths.x[0], BoxLengths.x[1],
1087 BoxLengths.x[2], &x0, &x1, &x2) == 1) // either 1 or 3 on return
1088 *out << Verbose(0) << "RESULT: The resulting spacing is: " << x0
1089 << " ." << endl;
1090 else
1091 {
1092 *out << Verbose(0) << "RESULT: The resulting spacings are: " << x0
1093 << " and " << x1 << " and " << x2 << " ." << endl;
1094 x0 = x2; // sorted in ascending order
1095 }
1096
1097 cellvolume = 1;
1098 for (int i = 0; i < NDIM; i++)
1099 {
1100 BoxLengths.x[i] = repetition[i] * (x0 + GreatestDiameter[i]);
1101 cellvolume *= BoxLengths.x[i];
1102 }
1103
1104 // set new box dimensions
1105 *out << Verbose(0) << "Translating to box with these boundaries." << endl;
1106 mol->SetBoxDimension(&BoxLengths);
1107 mol->CenterInBox((ofstream *) &cout);
1108 }
1109 // update Box of atoms by boundary
1110 mol->SetBoxDimension(&BoxLengths);
1111 *out << Verbose(0) << "RESULT: The resulting cell dimensions are: "
1112 << BoxLengths.x[0] << " and " << BoxLengths.x[1] << " and "
1113 << BoxLengths.x[2] << " with total volume of " << cellvolume << " "
1114 << (IsAngstroem ? "angstrom" : "atomiclength") << "^3." << endl;
1115}
1116;
1117
1118
1119/** Fills the empty space of the simulation box with water/
1120 * \param *out output stream for debugging
1121 * \param *List list of molecules already present in box
1122 * \param *TesselStruct contains tesselated surface
1123 * \param *filler molecule which the box is to be filled with
1124 * \param configuration contains box dimensions
1125 * \param distance[NDIM] distance between filling molecules in each direction
1126 * \param RandAtomDisplacement maximum distance for random displacement per atom
1127 * \param RandMolDisplacement maximum distance for random displacement per filler molecule
1128 * \param DoRandomRotation true - do random rotiations, false - don't
1129 * \return *mol pointer to new molecule with filled atoms
1130 */
1131molecule * FillBoxWithMolecule(ofstream *out, MoleculeListClass *List, molecule *filler, config &configuration, double distance[NDIM], double RandomAtomDisplacement, double RandomMolDisplacement, bool DoRandomRotation)
1132{
1133 molecule *Filling = new molecule(filler->elemente);
1134 Vector CurrentPosition;
1135 int N[NDIM];
1136 int n[NDIM];
1137 double *M = filler->ReturnFullMatrixforSymmetric(filler->cell_size);
1138 double Rotations[NDIM*NDIM];
1139 Vector AtomTranslations;
1140 Vector FillerTranslations;
1141 Vector FillerDistance;
1142 double FillIt = false;
1143 atom *Walker = NULL, *Runner = NULL;
1144 bond *Binder = NULL;
1145
1146 // Center filler at origin
1147 filler->CenterOrigin(out);
1148 filler->Center.Zero();
1149
1150 // calculate filler grid in [0,1]^3
1151 FillerDistance.Init(distance[0], distance[1], distance[2]);
1152 FillerDistance.InverseMatrixMultiplication(M);
1153 for(int i=0;i<NDIM;i++)
1154 N[i] = ceil(1./FillerDistance.x[i]);
1155
1156 // go over [0,1]^3 filler grid
1157 for (n[0] = 0; n[0] < N[0]; n[0]++)
1158 for (n[1] = 0; n[1] < N[1]; n[1]++)
1159 for (n[2] = 0; n[2] < N[2]; n[2]++) {
1160 // calculate position of current grid vector in untransformed box
1161 CurrentPosition.Init(n[0], n[1], n[2]);
1162 CurrentPosition.MatrixMultiplication(M);
1163 // Check whether point is in- or outside
1164 FillIt = true;
1165 for (MoleculeList::iterator ListRunner = List->ListOfMolecules.begin(); ListRunner != List->ListOfMolecules.end(); ListRunner++) {
1166 FillIt = FillIt && (!(*ListRunner)->TesselStruct->IsInside(&CurrentPosition));
1167 }
1168
1169 if (FillIt) {
1170 // fill in Filler
1171 *out << Verbose(2) << "Space at " << CurrentPosition << " is unoccupied by any molecule, filling in." << endl;
1172
1173 // create molecule random translation vector ...
1174 for (int i=0;i<NDIM;i++)
1175 FillerTranslations.x[i] = RandomMolDisplacement*(rand()/(RAND_MAX/2.) - 1.);
1176
1177 // go through all atoms
1178 Walker = filler->start;
1179 while (Walker != filler->end) {
1180 Walker = Walker->next;
1181 // copy atom ...
1182 *Runner = new atom(Walker);
1183
1184 // create atomic random translation vector ...
1185 for (int i=0;i<NDIM;i++)
1186 AtomTranslations.x[i] = RandomAtomDisplacement*(rand()/(RAND_MAX/2.) - 1.);
1187
1188 // ... and rotation matrix
1189 if (DoRandomRotation) {
1190 double phi[NDIM];
1191 for (int i=0;i<NDIM;i++) {
1192 phi[i] = rand()/(RAND_MAX/(2.*M_PI));
1193 }
1194
1195 Rotations[0] = cos(phi[0]) *cos(phi[2]) + (sin(phi[0])*sin(phi[1])*sin(phi[2]));
1196 Rotations[3] = sin(phi[0]) *cos(phi[2]) - (cos(phi[0])*sin(phi[1])*sin(phi[2]));
1197 Rotations[6] = cos(phi[1])*sin(phi[2]) ;
1198 Rotations[1] = - sin(phi[0])*cos(phi[1]) ;
1199 Rotations[4] = cos(phi[0])*cos(phi[1]) ;
1200 Rotations[7] = sin(phi[1]) ;
1201 Rotations[3] = - cos(phi[0]) *sin(phi[2]) + (sin(phi[0])*sin(phi[1])*cos(phi[2]));
1202 Rotations[5] = - sin(phi[0]) *sin(phi[2]) - (cos(phi[0])*sin(phi[1])*cos(phi[2]));
1203 Rotations[8] = cos(phi[1])*cos(phi[2]) ;
1204 }
1205
1206 // ... and put at new position
1207 if (DoRandomRotation)
1208 Runner->x.MatrixMultiplication(Rotations);
1209 Runner->x.AddVector(&AtomTranslations);
1210 Runner->x.AddVector(&FillerTranslations);
1211 Runner->x.AddVector(&CurrentPosition);
1212 // insert into Filling
1213 Filling->AddAtom(Runner);
1214 }
1215
1216 // go through all bonds and add as well
1217 Binder = filler->first;
1218 while(Binder != filler->last) {
1219 Binder = Binder->next;
1220 }
1221 } else {
1222 // leave empty
1223 *out << Verbose(2) << "Space at " << CurrentPosition << " is occupied." << endl;
1224 }
1225 }
1226 return Filling;
1227};
1228
1229
1230// =========================================================== class TESSELATION ===========================================
1231
1232/** Constructor of class Tesselation.
1233 */
1234Tesselation::Tesselation()
1235{
1236 PointsOnBoundaryCount = 0;
1237 LinesOnBoundaryCount = 0;
1238 TrianglesOnBoundaryCount = 0;
1239 TriangleFilesWritten = 0;
1240}
1241;
1242
1243/** Constructor of class Tesselation.
1244 * We have to free all points, lines and triangles.
1245 */
1246Tesselation::~Tesselation()
1247{
1248 cout << Verbose(1) << "Free'ing TesselStruct ... " << endl;
1249 for (TriangleMap::iterator runner = TrianglesOnBoundary.begin(); runner != TrianglesOnBoundary.end(); runner++) {
1250 if (runner->second != NULL) {
1251 delete (runner->second);
1252 runner->second = NULL;
1253 } else
1254 cerr << "ERROR: The triangle " << runner->first << " has already been free'd." << endl;
1255 }
1256}
1257;
1258
1259/** Gueses first starting triangle of the convex envelope.
1260 * We guess the starting triangle by taking the smallest distance between two points and looking for a fitting third.
1261 * \param *out output stream for debugging
1262 * \param PointsOnBoundary set of boundary points defining the convex envelope of the cluster
1263 */
1264void
1265Tesselation::GuessStartingTriangle(ofstream *out)
1266{
1267 // 4b. create a starting triangle
1268 // 4b1. create all distances
1269 DistanceMultiMap DistanceMMap;
1270 double distance, tmp;
1271 Vector PlaneVector, TrialVector;
1272 PointMap::iterator A, B, C; // three nodes of the first triangle
1273 A = PointsOnBoundary.begin(); // the first may be chosen arbitrarily
1274
1275 // with A chosen, take each pair B,C and sort
1276 if (A != PointsOnBoundary.end())
1277 {
1278 B = A;
1279 B++;
1280 for (; B != PointsOnBoundary.end(); B++)
1281 {
1282 C = B;
1283 C++;
1284 for (; C != PointsOnBoundary.end(); C++)
1285 {
1286 tmp = A->second->node->x.DistanceSquared(&B->second->node->x);
1287 distance = tmp * tmp;
1288 tmp = A->second->node->x.DistanceSquared(&C->second->node->x);
1289 distance += tmp * tmp;
1290 tmp = B->second->node->x.DistanceSquared(&C->second->node->x);
1291 distance += tmp * tmp;
1292 DistanceMMap.insert(DistanceMultiMapPair(distance, pair<
1293 PointMap::iterator, PointMap::iterator> (B, C)));
1294 }
1295 }
1296 }
1297 // // listing distances
1298 // *out << Verbose(1) << "Listing DistanceMMap:";
1299 // for(DistanceMultiMap::iterator runner = DistanceMMap.begin(); runner != DistanceMMap.end(); runner++) {
1300 // *out << " " << runner->first << "(" << *runner->second.first->second << ", " << *runner->second.second->second << ")";
1301 // }
1302 // *out << endl;
1303 // 4b2. pick three baselines forming a triangle
1304 // 1. we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
1305 DistanceMultiMap::iterator baseline = DistanceMMap.begin();
1306 for (; baseline != DistanceMMap.end(); baseline++)
1307 {
1308 // we take from the smallest sum of squared distance as the base line BC (with peak A) onward as the triangle candidate
1309 // 2. next, we have to check whether all points reside on only one side of the triangle
1310 // 3. construct plane vector
1311 PlaneVector.MakeNormalVector(&A->second->node->x,
1312 &baseline->second.first->second->node->x,
1313 &baseline->second.second->second->node->x);
1314 *out << Verbose(2) << "Plane vector of candidate triangle is ";
1315 PlaneVector.Output(out);
1316 *out << endl;
1317 // 4. loop over all points
1318 double sign = 0.;
1319 PointMap::iterator checker = PointsOnBoundary.begin();
1320 for (; checker != PointsOnBoundary.end(); checker++)
1321 {
1322 // (neglecting A,B,C)
1323 if ((checker == A) || (checker == baseline->second.first) || (checker
1324 == baseline->second.second))
1325 continue;
1326 // 4a. project onto plane vector
1327 TrialVector.CopyVector(&checker->second->node->x);
1328 TrialVector.SubtractVector(&A->second->node->x);
1329 distance = TrialVector.Projection(&PlaneVector);
1330 if (fabs(distance) < 1e-4) // we need to have a small epsilon around 0 which is still ok
1331 continue;
1332 *out << Verbose(3) << "Projection of " << checker->second->node->Name
1333 << " yields distance of " << distance << "." << endl;
1334 tmp = distance / fabs(distance);
1335 // 4b. Any have different sign to than before? (i.e. would lie outside convex hull with this starting triangle)
1336 if ((sign != 0) && (tmp != sign))
1337 {
1338 // 4c. If so, break 4. loop and continue with next candidate in 1. loop
1339 *out << Verbose(2) << "Current candidates: "
1340 << A->second->node->Name << ","
1341 << baseline->second.first->second->node->Name << ","
1342 << baseline->second.second->second->node->Name << " leaves "
1343 << checker->second->node->Name << " outside the convex hull."
1344 << endl;
1345 break;
1346 }
1347 else
1348 { // note the sign for later
1349 *out << Verbose(2) << "Current candidates: "
1350 << A->second->node->Name << ","
1351 << baseline->second.first->second->node->Name << ","
1352 << baseline->second.second->second->node->Name << " leave "
1353 << checker->second->node->Name << " inside the convex hull."
1354 << endl;
1355 sign = tmp;
1356 }
1357 // 4d. Check whether the point is inside the triangle (check distance to each node
1358 tmp = checker->second->node->x.DistanceSquared(&A->second->node->x);
1359 int innerpoint = 0;
1360 if ((tmp < A->second->node->x.DistanceSquared(
1361 &baseline->second.first->second->node->x)) && (tmp
1362 < A->second->node->x.DistanceSquared(
1363 &baseline->second.second->second->node->x)))
1364 innerpoint++;
1365 tmp = checker->second->node->x.DistanceSquared(
1366 &baseline->second.first->second->node->x);
1367 if ((tmp < baseline->second.first->second->node->x.DistanceSquared(
1368 &A->second->node->x)) && (tmp
1369 < baseline->second.first->second->node->x.DistanceSquared(
1370 &baseline->second.second->second->node->x)))
1371 innerpoint++;
1372 tmp = checker->second->node->x.DistanceSquared(
1373 &baseline->second.second->second->node->x);
1374 if ((tmp < baseline->second.second->second->node->x.DistanceSquared(
1375 &baseline->second.first->second->node->x)) && (tmp
1376 < baseline->second.second->second->node->x.DistanceSquared(
1377 &A->second->node->x)))
1378 innerpoint++;
1379 // 4e. If so, break 4. loop and continue with next candidate in 1. loop
1380 if (innerpoint == 3)
1381 break;
1382 }
1383 // 5. come this far, all on same side? Then break 1. loop and construct triangle
1384 if (checker == PointsOnBoundary.end())
1385 {
1386 *out << "Looks like we have a candidate!" << endl;
1387 break;
1388 }
1389 }
1390 if (baseline != DistanceMMap.end())
1391 {
1392 BPS[0] = baseline->second.first->second;
1393 BPS[1] = baseline->second.second->second;
1394 BLS[0] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1395 BPS[0] = A->second;
1396 BPS[1] = baseline->second.second->second;
1397 BLS[1] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1398 BPS[0] = baseline->second.first->second;
1399 BPS[1] = A->second;
1400 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1401
1402 // 4b3. insert created triangle
1403 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1404 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1405 TrianglesOnBoundaryCount++;
1406 for (int i = 0; i < NDIM; i++)
1407 {
1408 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BTS->lines[i]));
1409 LinesOnBoundaryCount++;
1410 }
1411
1412 *out << Verbose(1) << "Starting triangle is " << *BTS << "." << endl;
1413 }
1414 else
1415 {
1416 *out << Verbose(1) << "No starting triangle found." << endl;
1417 exit(255);
1418 }
1419}
1420;
1421
1422/** Tesselates the convex envelope of a cluster from a single starting triangle.
1423 * The starting triangle is made out of three baselines. Each line in the final tesselated cluster may belong to at most
1424 * 2 triangles. Hence, we go through all current lines:
1425 * -# if the lines contains to only one triangle
1426 * -# We search all points in the boundary
1427 * -# if the triangle is in forward direction of the baseline (at most 90 degrees angle between vector orthogonal to
1428 * baseline in triangle plane pointing out of the triangle and normal vector of new triangle)
1429 * -# if the triangle with the baseline and the current point has the smallest of angles (comparison between normal vectors)
1430 * -# then we have a new triangle, whose baselines we again add (or increase their TriangleCount)
1431 * \param *out output stream for debugging
1432 * \param *configuration for IsAngstroem
1433 * \param *mol the cluster as a molecule structure
1434 */
1435void Tesselation::TesselateOnBoundary(ofstream *out, molecule *mol)
1436{
1437 bool flag;
1438 PointMap::iterator winner;
1439 class BoundaryPointSet *peak = NULL;
1440 double SmallestAngle, TempAngle;
1441 Vector NormalVector, VirtualNormalVector, CenterVector, TempVector, helper, PropagationVector, *MolCenter = NULL;
1442 LineMap::iterator LineChecker[2];
1443
1444 MolCenter = mol->DetermineCenterOfAll(out);
1445 // create a first tesselation with the given BoundaryPoints
1446 do {
1447 flag = false;
1448 for (LineMap::iterator baseline = LinesOnBoundary.begin(); baseline != LinesOnBoundary.end(); baseline++)
1449 if (baseline->second->TrianglesCount == 1) {
1450 // 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)
1451 SmallestAngle = M_PI;
1452
1453 // get peak point with respect to this base line's only triangle
1454 BTS = baseline->second->triangles.begin()->second; // there is only one triangle so far
1455 *out << Verbose(2) << "Current baseline is between " << *(baseline->second) << "." << endl;
1456 for (int i = 0; i < 3; i++)
1457 if ((BTS->endpoints[i] != baseline->second->endpoints[0]) && (BTS->endpoints[i] != baseline->second->endpoints[1]))
1458 peak = BTS->endpoints[i];
1459 *out << Verbose(3) << " and has peak " << *peak << "." << endl;
1460
1461 // prepare some auxiliary vectors
1462 Vector BaseLineCenter, BaseLine;
1463 BaseLineCenter.CopyVector(&baseline->second->endpoints[0]->node->x);
1464 BaseLineCenter.AddVector(&baseline->second->endpoints[1]->node->x);
1465 BaseLineCenter.Scale(1. / 2.); // points now to center of base line
1466 BaseLine.CopyVector(&baseline->second->endpoints[0]->node->x);
1467 BaseLine.SubtractVector(&baseline->second->endpoints[1]->node->x);
1468
1469 // offset to center of triangle
1470 CenterVector.Zero();
1471 for (int i = 0; i < 3; i++)
1472 CenterVector.AddVector(&BTS->endpoints[i]->node->x);
1473 CenterVector.Scale(1. / 3.);
1474 *out << Verbose(4) << "CenterVector of base triangle is " << CenterVector << endl;
1475
1476 // normal vector of triangle
1477 NormalVector.CopyVector(MolCenter);
1478 NormalVector.SubtractVector(&CenterVector);
1479 BTS->GetNormalVector(NormalVector);
1480 NormalVector.CopyVector(&BTS->NormalVector);
1481 *out << Verbose(4) << "NormalVector of base triangle is " << NormalVector << endl;
1482
1483 // vector in propagation direction (out of triangle)
1484 // project center vector onto triangle plane (points from intersection plane-NormalVector to plane-CenterVector intersection)
1485 PropagationVector.MakeNormalVector(&BaseLine, &NormalVector);
1486 TempVector.CopyVector(&CenterVector);
1487 TempVector.SubtractVector(&baseline->second->endpoints[0]->node->x); // TempVector is vector on triangle plane pointing from one baseline egde towards center!
1488 //*out << Verbose(2) << "Projection of propagation onto temp: " << PropagationVector.Projection(&TempVector) << "." << endl;
1489 if (PropagationVector.Projection(&TempVector) > 0) // make sure normal propagation vector points outward from baseline
1490 PropagationVector.Scale(-1.);
1491 *out << Verbose(4) << "PropagationVector of base triangle is " << PropagationVector << endl;
1492 winner = PointsOnBoundary.end();
1493
1494 // loop over all points and calculate angle between normal vector of new and present triangle
1495 for (PointMap::iterator target = PointsOnBoundary.begin(); target != PointsOnBoundary.end(); target++) {
1496 if ((target->second != baseline->second->endpoints[0]) && (target->second != baseline->second->endpoints[1])) { // don't take the same endpoints
1497 *out << Verbose(3) << "Target point is " << *(target->second) << ":" << endl;
1498
1499 // first check direction, so that triangles don't intersect
1500 VirtualNormalVector.CopyVector(&target->second->node->x);
1501 VirtualNormalVector.SubtractVector(&BaseLineCenter); // points from center of base line to target
1502 VirtualNormalVector.ProjectOntoPlane(&NormalVector);
1503 TempAngle = VirtualNormalVector.Angle(&PropagationVector);
1504 *out << Verbose(4) << "VirtualNormalVector is " << VirtualNormalVector << " and PropagationVector is " << PropagationVector << "." << endl;
1505 if (TempAngle > (M_PI/2.)) { // no bends bigger than Pi/2 (90 degrees)
1506 *out << Verbose(4) << "Angle on triangle plane between propagation direction and base line to " << *(target->second) << " is " << TempAngle << ", bad direction!" << endl;
1507 continue;
1508 } else
1509 *out << Verbose(4) << "Angle on triangle plane between propagation direction and base line to " << *(target->second) << " is " << TempAngle << ", good direction!" << endl;
1510
1511 // check first and second endpoint (if any connecting line goes to target has at least not more than 1 triangle)
1512 LineChecker[0] = baseline->second->endpoints[0]->lines.find(target->first);
1513 LineChecker[1] = baseline->second->endpoints[1]->lines.find(target->first);
1514 if (((LineChecker[0] != baseline->second->endpoints[0]->lines.end()) && (LineChecker[0]->second->TrianglesCount == 2))) {
1515 *out << Verbose(4) << *(baseline->second->endpoints[0]) << " has line " << *(LineChecker[0]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[0]->second->TrianglesCount << " triangles." << endl;
1516 continue;
1517 }
1518 if (((LineChecker[1] != baseline->second->endpoints[1]->lines.end()) && (LineChecker[1]->second->TrianglesCount == 2))) {
1519 *out << Verbose(4) << *(baseline->second->endpoints[1]) << " has line " << *(LineChecker[1]->second) << " to " << *(target->second) << " as endpoint with " << LineChecker[1]->second->TrianglesCount << " triangles." << endl;
1520 continue;
1521 }
1522
1523 // check whether the envisaged triangle does not already exist (if both lines exist and have same endpoint)
1524 if ((((LineChecker[0] != baseline->second->endpoints[0]->lines.end()) && (LineChecker[1] != baseline->second->endpoints[1]->lines.end()) && (GetCommonEndpoint(LineChecker[0]->second, LineChecker[1]->second) == peak)))) {
1525 *out << Verbose(4) << "Current target is peak!" << endl;
1526 continue;
1527 }
1528
1529 // check for linear dependence
1530 TempVector.CopyVector(&baseline->second->endpoints[0]->node->x);
1531 TempVector.SubtractVector(&target->second->node->x);
1532 helper.CopyVector(&baseline->second->endpoints[1]->node->x);
1533 helper.SubtractVector(&target->second->node->x);
1534 helper.ProjectOntoPlane(&TempVector);
1535 if (fabs(helper.NormSquared()) < MYEPSILON) {
1536 *out << Verbose(4) << "Chosen set of vectors is linear dependent." << endl;
1537 continue;
1538 }
1539
1540 // in case NOT both were found, create virtually this triangle, get its normal vector, calculate angle
1541 flag = true;
1542 VirtualNormalVector.MakeNormalVector(&baseline->second->endpoints[0]->node->x, &baseline->second->endpoints[1]->node->x, &target->second->node->x);
1543 TempVector.CopyVector(&baseline->second->endpoints[0]->node->x);
1544 TempVector.AddVector(&baseline->second->endpoints[1]->node->x);
1545 TempVector.AddVector(&target->second->node->x);
1546 TempVector.Scale(1./3.);
1547 TempVector.SubtractVector(MolCenter);
1548 // make it always point outward
1549 if (VirtualNormalVector.Projection(&TempVector) < 0)
1550 VirtualNormalVector.Scale(-1.);
1551 // calculate angle
1552 TempAngle = NormalVector.Angle(&VirtualNormalVector);
1553 *out << Verbose(4) << "NormalVector is " << VirtualNormalVector << " and the angle is " << TempAngle << "." << endl;
1554 if ((SmallestAngle - TempAngle) > MYEPSILON) { // set to new possible winner
1555 SmallestAngle = TempAngle;
1556 winner = target;
1557 *out << Verbose(4) << "New winner " << *winner->second->node << " due to smaller angle between normal vectors." << endl;
1558 } else if (fabs(SmallestAngle - TempAngle) < MYEPSILON) { // check the angle to propagation, both possible targets are in one plane! (their normals have same angle)
1559 // hence, check the angles to some normal direction from our base line but in this common plane of both targets...
1560 helper.CopyVector(&target->second->node->x);
1561 helper.SubtractVector(&BaseLineCenter);
1562 helper.ProjectOntoPlane(&BaseLine);
1563 // ...the one with the smaller angle is the better candidate
1564 TempVector.CopyVector(&target->second->node->x);
1565 TempVector.SubtractVector(&BaseLineCenter);
1566 TempVector.ProjectOntoPlane(&VirtualNormalVector);
1567 TempAngle = TempVector.Angle(&helper);
1568 TempVector.CopyVector(&winner->second->node->x);
1569 TempVector.SubtractVector(&BaseLineCenter);
1570 TempVector.ProjectOntoPlane(&VirtualNormalVector);
1571 if (TempAngle < TempVector.Angle(&helper)) {
1572 TempAngle = NormalVector.Angle(&VirtualNormalVector);
1573 SmallestAngle = TempAngle;
1574 winner = target;
1575 *out << Verbose(4) << "New winner " << *winner->second->node << " due to smaller angle " << TempAngle << " to propagation direction." << endl;
1576 } else
1577 *out << Verbose(4) << "Keeping old winner " << *winner->second->node << " due to smaller angle to propagation direction." << endl;
1578 } else
1579 *out << Verbose(4) << "Keeping old winner " << *winner->second->node << " due to smaller angle between normal vectors." << endl;
1580 }
1581 } // end of loop over all boundary points
1582
1583 // 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
1584 if (winner != PointsOnBoundary.end()) {
1585 *out << Verbose(2) << "Winning target point is " << *(winner->second) << " with angle " << SmallestAngle << "." << endl;
1586 // create the lins of not yet present
1587 BLS[0] = baseline->second;
1588 // 5c. add lines to the line set if those were new (not yet part of a triangle), delete lines that belong to two triangles)
1589 LineChecker[0] = baseline->second->endpoints[0]->lines.find(winner->first);
1590 LineChecker[1] = baseline->second->endpoints[1]->lines.find(winner->first);
1591 if (LineChecker[0] == baseline->second->endpoints[0]->lines.end()) { // create
1592 BPS[0] = baseline->second->endpoints[0];
1593 BPS[1] = winner->second;
1594 BLS[1] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1595 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[1]));
1596 LinesOnBoundaryCount++;
1597 } else
1598 BLS[1] = LineChecker[0]->second;
1599 if (LineChecker[1] == baseline->second->endpoints[1]->lines.end()) { // create
1600 BPS[0] = baseline->second->endpoints[1];
1601 BPS[1] = winner->second;
1602 BLS[2] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1603 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[2]));
1604 LinesOnBoundaryCount++;
1605 } else
1606 BLS[2] = LineChecker[1]->second;
1607 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1608 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1609 TrianglesOnBoundaryCount++;
1610 } else {
1611 *out << Verbose(1) << "I could not determine a winner for this baseline " << *(baseline->second) << "." << endl;
1612 }
1613
1614 // 5d. If the set of lines is not yet empty, go to 5. and continue
1615 } else
1616 *out << Verbose(2) << "Baseline candidate " << *(baseline->second) << " has a triangle count of " << baseline->second->TrianglesCount << "." << endl;
1617 } while (flag);
1618
1619 // exit
1620 delete(MolCenter);
1621};
1622
1623/** Inserts all atoms outside of the tesselated surface into it by adding new triangles.
1624 * \param *out output stream for debugging
1625 * \param *mol molecule structure with atoms
1626 * \return true - all straddling points insert, false - something went wrong
1627 */
1628bool Tesselation::InsertStraddlingPoints(ofstream *out, molecule *mol)
1629{
1630 Vector Intersection;
1631 atom *Walker = mol->start;
1632 Vector *MolCenter = mol->DetermineCenterOfAll(out);
1633 while (Walker->next != mol->end) { // we only have to go once through all points, as boundary can become only bigger
1634 // get the next triangle
1635 BTS = FindClosestTriangleToPoint(out, &Walker->x);
1636 if (BTS == NULL) {
1637 *out << Verbose(1) << "ERROR: No triangle closest to " << Walker << " was found." << endl;
1638 return false;
1639 }
1640 // get the intersection point
1641 if (BTS->GetIntersectionInsideTriangle(out, MolCenter, &Walker->x, &Intersection)) {
1642 // we have the intersection, check whether in- or outside of boundary
1643 if ((MolCenter->DistanceSquared(&Walker->x) - MolCenter->DistanceSquared(&Intersection)) < -MYEPSILON) {
1644 // inside, next!
1645 *out << Verbose(4) << Walker << " is inside wrt triangle " << BTS << "." << endl;
1646 } else {
1647 // outside!
1648 *out << Verbose(3) << Walker << " is outside wrt triangle " << BTS << "." << endl;
1649 class BoundaryLineSet *OldLines[3], *NewLines[3];
1650 class BoundaryPointSet *OldPoints[3], *NewPoint;
1651 // store the three old lines and old points
1652 for (int i=0;i<3;i++) {
1653 OldLines[i] = BTS->lines[i];
1654 OldPoints[i] = BTS->endpoints[i];
1655 }
1656 // add Walker to boundary points
1657 AddPoint(Walker);
1658 if (BPS[0] == NULL)
1659 NewPoint = BPS[0];
1660 else
1661 continue;
1662 // remove triangle
1663 TrianglesOnBoundary.erase(BTS->Nr);
1664 // create three new boundary lines
1665 for (int i=0;i<3;i++) {
1666 BPS[0] = NewPoint;
1667 BPS[1] = OldPoints[i];
1668 NewLines[i] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount);
1669 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, NewLines[i])); // no need for check for unique insertion as BPS[0] is definitely a new one
1670 LinesOnBoundaryCount++;
1671 }
1672 // create three new triangle with new point
1673 for (int i=0;i<3;i++) { // find all baselines
1674 BLS[0] = OldLines[i];
1675 int n = 1;
1676 for (int j=0;j<3;j++) {
1677 if (NewLines[j]->IsConnectedTo(BLS[0])) {
1678 if (n>2) {
1679 *out << Verbose(1) << "ERROR: " << BLS[0] << " connects to all of the new lines?!" << endl;
1680 return false;
1681 } else
1682 BLS[n++] = NewLines[j];
1683 }
1684 }
1685 // create the triangle
1686 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
1687 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1688 TrianglesOnBoundaryCount++;
1689 }
1690 }
1691 } else { // something is wrong with FindClosestTriangleToPoint!
1692 *out << Verbose(1) << "ERROR: The closest triangle did not produce an intersection!" << endl;
1693 return false;
1694 }
1695 }
1696
1697 // exit
1698 delete(MolCenter);
1699 return true;
1700};
1701
1702/** Goes over all baselines and checks whether adjacent triangles and convex to each other.
1703 * \param *out output stream for debugging
1704 * \return true - all baselines were corrected, false - there are still concave pieces
1705 */
1706bool Tesselation::CorrectConcaveBaselines(ofstream *out)
1707{
1708 class BoundaryTriangleSet *triangle[2];
1709 class BoundaryLineSet *OldLines[4], *NewLine;
1710 class BoundaryPointSet *OldPoints[2];
1711 Vector BaseLineNormal;
1712 class BoundaryLineSet *Base = NULL;
1713 int OldTriangles[2], OldBaseLine;
1714 int i;
1715 for (LineMap::iterator baseline = LinesOnBoundary.begin(); baseline != LinesOnBoundary.end(); baseline++) {
1716 Base = baseline->second;
1717
1718 // check convexity
1719 if (Base->CheckConvexityCriterion(out)) { // triangles are convex
1720 *out << Verbose(3) << Base << " has two convex triangles." << endl;
1721 } else { // not convex!
1722 // get the two triangles
1723 i=0;
1724 for(TriangleMap::iterator runner = Base->triangles.begin(); runner != Base->triangles.end(); runner++)
1725 triangle[i++] = runner->second;
1726 // gather four endpoints and four lines
1727 for (int j=0;j<4;j++)
1728 OldLines[j] = NULL;
1729 for (int j=0;j<2;j++)
1730 OldPoints[j] = NULL;
1731 i=0;
1732 for (int m=0;m<2;m++) { // go over both triangles
1733 for (int j=0;j<3;j++) { // all of their endpoints and baselines
1734 if (triangle[m]->lines[j] != Base) // pick not the central baseline
1735 OldLines[i++] = triangle[m]->lines[j];
1736 if (!Base->ContainsBoundaryPoint(triangle[m]->endpoints[j])) // and neither of its endpoints
1737 OldPoints[m] = triangle[m]->endpoints[j];
1738 }
1739 }
1740 if (i<4) {
1741 *out << Verbose(1) << "ERROR: We have not gathered enough baselines!" << endl;
1742 return false;
1743 }
1744 for (int j=0;j<4;j++)
1745 if (OldLines[j] == NULL) {
1746 *out << Verbose(1) << "ERROR: We have not gathered enough baselines!" << endl;
1747 return false;
1748 }
1749 for (int j=0;j<2;j++)
1750 if (OldPoints[j] == NULL) {
1751 *out << Verbose(1) << "ERROR: We have not gathered enough endpoints!" << endl;
1752 return false;
1753 }
1754
1755 // remove triangles
1756 for (int j=0;j<2;j++) {
1757 OldTriangles[j] = triangle[j]->Nr;
1758 TrianglesOnBoundary.erase(OldTriangles[j]);
1759 triangle[j] = NULL;
1760 }
1761
1762 // remove baseline
1763 OldBaseLine = Base->Nr;
1764 LinesOnBoundary.erase(OldBaseLine);
1765 Base = NULL;
1766
1767 // construct new baseline (with same number as old one)
1768 BPS[0] = OldPoints[0];
1769 BPS[1] = OldPoints[1];
1770 NewLine = new class BoundaryLineSet(BPS, OldBaseLine);
1771 LinesOnBoundary.insert(LinePair(OldBaseLine, NewLine)); // no need for check for unique insertion as NewLine is definitely a new one
1772
1773 // construct new triangles with flipped baseline
1774 i=-1;
1775 if (BLS[0]->IsConnectedTo(OldLines[2]))
1776 i=2;
1777 if (BLS[0]->IsConnectedTo(OldLines[2]))
1778 i=3;
1779 if (i!=-1) {
1780 BLS[0] = OldLines[0];
1781 BLS[1] = OldLines[i];
1782 BLS[2] = NewLine;
1783 BTS = new class BoundaryTriangleSet(BLS, OldTriangles[0]);
1784 TrianglesOnBoundary.insert(TrianglePair(OldTriangles[0], BTS));
1785
1786 BLS[0] = (i==2 ? OldLines[3] : OldLines[2]);
1787 BLS[1] = OldLines[1];
1788 BLS[2] = NewLine;
1789 BTS = new class BoundaryTriangleSet(BLS, OldTriangles[1]);
1790 TrianglesOnBoundary.insert(TrianglePair(OldTriangles[1], BTS));
1791 } else {
1792 *out << Verbose(1) << "The four old lines do not connect, something's utterly wrong here!" << endl;
1793 return false;
1794 }
1795 }
1796 }
1797 return true;
1798};
1799
1800
1801/** States whether point is in- or outside of a tesselated surface.
1802 * \param *pointer point to be checked
1803 * \return true - is inside, false - is outside
1804 */
1805bool Tesselation::IsInside(Vector *pointer)
1806{
1807
1808 // hier kommt dann Saskias Routine hin...
1809
1810 return true;
1811};
1812
1813/** Finds the closest triangle to a given point.
1814 * \param *out output stream for debugging
1815 * \param *x second endpoint of line
1816 * \return pointer triangle that is closest, NULL if none was found
1817 */
1818class BoundaryTriangleSet * Tesselation::FindClosestTriangleToPoint(ofstream *out, Vector *x)
1819{
1820 class BoundaryTriangleSet *triangle = NULL;
1821
1822 // hier kommt dann Saskias Routine hin...
1823
1824 return triangle;
1825};
1826
1827/** Adds an atom to the tesselation::PointsOnBoundary list.
1828 * \param *Walker atom to add
1829 */
1830void
1831Tesselation::AddPoint(atom *Walker)
1832{
1833 PointTestPair InsertUnique;
1834 BPS[0] = new class BoundaryPointSet(Walker);
1835 InsertUnique = PointsOnBoundary.insert(PointPair(Walker->nr, BPS[0]));
1836 if (InsertUnique.second) // if new point was not present before, increase counter
1837 PointsOnBoundaryCount++;
1838 else {
1839 delete(BPS[0]);
1840 BPS[0] = NULL;
1841 }
1842}
1843;
1844
1845/** Adds point to Tesselation::PointsOnBoundary if not yet present.
1846 * Tesselation::TPS is set to either this new BoundaryPointSet or to the existing one of not unique.
1847 * @param Candidate point to add
1848 * @param n index for this point in Tesselation::TPS array
1849 */
1850void
1851Tesselation::AddTrianglePoint(atom* Candidate, int n)
1852{
1853 PointTestPair InsertUnique;
1854 TPS[n] = new class BoundaryPointSet(Candidate);
1855 InsertUnique = PointsOnBoundary.insert(PointPair(Candidate->nr, TPS[n]));
1856 if (InsertUnique.second) { // if new point was not present before, increase counter
1857 PointsOnBoundaryCount++;
1858 } else {
1859 delete TPS[n];
1860 cout << Verbose(3) << "Atom " << *((InsertUnique.first)->second->node) << " is already present in PointsOnBoundary." << endl;
1861 TPS[n] = (InsertUnique.first)->second;
1862 }
1863}
1864;
1865
1866/** Function tries to add line from current Points in BPS to BoundaryLineSet.
1867 * If successful it raises the line count and inserts the new line into the BLS,
1868 * if unsuccessful, it writes the line which had been present into the BLS, deleting the new constructed one.
1869 * @param *a first endpoint
1870 * @param *b second endpoint
1871 * @param n index of Tesselation::BLS giving the line with both endpoints
1872 */
1873void Tesselation::AddTriangleLine(class BoundaryPointSet *a, class BoundaryPointSet *b, int n) {
1874 bool insertNewLine = true;
1875
1876 if (a->lines.find(b->node->nr) != a->lines.end()) {
1877 LineMap::iterator FindLine;
1878 pair<LineMap::iterator,LineMap::iterator> FindPair;
1879 FindPair = a->lines.equal_range(b->node->nr);
1880
1881 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
1882 // If there is a line with less than two attached triangles, we don't need a new line.
1883 if (FindLine->second->TrianglesCount < 2) {
1884 insertNewLine = false;
1885 cout << Verbose(3) << "Using existing line " << *FindLine->second << endl;
1886
1887 BPS[0] = FindLine->second->endpoints[0];
1888 BPS[1] = FindLine->second->endpoints[1];
1889 BLS[n] = FindLine->second;
1890
1891 break;
1892 }
1893 }
1894 }
1895
1896 if (insertNewLine) {
1897 AlwaysAddTriangleLine(a, b, n);
1898 }
1899}
1900;
1901
1902/**
1903 * Adds lines from each of the current points in the BPS to BoundaryLineSet.
1904 * Raises the line count and inserts the new line into the BLS.
1905 *
1906 * @param *a first endpoint
1907 * @param *b second endpoint
1908 * @param n index of Tesselation::BLS giving the line with both endpoints
1909 */
1910void Tesselation::AlwaysAddTriangleLine(class BoundaryPointSet *a, class BoundaryPointSet *b, int n)
1911{
1912 cout << Verbose(3) << "Adding line between " << *(a->node) << " and " << *(b->node) << "." << endl;
1913 BPS[0] = a;
1914 BPS[1] = b;
1915 BLS[n] = new class BoundaryLineSet(BPS, LinesOnBoundaryCount); // this also adds the line to the local maps
1916 // add line to global map
1917 LinesOnBoundary.insert(LinePair(LinesOnBoundaryCount, BLS[n]));
1918 // increase counter
1919 LinesOnBoundaryCount++;
1920};
1921
1922/** Function tries to add Triangle just created to Triangle and remarks if already existent (Failure of algorithm).
1923 * Furthermore it adds the triangle to all of its lines, in order to recognize those which are saturated later.
1924 */
1925void
1926Tesselation::AddTriangle()
1927{
1928 cout << Verbose(1) << "Adding triangle to global TrianglesOnBoundary map." << endl;
1929
1930 // add triangle to global map
1931 TrianglesOnBoundary.insert(TrianglePair(TrianglesOnBoundaryCount, BTS));
1932 TrianglesOnBoundaryCount++;
1933
1934 // NOTE: add triangle to local maps is done in constructor of BoundaryTriangleSet
1935}
1936;
1937
1938
1939double det_get(gsl_matrix *A, int inPlace) {
1940 /*
1941 inPlace = 1 => A is replaced with the LU decomposed copy.
1942 inPlace = 0 => A is retained, and a copy is used for LU.
1943 */
1944
1945 double det;
1946 int signum;
1947 gsl_permutation *p = gsl_permutation_alloc(A->size1);
1948 gsl_matrix *tmpA;
1949
1950 if (inPlace)
1951 tmpA = A;
1952 else {
1953 gsl_matrix *tmpA = gsl_matrix_alloc(A->size1, A->size2);
1954 gsl_matrix_memcpy(tmpA , A);
1955 }
1956
1957
1958 gsl_linalg_LU_decomp(tmpA , p , &signum);
1959 det = gsl_linalg_LU_det(tmpA , signum);
1960 gsl_permutation_free(p);
1961 if (! inPlace)
1962 gsl_matrix_free(tmpA);
1963
1964 return det;
1965};
1966
1967void get_sphere(Vector *center, Vector &a, Vector &b, Vector &c, double RADIUS)
1968{
1969 gsl_matrix *A = gsl_matrix_calloc(3,3);
1970 double m11, m12, m13, m14;
1971
1972 for(int i=0;i<3;i++) {
1973 gsl_matrix_set(A, i, 0, a.x[i]);
1974 gsl_matrix_set(A, i, 1, b.x[i]);
1975 gsl_matrix_set(A, i, 2, c.x[i]);
1976 }
1977 m11 = det_get(A, 1);
1978
1979 for(int i=0;i<3;i++) {
1980 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
1981 gsl_matrix_set(A, i, 1, b.x[i]);
1982 gsl_matrix_set(A, i, 2, c.x[i]);
1983 }
1984 m12 = det_get(A, 1);
1985
1986 for(int i=0;i<3;i++) {
1987 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
1988 gsl_matrix_set(A, i, 1, a.x[i]);
1989 gsl_matrix_set(A, i, 2, c.x[i]);
1990 }
1991 m13 = det_get(A, 1);
1992
1993 for(int i=0;i<3;i++) {
1994 gsl_matrix_set(A, i, 0, a.x[i]*a.x[i] + b.x[i]*b.x[i] + c.x[i]*c.x[i]);
1995 gsl_matrix_set(A, i, 1, a.x[i]);
1996 gsl_matrix_set(A, i, 2, b.x[i]);
1997 }
1998 m14 = det_get(A, 1);
1999
2000 if (fabs(m11) < MYEPSILON)
2001 cerr << "ERROR: three points are colinear." << endl;
2002
2003 center->x[0] = 0.5 * m12/ m11;
2004 center->x[1] = -0.5 * m13/ m11;
2005 center->x[2] = 0.5 * m14/ m11;
2006
2007 if (fabs(a.Distance(center) - RADIUS) > MYEPSILON)
2008 cerr << "ERROR: The given center is further way by " << fabs(a.Distance(center) - RADIUS) << " from a than RADIUS." << endl;
2009
2010 gsl_matrix_free(A);
2011};
2012
2013
2014
2015/**
2016 * Function returns center of sphere with RADIUS, which rests on points a, b, c
2017 * @param Center this vector will be used for return
2018 * @param a vector first point of triangle
2019 * @param b vector second point of triangle
2020 * @param c vector third point of triangle
2021 * @param *Umkreismittelpunkt new cneter point of circumference
2022 * @param Direction vector indicates up/down
2023 * @param AlternativeDirection vecotr, needed in case the triangles have 90 deg angle
2024 * @param Halfplaneindicator double indicates whether Direction is up or down
2025 * @param AlternativeIndicator doube indicates in case of orthogonal triangles which direction of AlternativeDirection is suitable
2026 * @param alpha double angle at a
2027 * @param beta double, angle at b
2028 * @param gamma, double, angle at c
2029 * @param Radius, double
2030 * @param Umkreisradius double radius of circumscribing circle
2031 */
2032void Get_center_of_sphere(Vector* Center, Vector a, Vector b, Vector c, Vector *NewUmkreismittelpunkt, Vector* Direction, Vector* AlternativeDirection,
2033 double HalfplaneIndicator, double AlternativeIndicator, double alpha, double beta, double gamma, double RADIUS, double Umkreisradius)
2034{
2035 Vector TempNormal, helper;
2036 double Restradius;
2037 Vector OtherCenter;
2038 cout << Verbose(3) << "Begin of Get_center_of_sphere.\n";
2039 Center->Zero();
2040 helper.CopyVector(&a);
2041 helper.Scale(sin(2.*alpha));
2042 Center->AddVector(&helper);
2043 helper.CopyVector(&b);
2044 helper.Scale(sin(2.*beta));
2045 Center->AddVector(&helper);
2046 helper.CopyVector(&c);
2047 helper.Scale(sin(2.*gamma));
2048 Center->AddVector(&helper);
2049 //*Center = a * sin(2.*alpha) + b * sin(2.*beta) + c * sin(2.*gamma) ;
2050 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
2051 NewUmkreismittelpunkt->CopyVector(Center);
2052 cout << Verbose(4) << "Center of new circumference is " << *NewUmkreismittelpunkt << ".\n";
2053 // Here we calculated center of circumscribing circle, using barycentric coordinates
2054 cout << Verbose(4) << "Center of circumference is " << *Center << " in direction " << *Direction << ".\n";
2055
2056 TempNormal.CopyVector(&a);
2057 TempNormal.SubtractVector(&b);
2058 helper.CopyVector(&a);
2059 helper.SubtractVector(&c);
2060 TempNormal.VectorProduct(&helper);
2061 if (fabs(HalfplaneIndicator) < MYEPSILON)
2062 {
2063 if ((TempNormal.ScalarProduct(AlternativeDirection) <0 and AlternativeIndicator >0) or (TempNormal.ScalarProduct(AlternativeDirection) >0 and AlternativeIndicator <0))
2064 {
2065 TempNormal.Scale(-1);
2066 }
2067 }
2068 else
2069 {
2070 if (TempNormal.ScalarProduct(Direction)<0 && HalfplaneIndicator >0 || TempNormal.ScalarProduct(Direction)>0 && HalfplaneIndicator<0)
2071 {
2072 TempNormal.Scale(-1);
2073 }
2074 }
2075
2076 TempNormal.Normalize();
2077 Restradius = sqrt(RADIUS*RADIUS - Umkreisradius*Umkreisradius);
2078 cout << Verbose(4) << "Height of center of circumference to center of sphere is " << Restradius << ".\n";
2079 TempNormal.Scale(Restradius);
2080 cout << Verbose(4) << "Shift vector to sphere of circumference is " << TempNormal << ".\n";
2081
2082 Center->AddVector(&TempNormal);
2083 cout << Verbose(0) << "Center of sphere of circumference is " << *Center << ".\n";
2084 get_sphere(&OtherCenter, a, b, c, RADIUS);
2085 cout << Verbose(0) << "OtherCenter of sphere of circumference is " << OtherCenter << ".\n";
2086 cout << Verbose(3) << "End of Get_center_of_sphere.\n";
2087};
2088
2089
2090/** Constructs the center of the circumcircle defined by three points \a *a, \a *b and \a *c.
2091 * \param *Center new center on return
2092 * \param *a first point
2093 * \param *b second point
2094 * \param *c third point
2095 */
2096void GetCenterofCircumcircle(Vector *Center, Vector *a, Vector *b, Vector *c)
2097{
2098 Vector helper;
2099 double alpha, beta, gamma;
2100 Vector SideA, SideB, SideC;
2101 SideA.CopyVector(b);
2102 SideA.SubtractVector(c);
2103 SideB.CopyVector(c);
2104 SideB.SubtractVector(a);
2105 SideC.CopyVector(a);
2106 SideC.SubtractVector(b);
2107 alpha = M_PI - SideB.Angle(&SideC);
2108 beta = M_PI - SideC.Angle(&SideA);
2109 gamma = M_PI - SideA.Angle(&SideB);
2110 //cout << Verbose(3) << "INFO: alpha = " << alpha/M_PI*180. << ", beta = " << beta/M_PI*180. << ", gamma = " << gamma/M_PI*180. << "." << endl;
2111 if (fabs(M_PI - alpha - beta - gamma) > HULLEPSILON)
2112 cerr << "GetCenterofCircumcircle: Sum of angles " << (alpha+beta+gamma)/M_PI*180. << " > 180 degrees by " << fabs(M_PI - alpha - beta - gamma)/M_PI*180. << "!" << endl;
2113
2114 Center->Zero();
2115 helper.CopyVector(a);
2116 helper.Scale(sin(2.*alpha));
2117 Center->AddVector(&helper);
2118 helper.CopyVector(b);
2119 helper.Scale(sin(2.*beta));
2120 Center->AddVector(&helper);
2121 helper.CopyVector(c);
2122 helper.Scale(sin(2.*gamma));
2123 Center->AddVector(&helper);
2124 Center->Scale(1./(sin(2.*alpha) + sin(2.*beta) + sin(2.*gamma)));
2125};
2126
2127/** Returns the parameter "path length" for a given \a NewSphereCenter relative to \a OldSphereCenter on a circle on the plane \a CirclePlaneNormal with center \a CircleCenter and radius \a CircleRadius.
2128 * Test whether the \a NewSphereCenter is really on the given plane and in distance \a CircleRadius from \a CircleCenter.
2129 * It calculates the angle, making it unique on [0,2.*M_PI) by comparing to SearchDirection.
2130 * Also the new center is invalid if it the same as the old one and does not lie right above (\a NormalVector) the base line (\a CircleCenter).
2131 * \param CircleCenter Center of the parameter circle
2132 * \param CirclePlaneNormal normal vector to plane of the parameter circle
2133 * \param CircleRadius radius of the parameter circle
2134 * \param NewSphereCenter new center of a circumcircle
2135 * \param OldSphereCenter old center of a circumcircle, defining the zero "path length" on the parameter circle
2136 * \param NormalVector normal vector
2137 * \param SearchDirection search direction to make angle unique on return.
2138 * \return Angle between \a NewSphereCenter and \a OldSphereCenter relative to \a CircleCenter, 2.*M_PI if one test fails
2139 */
2140double GetPathLengthonCircumCircle(Vector &CircleCenter, Vector &CirclePlaneNormal, double CircleRadius, Vector &NewSphereCenter, Vector &OldSphereCenter, Vector &NormalVector, Vector &SearchDirection)
2141{
2142 Vector helper;
2143 double radius, alpha;
2144
2145 helper.CopyVector(&NewSphereCenter);
2146 // test whether new center is on the parameter circle's plane
2147 if (fabs(helper.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
2148 cerr << "ERROR: Something's very wrong here: NewSphereCenter is not on the band's plane as desired by " <<fabs(helper.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
2149 helper.ProjectOntoPlane(&CirclePlaneNormal);
2150 }
2151 radius = helper.ScalarProduct(&helper);
2152 // test whether the new center vector has length of CircleRadius
2153 if (fabs(radius - CircleRadius) > HULLEPSILON)
2154 cerr << Verbose(1) << "ERROR: The projected center of the new sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
2155 alpha = helper.Angle(&OldSphereCenter);
2156 // make the angle unique by checking the halfplanes/search direction
2157 if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON) // acos is not unique on [0, 2.*M_PI), hence extra check to decide between two half intervals
2158 alpha = 2.*M_PI - alpha;
2159 //cout << Verbose(2) << "INFO: RelativeNewSphereCenter is " << helper << ", RelativeOldSphereCenter is " << OldSphereCenter << " and resulting angle is " << alpha << "." << endl;
2160 radius = helper.Distance(&OldSphereCenter);
2161 helper.ProjectOntoPlane(&NormalVector);
2162 // check whether new center is somewhat away or at least right over the current baseline to prevent intersecting triangles
2163 if ((radius > HULLEPSILON) || (helper.Norm() < HULLEPSILON)) {
2164 //cout << Verbose(2) << "INFO: Distance between old and new center is " << radius << " and between new center and baseline center is " << helper.Norm() << "." << endl;
2165 return alpha;
2166 } else {
2167 //cout << Verbose(1) << "INFO: NewSphereCenter " << helper << " is too close to OldSphereCenter" << OldSphereCenter << "." << endl;
2168 return 2.*M_PI;
2169 }
2170};
2171
2172
2173/** Checks whether the triangle consisting of the three atoms is already present.
2174 * Searches for the points in Tesselation::PointsOnBoundary and checks their
2175 * lines. If any of the three edges already has two triangles attached, false is
2176 * returned.
2177 * \param *out output stream for debugging
2178 * \param *Candidates endpoints of the triangle candidate
2179 * \return integer 0 if no triangle exists, 1 if one triangle exists, 2 if two
2180 * triangles exist which is the maximum for three points
2181 */
2182int Tesselation::CheckPresenceOfTriangle(ofstream *out, atom *Candidates[3]) {
2183 LineMap::iterator FindLine;
2184 PointMap::iterator FindPoint;
2185 TriangleMap::iterator FindTriangle;
2186 int adjacentTriangleCount = 0;
2187 class BoundaryPointSet *Points[3];
2188
2189 //*out << Verbose(2) << "Begin of CheckPresenceOfTriangle" << endl;
2190 // builds a triangle point set (Points) of the end points
2191 for (int i = 0; i < 3; i++) {
2192 FindPoint = PointsOnBoundary.find(Candidates[i]->nr);
2193 if (FindPoint != PointsOnBoundary.end()) {
2194 Points[i] = FindPoint->second;
2195 } else {
2196 Points[i] = NULL;
2197 }
2198 }
2199
2200 // checks lines between the points in the Points for their adjacent triangles
2201 for (int i = 0; i < 3; i++) {
2202 if (Points[i] != NULL) {
2203 for (int j = i; j < 3; j++) {
2204 if (Points[j] != NULL) {
2205 FindLine = Points[i]->lines.find(Points[j]->node->nr);
2206 if (FindLine != Points[i]->lines.end()) {
2207 for (; FindLine->first == Points[j]->node->nr; FindLine++) {
2208 FindTriangle = FindLine->second->triangles.begin();
2209 for (; FindTriangle != FindLine->second->triangles.end(); FindTriangle++) {
2210 if ((
2211 (FindTriangle->second->endpoints[0] == Points[0])
2212 || (FindTriangle->second->endpoints[0] == Points[1])
2213 || (FindTriangle->second->endpoints[0] == Points[2])
2214 ) && (
2215 (FindTriangle->second->endpoints[1] == Points[0])
2216 || (FindTriangle->second->endpoints[1] == Points[1])
2217 || (FindTriangle->second->endpoints[1] == Points[2])
2218 ) && (
2219 (FindTriangle->second->endpoints[2] == Points[0])
2220 || (FindTriangle->second->endpoints[2] == Points[1])
2221 || (FindTriangle->second->endpoints[2] == Points[2])
2222 )
2223 ) {
2224 adjacentTriangleCount++;
2225 }
2226 }
2227 }
2228 // Only one of the triangle lines must be considered for the triangle count.
2229 *out << Verbose(2) << "Found " << adjacentTriangleCount << " adjacent triangles for the point set." << endl;
2230 return adjacentTriangleCount;
2231
2232 }
2233 }
2234 }
2235 }
2236 }
2237
2238 *out << Verbose(2) << "Found " << adjacentTriangleCount << " adjacent triangles for the point set." << endl;
2239 return adjacentTriangleCount;
2240};
2241
2242/** This recursive function finds a third point, to form a triangle with two given ones.
2243 * Note that this function is for the starting triangle.
2244 * The idea is as follows: A sphere with fixed radius is (almost) uniquely defined in space by three points
2245 * that sit on its boundary. Hence, when two points are given and we look for the (next) third point, then
2246 * the center of the sphere is still fixed up to a single parameter. The band of possible values
2247 * describes a circle in 3D-space. The old center of the sphere for the current base triangle gives
2248 * us the "null" on this circle, the new center of the candidate point will be some way along this
2249 * circle. The shorter the way the better is the candidate. Note that the direction is clearly given
2250 * by the normal vector of the base triangle that always points outwards by construction.
2251 * Hence, we construct a Center of this circle which sits right in the middle of the current base line.
2252 * We construct the normal vector that defines the plane this circle lies in, it is just in the
2253 * direction of the baseline. And finally, we need the radius of the circle, which is given by the rest
2254 * with respect to the length of the baseline and the sphere's fixed \a RADIUS.
2255 * Note that there is one difficulty: The circumcircle is uniquely defined, but for the circumsphere's center
2256 * there are two possibilities which becomes clear from the construction as seen below. Hence, we must check
2257 * both.
2258 * Note also that the acos() function is not unique on [0, 2.*M_PI). Hence, we need an additional check
2259 * to decide for one of the two possible angles. Therefore we need a SearchDirection and to make this check
2260 * sensible we need OldSphereCenter to be orthogonal to it. Either we construct SearchDirection orthogonal
2261 * right away, or -- what we do here -- we rotate the relative sphere centers such that this orthogonality
2262 * holds. Then, the normalized projection onto the SearchDirection is either +1 or -1 and thus states whether
2263 * the angle is uniquely in either (0,M_PI] or [M_PI, 2.*M_PI).
2264 * @param NormalVector normal direction of the base triangle (here the unit axis vector, \sa Find_starting_triangle())
2265 * @param SearchDirection general direction where to search for the next point, relative to center of BaseLine
2266 * @param OldSphereCenter center of sphere for base triangle, relative to center of BaseLine, giving null angle for the parameter circle
2267 * @param BaseLine BoundaryLineSet with the current base line
2268 * @param ThirdNode third atom to avoid in search
2269 * @param candidates list of equally good candidates to return
2270 * @param ShortestAngle the current path length on this circle band for the current Opt_Candidate
2271 * @param RADIUS radius of sphere
2272 * @param *LC LinkedCell structure with neighbouring atoms
2273 */
2274void Find_third_point_for_Tesselation(
2275 Vector NormalVector, Vector SearchDirection, Vector OldSphereCenter,
2276 class BoundaryLineSet *BaseLine, atom *ThirdNode, CandidateList* &candidates,
2277 double *ShortestAngle, const double RADIUS, LinkedCell *LC
2278) {
2279 Vector CircleCenter; // center of the circle, i.e. of the band of sphere's centers
2280 Vector CirclePlaneNormal; // normal vector defining the plane this circle lives in
2281 Vector SphereCenter;
2282 Vector NewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, first possibility
2283 Vector OtherNewSphereCenter; // center of the sphere defined by the two points of BaseLine and the one of Candidate, second possibility
2284 Vector NewNormalVector; // normal vector of the Candidate's triangle
2285 Vector helper, OptCandidateCenter, OtherOptCandidateCenter;
2286 LinkedAtoms *List = NULL;
2287 double CircleRadius; // radius of this circle
2288 double radius;
2289 double alpha, Otheralpha; // angles (i.e. parameter for the circle).
2290 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2291 atom *Candidate = NULL;
2292 CandidateForTesselation *optCandidate = NULL;
2293
2294 cout << Verbose(1) << "Begin of Find_third_point_for_Tesselation" << endl;
2295
2296 //cout << Verbose(2) << "INFO: NormalVector of BaseTriangle is " << NormalVector << "." << endl;
2297
2298 // construct center of circle
2299 CircleCenter.CopyVector(&(BaseLine->endpoints[0]->node->x));
2300 CircleCenter.AddVector(&BaseLine->endpoints[1]->node->x);
2301 CircleCenter.Scale(0.5);
2302
2303 // construct normal vector of circle
2304 CirclePlaneNormal.CopyVector(&BaseLine->endpoints[0]->node->x);
2305 CirclePlaneNormal.SubtractVector(&BaseLine->endpoints[1]->node->x);
2306
2307 // calculate squared radius atom *ThirdNode,f circle
2308 radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
2309 if (radius/4. < RADIUS*RADIUS) {
2310 CircleRadius = RADIUS*RADIUS - radius/4.;
2311 CirclePlaneNormal.Normalize();
2312 //cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
2313
2314 // test whether old center is on the band's plane
2315 if (fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) > HULLEPSILON) {
2316 cerr << "ERROR: Something's very wrong here: OldSphereCenter is not on the band's plane as desired by " << fabs(OldSphereCenter.ScalarProduct(&CirclePlaneNormal)) << "!" << endl;
2317 OldSphereCenter.ProjectOntoPlane(&CirclePlaneNormal);
2318 }
2319 radius = OldSphereCenter.ScalarProduct(&OldSphereCenter);
2320 if (fabs(radius - CircleRadius) < HULLEPSILON) {
2321
2322 // check SearchDirection
2323 //cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
2324 if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) { // rotated the wrong way!
2325 cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are not orthogonal!" << endl;
2326 }
2327
2328 // get cell for the starting atom
2329 if (LC->SetIndexToVector(&CircleCenter)) {
2330 for(int i=0;i<NDIM;i++) // store indices of this cell
2331 N[i] = LC->n[i];
2332 //cout << Verbose(2) << "INFO: Center cell is " << N[0] << ", " << N[1] << ", " << N[2] << " with No. " << LC->index << "." << endl;
2333 } else {
2334 cerr << "ERROR: Vector " << CircleCenter << " is outside of LinkedCell's bounding box." << endl;
2335 return;
2336 }
2337 // then go through the current and all neighbouring cells and check the contained atoms for possible candidates
2338 //cout << Verbose(2) << "LC Intervals:";
2339 for (int i=0;i<NDIM;i++) {
2340 Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2341 Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2342 //cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2343 }
2344 //cout << endl;
2345 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2346 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2347 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2348 List = LC->GetCurrentCell();
2349 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2350 if (List != NULL) {
2351 for (LinkedAtoms::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2352 Candidate = (*Runner);
2353
2354 // check for three unique points
2355 //cout << Verbose(2) << "INFO: Current Candidate is " << *Candidate << " at " << Candidate->x << "." << endl;
2356 if ((Candidate != BaseLine->endpoints[0]->node) && (Candidate != BaseLine->endpoints[1]->node) ){
2357
2358 // construct both new centers
2359 GetCenterofCircumcircle(&NewSphereCenter, &(BaseLine->endpoints[0]->node->x), &(BaseLine->endpoints[1]->node->x), &(Candidate->x));
2360 OtherNewSphereCenter.CopyVector(&NewSphereCenter);
2361
2362 if ((NewNormalVector.MakeNormalVector(&(BaseLine->endpoints[0]->node->x), &(BaseLine->endpoints[1]->node->x), &(Candidate->x)))
2363 && (fabs(NewNormalVector.ScalarProduct(&NewNormalVector)) > HULLEPSILON)
2364 ) {
2365 helper.CopyVector(&NewNormalVector);
2366 //cout << Verbose(2) << "INFO: NewNormalVector is " << NewNormalVector << "." << endl;
2367 radius = BaseLine->endpoints[0]->node->x.DistanceSquared(&NewSphereCenter);
2368 if (radius < RADIUS*RADIUS) {
2369 helper.Scale(sqrt(RADIUS*RADIUS - radius));
2370 //cout << Verbose(2) << "INFO: Distance of NewCircleCenter to NewSphereCenter is " << helper.Norm() << " with sphere radius " << RADIUS << "." << endl;
2371 NewSphereCenter.AddVector(&helper);
2372 NewSphereCenter.SubtractVector(&CircleCenter);
2373 //cout << Verbose(2) << "INFO: NewSphereCenter is at " << NewSphereCenter << "." << endl;
2374
2375 // OtherNewSphereCenter is created by the same vector just in the other direction
2376 helper.Scale(-1.);
2377 OtherNewSphereCenter.AddVector(&helper);
2378 OtherNewSphereCenter.SubtractVector(&CircleCenter);
2379 //cout << Verbose(2) << "INFO: OtherNewSphereCenter is at " << OtherNewSphereCenter << "." << endl;
2380
2381 alpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, NewSphereCenter, OldSphereCenter, NormalVector, SearchDirection);
2382 Otheralpha = GetPathLengthonCircumCircle(CircleCenter, CirclePlaneNormal, CircleRadius, OtherNewSphereCenter, OldSphereCenter, NormalVector, SearchDirection);
2383 alpha = min(alpha, Otheralpha);
2384 // if there is a better candidate, drop the current list and add the new candidate
2385 // otherwise ignore the new candidate and keep the list
2386 if (*ShortestAngle > (alpha - HULLEPSILON)) {
2387 optCandidate = new CandidateForTesselation(Candidate, BaseLine, OptCandidateCenter, OtherOptCandidateCenter);
2388 if (fabs(alpha - Otheralpha) > MYEPSILON) {
2389 optCandidate->OptCenter.CopyVector(&NewSphereCenter);
2390 optCandidate->OtherOptCenter.CopyVector(&OtherNewSphereCenter);
2391 } else {
2392 optCandidate->OptCenter.CopyVector(&OtherNewSphereCenter);
2393 optCandidate->OtherOptCenter.CopyVector(&NewSphereCenter);
2394 }
2395 // if there is an equal candidate, add it to the list without clearing the list
2396 if ((*ShortestAngle - HULLEPSILON) < alpha) {
2397 candidates->push_back(optCandidate);
2398 cout << Verbose(2) << "ACCEPT: We have found an equally good candidate: " << *(optCandidate->point) << " with "
2399 << alpha << " and circumsphere's center at " << optCandidate->OptCenter << "." << endl;
2400 } else {
2401 // remove all candidates from the list and then the list itself
2402 class CandidateForTesselation *remover = NULL;
2403 for (CandidateList::iterator it = candidates->begin(); it != candidates->end(); ++it) {
2404 remover = *it;
2405 delete(remover);
2406 }
2407 candidates->clear();
2408 candidates->push_back(optCandidate);
2409 cout << Verbose(2) << "ACCEPT: We have found a better candidate: " << *(optCandidate->point) << " with "
2410 << alpha << " and circumsphere's center at " << optCandidate->OptCenter << "." << endl;
2411 }
2412 *ShortestAngle = alpha;
2413 //cout << Verbose(2) << "INFO: There are " << candidates->size() << " candidates in the list now." << endl;
2414 } else {
2415 if ((optCandidate != NULL) && (optCandidate->point != NULL)) {
2416 //cout << Verbose(2) << "REJECT: Old candidate: " << *(optCandidate->point) << " is better than " << alpha << " with " << *ShortestAngle << "." << endl;
2417 } else {
2418 //cout << Verbose(2) << "REJECT: Candidate " << *Candidate << " with " << alpha << " was rejected." << endl;
2419 }
2420 }
2421
2422 } else {
2423 //cout << Verbose(2) << "REJECT: NewSphereCenter " << NewSphereCenter << " is too far away: " << radius << "." << endl;
2424 }
2425 } else {
2426 //cout << Verbose(2) << "REJECT: Three points from " << *BaseLine << " and Candidate " << *Candidate << " are linear-dependent." << endl;
2427 }
2428 } else {
2429 if (ThirdNode != NULL) {
2430 //cout << Verbose(2) << "REJECT: Base triangle " << *BaseLine << " and " << *ThirdNode << " contains Candidate " << *Candidate << "." << endl;
2431 } else {
2432 //cout << Verbose(2) << "REJECT: Base triangle " << *BaseLine << " contains Candidate " << *Candidate << "." << endl;
2433 }
2434 }
2435 }
2436 }
2437 }
2438 } else {
2439 cerr << Verbose(2) << "ERROR: The projected center of the old sphere has radius " << radius << " instead of " << CircleRadius << "." << endl;
2440 }
2441 } else {
2442 if (ThirdNode != NULL)
2443 cout << Verbose(2) << "Circumcircle for base line " << *BaseLine << " and third node " << *ThirdNode << " is too big!" << endl;
2444 else
2445 cout << Verbose(2) << "Circumcircle for base line " << *BaseLine << " is too big!" << endl;
2446 }
2447
2448 //cout << Verbose(2) << "INFO: Sorting candidate list ..." << endl;
2449 if (candidates->size() > 1) {
2450 candidates->unique();
2451 candidates->sort(sortCandidates);
2452 }
2453
2454 cout << Verbose(1) << "End of Find_third_point_for_Tesselation" << endl;
2455};
2456
2457struct Intersection {
2458 Vector x1;
2459 Vector x2;
2460 Vector x3;
2461 Vector x4;
2462};
2463
2464/**
2465 * Intersection calculation function.
2466 *
2467 * @param x to find the result for
2468 * @param function parameter
2469 */
2470double MinIntersectDistance(const gsl_vector * x, void *params) {
2471 double retval = 0;
2472 struct Intersection *I = (struct Intersection *)params;
2473 Vector intersection;
2474 Vector SideA,SideB,HeightA, HeightB;
2475 for (int i=0;i<NDIM;i++)
2476 intersection.x[i] = gsl_vector_get(x, i);
2477
2478 SideA.CopyVector(&(I->x1));
2479 SideA.SubtractVector(&I->x2);
2480 HeightA.CopyVector(&intersection);
2481 HeightA.SubtractVector(&I->x1);
2482 HeightA.ProjectOntoPlane(&SideA);
2483
2484 SideB.CopyVector(&I->x3);
2485 SideB.SubtractVector(&I->x4);
2486 HeightB.CopyVector(&intersection);
2487 HeightB.SubtractVector(&I->x3);
2488 HeightB.ProjectOntoPlane(&SideB);
2489
2490 retval = HeightA.ScalarProduct(&HeightA) + HeightB.ScalarProduct(&HeightB);
2491 //cout << Verbose(2) << "MinIntersectDistance called, result: " << retval << endl;
2492
2493 return retval;
2494};
2495
2496
2497/**
2498 * Calculates whether there is an intersection between two lines. The first line
2499 * always goes through point 1 and point 2 and the second line is given by the
2500 * connection between point 4 and point 5.
2501 *
2502 * @param point 1 of line 1
2503 * @param point 2 of line 1
2504 * @param point 1 of line 2
2505 * @param point 2 of line 2
2506 *
2507 * @return true if there is an intersection between the given lines, false otherwise
2508 */
2509bool existsIntersection(Vector point1, Vector point2, Vector point3, Vector point4) {
2510 bool result;
2511
2512 struct Intersection par;
2513 par.x1.CopyVector(&point1);
2514 par.x2.CopyVector(&point2);
2515 par.x3.CopyVector(&point3);
2516 par.x4.CopyVector(&point4);
2517
2518 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex;
2519 gsl_multimin_fminimizer *s = NULL;
2520 gsl_vector *ss, *x;
2521 gsl_multimin_function minex_func;
2522
2523 size_t iter = 0;
2524 int status;
2525 double size;
2526
2527 /* Starting point */
2528 x = gsl_vector_alloc(NDIM);
2529 gsl_vector_set(x, 0, point1.x[0]);
2530 gsl_vector_set(x, 1, point1.x[1]);
2531 gsl_vector_set(x, 2, point1.x[2]);
2532
2533 /* Set initial step sizes to 1 */
2534 ss = gsl_vector_alloc(NDIM);
2535 gsl_vector_set_all(ss, 1.0);
2536
2537 /* Initialize method and iterate */
2538 minex_func.n = NDIM;
2539 minex_func.f = &MinIntersectDistance;
2540 minex_func.params = (void *)&par;
2541
2542 s = gsl_multimin_fminimizer_alloc(T, NDIM);
2543 gsl_multimin_fminimizer_set(s, &minex_func, x, ss);
2544
2545 do {
2546 iter++;
2547 status = gsl_multimin_fminimizer_iterate(s);
2548
2549 if (status) {
2550 break;
2551 }
2552
2553 size = gsl_multimin_fminimizer_size(s);
2554 status = gsl_multimin_test_size(size, 1e-2);
2555
2556 if (status == GSL_SUCCESS) {
2557 cout << Verbose(2) << "converged to minimum" << endl;
2558 }
2559 } while (status == GSL_CONTINUE && iter < 100);
2560
2561 // check whether intersection is in between or not
2562 Vector intersection, SideA, SideB, HeightA, HeightB;
2563 double t1, t2;
2564 for (int i = 0; i < NDIM; i++) {
2565 intersection.x[i] = gsl_vector_get(s->x, i);
2566 }
2567
2568 SideA.CopyVector(&par.x2);
2569 SideA.SubtractVector(&par.x1);
2570 HeightA.CopyVector(&intersection);
2571 HeightA.SubtractVector(&par.x1);
2572
2573 t1 = HeightA.Projection(&SideA)/SideA.ScalarProduct(&SideA);
2574
2575 SideB.CopyVector(&par.x4);
2576 SideB.SubtractVector(&par.x3);
2577 HeightB.CopyVector(&intersection);
2578 HeightB.SubtractVector(&par.x3);
2579
2580 t2 = HeightB.Projection(&SideB)/SideB.ScalarProduct(&SideB);
2581
2582 cout << Verbose(2) << "Intersection " << intersection << " is at "
2583 << t1 << " for (" << point1 << "," << point2 << ") and at "
2584 << t2 << " for (" << point3 << "," << point4 << "): ";
2585
2586 if (((t1 >= 0) && (t1 <= 1)) && ((t2 >= 0) && (t2 <= 1))) {
2587 cout << "true intersection." << endl;
2588 result = true;
2589 } else {
2590 cout << "intersection out of region of interest." << endl;
2591 result = false;
2592 }
2593
2594 // free minimizer stuff
2595 gsl_vector_free(x);
2596 gsl_vector_free(ss);
2597 gsl_multimin_fminimizer_free(s);
2598
2599 return result;
2600}
2601
2602/** Finds the second point of starting triangle.
2603 * \param *a first atom
2604 * \param *Candidate pointer to candidate atom on return
2605 * \param Oben vector indicating the outside
2606 * \param Opt_Candidate reference to recommended candidate on return
2607 * \param Storage[3] array storing angles and other candidate information
2608 * \param RADIUS radius of virtual sphere
2609 * \param *LC LinkedCell structure with neighbouring atoms
2610 */
2611void Find_second_point_for_Tesselation(atom* a, atom* Candidate, Vector Oben, atom*& Opt_Candidate, double Storage[3], double RADIUS, LinkedCell *LC)
2612{
2613 cout << Verbose(2) << "Begin of Find_second_point_for_Tesselation" << endl;
2614 Vector AngleCheck;
2615 double norm = -1., angle;
2616 LinkedAtoms *List = NULL;
2617 int N[NDIM], Nlower[NDIM], Nupper[NDIM];
2618
2619 if (LC->SetIndexToAtom(a)) { // get cell for the starting atom
2620 for(int i=0;i<NDIM;i++) // store indices of this cell
2621 N[i] = LC->n[i];
2622 } else {
2623 cerr << "ERROR: Atom " << *a << " is not found in cell " << LC->index << "." << endl;
2624 return;
2625 }
2626 // then go through the current and all neighbouring cells and check the contained atoms for possible candidates
2627 cout << Verbose(3) << "LC Intervals from [";
2628 for (int i=0;i<NDIM;i++) {
2629 cout << " " << N[i] << "<->" << LC->N[i];
2630 }
2631 cout << "] :";
2632 for (int i=0;i<NDIM;i++) {
2633 Nlower[i] = ((N[i]-1) >= 0) ? N[i]-1 : 0;
2634 Nupper[i] = ((N[i]+1) < LC->N[i]) ? N[i]+1 : LC->N[i]-1;
2635 cout << " [" << Nlower[i] << "," << Nupper[i] << "] ";
2636 }
2637 cout << endl;
2638
2639
2640 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
2641 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
2642 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
2643 List = LC->GetCurrentCell();
2644 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2645 if (List != NULL) {
2646 for (LinkedAtoms::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
2647 Candidate = (*Runner);
2648 // check if we only have one unique point yet ...
2649 if (a != Candidate) {
2650 // Calculate center of the circle with radius RADIUS through points a and Candidate
2651 Vector OrthogonalizedOben, a_Candidate, Center;
2652 double distance, scaleFactor;
2653
2654 OrthogonalizedOben.CopyVector(&Oben);
2655 a_Candidate.CopyVector(&(a->x));
2656 a_Candidate.SubtractVector(&(Candidate->x));
2657 OrthogonalizedOben.ProjectOntoPlane(&a_Candidate);
2658 OrthogonalizedOben.Normalize();
2659 distance = 0.5 * a_Candidate.Norm();
2660 scaleFactor = sqrt(((RADIUS * RADIUS) - (distance * distance)));
2661 OrthogonalizedOben.Scale(scaleFactor);
2662
2663 Center.CopyVector(&(Candidate->x));
2664 Center.AddVector(&(a->x));
2665 Center.Scale(0.5);
2666 Center.AddVector(&OrthogonalizedOben);
2667
2668 AngleCheck.CopyVector(&Center);
2669 AngleCheck.SubtractVector(&(a->x));
2670 norm = a_Candidate.Norm();
2671 // second point shall have smallest angle with respect to Oben vector
2672 if (norm < RADIUS*2.) {
2673 angle = AngleCheck.Angle(&Oben);
2674 if (angle < Storage[0]) {
2675 //cout << Verbose(3) << "Old values of Storage: %lf %lf \n", Storage[0], Storage[1]);
2676 cout << Verbose(3) << "Current candidate is " << *Candidate << ": Is a better candidate with distance " << norm << " and angle " << angle << " to oben " << Oben << ".\n";
2677 Opt_Candidate = Candidate;
2678 Storage[0] = angle;
2679 //cout << Verbose(3) << "Changing something in Storage: %lf %lf. \n", Storage[0], Storage[2]);
2680 } else {
2681 //cout << Verbose(3) << "Current candidate is " << *Candidate << ": Looses with angle " << angle << " to a better candidate " << *Opt_Candidate << endl;
2682 }
2683 } else {
2684 //cout << Verbose(3) << "Current candidate is " << *Candidate << ": Refused due to Radius " << norm << endl;
2685 }
2686 } else {
2687 //cout << Verbose(3) << "Current candidate is " << *Candidate << ": Candidate is equal to first endpoint." << *a << "." << endl;
2688 }
2689 }
2690 } else {
2691 cout << Verbose(3) << "Linked cell list is empty." << endl;
2692 }
2693 }
2694 cout << Verbose(2) << "End of Find_second_point_for_Tesselation" << endl;
2695};
2696
2697/** Finds the starting triangle for find_non_convex_border().
2698 * Looks at the outermost atom per axis, then Find_second_point_for_Tesselation()
2699 * for the second and Find_next_suitable_point_via_Angle_of_Sphere() for the third
2700 * point are called.
2701 * \param RADIUS radius of virtual rolling sphere
2702 * \param *LC LinkedCell structure with neighbouring atoms
2703 */
2704void Tesselation::Find_starting_triangle(ofstream *out, molecule *mol, const double RADIUS, LinkedCell *LC)
2705{
2706 cout << Verbose(1) << "Begin of Find_starting_triangle\n";
2707 int i = 0;
2708 LinkedAtoms *List = NULL;
2709 atom* FirstPoint = NULL;
2710 atom* SecondPoint = NULL;
2711 atom* MaxAtom[NDIM];
2712 double max_coordinate[NDIM];
2713 Vector Oben;
2714 Vector helper;
2715 Vector Chord;
2716 Vector SearchDirection;
2717
2718 Oben.Zero();
2719
2720 for (i = 0; i < 3; i++) {
2721 MaxAtom[i] = NULL;
2722 max_coordinate[i] = -1;
2723 }
2724
2725 // 1. searching topmost atom with respect to each axis
2726 for (int i=0;i<NDIM;i++) { // each axis
2727 LC->n[i] = LC->N[i]-1; // current axis is topmost cell
2728 for (LC->n[(i+1)%NDIM]=0;LC->n[(i+1)%NDIM]<LC->N[(i+1)%NDIM];LC->n[(i+1)%NDIM]++)
2729 for (LC->n[(i+2)%NDIM]=0;LC->n[(i+2)%NDIM]<LC->N[(i+2)%NDIM];LC->n[(i+2)%NDIM]++) {
2730 List = LC->GetCurrentCell();
2731 //cout << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << "." << endl;
2732 if (List != NULL) {
2733 for (LinkedAtoms::iterator Runner = List->begin();Runner != List->end();Runner++) {
2734 if ((*Runner)->x.x[i] > max_coordinate[i]) {
2735 cout << Verbose(2) << "New maximal for axis " << i << " atom is " << *(*Runner) << " at " << (*Runner)->x << "." << endl;
2736 max_coordinate[i] = (*Runner)->x.x[i];
2737 MaxAtom[i] = (*Runner);
2738 }
2739 }
2740 } else {
2741 cerr << "ERROR: The current cell " << LC->n[0] << "," << LC->n[1] << "," << LC->n[2] << " is invalid!" << endl;
2742 }
2743 }
2744 }
2745
2746 cout << Verbose(2) << "Found maximum coordinates: ";
2747 for (int i=0;i<NDIM;i++)
2748 cout << i << ": " << *MaxAtom[i] << "\t";
2749 cout << endl;
2750
2751 BTS = NULL;
2752 CandidateList *Opt_Candidates = new CandidateList();
2753 for (int k=0;k<NDIM;k++) {
2754 Oben.x[k] = 1.;
2755 FirstPoint = MaxAtom[k];
2756 cout << Verbose(1) << "Coordinates of start atom " << *FirstPoint << " at " << FirstPoint->x << "." << endl;
2757
2758 double ShortestAngle;
2759 atom* Opt_Candidate = NULL;
2760 ShortestAngle = 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.
2761
2762 Find_second_point_for_Tesselation(FirstPoint, NULL, Oben, Opt_Candidate, &ShortestAngle, RADIUS, LC); // we give same point as next candidate as its bonds are looked into in find_second_...
2763 SecondPoint = Opt_Candidate;
2764 if (SecondPoint == NULL) // have we found a second point?
2765 continue;
2766 else
2767 cout << Verbose(1) << "Found second point is " << *SecondPoint << " at " << SecondPoint->x << ".\n";
2768
2769 helper.CopyVector(&(FirstPoint->x));
2770 helper.SubtractVector(&(SecondPoint->x));
2771 helper.Normalize();
2772 Oben.ProjectOntoPlane(&helper);
2773 Oben.Normalize();
2774 helper.VectorProduct(&Oben);
2775 ShortestAngle = 2.*M_PI; // This will indicate the quadrant.
2776
2777 Chord.CopyVector(&(FirstPoint->x)); // bring into calling function
2778 Chord.SubtractVector(&(SecondPoint->x));
2779 double radius = Chord.ScalarProduct(&Chord);
2780 double CircleRadius = sqrt(RADIUS*RADIUS - radius/4.);
2781 helper.CopyVector(&Oben);
2782 helper.Scale(CircleRadius);
2783 // Now, oben and helper are two orthonormalized vectors in the plane defined by Chord (not normalized)
2784
2785 // look in one direction of baseline for initial candidate
2786 SearchDirection.MakeNormalVector(&Chord, &Oben); // whether we look "left" first or "right" first is not important ...
2787
2788 // adding point 1 and point 2 and the line between them
2789 AddTrianglePoint(FirstPoint, 0);
2790 AddTrianglePoint(SecondPoint, 1);
2791 AddTriangleLine(TPS[0], TPS[1], 0);
2792
2793 //cout << Verbose(2) << "INFO: OldSphereCenter is at " << helper << ".\n";
2794 Find_third_point_for_Tesselation(
2795 Oben, SearchDirection, helper, BLS[0], NULL, *&Opt_Candidates, &ShortestAngle, RADIUS, LC
2796 );
2797 cout << Verbose(1) << "List of third Points is ";
2798 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2799 cout << " " << *(*it)->point;
2800 }
2801 cout << endl;
2802
2803 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2804 // add third triangle point
2805 AddTrianglePoint((*it)->point, 2);
2806 // add the second and third line
2807 AddTriangleLine(TPS[1], TPS[2], 1);
2808 AddTriangleLine(TPS[0], TPS[2], 2);
2809 // ... and triangles to the Maps of the Tesselation class
2810 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
2811 AddTriangle();
2812 // ... and calculate its normal vector (with correct orientation)
2813 (*it)->OptCenter.Scale(-1.);
2814 cout << Verbose(2) << "Anti-Oben is currently " << (*it)->OptCenter << "." << endl;
2815 BTS->GetNormalVector((*it)->OptCenter); // vector to compare with should point inwards
2816 cout << Verbose(0) << "==> Found starting triangle consists of " << *FirstPoint << ", " << *SecondPoint << " and "
2817 << *(*it)->point << " with normal vector " << BTS->NormalVector << ".\n";
2818
2819 // if we do not reach the end with the next step of iteration, we need to setup a new first line
2820 if (it != Opt_Candidates->end()--) {
2821 FirstPoint = (*it)->BaseLine->endpoints[0]->node;
2822 SecondPoint = (*it)->point;
2823 // adding point 1 and point 2 and the line between them
2824 AddTrianglePoint(FirstPoint, 0);
2825 AddTrianglePoint(SecondPoint, 1);
2826 AddTriangleLine(TPS[0], TPS[1], 0);
2827 }
2828 cout << Verbose(2) << "Projection is " << BTS->NormalVector.Projection(&Oben) << "." << endl;
2829 }
2830 if (BTS != NULL) // we have created one starting triangle
2831 break;
2832 else {
2833 // remove all candidates from the list and then the list itself
2834 class CandidateForTesselation *remover = NULL;
2835 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2836 remover = *it;
2837 delete(remover);
2838 }
2839 Opt_Candidates->clear();
2840 }
2841 }
2842
2843 // remove all candidates from the list and then the list itself
2844 class CandidateForTesselation *remover = NULL;
2845 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2846 remover = *it;
2847 delete(remover);
2848 }
2849 delete(Opt_Candidates);
2850 cout << Verbose(1) << "End of Find_starting_triangle\n";
2851};
2852
2853/** Checks for a new special triangle whether one of its edges is already present with one one triangle connected.
2854 * This enforces that special triangles (i.e. degenerated ones) should at last close the open-edge frontier and not
2855 * make it bigger (i.e. closing one (the baseline) and opening two new ones).
2856 * \param TPS[3] nodes of the triangle
2857 * \return true - there is such a line (i.e. creation of degenerated triangle is valid), false - no such line (don't create)
2858 */
2859bool CheckLineCriteriaforDegeneratedTriangle(class BoundaryPointSet *nodes[3])
2860{
2861 bool result = false;
2862 int counter = 0;
2863
2864 // check all three points
2865 for (int i=0;i<3;i++)
2866 for (int j=i+1; j<3; j++) {
2867 if (nodes[i]->lines.find(nodes[j]->node->nr) != nodes[i]->lines.end()) { // there already is a line
2868 LineMap::iterator FindLine;
2869 pair<LineMap::iterator,LineMap::iterator> FindPair;
2870 FindPair = nodes[i]->lines.equal_range(nodes[j]->node->nr);
2871 for (FindLine = FindPair.first; FindLine != FindPair.second; ++FindLine) {
2872 // If there is a line with less than two attached triangles, we don't need a new line.
2873 if (FindLine->second->TrianglesCount < 2) {
2874 counter++;
2875 break; // increase counter only once per edge
2876 }
2877 }
2878 } else { // no line
2879 cout << Verbose(1) << "ERROR: The line between " << nodes[i] << " and " << nodes[j] << " is not yet present, hence no need for a degenerate triangle!" << endl;
2880 result = true;
2881 }
2882 }
2883 if (counter > 1) {
2884 cout << Verbose(2) << "INFO: Degenerate triangle is ok, at least two, here " << counter << ", existing lines are used." << endl;
2885 result = true;
2886 }
2887 return result;
2888};
2889
2890
2891/** This function finds a triangle to a line, adjacent to an existing one.
2892 * @param out output stream for debugging
2893 * @param *mol molecule with Atom's and Bond's
2894 * @param Line current baseline to search from
2895 * @param T current triangle which \a Line is edge of
2896 * @param RADIUS radius of the rolling ball
2897 * @param N number of found triangles
2898 * @param *filename filename base for intermediate envelopes
2899 * @param *LC LinkedCell structure with neighbouring atoms
2900 */
2901bool Tesselation::Find_next_suitable_triangle(ofstream *out,
2902 molecule *mol, BoundaryLineSet &Line, BoundaryTriangleSet &T,
2903 const double& RADIUS, int N, const char *tempbasename, LinkedCell *LC)
2904{
2905 cout << Verbose(0) << "Begin of Find_next_suitable_triangle\n";
2906 ofstream *tempstream = NULL;
2907 char NumberName[255];
2908 bool result = true;
2909 CandidateList *Opt_Candidates = new CandidateList();
2910
2911 Vector CircleCenter;
2912 Vector CirclePlaneNormal;
2913 Vector OldSphereCenter;
2914 Vector SearchDirection;
2915 Vector helper;
2916 atom *ThirdNode = NULL;
2917 LineMap::iterator testline;
2918 double ShortestAngle = 2.*M_PI; // This will indicate the quadrant.
2919 double radius, CircleRadius;
2920
2921 cout << Verbose(1) << "Current baseline is " << Line << " of triangle " << T << "." << endl;
2922 for (int i=0;i<3;i++)
2923 if ((T.endpoints[i]->node != Line.endpoints[0]->node) && (T.endpoints[i]->node != Line.endpoints[1]->node))
2924 ThirdNode = T.endpoints[i]->node;
2925
2926 // construct center of circle
2927 CircleCenter.CopyVector(&Line.endpoints[0]->node->x);
2928 CircleCenter.AddVector(&Line.endpoints[1]->node->x);
2929 CircleCenter.Scale(0.5);
2930
2931 // construct normal vector of circle
2932 CirclePlaneNormal.CopyVector(&Line.endpoints[0]->node->x);
2933 CirclePlaneNormal.SubtractVector(&Line.endpoints[1]->node->x);
2934
2935 // calculate squared radius of circle
2936 radius = CirclePlaneNormal.ScalarProduct(&CirclePlaneNormal);
2937 if (radius/4. < RADIUS*RADIUS) {
2938 CircleRadius = RADIUS*RADIUS - radius/4.;
2939 CirclePlaneNormal.Normalize();
2940 cout << Verbose(2) << "INFO: CircleCenter is at " << CircleCenter << ", CirclePlaneNormal is " << CirclePlaneNormal << " with circle radius " << sqrt(CircleRadius) << "." << endl;
2941
2942 // construct old center
2943 GetCenterofCircumcircle(&OldSphereCenter, &(T.endpoints[0]->node->x), &(T.endpoints[1]->node->x), &(T.endpoints[2]->node->x));
2944 helper.CopyVector(&T.NormalVector); // normal vector ensures that this is correct center of the two possible ones
2945 radius = Line.endpoints[0]->node->x.DistanceSquared(&OldSphereCenter);
2946 helper.Scale(sqrt(RADIUS*RADIUS - radius));
2947 OldSphereCenter.AddVector(&helper);
2948 OldSphereCenter.SubtractVector(&CircleCenter);
2949 //cout << Verbose(2) << "INFO: OldSphereCenter is at " << OldSphereCenter << "." << endl;
2950
2951 // construct SearchDirection
2952 SearchDirection.MakeNormalVector(&T.NormalVector, &CirclePlaneNormal);
2953 helper.CopyVector(&Line.endpoints[0]->node->x);
2954 helper.SubtractVector(&ThirdNode->x);
2955 if (helper.ScalarProduct(&SearchDirection) < -HULLEPSILON)// ohoh, SearchDirection points inwards!
2956 SearchDirection.Scale(-1.);
2957 SearchDirection.ProjectOntoPlane(&OldSphereCenter);
2958 SearchDirection.Normalize();
2959 cout << Verbose(2) << "INFO: SearchDirection is " << SearchDirection << "." << endl;
2960 if (fabs(OldSphereCenter.ScalarProduct(&SearchDirection)) > HULLEPSILON) {
2961 // rotated the wrong way!
2962 cerr << "ERROR: SearchDirection and RelativeOldSphereCenter are still not orthogonal!" << endl;
2963 }
2964
2965 // add third point
2966 Find_third_point_for_Tesselation(
2967 T.NormalVector, SearchDirection, OldSphereCenter, &Line, ThirdNode, Opt_Candidates,
2968 &ShortestAngle, RADIUS, LC
2969 );
2970
2971 } else {
2972 cout << Verbose(1) << "Circumcircle for base line " << Line << " and base triangle " << T << " is too big!" << endl;
2973 }
2974
2975 if (Opt_Candidates->begin() == Opt_Candidates->end()) {
2976 cerr << "WARNING: Could not find a suitable candidate." << endl;
2977 return false;
2978 }
2979 cout << Verbose(1) << "Third Points are ";
2980 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2981 cout << " " << *(*it)->point;
2982 }
2983 cout << endl;
2984
2985 BoundaryLineSet *BaseRay = &Line;
2986 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
2987 cout << Verbose(1) << " Third point candidate is " << *(*it)->point
2988 << " with circumsphere's center at " << (*it)->OptCenter << "." << endl;
2989 cout << Verbose(1) << " Baseline is " << *BaseRay << endl;
2990
2991 // check whether all edges of the new triangle still have space for one more triangle (i.e. TriangleCount <2)
2992 atom *AtomCandidates[3];
2993 AtomCandidates[0] = (*it)->point;
2994 AtomCandidates[1] = BaseRay->endpoints[0]->node;
2995 AtomCandidates[2] = BaseRay->endpoints[1]->node;
2996 int existentTrianglesCount = CheckPresenceOfTriangle(out, AtomCandidates);
2997
2998 BTS = NULL;
2999 // If there is no triangle, add it regularly.
3000 if (existentTrianglesCount == 0) {
3001 AddTrianglePoint((*it)->point, 0);
3002 AddTrianglePoint(BaseRay->endpoints[0]->node, 1);
3003 AddTrianglePoint(BaseRay->endpoints[1]->node, 2);
3004
3005 AddTriangleLine(TPS[0], TPS[1], 0);
3006 AddTriangleLine(TPS[0], TPS[2], 1);
3007 AddTriangleLine(TPS[1], TPS[2], 2);
3008
3009 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
3010 AddTriangle();
3011 (*it)->OptCenter.Scale(-1.);
3012 BTS->GetNormalVector((*it)->OptCenter);
3013 (*it)->OptCenter.Scale(-1.);
3014
3015 cout << "--> New triangle with " << *BTS << " and normal vector " << BTS->NormalVector
3016 << " for this triangle ... " << endl;
3017 //cout << Verbose(1) << "We have "<< TrianglesOnBoundaryCount << " for line " << *BaseRay << "." << endl;
3018 } else if (existentTrianglesCount == 1) { // If there is a planar region within the structure, we need this triangle a second time.
3019 AddTrianglePoint((*it)->point, 0);
3020 AddTrianglePoint(BaseRay->endpoints[0]->node, 1);
3021 AddTrianglePoint(BaseRay->endpoints[1]->node, 2);
3022
3023 // We demand that at most one new degenerate line is created and that this line also already exists (which has to be the case due to existentTrianglesCount == 1)
3024 // i.e. at least one of the three lines must be present with TriangleCount <= 1
3025 if (CheckLineCriteriaforDegeneratedTriangle(TPS)) {
3026 AddTriangleLine(TPS[0], TPS[1], 0);
3027 AddTriangleLine(TPS[0], TPS[2], 1);
3028 AddTriangleLine(TPS[1], TPS[2], 2);
3029
3030 BTS = new class BoundaryTriangleSet(BLS, TrianglesOnBoundaryCount);
3031 AddTriangle();
3032
3033 (*it)->OtherOptCenter.Scale(-1.);
3034 BTS->GetNormalVector((*it)->OtherOptCenter);
3035 (*it)->OtherOptCenter.Scale(-1.);
3036
3037 cout << "--> WARNING: Special new triangle with " << *BTS << " and normal vector " << BTS->NormalVector
3038 << " for this triangle ... " << endl;
3039 cout << Verbose(1) << "We have "<< BaseRay->TrianglesCount << " for line " << BaseRay << "." << endl;
3040 } else {
3041 cout << Verbose(1) << "WARNING: This triangle consisting of ";
3042 cout << *(*it)->point << ", ";
3043 cout << *BaseRay->endpoints[0]->node << " and ";
3044 cout << *BaseRay->endpoints[1]->node << " ";
3045 cout << "exists and is not added, as it does not seem helpful!" << endl;
3046 result = false;
3047 }
3048 } else {
3049 cout << Verbose(1) << "This triangle consisting of ";
3050 cout << *(*it)->point << ", ";
3051 cout << *BaseRay->endpoints[0]->node << " and ";
3052 cout << *BaseRay->endpoints[1]->node << " ";
3053 cout << "is invalid!" << endl;
3054 result = false;
3055 }
3056
3057 if ((result) && (existentTrianglesCount < 2) && (DoSingleStepOutput && (TrianglesOnBoundaryCount % 1 == 0))) { // if we have a new triangle and want to output each new triangle configuration
3058 sprintf(NumberName, "-%04d-%s_%s_%s", TriangleFilesWritten, BTS->endpoints[0]->node->Name, BTS->endpoints[1]->node->Name, BTS->endpoints[2]->node->Name);
3059 if (DoTecplotOutput) {
3060 string NameofTempFile(tempbasename);
3061 NameofTempFile.append(NumberName);
3062 for(size_t npos = NameofTempFile.find_first_of(' '); npos != string::npos; npos = NameofTempFile.find(' ', npos))
3063 NameofTempFile.erase(npos, 1);
3064 NameofTempFile.append(TecplotSuffix);
3065 cout << Verbose(1) << "Writing temporary non convex hull to file " << NameofTempFile << ".\n";
3066 tempstream = new ofstream(NameofTempFile.c_str(), ios::trunc);
3067 write_tecplot_file(out, tempstream, this, mol, TriangleFilesWritten);
3068 tempstream->close();
3069 tempstream->flush();
3070 delete(tempstream);
3071 }
3072
3073 if (DoRaster3DOutput) {
3074 string NameofTempFile(tempbasename);
3075 NameofTempFile.append(NumberName);
3076 for(size_t npos = NameofTempFile.find_first_of(' '); npos != string::npos; npos = NameofTempFile.find(' ', npos))
3077 NameofTempFile.erase(npos, 1);
3078 NameofTempFile.append(Raster3DSuffix);
3079 cout << Verbose(1) << "Writing temporary non convex hull to file " << NameofTempFile << ".\n";
3080 tempstream = new ofstream(NameofTempFile.c_str(), ios::trunc);
3081 write_raster3d_file(out, tempstream, this, mol);
3082 // include the current position of the virtual sphere in the temporary raster3d file
3083 // make the circumsphere's center absolute again
3084 helper.CopyVector(&BaseRay->endpoints[0]->node->x);
3085 helper.AddVector(&BaseRay->endpoints[1]->node->x);
3086 helper.Scale(0.5);
3087 (*it)->OptCenter.AddVector(&helper);
3088 Vector *center = mol->DetermineCenterOfAll(out);
3089 (*it)->OptCenter.SubtractVector(center);
3090 delete(center);
3091 // and add to file plus translucency object
3092 *tempstream << "# current virtual sphere\n";
3093 *tempstream << "8\n 25.0 0.6 -1.0 -1.0 -1.0 0.2 0 0 0 0\n";
3094 *tempstream << "2\n " << (*it)->OptCenter.x[0] << " "
3095 << (*it)->OptCenter.x[1] << " " << (*it)->OptCenter.x[2]
3096 << "\t" << RADIUS << "\t1 0 0\n";
3097 *tempstream << "9\n terminating special property\n";
3098 tempstream->close();
3099 tempstream->flush();
3100 delete(tempstream);
3101 }
3102 if (DoTecplotOutput || DoRaster3DOutput)
3103 TriangleFilesWritten++;
3104 }
3105
3106 // set baseline to new ray from ref point (here endpoints[0]->node) to current candidate (here (*it)->point))
3107 BaseRay = BLS[0];
3108 }
3109
3110 // remove all candidates from the list and then the list itself
3111 class CandidateForTesselation *remover = NULL;
3112 for (CandidateList::iterator it = Opt_Candidates->begin(); it != Opt_Candidates->end(); ++it) {
3113 remover = *it;
3114 delete(remover);
3115 }
3116 delete(Opt_Candidates);
3117 cout << Verbose(0) << "End of Find_next_suitable_triangle\n";
3118 return result;
3119};
3120
3121/**
3122 * Sort function for the candidate list.
3123 */
3124bool sortCandidates(CandidateForTesselation* candidate1, CandidateForTesselation* candidate2) {
3125 Vector BaseLineVector, OrthogonalVector, helper;
3126 if (candidate1->BaseLine != candidate2->BaseLine) { // sanity check
3127 cout << Verbose(0) << "ERROR: sortCandidates was called for two different baselines: " << candidate1->BaseLine << " and " << candidate2->BaseLine << "." << endl;
3128 //return false;
3129 exit(1);
3130 }
3131 // create baseline vector
3132 BaseLineVector.CopyVector(&(candidate1->BaseLine->endpoints[1]->node->x));
3133 BaseLineVector.SubtractVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3134 BaseLineVector.Normalize();
3135
3136 // create normal in-plane vector to cope with acos() non-uniqueness on [0,2pi] (note that is pointing in the "right" direction already, hence ">0" test!)
3137 helper.CopyVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3138 helper.SubtractVector(&(candidate1->point->x));
3139 OrthogonalVector.CopyVector(&helper);
3140 helper.VectorProduct(&BaseLineVector);
3141 OrthogonalVector.SubtractVector(&helper);
3142 OrthogonalVector.Normalize();
3143
3144 // calculate both angles and correct with in-plane vector
3145 helper.CopyVector(&(candidate1->point->x));
3146 helper.SubtractVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3147 double phi = BaseLineVector.Angle(&helper);
3148 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
3149 phi = 2.*M_PI - phi;
3150 }
3151 helper.CopyVector(&(candidate2->point->x));
3152 helper.SubtractVector(&(candidate1->BaseLine->endpoints[0]->node->x));
3153 double psi = BaseLineVector.Angle(&helper);
3154 if (OrthogonalVector.ScalarProduct(&helper) > 0) {
3155 psi = 2.*M_PI - psi;
3156 }
3157
3158 cout << Verbose(2) << *candidate1->point << " has angle " << phi << endl;
3159 cout << Verbose(2) << *candidate2->point << " has angle " << psi << endl;
3160
3161 // return comparison
3162 return phi < psi;
3163}
3164
3165/** Tesselates the non convex boundary by rolling a virtual sphere along the surface of the molecule.
3166 * \param *out output stream for debugging
3167 * \param *mol molecule structure with Atom's and Bond's
3168 * \param *Tess Tesselation filled with points, lines and triangles on boundary on return
3169 * \param *LCList atoms in LinkedCell list
3170 * \param *filename filename prefix for output of vertex data
3171 * \para RADIUS radius of the virtual sphere
3172 */
3173void Find_non_convex_border(ofstream *out, molecule* mol, class Tesselation *Tess, class LinkedCell *LCList, const char *filename, const double RADIUS)
3174{
3175 int N = 0;
3176 bool freeTess = false;
3177 bool freeLC = false;
3178 *out << Verbose(1) << "Entering search for non convex hull. " << endl;
3179 if (Tess == NULL) {
3180 *out << Verbose(1) << "Allocating Tesselation struct ..." << endl;
3181 Tess = new Tesselation;
3182 freeTess = true;
3183 }
3184 LineMap::iterator baseline;
3185 LineMap::iterator testline;
3186 *out << Verbose(0) << "Begin of Find_non_convex_border\n";
3187 bool flag = false; // marks whether we went once through all baselines without finding any without two triangles
3188 bool failflag = false;
3189
3190 if (LCList == NULL) {
3191 LCList = new LinkedCell(mol, 2.*RADIUS);
3192 freeLC = true;
3193 }
3194
3195 Tess->Find_starting_triangle(out, mol, RADIUS, LCList);
3196
3197 baseline = Tess->LinesOnBoundary.begin();
3198 while ((baseline != Tess->LinesOnBoundary.end()) || (flag)) {
3199 if (baseline->second->TrianglesCount == 1) {
3200 failflag = Tess->Find_next_suitable_triangle(out, mol, *(baseline->second), *(((baseline->second->triangles.begin()))->second), RADIUS, N, filename, LCList); //the line is there, so there is a triangle, but only one.
3201 flag = flag || failflag;
3202 if (!failflag)
3203 cerr << "WARNING: Find_next_suitable_triangle failed." << endl;
3204 } else {
3205 //cout << Verbose(1) << "Line " << *baseline->second << " has " << baseline->second->TrianglesCount << " triangles adjacent" << endl;
3206 if (baseline->second->TrianglesCount != 2)
3207 cout << Verbose(1) << "ERROR: TESSELATION FINISHED WITH INVALID TRIANGLE COUNT!" << endl;
3208 }
3209
3210 N++;
3211 baseline++;
3212 if ((baseline == Tess->LinesOnBoundary.end()) && (flag)) {
3213 baseline = Tess->LinesOnBoundary.begin(); // restart if we reach end due to newly inserted lines
3214 flag = false;
3215 }
3216 }
3217 if (filename != 0) {
3218 *out << Verbose(1) << "Writing final tecplot file\n";
3219 if (DoTecplotOutput) {
3220 string OutputName(filename);
3221 OutputName.append(TecplotSuffix);
3222 ofstream *tecplot = new ofstream(OutputName.c_str());
3223 write_tecplot_file(out, tecplot, Tess, mol, -1);
3224 tecplot->close();
3225 delete(tecplot);
3226 }
3227 if (DoRaster3DOutput) {
3228 string OutputName(filename);
3229 OutputName.append(Raster3DSuffix);
3230 ofstream *raster = new ofstream(OutputName.c_str());
3231 write_raster3d_file(out, raster, Tess, mol);
3232 raster->close();
3233 delete(raster);
3234 }
3235 } else {
3236 cerr << "ERROR: Could definitively not find all necessary triangles!" << endl;
3237 }
3238
3239 cout << Verbose(2) << "Check: List of Baselines with not two connected triangles:" << endl;
3240 int counter = 0;
3241 for (testline = Tess->LinesOnBoundary.begin(); testline != Tess->LinesOnBoundary.end(); testline++) {
3242 if (testline->second->TrianglesCount != 2) {
3243 cout << Verbose(2) << *testline->second << "\t" << testline->second->TrianglesCount << endl;
3244 counter++;
3245 }
3246 }
3247 if (counter == 0)
3248 cout << Verbose(2) << "None." << endl;
3249
3250 if (freeTess)
3251 delete(Tess);
3252 if (freeLC)
3253 delete(LCList);
3254 *out << Verbose(0) << "End of Find_non_convex_border\n";
3255};
3256
3257/** Finds a hole of sufficient size in \a this molecule to embed \a *srcmol into it.
3258 * \param *out output stream for debugging
3259 * \param *srcmol molecule to embed into
3260 * \return *Vector new center of \a *srcmol for embedding relative to \a this
3261 */
3262Vector* molecule::FindEmbeddingHole(ofstream *out, molecule *srcmol)
3263{
3264 Vector *Center = new Vector;
3265 Center->Zero();
3266 // calculate volume/shape of \a *srcmol
3267
3268 // find embedding holes
3269
3270 // if more than one, let user choose
3271
3272 // return embedding center
3273 return Center;
3274};
3275
Note: See TracBrowser for help on using the repository browser.