source: src/Fragmentation/Exporters/SphericalPointDistribution.cpp@ 653cea

Action_Thermostats Add_AtomRandomPerturbation Add_FitFragmentPartialChargesAction Add_RotateAroundBondAction Add_SelectAtomByNameAction Adding_Graph_to_ChangeBondActions Adding_MD_integration_tests Adding_StructOpt_integration_tests Automaking_mpqc_open AutomationFragmentation_failures Candidate_v1.5.4 Candidate_v1.6.0 Candidate_v1.6.1 ChangeBugEmailaddress ChangingTestPorts ChemicalSpaceEvaluator 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_ChargeSampling_PBC Fix_ChronosMutex Fix_FitPartialCharges Fix_FitPotential_needs_atomicnumbers Fix_ForceAnnealing Fix_IndependentFragmentGrids Fix_ParseParticles Fix_ParseParticles_split_forward_backward_Actions Fix_StatusMsg Fix_StepWorldTime_single_argument Fix_Verbose_Codepatterns ForceAnnealing_goodresults ForceAnnealing_oldresults ForceAnnealing_tocheck ForceAnnealing_with_BondGraph ForceAnnealing_with_BondGraph_continued ForceAnnealing_with_BondGraph_continued_betteresults ForceAnnealing_with_BondGraph_contraction-expansion GeometryObjects Gui_displays_atomic_force_velocity IndependentFragmentGrids IndependentFragmentGrids_IndividualZeroInstances IndependentFragmentGrids_IntegrationTest IndependentFragmentGrids_Sole_NN_Calculation JobMarket_RobustOnKillsSegFaults JobMarket_StableWorkerPool JobMarket_unresolvable_hostname_fix ODR_violation_mpqc_open PartialCharges_OrthogonalSummation PythonUI_with_named_parameters QtGui_reactivate_TimeChanged_changes Recreated_GuiChecks RotateToPrincipalAxisSystem_UndoRedo SaturateAtoms_findBestMatching StoppableMakroAction Subpackage_CodePatterns Subpackage_JobMarket Subpackage_LinearAlgebra Subpackage_levmar Subpackage_mpqc_open Subpackage_vmg ThirdParty_MPQC_rebuilt_buildsystem TrajectoryDependenant_MaxOrder TremoloParser_IncreasedPrecision TremoloParser_MultipleTimesteps Ubuntu_1604_changes stable
Last change on this file since 653cea was 653cea, checked in by Frederik Heber <heber@…>, 9 years ago

SphericalPointDistribution is now working with bond degree weights.

  • recurseMatching() now works on IndexTupleList_t.
  • also rewrote calculatePairwiseDistances() and calculateErrorOfMatching().
  • L1THRESHOLD in recurseMatching() moved over to class body.
  • increased verbosity level of ...Matching() functions by one, added note on eventually chosen matching and why.
  • we assert that bestL2 is not too large.
  • FIX: calculateErrorOfMatching() did not use absolute value of gap for L1 error.
  • TESTFIX: Using limited accuracy on point coordinates.
  • TESTS: Regresssion test FragmentMolecule-cycles working again.
  • Property mode set to 100644
