source: molecuilder/src/ellipsoid.cpp@ 447896

Last change on this file since 447896 was 1f2e46, checked in by Frederik Heber <heber@…>, 15 years ago

Huge change: Log() << Verbose(.) --> DoLog(.) && (Log() << Verbose(.) << ...);

Most of the files are affected, but this is necessary as if DoLog() says verbosity is not enough, all the stream operators won"t get executed which saves substantial amount of computation time.

Signed-off-by: Frederik Heber <heber@…>

  • Property mode set to 100644
File size: 17.0 KB
Line 
1/*
2 * ellipsoid.cpp
3 *
4 * Created on: Jan 20, 2009
5 * Author: heber
6 */
7
8#include <gsl/gsl_multimin.h>
9#include <gsl/gsl_vector.h>
10
11#include <iomanip>
12
13#include <set>
14
15#include "boundary.hpp"
16#include "ellipsoid.hpp"
17#include "linkedcell.hpp"
18#include "log.hpp"
19#include "tesselation.hpp"
20#include "vector.hpp"
21#include "verbose.hpp"
22
23/** Determines squared distance for a given point \a x to surface of ellipsoid.
24 * \param x given point
25 * \param EllipsoidCenter center of ellipsoid
26 * \param EllipsoidLength[3] three lengths of half axis of ellipsoid
27 * \param EllipsoidAngle[3] three rotation angles of ellipsoid
28 * \return squared distance from point to surface
29 */
30double SquaredDistanceToEllipsoid(Vector &x, Vector &EllipsoidCenter, double *EllipsoidLength, double *EllipsoidAngle)
31{
32 Vector helper, RefPoint;
33 double distance = -1.;
34 double Matrix[NDIM*NDIM];
35 double InverseLength[3];
36 double psi,theta,phi; // euler angles in ZX'Z'' convention
37
38 //Log() << Verbose(3) << "Begin of SquaredDistanceToEllipsoid" << endl;
39
40 for(int i=0;i<3;i++)
41 InverseLength[i] = 1./EllipsoidLength[i];
42
43 // 1. translate coordinate system so that ellipsoid center is in origin
44 helper.CopyVector(&x);
45 helper.SubtractVector(&EllipsoidCenter);
46 RefPoint.CopyVector(&helper);
47 //Log() << Verbose(4) << "Translated given point is at " << RefPoint << "." << endl;
48
49 // 2. transform coordinate system by inverse of rotation matrix and of diagonal matrix
50 psi = EllipsoidAngle[0];
51 theta = EllipsoidAngle[1];
52 phi = EllipsoidAngle[2];
53 Matrix[0] = cos(psi)*cos(phi) - sin(psi)*cos(theta)*sin(phi);
54 Matrix[1] = -cos(psi)*sin(phi) - sin(psi)*cos(theta)*cos(phi);
55 Matrix[2] = sin(psi)*sin(theta);
56 Matrix[3] = sin(psi)*cos(phi) + cos(psi)*cos(theta)*sin(phi);
57 Matrix[4] = cos(psi)*cos(theta)*cos(phi) - sin(psi)*sin(phi);
58 Matrix[5] = -cos(psi)*sin(theta);
59 Matrix[6] = sin(theta)*sin(phi);
60 Matrix[7] = sin(theta)*cos(phi);
61 Matrix[8] = cos(theta);
62 helper.MatrixMultiplication(Matrix);
63 helper.Scale(InverseLength);
64 //Log() << Verbose(4) << "Transformed RefPoint is at " << helper << "." << endl;
65
66 // 3. construct intersection point with unit sphere and ray between origin and x
67 helper.Normalize(); // is simply normalizes vector in distance direction
68 //Log() << Verbose(4) << "Transformed intersection is at " << helper << "." << endl;
69
70 // 4. transform back the constructed intersection point
71 psi = -EllipsoidAngle[0];
72 theta = -EllipsoidAngle[1];
73 phi = -EllipsoidAngle[2];
74 helper.Scale(EllipsoidLength);
75 Matrix[0] = cos(psi)*cos(phi) - sin(psi)*cos(theta)*sin(phi);
76 Matrix[1] = -cos(psi)*sin(phi) - sin(psi)*cos(theta)*cos(phi);
77 Matrix[2] = sin(psi)*sin(theta);
78 Matrix[3] = sin(psi)*cos(phi) + cos(psi)*cos(theta)*sin(phi);
79 Matrix[4] = cos(psi)*cos(theta)*cos(phi) - sin(psi)*sin(phi);
80 Matrix[5] = -cos(psi)*sin(theta);
81 Matrix[6] = sin(theta)*sin(phi);
82 Matrix[7] = sin(theta)*cos(phi);
83 Matrix[8] = cos(theta);
84 helper.MatrixMultiplication(Matrix);
85 //Log() << Verbose(4) << "Intersection is at " << helper << "." << endl;
86
87 // 5. determine distance between backtransformed point and x
88 distance = RefPoint.DistanceSquared(&helper);
89 //Log() << Verbose(4) << "Squared distance between intersection and RefPoint is " << distance << "." << endl;
90
91 return distance;
92 //Log() << Verbose(3) << "End of SquaredDistanceToEllipsoid" << endl;
93};
94
95/** structure for ellipsoid minimisation containing points to fit to.
96 */
97struct EllipsoidMinimisation {
98 int N; //!< dimension of vector set
99 Vector *x; //!< array of vectors
100};
101
102/** Sum of squared distance to ellipsoid to be minimised.
103 * \param *x parameters for the ellipsoid
104 * \param *params EllipsoidMinimisation with set of data points to minimise distance to and dimension
105 * \return sum of squared distance, \sa SquaredDistanceToEllipsoid()
106 */
107double SumSquaredDistance (const gsl_vector * x, void * params)
108{
109 Vector *set= ((struct EllipsoidMinimisation *)params)->x;
110 int N = ((struct EllipsoidMinimisation *)params)->N;
111 double SumDistance = 0.;
112 double distance;
113 Vector Center;
114 double EllipsoidLength[3], EllipsoidAngle[3];
115
116 // put parameters into suitable ellipsoid form
117 for (int i=0;i<3;i++) {
118 Center.x[i] = gsl_vector_get(x, i+0);
119 EllipsoidLength[i] = gsl_vector_get(x, i+3);
120 EllipsoidAngle[i] = gsl_vector_get(x, i+6);
121 }
122
123 // go through all points and sum distance
124 for (int i=0;i<N;i++) {
125 distance = SquaredDistanceToEllipsoid(set[i], Center, EllipsoidLength, EllipsoidAngle);
126 if (!isnan(distance)) {
127 SumDistance += distance;
128 } else {
129 SumDistance = GSL_NAN;
130 break;
131 }
132 }
133
134 //Log() << Verbose(0) << "Current summed distance is " << SumDistance << "." << endl;
135 return SumDistance;
136};
137
138/** Finds best fitting ellipsoid parameter set in Least square sense for a given point set.
139 * \param *out output stream for debugging
140 * \param *set given point set
141 * \param N number of points in set
142 * \param EllipsoidParamter[3] three parameters in ellipsoid equation
143 * \return true - fit successful, false - fit impossible
144 */
145bool FitPointSetToEllipsoid(Vector *set, int N, Vector *EllipsoidCenter, double *EllipsoidLength, double *EllipsoidAngle)
146{
147 int status = GSL_SUCCESS;
148 DoLog(2) && (Log() << Verbose(2) << "Begin of FitPointSetToEllipsoid " << endl);
149 if (N >= 3) { // check that enough points are given (9 d.o.f.)
150 struct EllipsoidMinimisation par;
151 const gsl_multimin_fminimizer_type *T = gsl_multimin_fminimizer_nmsimplex;
152 gsl_multimin_fminimizer *s = NULL;
153 gsl_vector *ss, *x;
154 gsl_multimin_function minex_func;
155
156 size_t iter = 0;
157 double size;
158
159 /* Starting point */
160 x = gsl_vector_alloc (9);
161 for (int i=0;i<3;i++) {
162 gsl_vector_set (x, i+0, EllipsoidCenter->x[i]);
163 gsl_vector_set (x, i+3, EllipsoidLength[i]);
164 gsl_vector_set (x, i+6, EllipsoidAngle[i]);
165 }
166 par.x = set;
167 par.N = N;
168
169 /* Set initial step sizes */
170 ss = gsl_vector_alloc (9);
171 for (int i=0;i<3;i++) {
172 gsl_vector_set (ss, i+0, 0.1);
173 gsl_vector_set (ss, i+3, 1.0);
174 gsl_vector_set (ss, i+6, M_PI/20.);
175 }
176
177 /* Initialize method and iterate */
178 minex_func.n = 9;
179 minex_func.f = &SumSquaredDistance;
180 minex_func.params = (void *)&par;
181
182 s = gsl_multimin_fminimizer_alloc (T, 9);
183 gsl_multimin_fminimizer_set (s, &minex_func, x, ss);
184
185 do {
186 iter++;
187 status = gsl_multimin_fminimizer_iterate(s);
188
189 if (status)
190 break;
191
192 size = gsl_multimin_fminimizer_size (s);
193 status = gsl_multimin_test_size (size, 1e-2);
194
195 if (status == GSL_SUCCESS) {
196 for (int i=0;i<3;i++) {
197 EllipsoidCenter->x[i] = gsl_vector_get (s->x,i+0);
198 EllipsoidLength[i] = gsl_vector_get (s->x, i+3);
199 EllipsoidAngle[i] = gsl_vector_get (s->x, i+6);
200 }
201 DoLog(4) && (Log() << Verbose(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 << "." << endl);
202 }
203
204 } while (status == GSL_CONTINUE && iter < 1000);
205
206 gsl_vector_free(x);
207 gsl_vector_free(ss);
208 gsl_multimin_fminimizer_free (s);
209
210 } else {
211 DoLog(3) && (Log() << Verbose(3) << "Not enough points provided for fit to ellipsoid." << endl);
212 return false;
213 }
214 DoLog(2) && (Log() << Verbose(2) << "End of FitPointSetToEllipsoid" << endl);
215 if (status == GSL_SUCCESS)
216 return true;
217 else
218 return false;
219};
220
221/** Picks a number of random points from a LC neighbourhood as a fitting set.
222 * \param *out output stream for debugging
223 * \param *T Tesselation containing boundary points
224 * \param *LC linked cell list of all atoms
225 * \param *&x random point set on return (not allocated!)
226 * \param PointsToPick number of points in set to pick
227 */
228void PickRandomNeighbouredPointSet(class Tesselation *T, class LinkedCell *LC, Vector *&x, size_t PointsToPick)
229{
230 size_t PointsLeft = 0;
231 size_t PointsPicked = 0;
232 int Nlower[NDIM], Nupper[NDIM];
233 set<int> PickedAtomNrs; // ordered list of picked atoms
234 set<int>::iterator current;
235 int index;
236 TesselPoint *Candidate = NULL;
237 DoLog(2) && (Log() << Verbose(2) << "Begin of PickRandomPointSet" << endl);
238
239 // allocate array
240 if (x == NULL) {
241 x = new Vector[PointsToPick];
242 } else {
243 DoeLog(2) && (eLog()<< Verbose(2) << "Given pointer to vector array seems already allocated." << endl);
244 }
245
246 do {
247 for(int i=0;i<NDIM;i++) // pick three random indices
248 LC->n[i] = (rand() % LC->N[i]);
249 DoLog(2) && (Log() << Verbose(2) << "INFO: Center cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " ... ");
250 // get random cell
251 const LinkedCell::LinkedNodes *List = LC->GetCurrentCell();
252 if (List == NULL) { // set index to it
253 continue;
254 }
255 DoLog(2) && (Log() << Verbose(2) << "with No. " << LC->index << "." << endl);
256
257 DoLog(2) && (Log() << Verbose(2) << "LC Intervals:");
258 for (int i=0;i<NDIM;i++) {
259 Nlower[i] = ((LC->n[i]-1) >= 0) ? LC->n[i]-1 : 0;
260 Nupper[i] = ((LC->n[i]+1) < LC->N[i]) ? LC->n[i]+1 : LC->N[i]-1;
261 DoLog(0) && (Log() << Verbose(0) << " [" << Nlower[i] << "," << Nupper[i] << "] ");
262 }
263 DoLog(0) && (Log() << Verbose(0) << endl);
264
265 // count whether there are sufficient atoms in this cell+neighbors
266 PointsLeft=0;
267 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
268 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
269 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
270 const LinkedCell::LinkedNodes *List = LC->GetCurrentCell();
271 PointsLeft += List->size();
272 }
273 DoLog(2) && (Log() << Verbose(2) << "There are " << PointsLeft << " atoms in this neighbourhood." << endl);
274 if (PointsLeft < PointsToPick) { // ensure that we can pick enough points in its neighbourhood at all.
275 continue;
276 }
277
278 // pre-pick a fixed number of atoms
279 PickedAtomNrs.clear();
280 do {
281 index = (rand() % PointsLeft);
282 current = PickedAtomNrs.find(index); // not present?
283 if (current == PickedAtomNrs.end()) {
284 //Log() << Verbose(2) << "Picking atom nr. " << index << "." << endl;
285 PickedAtomNrs.insert(index);
286 }
287 } while (PickedAtomNrs.size() < PointsToPick);
288
289 index = 0; // now go through all and pick those whose from PickedAtomsNr
290 PointsPicked=0;
291 current = PickedAtomNrs.begin();
292 for (LC->n[0] = Nlower[0]; LC->n[0] <= Nupper[0]; LC->n[0]++)
293 for (LC->n[1] = Nlower[1]; LC->n[1] <= Nupper[1]; LC->n[1]++)
294 for (LC->n[2] = Nlower[2]; LC->n[2] <= Nupper[2]; LC->n[2]++) {
295 const LinkedCell::LinkedNodes *List = LC->GetCurrentCell();
296// Log() << Verbose(2) << "Current cell is " << LC->n[0] << ", " << LC->n[1] << ", " << LC->n[2] << " with No. " << LC->index << " containing " << List->size() << " points." << endl;
297 if (List != NULL) {
298// if (List->begin() != List->end())
299// Log() << Verbose(2) << "Going through candidates ... " << endl;
300// else
301// Log() << Verbose(2) << "Cell is empty ... " << endl;
302 for (LinkedCell::LinkedNodes::const_iterator Runner = List->begin(); Runner != List->end(); Runner++) {
303 if ((current != PickedAtomNrs.end()) && (*current == index)) {
304 Candidate = (*Runner);
305 DoLog(2) && (Log() << Verbose(2) << "Current picked node is " << **Runner << " with index " << index << "." << endl);
306 x[PointsPicked++].CopyVector(Candidate->node); // we have one more atom picked
307 current++; // next pre-picked atom
308 }
309 index++; // next atom nr.
310 }
311// } else {
312// Log() << Verbose(2) << "List for this index not allocated!" << endl;
313 }
314 }
315 DoLog(2) && (Log() << Verbose(2) << "The following points were picked: " << endl);
316 for (size_t i=0;i<PointsPicked;i++)
317 DoLog(2) && (Log() << Verbose(2) << x[i] << endl);
318 if (PointsPicked == PointsToPick) // break out of loop if we have all
319 break;
320 } while(1);
321
322 DoLog(2) && (Log() << Verbose(2) << "End of PickRandomPointSet" << endl);
323};
324
325/** Picks a number of random points from a set of boundary points as a fitting set.
326 * \param *out output stream for debugging
327 * \param *T Tesselation containing boundary points
328 * \param *&x random point set on return (not allocated!)
329 * \param PointsToPick number of points in set to pick
330 */
331void PickRandomPointSet(class Tesselation *T, Vector *&x, size_t PointsToPick)
332{
333 size_t PointsLeft = (size_t) T->PointsOnBoundaryCount;
334 size_t PointsPicked = 0;
335 double value, threshold;
336 PointMap *List = &T->PointsOnBoundary;
337 DoLog(2) && (Log() << Verbose(2) << "Begin of PickRandomPointSet" << endl);
338
339 // allocate array
340 if (x == NULL) {
341 x = new Vector[PointsToPick];
342 } else {
343 DoeLog(2) && (eLog()<< Verbose(2) << "Given pointer to vector array seems already allocated." << endl);
344 }
345
346 if (List != NULL)
347 for (PointMap::iterator Runner = List->begin(); Runner != List->end(); Runner++) {
348 threshold = 1. - (double)(PointsToPick - PointsPicked)/(double)PointsLeft;
349 value = (double)rand()/(double)RAND_MAX;
350 //Log() << Verbose(3) << "Current node is " << *Runner->second->node << " with " << value << " ... " << threshold << ": ";
351 if (value > threshold) {
352 x[PointsPicked].CopyVector(Runner->second->node->node);
353 PointsPicked++;
354 //Log() << Verbose(0) << "IN." << endl;
355 } else {
356 //Log() << Verbose(0) << "OUT." << endl;
357 }
358 PointsLeft--;
359 }
360 DoLog(2) && (Log() << Verbose(2) << "The following points were picked: " << endl);
361 for (size_t i=0;i<PointsPicked;i++)
362 DoLog(3) && (Log() << Verbose(3) << x[i] << endl);
363
364 DoLog(2) && (Log() << Verbose(2) << "End of PickRandomPointSet" << endl);
365};
366
367/** Finds best fitting ellipsoid parameter set in least square sense for a given point set.
368 * \param *out output stream for debugging
369 * \param *T Tesselation containing boundary points
370 * \param *LCList linked cell list of all atoms
371 * \param N number of unique points in ellipsoid fit, must be greater equal 6
372 * \param number of fits (i.e. parameter sets in output file)
373 * \param *filename name for output file
374 */
375void FindDistributionOfEllipsoids(class Tesselation *T, class LinkedCell *LCList, int N, int number, const char *filename)
376{
377 ofstream output;
378 Vector *x = NULL;
379 Vector Center;
380 Vector EllipsoidCenter;
381 double EllipsoidLength[3];
382 double EllipsoidAngle[3];
383 double distance, MaxDistance, MinDistance;
384 DoLog(0) && (Log() << Verbose(0) << "Begin of FindDistributionOfEllipsoids" << endl);
385
386 // construct center of gravity of boundary point set for initial ellipsoid center
387 Center.Zero();
388 for (PointMap::iterator Runner = T->PointsOnBoundary.begin(); Runner != T->PointsOnBoundary.end(); Runner++)
389 Center.AddVector(Runner->second->node->node);
390 Center.Scale(1./T->PointsOnBoundaryCount);
391 DoLog(1) && (Log() << Verbose(1) << "Center is at " << Center << "." << endl);
392
393 // Output header
394 output.open(filename, ios::trunc);
395 output << "# Nr.\tCenterX\tCenterY\tCenterZ\ta\tb\tc\tpsi\ttheta\tphi" << endl;
396
397 // loop over desired number of parameter sets
398 for (;number >0;number--) {
399 DoLog(1) && (Log() << Verbose(1) << "Determining data set " << number << " ... " << endl);
400 // pick the point set
401 x = NULL;
402 //PickRandomPointSet(T, LCList, x, N);
403 PickRandomNeighbouredPointSet(T, LCList, x, N);
404
405 // calculate some sensible starting values for parameter fit
406 MaxDistance = 0.;
407 MinDistance = x[0].ScalarProduct(&x[0]);
408 for (int i=0;i<N;i++) {
409 distance = x[i].ScalarProduct(&x[i]);
410 if (distance > MaxDistance)
411 MaxDistance = distance;
412 if (distance < MinDistance)
413 MinDistance = distance;
414 }
415 //Log() << Verbose(2) << "MinDistance " << MinDistance << ", MaxDistance " << MaxDistance << "." << endl;
416 EllipsoidCenter.CopyVector(&Center); // use Center of Gravity as initial center of ellipsoid
417 for (int i=0;i<3;i++)
418 EllipsoidAngle[i] = 0.;
419 EllipsoidLength[0] = sqrt(MaxDistance);
420 EllipsoidLength[1] = sqrt((MaxDistance+MinDistance)/2.);
421 EllipsoidLength[2] = sqrt(MinDistance);
422
423 // fit the parameters
424 if (FitPointSetToEllipsoid(x, N, &EllipsoidCenter, &EllipsoidLength[0], &EllipsoidAngle[0])) {
425 DoLog(1) && (Log() << Verbose(1) << "Picking succeeded!" << endl);
426 // output obtained parameter set
427 output << number << "\t";
428 for (int i=0;i<3;i++)
429 output << setprecision(9) << EllipsoidCenter.x[i] << "\t";
430 for (int i=0;i<3;i++)
431 output << setprecision(9) << EllipsoidLength[i] << "\t";
432 for (int i=0;i<3;i++)
433 output << setprecision(9) << EllipsoidAngle[i] << "\t";
434 output << endl;
435 } else { // increase N to pick one more
436 DoLog(1) && (Log() << Verbose(1) << "Picking failed!" << endl);
437 number++;
438 }
439 delete[](x); // free allocated memory for point set
440 }
441 // close output and finish
442 output.close();
443
444 DoLog(0) && (Log() << Verbose(0) << "End of FindDistributionOfEllipsoids" << endl);
445};
Note: See TracBrowser for help on using the repository browser.