source: src/Tesselation/ellipsoid.cpp

Candidate_v1.6.1
Last change on this file was 9eb71b3, checked in by Frederik Heber <frederik.heber@…>, 8 years ago

Commented out MemDebug include and Memory::ignore.

  • MemDebug clashes with various allocation operators that use a specific placement in memory. It is so far not possible to wrap new/delete fully. Hence, we stop this effort which so far has forced us to put ever more includes (with clashes) into MemDebug and thereby bloat compilation time.
  • MemDebug does not add that much usefulness which is not also provided by valgrind.
  • Property mode set to 100644
File size: 18.2 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2010-2012 University of Bonn. All rights reserved.
5 *
6 *
7 * This file is part of MoleCuilder.
8 *
9 * MoleCuilder is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * MoleCuilder is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with MoleCuilder. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24 * ellipsoid.cpp
25 *
26 * Created on: Jan 20, 2009
27 * Author: heber
28 */
29
30// include config.h
31#ifdef HAVE_CONFIG_H
32#include <config.h>
33#endif
34
35//#include "CodePatterns/MemDebug.hpp"
36
37#include <gsl/gsl_multimin.h>
38#include <gsl/gsl_vector.h>
39
40#include <iomanip>
41
42#include <set>
43
44#include "CodePatterns/Log.hpp"
45#include "ellipsoid.hpp"
46#include "LinearAlgebra/Vector.hpp"
47#include "LinearAlgebra/RealSpaceMatrix.hpp"
48#include "LinkedCell/linkedcell.hpp"
49#include "Tesselation/BoundaryPointSet.hpp"
50#include "Tesselation/boundary.hpp"
51#include "Tesselation/tesselation.hpp"
52
53#include "RandomNumbers/RandomNumberGeneratorFactory.hpp"
54#include "RandomNumbers/RandomNumberGenerator.hpp"
55
56/** Determines squared distance for a given point \a x to surface of ellipsoid.
57 * \param x given point
58 * \param EllipsoidCenter center of ellipsoid
59 * \param EllipsoidLength[3] three lengths of half axis of ellipsoid
60 * \param EllipsoidAngle[3] three rotation angles of ellipsoid
61 * \return squared distance from point to surface
62 */
63double SquaredDistanceToEllipsoid(Vector &x, Vector &EllipsoidCenter, double *EllipsoidLength, double *EllipsoidAngle)
64{
65 Vector helper, RefPoint;
66 double distance = -1.;
67 RealSpaceMatrix Matrix;
68 double InverseLength[3];
69 double psi,theta,phi; // euler angles in ZX'Z'' convention
70
71 //LOG(3, "Begin of SquaredDistanceToEllipsoid");
72
73 for(int i=0;i<3;i++)
74 InverseLength[i] = 1./EllipsoidLength[i];
75
76 // 1. translate coordinate system so that ellipsoid center is in origin
77 RefPoint = helper = x - EllipsoidCenter;
78 //LOG(4, "Translated given point is at " << RefPoint << ".");
79
80 // 2. transform coordinate system by inverse of rotation matrix and of diagonal matrix
81 psi = EllipsoidAngle[0];
82 theta = EllipsoidAngle[1];
83 phi = EllipsoidAngle[2];
84 Matrix.set(0,0, cos(psi)*cos(phi) - sin(psi)*cos(theta)*sin(phi));
85 Matrix.set(1,0, -cos(psi)*sin(phi) - sin(psi)*cos(theta)*cos(phi));
86 Matrix.set(2,0, sin(psi)*sin(theta));
87 Matrix.set(0,1, sin(psi)*cos(phi) + cos(psi)*cos(theta)*sin(phi));
88 Matrix.set(1,1, cos(psi)*cos(theta)*cos(phi) - sin(psi)*sin(phi));
89 Matrix.set(2,1, -cos(psi)*sin(theta));
90 Matrix.set(0,2, sin(theta)*sin(phi));
91 Matrix.set(1,2, sin(theta)*cos(phi));
92 Matrix.set(2,2, cos(theta));
93 helper *= Matrix;
94 helper.ScaleAll(InverseLength);
95 //LOG(4, "Transformed RefPoint is at " << helper << ".");
96
97 // 3. construct intersection point with unit sphere and ray between origin and x
98 helper.Normalize(); // is simply normalizes vector in distance direction
99 //LOG(4, "Transformed intersection is at " << helper << ".");
100
101 // 4. transform back the constructed intersection point
102 psi = -EllipsoidAngle[0];
103 theta = -EllipsoidAngle[1];
104 phi = -EllipsoidAngle[2];
105 helper.ScaleAll(EllipsoidLength);
106 Matrix.set(0,0, cos(psi)*cos(phi) - sin(psi)*cos(theta)*sin(phi));
107 Matrix.set(1,0, -cos(psi)*sin(phi) - sin(psi)*cos(theta)*cos(phi));
108 Matrix.set(2,0, sin(psi)*sin(theta));
109 Matrix.set(0,1, sin(psi)*cos(phi) + cos(psi)*cos(theta)*sin(phi));
110 Matrix.set(1,1, cos(psi)*cos(theta)*cos(phi) - sin(psi)*sin(phi));
111 Matrix.set(2,1, -cos(psi)*sin(theta));
112 Matrix.set(0,2, sin(theta)*sin(phi));
113 Matrix.set(1,2, sin(theta)*cos(phi));
114 Matrix.set(2,2, cos(theta));
115 helper *= Matrix;
116 //LOG(4, "Intersection is at " << helper << ".");
117
118 // 5. determine distance between backtransformed point and x
119 distance = RefPoint.DistanceSquared(helper);
120 //LOG(4, "Squared distance between intersection and RefPoint is " << distance << ".");
121
122 return distance;
123 //LOG(3, "End of SquaredDistanceToEllipsoid");
124};
125
126/** structure for ellipsoid minimisation containing points to fit to.
127 */
128struct EllipsoidMinimisation {
129 int N; //!< dimension of vector set
130 Vector *x; //!< array of vectors
131};
132
133/** Sum of squared distance to ellipsoid to be minimised.
134 * \param *x parameters for the ellipsoid
135 * \param *params EllipsoidMinimisation with set of data points to minimise distance to and dimension
136 * \return sum of squared distance, \sa SquaredDistanceToEllipsoid()
137 */
138double SumSquaredDistance (const gsl_vector * x, void * params)
139{
140 Vector *set= ((struct EllipsoidMinimisation *)params)->x;
141 int N = ((struct EllipsoidMinimisation *)params)->N;
142 double SumDistance = 0.;
143 double distance;
144 Vector Center;
145 double EllipsoidLength[3], EllipsoidAngle[3];
146
147 // put parameters into suitable ellipsoid form
148 for (int i=0;i<3;i++) {
149 Center[i] = gsl_vector_get(x, i+0);
150 EllipsoidLength[i] = gsl_vector_get(x, i+3);
151 EllipsoidAngle[i] = gsl_vector_get(x, i+6);
152 }
153
154 // go through all points and sum distance
155 for (int i=0;i<N;i++) {
156 distance = SquaredDistanceToEllipsoid(set[i], Center, EllipsoidLength, EllipsoidAngle);
157 if (!isnan(distance)) {
158 SumDistance += distance;
159 } else {
160 SumDistance = GSL_NAN;
161 break;
162 }
163 }
164
165 //LOG(0, "Current summed distance is " << SumDistance << ".");
166 return SumDistance;
167};
168
169/** Finds best fitting ellipsoid parameter set in Least square sense for a given point set.
170 * \param *out output stream for debugging
171 * \param *set given point set
172 * \param N number of points in set
173 * \param EllipsoidParamter[3] three parameters in ellipsoid equation
174 * \return true - fit successful, false - fit impossible
175 */
176bool FitPointSetToEllipsoid(Vector *set, int N, Vector *EllipsoidCenter, double *EllipsoidLength, double *EllipsoidAngle)
177{
178 int status = GSL_SUCCESS;
179 LOG(2, "Begin of FitPointSetToEllipsoid ");
180 if (N >= 3) { // check that enough points are given (9 d.o.f.)
181 struct EllipsoidMinimisation par;
182 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex;
183 gsl_multimin_fminimizer *s = NULL;
184 gsl_vector *ss, *x;
185 gsl_multimin_function minex_func;
186
187 size_t iter = 0;
188 double size;
189
190 /* Starting point */
191 x = gsl_vector_alloc (9);
192 for (int i=0;i<3;i++) {
193 gsl_vector_set (x, i+0, EllipsoidCenter->at(i));
194 gsl_vector_set (x, i+3, EllipsoidLength[i]);
195 gsl_vector_set (x, i+6, EllipsoidAngle[i]);
196 }
197 par.x = set;
198 par.N = N;
199
200 /* Set initial step sizes */
201 ss = gsl_vector_alloc (9);
202 for (int i=0;i<3;i++) {
203 gsl_vector_set (ss, i+0, 0.1);
204 gsl_vector_set (ss, i+3, 1.0);
205 gsl_vector_set (ss, i+6, M_PI/20.);
206 }
207
208 /* Initialize method and iterate */
209 minex_func.n = 9;
210 minex_func.f = &SumSquaredDistance;
211 minex_func.params = (void *)&par;
212
213 s = gsl_multimin_fminimizer_alloc (T, 9);
214 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
215
216 do {
217 iter++;
218 status = gsl_multimin_fminimizer_iterate(s);
219
220 if (status)
221 break;
222
223 size = gsl_multimin_fminimizer_size (s);
224 status = gsl_multimin_test_size (size, 1e-2);
225
226 if (status == GSL_SUCCESS) {
227 for (int i=0;i<3;i++) {
228 EllipsoidCenter->at(i) = gsl_vector_get (s->x,i+0);
229 EllipsoidLength[i] = gsl_vector_get (s->x, i+3);
230 EllipsoidAngle[i] = gsl_vector_get (s->x, i+6);
231 }
232 LOG(4, setprecision(3) << "Converged fit at: " << *EllipsoidCenter << ", lengths " << EllipsoidLength[0] << ", " << EllipsoidLength[1] << ", " << EllipsoidLength[2] << ", angles " << EllipsoidAngle[0] << ", " << EllipsoidAngle[1] << ", " << EllipsoidAngle[2] << " with summed distance " << s->fval << ".");
233 }
234
235 } while (status == GSL_CONTINUE && iter < 1000);
236
237 gsl_vector_free(x);
238 gsl_vector_free(ss);
239 gsl_multimin_fminimizer_free (s);
240
241 } else {
242 LOG(3, "Not enough points provided for fit to ellipsoid.");
243 return false;
244 }
245 LOG(2, "End of FitPointSetToEllipsoid");
246 if (status == GSL_SUCCESS)
247 return true;
248 else
249 return false;
250};
251
252/** Picks a number of random points from a LC neighbourhood as a fitting set.
253 * \param *out output stream for debugging
254 * \param *T Tesselation containing boundary points
255 * \param *LC linked cell list of all atoms
256 * \param *&x random point set on return (not allocated!)
257 * \param PointsToPick number of points in set to pick
258 */
259void PickRandomNeighbouredPointSet(class Tesselation *T, class LinkedCell_deprecated *LC, Vector *&x, size_t PointsToPick)
260{
261 size_t PointsLeft = 0;
262 size_t PointsPicked = 0;
263 int Nlower[NDIM], Nupper[NDIM];
264 set<int> PickedAtomNrs; // ordered list of picked atoms
265 set<int>::iterator current;
266 int index;
267 TesselPoint *Candidate = NULL;
268 LOG(2, "Begin of PickRandomPointSet");
269
270 // allocate array
271 if (x == NULL) {
272 x = new Vector[PointsToPick];
273 } else {
274 ELOG(2, "Given pointer to vector array seems already allocated.");
275 }
276
277 RandomNumberGenerator &random = RandomNumberGeneratorFactory::getInstance().makeRandomNumberGenerator("mt19937", "uniform_int");
278 // check that random number generator's bounds are ok
279 ASSERT(random.min() == 0,
280 "PickRandomNeighbouredPointSet: Chosen RandomNumberGenerator's min "
281 +toString(random.min())+" is not 0!");
282 ASSERT(random.max() >= LC->N[0],
283 "PickRandomNeighbouredPointSet: Chosen RandomNumberGenerator's max "
284 +toString(random.max())+" is too small"+toString(LC->N[0])
285 +" for axis 0!");
286 ASSERT(random.max() >= LC->N[1],
287 "PickRandomNeighbouredPointSet: Chosen RandomNumberGenerator's max "
288 +toString(random.max())+" is too small"+toString(LC->N[1])
289 +" for axis 1!");
290 ASSERT(random.max() >= LC->N[2],
291 "PickRandomNeighbouredPointSet: Chosen RandomNumberGenerator's max "
292 +toString(random.max())+" is too small"+toString(LC->N[2])
293 +" for axis 2!");
294
295 do {
296 for(int i=0;i<NDIM;i++) // pick three random indices
297 LC->n[i] = ((int)random() % LC->N[i]);
298 LOG(2, "INFO: Center cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << ".");
299 // get random cell
300 const TesselPointSTLList *List = LC->GetCurrentCell();
301 if (List == NULL) { // set index to it
302 continue;
303 }
304 LOG(2, "INFO: Cell index is No. " << LC->index << ".");
305
306 if (DoLog(2)) {
307 std::stringstream output;
308 output << "LC Intervals:";
309 for (int i=0;i<NDIM;i++)
310 output << " [" << Nlower[i] << "," << Nupper[i] << "] ";
311 LOG(2, output.str());
312 }
313
314 for (int i=0;i<NDIM;i++) {
315 Nlower[i] = ((LC->n[i]-1) >= 0) ? LC->n[i]-1 : 0;
316 Nupper[i] = ((LC->n[i]+1) < LC->N[i]) ? LC->n[i]+1 : LC->N[i]-1;
317 }
318
319 // count whether there are sufficient atoms in this cell+neighbors
320 PointsLeft=0;
321 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
322 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
323 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
324 const TesselPointSTLList *List = LC->GetCurrentCell();
325 PointsLeft += List->size();
326 }
327 LOG(2, "There are " << PointsLeft << " atoms in this neighbourhood.");
328 if (PointsLeft < PointsToPick) { // ensure that we can pick enough points in its neighbourhood at all.
329 continue;
330 }
331
332 // pre-pick a fixed number of atoms
333 PickedAtomNrs.clear();
334 do {
335 index = (((int)random()) % PointsLeft);
336 current = PickedAtomNrs.find(index); // not present?
337 if (current == PickedAtomNrs.end()) {
338 //LOG(2, "Picking atom Nr. " << index << ".");
339 PickedAtomNrs.insert(index);
340 }
341 } while (PickedAtomNrs.size() < PointsToPick);
342
343 index = 0; // now go through all and pick those whose from PickedAtomsNr
344 PointsPicked=0;
345 current = PickedAtomNrs.begin();
346 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
347 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
348 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
349 const TesselPointSTLList *List = LC->GetCurrentCell();
350// LOG(2, "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << " containing " << List->size() << " points.");
351 if (List != NULL) {
352// if (List->begin() != List->end())
353// LOG(2, "Going through candidates ... ");
354// else
355// LOG(2, "Cell is empty ... ");
356 for (TesselPointSTLList::const_iterator Runner = List->begin(); Runner != List->end(); Runner++) {
357 if ((current != PickedAtomNrs.end()) && (*current == index)) {
358 Candidate = (*Runner);
359 LOG(2, "Current picked node is " << (*Runner)->getName() << " with index " << index << ".");
360 x[PointsPicked++] = Candidate->getPosition(); // we have one more atom picked
361 current++; // next pre-picked atom
362 }
363 index++; // next atom Nr.
364 }
365// } else {
366// LOG(2, "List for this index not allocated!");
367 }
368 }
369 LOG(2, "The following points were picked: ");
370 for (size_t i=0;i<PointsPicked;i++)
371 LOG(2, x[i]);
372 if (PointsPicked == PointsToPick) // break out of loop if we have all
373 break;
374 } while(1);
375
376 LOG(2, "End of PickRandomPointSet");
377};
378
379/** Picks a number of random points from a set of boundary points as a fitting set.
380 * \param *out output stream for debugging
381 * \param *T Tesselation containing boundary points
382 * \param *&x random point set on return (not allocated!)
383 * \param PointsToPick number of points in set to pick
384 */
385void PickRandomPointSet(class Tesselation *T, Vector *&x, size_t PointsToPick)
386{
387 size_t PointsLeft = (size_t) T->PointsOnBoundaryCount;
388 size_t PointsPicked = 0;
389 double value, threshold;
390 PointMap *List = &T->PointsOnBoundary;
391 LOG(2, "Begin of PickRandomPointSet");
392
393 // allocate array
394 if (x == NULL) {
395 x = new Vector[PointsToPick];
396 } else {
397 ELOG(2, "Given pointer to vector array seems already allocated.");
398 }
399
400 RandomNumberGenerator &random = RandomNumberGeneratorFactory::getInstance().makeRandomNumberGenerator("mt19937", "uniform_int");
401 const double rng_min = random.min();
402 const double rng_max = random.max();
403 if (List != NULL)
404 for (PointMap::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
405 threshold = 1. - (double)(PointsToPick - PointsPicked)/(double)PointsLeft;
406 value = (double)random()/(double)(rng_max-rng_min);
407 if (value > threshold) {
408 x[PointsPicked] = (Runner->second->node->getPosition());
409 PointsPicked++;
410 //LOG(3, "Current node is " << *Runner->second->node << " with " << value << " ... " << threshold << ": IN.");
411 } else {
412 //LOG(3, "Current node is " << *Runner->second->node << " with " << value << " ... " << threshold << ": OUT.");
413 }
414 PointsLeft--;
415 }
416 LOG(2, "The following points were picked: ");
417 for (size_t i=0;i<PointsPicked;i++)
418 LOG(3, x[i]);
419
420 LOG(2, "End of PickRandomPointSet");
421};
422
423/** Finds best fitting ellipsoid parameter set in least square sense for a given point set.
424 * \param *out output stream for debugging
425 * \param *T Tesselation containing boundary points
426 * \param *LCList linked cell list of all atoms
427 * \param N number of unique points in ellipsoid fit, must be greater equal 6
428 * \param number of fits (i.e. parameter sets in output file)
429 * \param *filename name for output file
430 */
431void FindDistributionOfEllipsoids(class Tesselation *T, class LinkedCell_deprecated *LCList, int N, int number, const char *filename)
432{
433 ofstream output;
434 Vector *x = NULL;
435 Vector Center;
436 Vector EllipsoidCenter;
437 double EllipsoidLength[3];
438 double EllipsoidAngle[3];
439 double distance, MaxDistance, MinDistance;
440 LOG(0, "Begin of FindDistributionOfEllipsoids");
441
442 // construct center of gravity of boundary point set for initial ellipsoid center
443 Center.Zero();
444 for (PointMap::iterator Runner = T->PointsOnBoundary.begin(); Runner != T->PointsOnBoundary.end(); Runner++)
445 Center += (Runner->second->node->getPosition());
446 Center.Scale(1./T->PointsOnBoundaryCount);
447 LOG(4, "DEBUG: Center of PointsOnBoundary is at " << Center << ".");
448
449 // Output header
450 output.open(filename, ios::trunc);
451 output << "# Nr.\tCenterX\tCenterY\tCenterZ\ta\tb\tc\tpsi\ttheta\tphi" << endl;
452
453 // loop over desired number of parameter sets
454 for (;number >0;number--) {
455 LOG(1, "Determining data set " << number << " ... ");
456 // pick the point set
457 x = NULL;
458 //PickRandomPointSet(T, LCList, x, N);
459 PickRandomNeighbouredPointSet(T, LCList, x, N);
460
461 // calculate some sensible starting values for parameter fit
462 MaxDistance = 0.;
463 MinDistance = x[0].ScalarProduct(x[0]);
464 for (int i=0;i<N;i++) {
465 distance = x[i].ScalarProduct(x[i]);
466 if (distance > MaxDistance)
467 MaxDistance = distance;
468 if (distance < MinDistance)
469 MinDistance = distance;
470 }
471 //LOG(2, "MinDistance " << MinDistance << ", MaxDistance " << MaxDistance << ".");
472 EllipsoidCenter = Center; // use Center of Gravity as initial center of ellipsoid
473 for (int i=0;i<3;i++)
474 EllipsoidAngle[i] = 0.;
475 EllipsoidLength[0] = sqrt(MaxDistance);
476 EllipsoidLength[1] = sqrt((MaxDistance+MinDistance)/2.);
477 EllipsoidLength[2] = sqrt(MinDistance);
478
479 // fit the parameters
480 if (FitPointSetToEllipsoid(x, N, &EllipsoidCenter, &EllipsoidLength[0], &EllipsoidAngle[0])) {
481 LOG(1, "Picking succeeded!");
482 // output obtained parameter set
483 output << number << "\t";
484 for (int i=0;i<3;i++)
485 output << setprecision(9) << EllipsoidCenter[i] << "\t";
486 for (int i=0;i<3;i++)
487 output << setprecision(9) << EllipsoidLength[i] << "\t";
488 for (int i=0;i<3;i++)
489 output << setprecision(9) << EllipsoidAngle[i] << "\t";
490 output << endl;
491 } else { // increase N to pick one more
492 LOG(1, "Picking failed!");
493 number++;
494 }
495 delete[](x); // free allocated memory for point set
496 }
497 // close output and finish
498 output.close();
499
500 LOG(0, "End of FindDistributionOfEllipsoids");
501};
Note: See TracBrowser for help on using the repository browser.