source: src/Fragmentation/Exporters/SphericalPointDistribution.cpp@ d734ff

Last change on this file since d734ff was 0d5ca7, checked in by Frederik Heber <heber@…>, 11 years ago

FIX: SphericalPointDistribution succeeds with unit test.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/*
2 * Project: MoleCuilder
3 * Description: creates and alters molecular systems
4 * Copyright (C) 2014 Frederik Heber. 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 * SphericalPointDistribution.cpp
25 *
26 * Created on: May 30, 2014
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 "SphericalPointDistribution.hpp"
38
39#include "CodePatterns/Assert.hpp"
40#include "CodePatterns/IteratorAdaptors.hpp"
41#include "CodePatterns/Log.hpp"
42#include "CodePatterns/toString.hpp"
43
44#include <algorithm>
45#include <cmath>
46#include <functional>
47#include <iterator>
48#include <limits>
49#include <list>
50#include <vector>
51#include <map>
52
53#include "LinearAlgebra/Line.hpp"
54#include "LinearAlgebra/RealSpaceMatrix.hpp"
55#include "LinearAlgebra/Vector.hpp"
56
57typedef std::list<unsigned int> IndexList_t;
58typedef std::vector<unsigned int> IndexArray_t;
59typedef std::vector<Vector> VectorArray_t;
60typedef std::vector<double> DistanceArray_t;
61
62DistanceArray_t calculatePairwiseDistances(
63 const std::vector<Vector> &_points,
64 const IndexList_t &_indices
65 )
66{
67 DistanceArray_t result;
68 for (IndexList_t::const_iterator firstiter = _indices.begin();
69 firstiter != _indices.end(); ++firstiter) {
70 for (IndexList_t::const_iterator seconditer = firstiter;
71 seconditer != _indices.end(); ++seconditer) {
72 if (firstiter == seconditer)
73 continue;
74 const double distance = (_points[*firstiter] - _points[*seconditer]).NormSquared();
75 result.push_back(distance);
76 }
77 }
78 return result;
79}
80
81// class generator: taken from www.cplusplus.com example std::generate
82struct c_unique {
83 int current;
84 c_unique() {current=0;}
85 int operator()() {return current++;}
86} UniqueNumber;
87
88/** Returns squared L2 error of the given \a _Matching.
89 *
90 * We compare the pair-wise distances of each associated matching
91 * and check whether these distances each match between \a _old and
92 * \a _new.
93 *
94 * \param _old first set of points (fewer or equal to \a _new)
95 * \param _new second set of points
96 * \param _Matching matching between the two sets
97 * \return pair with L1 and squared L2 error
98 */
99std::pair<double, double> calculateErrorOfMatching(
100 const std::vector<Vector> &_old,
101 const std::vector<Vector> &_new,
102 const IndexList_t &_Matching)
103{
104 std::pair<double, double> errors( std::make_pair( 0., 0. ) );
105
106 if (_Matching.size() > 1) {
107 LOG(3, "INFO: Matching is " << _Matching);
108
109 // calculate all pair-wise distances
110 IndexList_t keys(_Matching.size());
111 std::generate (keys.begin(), keys.end(), UniqueNumber);
112 const DistanceArray_t firstdistances = calculatePairwiseDistances(_old, keys);
113 const DistanceArray_t seconddistances = calculatePairwiseDistances(_new, _Matching);
114
115 ASSERT( firstdistances.size() == seconddistances.size(),
116 "calculateL2ErrorOfMatching() - mismatch in pair-wise distance array sizes.");
117 DistanceArray_t::const_iterator firstiter = firstdistances.begin();
118 DistanceArray_t::const_iterator seconditer = seconddistances.begin();
119 for (;(firstiter != firstdistances.end()) && (seconditer != seconddistances.end());
120 ++firstiter, ++seconditer) {
121 const double gap = *firstiter - *seconditer;
122 // L1 error
123 if (errors.first < gap)
124 errors.first = gap;
125 // L2 error
126 errors.second += gap*gap;
127 }
128 } else
129 ELOG(3, "calculateErrorOfMatching() - Given matching's size is less than 2.");
130 LOG(3, "INFO: Resulting errors for matching (L1, L2): "
131 << errors.first << "," << errors.second << ".");
132
133 return errors;
134}
135
136SphericalPointDistribution::Polygon_t removeMatchingPoints(
137 const SphericalPointDistribution::Polygon_t &_points,
138 const IndexList_t &_matchingindices
139 )
140{
141 SphericalPointDistribution::Polygon_t remainingpoints;
142 IndexArray_t indices(_matchingindices.begin(), _matchingindices.end());
143 std::sort(indices.begin(), indices.end());
144 LOG(4, "DEBUG: sorted matching is " << indices);
145 IndexArray_t::const_iterator valueiter = indices.begin();
146 SphericalPointDistribution::Polygon_t::const_iterator pointiter =
147 _points.begin();
148 for (unsigned int i=0; i< _points.size(); ++i, ++pointiter) {
149 // skip all those in values
150 if (*valueiter == i)
151 ++valueiter;
152 else
153 remainingpoints.push_back(*pointiter);
154 }
155 LOG(4, "DEBUG: remaining indices are " << remainingpoints);
156
157 return remainingpoints;
158}
159
160struct MatchingControlStructure {
161 bool foundflag;
162 double bestL2;
163 IndexList_t bestmatching;
164 VectorArray_t oldpoints;
165 VectorArray_t newpoints;
166};
167
168/** Recursive function to go through all possible matchings.
169 *
170 * \param _MCS structure holding global information to the recursion
171 * \param _matching current matching being build up
172 * \param _indices contains still available indices
173 * \param _matchingsize
174 */
175void recurseMatchings(
176 MatchingControlStructure &_MCS,
177 IndexList_t &_matching,
178 IndexList_t _indices,
179 unsigned int _matchingsize)
180{
181 LOG(4, "DEBUG: Recursing with current matching " << _matching
182 << ", remaining indices " << _indices
183 << ", and sought size " << _matching.size()+_matchingsize);
184 //!> threshold for L1 error below which matching is immediately acceptable
185 const double L1THRESHOLD = 1e-2;
186 if (!_MCS.foundflag) {
187 LOG(4, "DEBUG: Current matching has size " << _matching.size() << ", places left " << _matchingsize);
188 if (_matchingsize > 0) {
189 // go through all indices
190 for (IndexList_t::iterator iter = _indices.begin();
191 (iter != _indices.end()) && (!_MCS.foundflag);) {
192 // add index to matching
193 _matching.push_back(*iter);
194 LOG(5, "DEBUG: Adding " << *iter << " to matching.");
195 // remove index but keep iterator to position (is the next to erase element)
196 IndexList_t::iterator backupiter = _indices.erase(iter);
197 // recurse with decreased _matchingsize
198 recurseMatchings(_MCS, _matching, _indices, _matchingsize-1);
199 // re-add chosen index and reset index to new position
200 _indices.insert(backupiter, _matching.back());
201 iter = backupiter;
202 // remove index from _matching to make space for the next one
203 _matching.pop_back();
204 }
205 // gone through all indices then exit recursion
206 if (_matching.empty())
207 _MCS.foundflag = true;
208 } else {
209 LOG(3, "INFO: Found matching " << _matching);
210 // calculate errors
211 std::pair<double, double> errors = calculateErrorOfMatching(
212 _MCS.oldpoints, _MCS.newpoints, _matching);
213 if (errors.first < L1THRESHOLD) {
214 _MCS.bestmatching = _matching;
215 _MCS.foundflag = true;
216 } else if (_MCS.bestL2 > errors.second) {
217 _MCS.bestmatching = _matching;
218 _MCS.bestL2 = errors.second;
219 }
220 }
221 }
222}
223
224/** Convert cartesian to polar coordinates.
225 *
226 * \param _cartesian vector in cartesian coordinates
227 * \return vector containing \f$ (r,\theta, \varphi \f$ tuple for polar coordinates
228 */
229std::vector<double> getPolarCoordinates(const Vector &_cartesian)
230{
231 std::vector<double> polar(3,0.);
232 const double xsqr = _cartesian[0] * _cartesian[0];
233 const double ysqr = _cartesian[1] * _cartesian[1];
234 polar[0] = sqrt(xsqr + ysqr + _cartesian[2]*_cartesian[2]);
235 if (fabs(_cartesian[2]) < std::numeric_limits<double>::epsilon()*1e4) {
236 if (fabs(xsqr + ysqr) < std::numeric_limits<double>::epsilon()*1e4) {
237 polar[1] = 0.;
238 } else {
239 // xsqr + ysqr is always non-negative
240 polar[1] = M_PI/2.;
241 }
242 } else {
243 polar[1] = atan( sqrt(xsqr + ysqr)/_cartesian[2]);
244 if (_cartesian[2] <= -std::numeric_limits<double>::epsilon()*1e4)
245 polar[1] += M_PI;
246 }
247
248 if (fabs(_cartesian[0]) < std::numeric_limits<double>::epsilon()*1e4) {
249 if (fabs(_cartesian[1]) < std::numeric_limits<double>::epsilon()*1e4) {
250 polar[2] = 0.;
251 } else if (_cartesian[1] > std::numeric_limits<double>::epsilon()*1e4) {
252 polar[2] = M_PI/2.;
253 } else {
254 polar[2] = -M_PI/2.;
255 }
256 } else {
257 polar[2] = atan ( _cartesian[1]/_cartesian[0] );
258 if (_cartesian[0] <= -std::numeric_limits<double>::epsilon()*1e4)
259 polar[2] += M_PI;
260 }
261 return polar;
262}
263
264/** Calculate cartesian coordinates from given polar ones.
265 *
266 * \param _polar vector with polar coordinates
267 * \return cartesian coordinates
268 */
269Vector getCartesianCoordinates(const std::vector<double> &_polar)
270{
271 Vector cartesian;
272 ASSERT( _polar.size() == 3,
273 "convertToCartesianCoordinates() - tuples has insufficient components.");
274 cartesian[0] = _polar[0] * sin(_polar[1]) * cos(_polar[2]);
275 cartesian[1] = _polar[0] * sin(_polar[1]) * sin(_polar[2]);
276 cartesian[2] = _polar[0] * cos(_polar[1]);
277 return cartesian;
278}
279
280/** Rotates a given polygon around x, y, and z axis by the given angles.
281 *
282 * \param _polygon polygon whose points to rotate
283 * \param _angles vector with rotation angles for x,y,z axis
284 */
285SphericalPointDistribution::Polygon_t rotatePolygon(
286 const SphericalPointDistribution::Polygon_t &_polygon,
287 const std::vector<double> &_angles)
288{
289 SphericalPointDistribution::Polygon_t rotated_polygon = _polygon;
290 RealSpaceMatrix rotation;
291 ASSERT( _angles.size() == 3,
292 "rotatePolygon() - not exactly "+toString(3)+" components given.");
293
294 // apply rotation angles
295 for (SphericalPointDistribution::Polygon_t::iterator iter = rotated_polygon.begin();
296 iter != rotated_polygon.end(); ++iter) {
297 // transform to polar
298 std::vector<double> polar = getPolarCoordinates(*iter);
299 LOG(5, "DEBUG: Converting " << *iter << " to " << polar);
300 // sum up difference
301 std::transform(
302 _angles.begin(), _angles.end(),
303 polar.begin(),
304 polar.begin(),
305 std::plus<double>());
306 // convert back
307 *iter = getCartesianCoordinates(polar);
308 LOG(5, "DEBUG: Converting modified " << polar << " to " << *iter);
309 }
310
311 return rotated_polygon;
312}
313
314
315SphericalPointDistribution::Polygon_t
316SphericalPointDistribution::matchSphericalPointDistributions(
317 const SphericalPointDistribution::Polygon_t &_polygon,
318 const SphericalPointDistribution::Polygon_t &_newpolygon
319 )
320{
321 SphericalPointDistribution::Polygon_t remainingpoints;
322 VectorArray_t remainingold(_polygon.begin(), _polygon.end());
323 VectorArray_t remainingnew(_newpolygon.begin(), _newpolygon.end());
324 LOG(2, "INFO: Matching old polygon " << _polygon
325 << " with new polygon " << _newpolygon);
326
327 if (_polygon.size() > 0) {
328 MatchingControlStructure MCS;
329 MCS.foundflag = false;
330 MCS.bestL2 = std::numeric_limits<double>::max();
331 MCS.oldpoints.insert(MCS.oldpoints.begin(), _polygon.begin(),_polygon.end() );
332 MCS.newpoints.insert(MCS.newpoints.begin(), _newpolygon.begin(),_newpolygon.end() );
333
334 // search for bestmatching combinatorially
335 {
336 // translate polygon into vector to enable index addressing
337 IndexList_t indices(_newpolygon.size());
338 std::generate(indices.begin(), indices.end(), UniqueNumber);
339 IndexList_t matching;
340
341 // walk through all matchings
342 const unsigned int matchingsize = _polygon.size();
343 ASSERT( matchingsize <= indices.size(),
344 "SphericalPointDistribution::matchSphericalPointDistributions() - not enough new points to choose for matching to old ones.");
345 recurseMatchings(MCS, matching, indices, matchingsize);
346 }
347 LOG(2, "INFO: Best matching is " << MCS.bestmatching);
348
349 // determine rotation angles to align the two point distributions with
350 // respect to bestmatching
351 std::vector<double> angles(NDIM);
352 Vector newCenter;
353 Vector oldCenter;
354 {
355 // calculate center of triangle/line/point consisting of first points of matching
356 IndexList_t::const_iterator iter = MCS.bestmatching.begin();
357 unsigned int i = 0;
358 for (; (i<3) && (i<MCS.bestmatching.size()); ++i, ++iter) {
359 oldCenter += remainingold[i];
360 newCenter += remainingnew[*iter];
361 }
362 oldCenter *= 1./(double)i;
363 newCenter *= 1./(double)i;
364 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
365
366 // transform to polar coordinates and note difference in angular parts
367 std::vector<double> oldpolar = getPolarCoordinates(oldCenter);
368 std::vector<double> newpolar = getPolarCoordinates(newCenter);
369 std::vector<double> differencepolar;
370 std::transform(
371 oldpolar.begin(), oldpolar.end(),
372 newpolar.begin(),
373 std::back_inserter(differencepolar),
374 std::minus<double>());
375 LOG(3, "INFO: (r,theta,phi) angles are" << differencepolar);
376 }
377 // rotate _newpolygon
378 SphericalPointDistribution::Polygon_t rotated_newpolygon =
379 rotatePolygon(_newpolygon, angles);
380 LOG(5, "DEBUG: Rotated new polygon is " << rotated_newpolygon);
381
382 const Line RotationAxis(zeroVec, oldCenter);
383 const double RotationAngle =
384 oldCenter.Angle(remainingold[0])
385 - oldCenter.Angle(remainingnew[*MCS.bestmatching.begin()]);
386 LOG(5, "DEBUG: Rotate around self is " << RotationAngle
387 << " around axis " << RotationAxis);
388
389 for (SphericalPointDistribution::Polygon_t::iterator iter = rotated_newpolygon.begin();
390 iter != rotated_newpolygon.end(); ++iter) {
391 RotationAxis.rotateVector(*iter, RotationAngle);
392 }
393
394 // remove all points in matching and return remaining ones
395 SphericalPointDistribution::Polygon_t remainingpoints =
396 removeMatchingPoints(rotated_newpolygon, MCS.bestmatching);
397 LOG(2, "INFO: Remaining points are " << remainingpoints);
398 return remainingpoints;
399 } else
400 return _newpolygon;
401}
402
403
Note: See TracBrowser for help on using the repository browser.