File size: 30.3 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 <boost/math/quaternion.hpp>
46#include <cmath>
47#include <functional>
48#include <iterator>
49#include <limits>
50#include <list>
51#include <numeric>
52#include <vector>
53#include <map>
54
55#include "LinearAlgebra/Line.hpp"
56#include "LinearAlgebra/Plane.hpp"
57#include "LinearAlgebra/RealSpaceMatrix.hpp"
58#include "LinearAlgebra/Vector.hpp"
59
60// static entities
61const double SphericalPointDistribution::SQRT_3(sqrt(3.0));
62const double SphericalPointDistribution::warn_amplitude = 1e-2;
63const double SphericalPointDistribution::L1THRESHOLD = 1e-2;
64const double SphericalPointDistribution::L2THRESHOLD = 2e-1;
65
66typedef std::vector<double> DistanceArray_t;
67
68// class generator: taken from www.cplusplus.com example std::generate
69struct c_unique {
70 unsigned int current;
71 c_unique() {current=0;}
72 unsigned int operator()() {return current++;}
73} UniqueNumber;
74
75struct c_unique_list {
76 unsigned int current;
77 c_unique_list() {current=0;}
78 std::list<unsigned int> operator()() {return std::list<unsigned int>(1, current++);}
79} UniqueNumberList;
80
81/** Calculate the center of a given set of points in \a _positions but only
82 * for those indicated by \a _indices.
83 *
84 */
85inline
86Vector calculateCenter(
87 const SphericalPointDistribution::VectorArray_t &_positions,
88 const SphericalPointDistribution::IndexList_t &_indices)
89{
90 Vector Center;
91 Center.Zero();
92 for (SphericalPointDistribution::IndexList_t::const_iterator iter = _indices.begin();
93 iter != _indices.end(); ++iter)
94 Center += _positions[*iter];
95 if (!_indices.empty())
96 Center *= 1./(double)_indices.size();
97
98 return Center;
99}
100
101inline
102DistanceArray_t calculatePairwiseDistances(
103 const SphericalPointDistribution::VectorArray_t &_points,
104 const SphericalPointDistribution::IndexTupleList_t &_indices
105 )
106{
107 DistanceArray_t result;
108 for (SphericalPointDistribution::IndexTupleList_t::const_iterator firstiter = _indices.begin();
109 firstiter != _indices.end(); ++firstiter) {
110
111 // calculate first center from possible tuple of indices
112 Vector FirstCenter;
113 ASSERT(!firstiter->empty(), "calculatePairwiseDistances() - there is an empty tuple.");
114 if (firstiter->size() == 1) {
115 FirstCenter = _points[*firstiter->begin()];
116 } else {
117 FirstCenter = calculateCenter( _points, *firstiter);
118 if (!FirstCenter.IsZero())
119 FirstCenter.Normalize();
120 }
121
122 for (SphericalPointDistribution::IndexTupleList_t::const_iterator seconditer = firstiter;
123 seconditer != _indices.end(); ++seconditer) {
124 if (firstiter == seconditer)
125 continue;
126
127 // calculate second center from possible tuple of indices
128 Vector SecondCenter;
129 ASSERT(!seconditer->empty(), "calculatePairwiseDistances() - there is an empty tuple.");
130 if (seconditer->size() == 1) {
131 SecondCenter = _points[*seconditer->begin()];
132 } else {
133 SecondCenter = calculateCenter( _points, *seconditer);
134 if (!SecondCenter.IsZero())
135 SecondCenter.Normalize();
136 }
137
138 // calculate distance between both centers
139 double distance = 2.; // greatest distance on surface of sphere with radius 1.
140 if ((!FirstCenter.IsZero()) && (!SecondCenter.IsZero()))
141 distance = (FirstCenter - SecondCenter).NormSquared();
142 result.push_back(distance);
143 }
144 }
145 return result;
146}
147
148/** Decides by an orthonormal third vector whether the sign of the rotation
149 * angle should be negative or positive.
150 *
151 * \return -1 or 1
152 */
153inline
154double determineSignOfRotation(
155 const Vector &_oldPosition,
156 const Vector &_newPosition,
157 const Vector &_RotationAxis
158 )
159{
160 Vector dreiBein(_oldPosition);
161 dreiBein.VectorProduct(_RotationAxis);
162 ASSERT( !dreiBein.IsZero(), "determineSignOfRotation() - dreiBein is zero.");
163 dreiBein.Normalize();
164 const double sign =
165 (dreiBein.ScalarProduct(_newPosition) < 0.) ? -1. : +1.;
166 LOG(6, "DEBUG: oldCenter on plane is " << _oldPosition
167 << ", newCenter on plane is " << _newPosition
168 << ", and dreiBein is " << dreiBein);
169 return sign;
170}
171
172/** Convenience function to recalculate old and new center for determining plane
173 * rotation.
174 */
175inline
176void calculateOldAndNewCenters(
177 Vector &_oldCenter,
178 Vector &_newCenter,
179 const SphericalPointDistribution::VectorArray_t &_referencepositions,
180 const SphericalPointDistribution::VectorArray_t &_currentpositions,
181 const SphericalPointDistribution::IndexList_t &_bestmatching)
182{
183 const size_t NumberIds = std::min(_bestmatching.size(), (size_t)3);
184 SphericalPointDistribution::IndexList_t continuousIds(NumberIds, -1);
185 std::generate(continuousIds.begin(), continuousIds.end(), UniqueNumber);
186 _oldCenter = calculateCenter(_referencepositions, continuousIds);
187 // C++11 defines a copy_n function ...
188 SphericalPointDistribution::IndexList_t::const_iterator enditer = _bestmatching.begin();
189 std::advance(enditer, NumberIds);
190 SphericalPointDistribution::IndexList_t firstbestmatchingIds(NumberIds, -1);
191 std::copy(_bestmatching.begin(), enditer, firstbestmatchingIds.begin());
192 _newCenter = calculateCenter( _currentpositions, firstbestmatchingIds);
193}
194/** Returns squared L2 error of the given \a _Matching.
195 *
196 * We compare the pair-wise distances of each associated matching
197 * and check whether these distances each match between \a _old and
198 * \a _new.
199 *
200 * \param _old first set of returnpolygon (fewer or equal to \a _new)
201 * \param _new second set of returnpolygon
202 * \param _Matching matching between the two sets
203 * \return pair with L1 and squared L2 error
204 */
205std::pair<double, double> SphericalPointDistribution::calculateErrorOfMatching(
206 const VectorArray_t &_old,
207 const VectorArray_t &_new,
208 const IndexTupleList_t &_Matching)
209{
210 std::pair<double, double> errors( std::make_pair( 0., 0. ) );
211
212 if (_Matching.size() > 1) {
213 LOG(5, "INFO: Matching is " << _Matching);
214
215 // calculate all pair-wise distances
216 IndexTupleList_t keys(_old.size(), IndexList_t() );
217 std::generate (keys.begin(), keys.end(), UniqueNumberList);
218
219 const DistanceArray_t firstdistances = calculatePairwiseDistances(_old, keys);
220 const DistanceArray_t seconddistances = calculatePairwiseDistances(_new, _Matching);
221
222 ASSERT( firstdistances.size() == seconddistances.size(),
223 "calculateL2ErrorOfMatching() - mismatch in pair-wise distance array sizes.");
224 DistanceArray_t::const_iterator firstiter = firstdistances.begin();
225 DistanceArray_t::const_iterator seconditer = seconddistances.begin();
226 for (;(firstiter != firstdistances.end()) && (seconditer != seconddistances.end());
227 ++firstiter, ++seconditer) {
228 const double gap = fabs(*firstiter - *seconditer);
229 // L1 error
230 if (errors.first < gap)
231 errors.first = gap;
232 // L2 error
233 errors.second += gap*gap;
234 }
235 } else {
236 // check whether we have any zero centers: Combining points on new sphere may result
237 // in zero centers
238 for (SphericalPointDistribution::IndexTupleList_t::const_iterator iter = _Matching.begin();
239 iter != _Matching.end(); ++iter) {
240 if ((iter->size() != 1) && (calculateCenter( _new, *iter).IsZero())) {
241 errors.first = 2.;
242 errors.second = 2.;
243 }
244 }
245 }
246 LOG(4, "INFO: Resulting errors for matching (L1, L2): "
247 << errors.first << "," << errors.second << ".");
248
249 return errors;
250}
251
252SphericalPointDistribution::Polygon_t SphericalPointDistribution::removeMatchingPoints(
253 const VectorArray_t &_points,
254 const IndexList_t &_matchingindices
255 )
256{
257 SphericalPointDistribution::Polygon_t remainingreturnpolygon;
258 IndexArray_t indices(_matchingindices.begin(), _matchingindices.end());
259 std::sort(indices.begin(), indices.end());
260 LOG(4, "DEBUG: sorted matching is " << indices);
261 IndexArray_t remainingindices(_points.size(), -1);
262 std::generate(remainingindices.begin(), remainingindices.end(), UniqueNumber);
263 IndexArray_t::iterator remainiter = std::set_difference(
264 remainingindices.begin(), remainingindices.end(),
265 indices.begin(), indices.end(),
266 remainingindices.begin());
267 remainingindices.erase(remainiter, remainingindices.end());
268 LOG(4, "DEBUG: remaining indices are " << remainingindices);
269 for (IndexArray_t::const_iterator iter = remainingindices.begin();
270 iter != remainingindices.end(); ++iter) {
271 remainingreturnpolygon.push_back(_points[*iter]);
272 }
273
274 return remainingreturnpolygon;
275}
276
277/** Recursive function to go through all possible matchings.
278 *
279 * \param _MCS structure holding global information to the recursion
280 * \param _matching current matching being build up
281 * \param _indices contains still available indices
282 * \param _remainingweights current weights to fill (each weight a place)
283 * \param _remainiter iterator over the weights, indicating the current position we match
284 * \param _matchingsize
285 */
286void SphericalPointDistribution::recurseMatchings(
287 MatchingControlStructure &_MCS,
288 IndexTupleList_t &_matching,
289 IndexList_t _indices,
290 WeightsArray_t &_remainingweights,
291 WeightsArray_t::iterator _remainiter,
292 const unsigned int _matchingsize
293 )
294{
295 LOG(5, "DEBUG: Recursing with current matching " << _matching
296 << ", remaining indices " << _indices
297 << ", and remaining weights " << _matchingsize);
298 if (!_MCS.foundflag) {
299 LOG(5, "DEBUG: Current matching has size " << _matching.size() << ", places left " << _matchingsize);
300 if (_matchingsize > 0) {
301 // go through all indices
302 for (IndexList_t::iterator iter = _indices.begin();
303 (iter != _indices.end()) && (!_MCS.foundflag);) {
304 // check whether we can stay in the current bin or have to move on to next one
305 if (*_remainiter == 0) {
306 // we need to move on
307 if (_remainiter != _remainingweights.end()) {
308 ++_remainiter;
309 } else {
310 // as we check _matchingsize > 0 this should be impossible
311 ASSERT( 0, "recurseMatchings() - we must not come to this position.");
312 }
313 }
314 // advance in matching to same position
315 const size_t OldIndex = std::distance(_remainingweights.begin(), _remainiter);
316 while (_matching.size() <= OldIndex) { // add empty lists of new bin is opened
317 LOG(6, "DEBUG: Extending _matching.");
318 _matching.push_back( IndexList_t() );
319 }
320 IndexTupleList_t::iterator filliniter = _matching.begin();
321 std::advance(filliniter, OldIndex);
322 // add index to matching
323 filliniter->push_back(*iter);
324 --(*_remainiter);
325 LOG(6, "DEBUG: Adding " << *iter << " to matching at " << OldIndex << ".");
326 // remove index but keep iterator to position (is the next to erase element)
327 IndexList_t::iterator backupiter = _indices.erase(iter);
328 // recurse with decreased _matchingsize
329 recurseMatchings(_MCS, _matching, _indices, _remainingweights, _remainiter, _matchingsize-1);
330 // re-add chosen index and reset index to new position
331 _indices.insert(backupiter, filliniter->back());
332 iter = backupiter;
333 // remove index from _matching to make space for the next one
334 filliniter->pop_back();
335 ++(*_remainiter);
336 }
337 // gone through all indices then exit recursion
338 if (_matching.empty())
339 _MCS.foundflag = true;
340 } else {
341 LOG(4, "INFO: Found matching " << _matching);
342 // calculate errors
343 std::pair<double, double> errors = calculateErrorOfMatching(
344 _MCS.oldpoints, _MCS.newpoints, _matching);
345 if (errors.first < L1THRESHOLD) {
346 _MCS.bestmatching = _matching;
347 _MCS.foundflag = true;
348 } else if (_MCS.bestL2 > errors.second) {
349 _MCS.bestmatching = _matching;
350 _MCS.bestL2 = errors.second;
351 }
352 }
353 }
354}
355
356/** Finds combinatorially the best matching between points in \a _polygon
357 * and \a _newpolygon.
358 *
359 * We find the matching with the smallest L2 error, where we break when we stumble
360 * upon a matching with zero error.
361 *
362 * As points in \a _polygon may be have a weight greater 1 we have to match it to
363 * multiple points in \a _newpolygon. Eventually, these multiple points are combined
364 * for their center of weight, which is the only thing follow-up function see of
365 * these multiple points. Hence, we actually modify \a _newpolygon in the process
366 * such that the returned IndexList_t indicates a bijective mapping in the end.
367 *
368 * \sa recurseMatchings() for going through all matchings
369 *
370 * \param _polygon here, we have indices 0,1,2,...
371 * \param _newpolygon and here we need to find the correct indices
372 * \return list of indices: first in \a _polygon goes to first index for \a _newpolygon
373 */
374SphericalPointDistribution::IndexList_t SphericalPointDistribution::findBestMatching(
375 const WeightedPolygon_t &_polygon,
376 Polygon_t &_newpolygon
377 )
378{
379 MatchingControlStructure MCS;
380 MCS.foundflag = false;
381 MCS.bestL2 = std::numeric_limits<double>::max();
382 // transform lists into arrays
383 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
384 iter != _polygon.end(); ++iter) {
385 MCS.oldpoints.push_back(iter->first);
386 MCS.weights.push_back(iter->second);
387 }
388 MCS.newpoints.insert(MCS.newpoints.begin(), _newpolygon.begin(),_newpolygon.end() );
389
390 // search for bestmatching combinatorially
391 {
392 // translate polygon into vector to enable index addressing
393 IndexList_t indices(_newpolygon.size());
394 std::generate(indices.begin(), indices.end(), UniqueNumber);
395 IndexTupleList_t matching;
396
397 // walk through all matchings
398 WeightsArray_t remainingweights = MCS.weights;
399 unsigned int placesleft = std::accumulate(remainingweights.begin(), remainingweights.end(), 0);
400 recurseMatchings(MCS, matching, indices, remainingweights, remainingweights.begin(), placesleft);
401 }
402 if (MCS.foundflag)
403 LOG(3, "Found a best matching beneath L1 threshold of " << L1THRESHOLD);
404 else {
405 if (MCS.bestL2 < warn_amplitude)
406 LOG(3, "Picking matching is " << MCS.bestmatching << " with best L2 error of "
407 << MCS.bestL2);
408 else if (MCS.bestL2 < L2THRESHOLD)
409 ELOG(2, "Picking matching is " << MCS.bestmatching
410 << " with rather large L2 error of " << MCS.bestL2);
411 else
412 ASSERT(0, "findBestMatching() - matching "+toString(MCS.bestmatching)
413 +" has L2 error of "+toString(MCS.bestL2)+" that is too large.");
414 }
415
416 // combine multiple points and create simple IndexList from IndexTupleList
417 const SphericalPointDistribution::IndexList_t IndexList =
418 joinPoints(_newpolygon, MCS.newpoints, MCS.bestmatching);
419
420 return IndexList;
421}
422
423SphericalPointDistribution::IndexList_t SphericalPointDistribution::joinPoints(
424 Polygon_t &_newpolygon,
425 const VectorArray_t &_newpoints,
426 const IndexTupleList_t &_bestmatching
427 )
428{
429 // combine all multiple points
430 IndexList_t IndexList;
431 IndexArray_t removalpoints;
432 unsigned int UniqueIndex = _newpolygon.size(); // all indices up to size are used right now
433 VectorArray_t newCenters;
434 newCenters.reserve(_bestmatching.size());
435 for (IndexTupleList_t::const_iterator tupleiter = _bestmatching.begin();
436 tupleiter != _bestmatching.end(); ++tupleiter) {
437 ASSERT (tupleiter->size() > 0,
438 "findBestMatching() - encountered tuple in bestmatching with size 0.");
439 if (tupleiter->size() == 1) {
440 // add point and index
441 IndexList.push_back(*tupleiter->begin());
442 } else {
443 // combine into weighted and normalized center
444 Vector Center = calculateCenter(_newpoints, *tupleiter);
445 Center.Normalize();
446 _newpolygon.push_back(Center);
447 LOG(5, "DEBUG: Combining " << tupleiter->size() << " points to weighted center "
448 << Center << " with new index " << UniqueIndex);
449 // mark for removal
450 removalpoints.insert(removalpoints.end(), tupleiter->begin(), tupleiter->end());
451 // add new index
452 IndexList.push_back(UniqueIndex++);
453 }
454 }
455 // IndexList is now our new bestmatching (that is bijective)
456 LOG(4, "DEBUG: Our new bijective IndexList reads as " << IndexList);
457
458 // modifying _newpolygon: remove all points in removalpoints, add those in newCenters
459 Polygon_t allnewpoints = _newpolygon;
460 {
461 _newpolygon.clear();
462 std::sort(removalpoints.begin(), removalpoints.end());
463 size_t i = 0;
464 IndexArray_t::const_iterator removeiter = removalpoints.begin();
465 for (Polygon_t::iterator iter = allnewpoints.begin();
466 iter != allnewpoints.end(); ++iter, ++i) {
467 if ((removeiter != removalpoints.end()) && (i == *removeiter)) {
468 // don't add, go to next remove index
469 ++removeiter;
470 } else {
471 // otherwise add points
472 _newpolygon.push_back(*iter);
473 }
474 }
475 }
476 LOG(4, "DEBUG: The polygon with recentered points removed is " << _newpolygon);
477
478 // map IndexList to new shrinked _newpolygon
479 typedef std::set<unsigned int> IndexSet_t;
480 IndexSet_t SortedIndexList(IndexList.begin(), IndexList.end());
481 IndexList.clear();
482 {
483 size_t offset = 0;
484 IndexSet_t::const_iterator listiter = SortedIndexList.begin();
485 IndexArray_t::const_iterator removeiter = removalpoints.begin();
486 for (size_t i = 0; i < allnewpoints.size(); ++i) {
487 if ((removeiter != removalpoints.end()) && (i == *removeiter)) {
488 ++offset;
489 ++removeiter;
490 } else if ((listiter != SortedIndexList.end()) && (i == *listiter)) {
491 IndexList.push_back(*listiter - offset);
492 ++listiter;
493 }
494 }
495 }
496 LOG(4, "DEBUG: Our new bijective IndexList corrected for removed points reads as "
497 << IndexList);
498
499 return IndexList;
500}
501
502SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPlaneAligningRotation(
503 const VectorArray_t &_referencepositions,
504 const VectorArray_t &_currentpositions,
505 const IndexList_t &_bestmatching
506 )
507{
508#ifndef NDEBUG
509 bool dontcheck = false;
510#endif
511 // initialize to no rotation
512 Rotation_t Rotation;
513 Rotation.first.Zero();
514 Rotation.first[0] = 1.;
515 Rotation.second = 0.;
516
517 // calculate center of triangle/line/point consisting of first points of matching
518 Vector oldCenter;
519 Vector newCenter;
520 calculateOldAndNewCenters(
521 oldCenter, newCenter,
522 _referencepositions, _currentpositions, _bestmatching);
523
524 if ((!oldCenter.IsZero()) && (!newCenter.IsZero())) {
525 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
526 oldCenter.Normalize();
527 newCenter.Normalize();
528 if (!oldCenter.IsEqualTo(newCenter)) {
529 // calculate rotation axis and angle
530 Rotation.first = oldCenter;
531 Rotation.first.VectorProduct(newCenter);
532 Rotation.second = oldCenter.Angle(newCenter); // /(M_PI/2.);
533 } else {
534 // no rotation required anymore
535 }
536 } else {
537 LOG(4, "DEBUG: oldCenter is " << oldCenter << ", newCenter is " << newCenter);
538 if ((oldCenter.IsZero()) && (newCenter.IsZero())) {
539 // either oldCenter or newCenter (or both) is directly at origin
540 if (_bestmatching.size() == 2) {
541 // line case
542 Vector oldPosition = _currentpositions[*_bestmatching.begin()];
543 Vector newPosition = _referencepositions[0];
544 // check whether we need to rotate at all
545 if (!oldPosition.IsEqualTo(newPosition)) {
546 Rotation.first = oldPosition;
547 Rotation.first.VectorProduct(newPosition);
548 // orientation will fix the sign here eventually
549 Rotation.second = oldPosition.Angle(newPosition);
550 } else {
551 // no rotation required anymore
552 }
553 } else {
554 // triangle case
555 // both triangles/planes have same center, hence get axis by
556 // VectorProduct of Normals
557 Plane newplane(_referencepositions[0], _referencepositions[1], _referencepositions[2]);
558 VectorArray_t vectors;
559 for (IndexList_t::const_iterator iter = _bestmatching.begin();
560 iter != _bestmatching.end(); ++iter)
561 vectors.push_back(_currentpositions[*iter]);
562 Plane oldplane(vectors[0], vectors[1], vectors[2]);
563 Vector oldPosition = oldplane.getNormal();
564 Vector newPosition = newplane.getNormal();
565 // check whether we need to rotate at all
566 if (!oldPosition.IsEqualTo(newPosition)) {
567 Rotation.first = oldPosition;
568 Rotation.first.VectorProduct(newPosition);
569 Rotation.first.Normalize();
570
571 // construct reference vector to determine direction of rotation
572 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
573 Rotation.second = sign * oldPosition.Angle(newPosition);
574 LOG(5, "DEBUG: Rotating plane normals by " << Rotation.second
575 << " around axis " << Rotation.first);
576 } else {
577 // else do nothing
578 }
579 }
580 } else {
581 // TODO: we can't do anything here, but this case needs to be dealt with when
582 // we have no ideal geometries anymore
583 if ((oldCenter-newCenter).Norm() > warn_amplitude)
584 ELOG(2, "oldCenter is " << oldCenter << ", yet newCenter is " << newCenter);
585#ifndef NDEBUG
586 // else they are considered close enough
587 dontcheck = true;
588#endif
589 }
590 }
591
592#ifndef NDEBUG
593 // check: rotation brings newCenter onto oldCenter position
594 if (!dontcheck) {
595 Line Axis(zeroVec, Rotation.first);
596 Vector test = Axis.rotateVector(newCenter, Rotation.second);
597 LOG(4, "CHECK: rotated newCenter is " << test
598 << ", oldCenter is " << oldCenter);
599 ASSERT( (test - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
600 "matchSphericalPointDistributions() - rotation does not work as expected by "
601 +toString((test - oldCenter).NormSquared())+".");
602 }
603#endif
604
605 return Rotation;
606}
607
608SphericalPointDistribution::Rotation_t SphericalPointDistribution::findPointAligningRotation(
609 const VectorArray_t &remainingold,
610 const VectorArray_t &remainingnew,
611 const IndexList_t &_bestmatching)
612{
613 // initialize rotation to zero
614 Rotation_t Rotation;
615 Rotation.first.Zero();
616 Rotation.first[0] = 1.;
617 Rotation.second = 0.;
618
619 // recalculate center
620 Vector oldCenter;
621 Vector newCenter;
622 calculateOldAndNewCenters(
623 oldCenter, newCenter,
624 remainingold, remainingnew, _bestmatching);
625
626 Vector oldPosition = remainingnew[*_bestmatching.begin()];
627 Vector newPosition = remainingold[0];
628 LOG(6, "DEBUG: oldPosition is " << oldPosition << " and newPosition is " << newPosition);
629 if (!oldPosition.IsEqualTo(newPosition)) {
630 if ((!oldCenter.IsZero()) && (!newCenter.IsZero())) {
631 oldCenter.Normalize(); // note weighted sum of normalized weight is not normalized
632 Rotation.first = oldCenter;
633 LOG(6, "DEBUG: Picking normalized oldCenter as Rotation.first " << oldCenter);
634 oldPosition.ProjectOntoPlane(Rotation.first);
635 newPosition.ProjectOntoPlane(Rotation.first);
636 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
637 } else {
638 if (_bestmatching.size() == 2) {
639 // line situation
640 try {
641 Plane oldplane(oldPosition, oldCenter, newPosition);
642 Rotation.first = oldplane.getNormal();
643 LOG(6, "DEBUG: Plane is " << oldplane << " and normal is " << Rotation.first);
644 } catch (LinearDependenceException &e) {
645 LOG(6, "DEBUG: Vectors defining plane are linearly dependent.");
646 // oldPosition and newPosition are on a line, just flip when not equal
647 if (!oldPosition.IsEqualTo(newPosition)) {
648 Rotation.first.Zero();
649 Rotation.first.GetOneNormalVector(oldPosition);
650 LOG(6, "DEBUG: For flipping we use Rotation.first " << Rotation.first);
651 assert( Rotation.first.ScalarProduct(oldPosition) < std::numeric_limits<double>::epsilon()*1e4);
652 // Rotation.second = M_PI;
653 } else {
654 LOG(6, "DEBUG: oldPosition and newPosition are equivalent.");
655 }
656 }
657 } else {
658 // triangle situation
659 Plane oldplane(remainingold[0], remainingold[1], remainingold[2]);
660 Rotation.first = oldplane.getNormal();
661 LOG(6, "DEBUG: oldPlane is " << oldplane << " and normal is " << Rotation.first);
662 oldPosition.ProjectOntoPlane(Rotation.first);
663 LOG(6, "DEBUG: Positions after projection are " << oldPosition << " and " << newPosition);
664 }
665 }
666 // construct reference vector to determine direction of rotation
667 const double sign = determineSignOfRotation(oldPosition, newPosition, Rotation.first);
668 Rotation.second = sign * oldPosition.Angle(newPosition);
669 } else {
670 LOG(6, "DEBUG: oldPosition and newPosition are equivalent, hence no orientating rotation.");
671 }
672
673 return Rotation;
674}
675
676
677SphericalPointDistribution::Polygon_t
678SphericalPointDistribution::matchSphericalPointDistributions(
679 const SphericalPointDistribution::WeightedPolygon_t &_polygon,
680 SphericalPointDistribution::Polygon_t &_newpolygon
681 )
682{
683 SphericalPointDistribution::Polygon_t remainingpoints;
684 VectorArray_t remainingold;
685 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
686 iter != _polygon.end(); ++iter)
687 remainingold.push_back(iter->first);
688 LOG(2, "INFO: Matching old polygon " << _polygon
689 << " with new polygon " << _newpolygon);
690
691 if (_polygon.size() == _newpolygon.size()) {
692 // same number of points desired as are present? Do nothing
693 LOG(2, "INFO: There are no vacant points to return.");
694 return remainingpoints;
695 }
696
697 if (_polygon.size() > 0) {
698 IndexList_t bestmatching = findBestMatching(_polygon, _newpolygon);
699 LOG(2, "INFO: Best matching is " << bestmatching);
700
701 // _newpolygon has changed, so now convert to array
702 VectorArray_t remainingnew(_newpolygon.begin(), _newpolygon.end());
703
704 // determine rotation angles to align the two point distributions with
705 // respect to bestmatching:
706 // we use the center between the three first matching points
707 /// the first rotation brings these two centers to coincide
708 VectorArray_t rotated_newpolygon = remainingnew;
709 {
710 Rotation_t Rotation = findPlaneAligningRotation(
711 remainingold,
712 remainingnew,
713 bestmatching);
714 LOG(5, "DEBUG: Rotating coordinate system by " << Rotation.second
715 << " around axis " << Rotation.first);
716 Line Axis(zeroVec, Rotation.first);
717
718 // apply rotation angle to bring newCenter to oldCenter
719 for (VectorArray_t::iterator iter = rotated_newpolygon.begin();
720 iter != rotated_newpolygon.end(); ++iter) {
721 Vector &current = *iter;
722 LOG(6, "DEBUG: Original point is " << current);
723 current = Axis.rotateVector(current, Rotation.second);
724 LOG(6, "DEBUG: Rotated point is " << current);
725 }
726
727#ifndef NDEBUG
728 // check: rotated "newCenter" should now equal oldCenter
729 {
730 Vector oldCenter;
731 Vector rotatednewCenter;
732 calculateOldAndNewCenters(
733 oldCenter, rotatednewCenter,
734 remainingold, rotated_newpolygon, bestmatching);
735 // NOTE: Center must not necessarily lie on the sphere with norm 1, hence, we
736 // have to normalize it just as before, as oldCenter and newCenter lengths may differ.
737 if ((!oldCenter.IsZero()) && (!rotatednewCenter.IsZero())) {
738 oldCenter.Normalize();
739 rotatednewCenter.Normalize();
740 LOG(4, "CHECK: rotatednewCenter is " << rotatednewCenter
741 << ", oldCenter is " << oldCenter);
742 ASSERT( (rotatednewCenter - oldCenter).NormSquared() < std::numeric_limits<double>::epsilon()*1e4,
743 "matchSphericalPointDistributions() - rotation does not work as expected by "
744 +toString((rotatednewCenter - oldCenter).NormSquared())+".");
745 }
746 }
747#endif
748 }
749 /// the second (orientation) rotation aligns the planes such that the
750 /// points themselves coincide
751 if (bestmatching.size() > 1) {
752 Rotation_t Rotation = findPointAligningRotation(
753 remainingold,
754 rotated_newpolygon,
755 bestmatching);
756
757 // construct RotationAxis and two points on its plane, defining the angle
758 Rotation.first.Normalize();
759 const Line RotationAxis(zeroVec, Rotation.first);
760
761 LOG(5, "DEBUG: Rotating around self is " << Rotation.second
762 << " around axis " << RotationAxis);
763
764#ifndef NDEBUG
765 // check: first bestmatching in rotated_newpolygon and remainingnew
766 // should now equal
767 {
768 const IndexList_t::const_iterator iter = bestmatching.begin();
769 Vector rotatednew = RotationAxis.rotateVector(
770 rotated_newpolygon[*iter],
771 Rotation.second);
772 LOG(4, "CHECK: rotated first new bestmatching is " << rotatednew
773 << " while old was " << remainingold[0]);
774 ASSERT( (rotatednew - remainingold[0]).Norm() < warn_amplitude,
775 "matchSphericalPointDistributions() - orientation rotation ends up off by more than "
776 +toString(warn_amplitude)+".");
777 }
778#endif
779
780 for (VectorArray_t::iterator iter = rotated_newpolygon.begin();
781 iter != rotated_newpolygon.end(); ++iter) {
782 Vector &current = *iter;
783 LOG(6, "DEBUG: Original point is " << current);
784 current = RotationAxis.rotateVector(current, Rotation.second);
785 LOG(6, "DEBUG: Rotated point is " << current);
786 }
787 }
788
789 // remove all points in matching and return remaining ones
790 SphericalPointDistribution::Polygon_t remainingpoints =
791 removeMatchingPoints(rotated_newpolygon, bestmatching);
792 LOG(2, "INFO: Remaining points are " << remainingpoints);
793 return remainingpoints;
794 } else
795 return _newpolygon;
796}
Note: See TracBrowser for help on using the repository browser.