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 Candidate_v1.7.0 Candidate_v1.7.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
RevLine 
[f54930]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"
[64cafb2]40#include "CodePatterns/IteratorAdaptors.hpp"
[0c42f2]41#include "CodePatterns/Log.hpp"
[64cafb2]42#include "CodePatterns/toString.hpp"
[f54930]43
44#include <algorithm>
[f9d85f]45#include <boost/math/quaternion.hpp>
[64cafb2]46#include <cmath>
[946948]47#include <functional>
48#include <iterator>
[64cafb2]49#include <limits>
50#include <list>
[653cea]51#include <numeric>
[f54930]52#include <vector>
[64cafb2]53#include <map>
[f54930]54
55#include "LinearAlgebra/Line.hpp"
[b67d89]56#include "LinearAlgebra/Plane.hpp"
[f54930]57#include "LinearAlgebra/RealSpaceMatrix.hpp"
58#include "LinearAlgebra/Vector.hpp"
59
[b67d89]60// static entities
[0c42f2]61const double SphericalPointDistribution::SQRT_3(sqrt(3.0));
[b67d89]62const double SphericalPointDistribution::warn_amplitude = 1e-2;
[653cea]63const double SphericalPointDistribution::L1THRESHOLD = 1e-2;
64const double SphericalPointDistribution::L2THRESHOLD = 2e-1;
[b67d89]65
66typedef std::vector<double> DistanceArray_t;
[0c42f2]67
[450adf]68// class generator: taken from www.cplusplus.com example std::generate
69struct c_unique {
[653cea]70 unsigned int current;
[450adf]71 c_unique() {current=0;}
[653cea]72 unsigned int operator()() {return current++;}
[450adf]73} UniqueNumber;
74
[653cea]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;
[64cafb2]80
[450adf]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}
[64cafb2]100
[653cea]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
[450adf]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);
[653cea]162 ASSERT( !dreiBein.IsZero(), "determineSignOfRotation() - dreiBein is zero.");
[450adf]163 dreiBein.Normalize();
164 const double sign =
165 (dreiBein.ScalarProduct(_newPosition) < 0.) ? -1. : +1.;
166 LOG(6, "DEBUG: oldCenter on plane is " << _oldPosition
[653cea]167 << ", newCenter on plane is " << _newPosition
[450adf]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}
[64cafb2]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 *
[0c42f2]200 * \param _old first set of returnpolygon (fewer or equal to \a _new)
201 * \param _new second set of returnpolygon
[64cafb2]202 * \param _Matching matching between the two sets
203 * \return pair with L1 and squared L2 error
204 */
[b67d89]205std::pair<double, double> SphericalPointDistribution::calculateErrorOfMatching(
[653cea]206 const VectorArray_t &_old,
207 const VectorArray_t &_new,
208 const IndexTupleList_t &_Matching)
[64cafb2]209{
210 std::pair<double, double> errors( std::make_pair( 0., 0. ) );
211
212 if (_Matching.size() > 1) {
[653cea]213 LOG(5, "INFO: Matching is " << _Matching);
[64cafb2]214
215 // calculate all pair-wise distances
[653cea]216 IndexTupleList_t keys(_old.size(), IndexList_t() );
217 std::generate (keys.begin(), keys.end(), UniqueNumberList);
218
[64cafb2]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) {
[653cea]228 const double gap = fabs(*firstiter - *seconditer);
[64cafb2]229 // L1 error
230 if (errors.first < gap)
231 errors.first = gap;
232 // L2 error
233 errors.second += gap*gap;
234 }
[653cea]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): "
[7e81ca]247 << errors.first << "," << errors.second << ".");
[64cafb2]248
249 return errors;
250}
251
[b67d89]252SphericalPointDistribution::Polygon_t SphericalPointDistribution::removeMatchingPoints(
[b3c052]253 const VectorArray_t &_points,
[64cafb2]254 const IndexList_t &_matchingindices
255 )
256{
[0c42f2]257 SphericalPointDistribution::Polygon_t remainingreturnpolygon;
[64cafb2]258 IndexArray_t indices(_matchingindices.begin(), _matchingindices.end());
259 std::sort(indices.begin(), indices.end());
[7e81ca]260 LOG(4, "DEBUG: sorted matching is " << indices);
[b3c052]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]);
[64cafb2]272 }
273
[0c42f2]274 return remainingreturnpolygon;
[64cafb2]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
[653cea]282 * \param _remainingweights current weights to fill (each weight a place)
283 * \param _remainiter iterator over the weights, indicating the current position we match
[64cafb2]284 * \param _matchingsize
285 */
[b67d89]286void SphericalPointDistribution::recurseMatchings(
[64cafb2]287 MatchingControlStructure &_MCS,
[653cea]288 IndexTupleList_t &_matching,
[64cafb2]289 IndexList_t _indices,
[653cea]290 WeightsArray_t &_remainingweights,
291 WeightsArray_t::iterator _remainiter,
292 const unsigned int _matchingsize
293 )
[f54930]294{
[653cea]295 LOG(5, "DEBUG: Recursing with current matching " << _matching
[7e81ca]296 << ", remaining indices " << _indices
[653cea]297 << ", and remaining weights " << _matchingsize);
[64cafb2]298 if (!_MCS.foundflag) {
[653cea]299 LOG(5, "DEBUG: Current matching has size " << _matching.size() << ", places left " << _matchingsize);
[946948]300 if (_matchingsize > 0) {
[64cafb2]301 // go through all indices
302 for (IndexList_t::iterator iter = _indices.begin();
[946948]303 (iter != _indices.end()) && (!_MCS.foundflag);) {
[653cea]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);
[64cafb2]322 // add index to matching
[653cea]323 filliniter->push_back(*iter);
324 --(*_remainiter);
325 LOG(6, "DEBUG: Adding " << *iter << " to matching at " << OldIndex << ".");
[64cafb2]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
[653cea]329 recurseMatchings(_MCS, _matching, _indices, _remainingweights, _remainiter, _matchingsize-1);
[64cafb2]330 // re-add chosen index and reset index to new position
[653cea]331 _indices.insert(backupiter, filliniter->back());
[64cafb2]332 iter = backupiter;
333 // remove index from _matching to make space for the next one
[653cea]334 filliniter->pop_back();
335 ++(*_remainiter);
[64cafb2]336 }
337 // gone through all indices then exit recursion
[946948]338 if (_matching.empty())
339 _MCS.foundflag = true;
[64cafb2]340 } else {
[653cea]341 LOG(4, "INFO: Found matching " << _matching);
[64cafb2]342 // calculate errors
343 std::pair<double, double> errors = calculateErrorOfMatching(
[b67d89]344 _MCS.oldpoints, _MCS.newpoints, _matching);
[64cafb2]345 if (errors.first < L1THRESHOLD) {
346 _MCS.bestmatching = _matching;
347 _MCS.foundflag = true;
[946948]348 } else if (_MCS.bestL2 > errors.second) {
[64cafb2]349 _MCS.bestmatching = _matching;
350 _MCS.bestL2 = errors.second;
351 }
352 }
[f54930]353 }
354}
355
[b67d89]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.
[946948]361 *
[450adf]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 *
[b67d89]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
[946948]373 */
[b67d89]374SphericalPointDistribution::IndexList_t SphericalPointDistribution::findBestMatching(
[450adf]375 const WeightedPolygon_t &_polygon,
376 Polygon_t &_newpolygon
[b67d89]377 )
[946948]378{
[b67d89]379 MatchingControlStructure MCS;
380 MCS.foundflag = false;
381 MCS.bestL2 = std::numeric_limits<double>::max();
[653cea]382 // transform lists into arrays
[2199c2]383 for (WeightedPolygon_t::const_iterator iter = _polygon.begin();
[653cea]384 iter != _polygon.end(); ++iter) {
[2199c2]385 MCS.oldpoints.push_back(iter->first);
[653cea]386 MCS.weights.push_back(iter->second);
387 }
[b67d89]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);
[653cea]395 IndexTupleList_t matching;
[b67d89]396
397 // walk through all matchings
[653cea]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.");
[946948]414 }
415
[450adf]416 // combine multiple points and create simple IndexList from IndexTupleList
417 const SphericalPointDistribution::IndexList_t IndexList =
[653cea]418 joinPoints(_newpolygon, MCS.newpoints, MCS.bestmatching);
[b67d89]419
[450adf]420 return IndexList;
[b67d89]421}
422
[450adf]423SphericalPointDistribution::IndexList_t SphericalPointDistribution::joinPoints(
424 Polygon_t &_newpolygon,
425 const VectorArray_t &_newpoints,
426 const IndexTupleList_t &_bestmatching
427 )
[b67d89]428{
[450adf]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);
[653cea]447 LOG(5, "DEBUG: Combining " << tupleiter->size() << " points to weighted center "
[450adf]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;
[b67d89]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;
[946948]674}
675
676
[64cafb2]677SphericalPointDistribution::Polygon_t
678SphericalPointDistribution::matchSphericalPointDistributions(
[2199c2]679 const SphericalPointDistribution::WeightedPolygon_t &_polygon,
[450adf]680 SphericalPointDistribution::Polygon_t &_newpolygon
[64cafb2]681 )
682{
[2199c2]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);
[946948]688 LOG(2, "INFO: Matching old polygon " << _polygon
[7e81ca]689 << " with new polygon " << _newpolygon);
[64cafb2]690
[b67d89]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.");
[2199c2]694 return remainingpoints;
[b67d89]695 }
[64cafb2]696
[b67d89]697 if (_polygon.size() > 0) {
698 IndexList_t bestmatching = findBestMatching(_polygon, _newpolygon);
699 LOG(2, "INFO: Best matching is " << bestmatching);
[64cafb2]700
[653cea]701 // _newpolygon has changed, so now convert to array
702 VectorArray_t remainingnew(_newpolygon.begin(), _newpolygon.end());
703
[64cafb2]704 // determine rotation angles to align the two point distributions with
[b67d89]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
[b3c052]708 VectorArray_t rotated_newpolygon = remainingnew;
[64cafb2]709 {
[b67d89]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);
[64cafb2]725 }
[b67d89]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())+".");
[b3c052]745 }
746 }
[b67d89]747#endif
[b3c052]748 }
[b67d89]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);
[b3c052]763
[f9d85f]764#ifndef NDEBUG
[b67d89]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 }
[f9d85f]778#endif
[946948]779
[b67d89]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);
[f9d85f]786 }
787 }
[64cafb2]788
[946948]789 // remove all points in matching and return remaining ones
790 SphericalPointDistribution::Polygon_t remainingpoints =
[b67d89]791 removeMatchingPoints(rotated_newpolygon, bestmatching);
[946948]792 LOG(2, "INFO: Remaining points are " << remainingpoints);
793 return remainingpoints;
[64cafb2]794 } else
795 return _newpolygon;
796}
Note: See TracBrowser for help on using the repository browser.