Changeset 6ef48c for src/Fragmentation
- Timestamp:
- Aug 20, 2014, 1:04:08 PM (11 years ago)
- Children:
- 0d4daf
- Parents:
- 11cc05
- git-author:
- Frederik Heber <heber@…> (05/29/14 18:15:49)
- git-committer:
- Frederik Heber <heber@…> (08/20/14 13:04:08)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/Fragmentation/Exporters/SphericalPointDistribution.hpp
r11cc05 r6ef48c 16 16 17 17 #include "CodePatterns/Assert.hpp" 18 18 #include "CodePatterns/IteratorAdaptors.hpp" 19 #include "CodePatterns/toString.hpp" 20 21 #include <algorithm> 19 22 #include <cmath> 23 #include <limits> 20 24 #include <list> 21 25 #include <vector> 26 #include <map> 27 28 #include "LinearAlgebra/Line.hpp" 29 #include "LinearAlgebra/RealSpaceMatrix.hpp" 22 30 #include "LinearAlgebra/Vector.hpp" 23 31 … … 59 67 /** Matches a given spherical distribution with another containing more 60 68 * points. 69 * 70 * The idea is to produce a matching from all points in \a _polygon to those 71 * in \a _newpolygon in such a way that their distance difference is minimal. 72 * As we just look at numbers of points determined by valency, i.e. 73 * independent of the number of atoms, we simply go through each of the possible 74 * mappings. We stop when the L1 error is below a certain \a threshold, 75 * otherwise we pick the matching with the lowest L2 error. 61 76 * 62 77 * This is a helper to determine points where to best insert saturation … … 80 95 }; 81 96 97 typedef std::list<unsigned int> IndexList_t; 98 typedef std::vector<unsigned int> IndexArray_t; 99 typedef std::vector<Vector> VectorArray_t; 100 typedef std::vector<double> DistanceArray_t; 101 102 DistanceArray_t calculatePairwiseDistances( 103 const std::vector<Vector> &_points, 104 const IndexList_t &_indices 105 ) 106 { 107 DistanceArray_t result; 108 for (IndexList_t::const_iterator firstiter = _indices.begin(); 109 firstiter != _indices.end(); ++firstiter) { 110 for (IndexList_t::const_iterator seconditer = firstiter; 111 seconditer != _indices.end(); ++seconditer) { 112 if (firstiter == seconditer) 113 continue; 114 const double distance = (_points[*firstiter] - _points[*seconditer]).NormSquared(); 115 result.push_back(distance); 116 } 117 } 118 return result; 119 } 120 121 // class generator: taken from www.cplusplus.com example std::generate 122 struct c_unique { 123 int current; 124 c_unique() {current=0;} 125 int operator()() {return ++current;} 126 } UniqueNumber; 127 128 /** Returns squared L2 error of the given \a _Matching. 129 * 130 * We compare the pair-wise distances of each associated matching 131 * and check whether these distances each match between \a _old and 132 * \a _new. 133 * 134 * \param _old first set of points (fewer or equal to \a _new) 135 * \param _new second set of points 136 * \param _Matching matching between the two sets 137 * \return pair with L1 and squared L2 error 138 */ 139 std::pair<double, double> calculateErrorOfMatching( 140 const std::vector<Vector> &_old, 141 const std::vector<Vector> &_new, 142 const IndexList_t &_Matching) 143 { 144 std::pair<double, double> errors( std::make_pair( 0., 0. ) ); 145 146 if (_Matching.size() > 1) { 147 // convert matching into two vectors to calculate distance among another 148 149 // calculate all pair-wise distances 150 IndexList_t keys(_Matching.size()); 151 std::generate (keys.begin(), keys.end(), UniqueNumber); 152 const DistanceArray_t firstdistances = calculatePairwiseDistances(_old, keys); 153 const DistanceArray_t seconddistances = calculatePairwiseDistances(_new, _Matching); 154 155 ASSERT( firstdistances.size() == keys.size()*(keys.size()+1)/2, 156 "calculateL2ErrorOfMatching() - unexpected pair-wise distance array size."); 157 ASSERT( firstdistances.size() == seconddistances.size(), 158 "calculateL2ErrorOfMatching() - mismatch in pair-wise distance array sizes."); 159 DistanceArray_t::const_iterator firstiter = firstdistances.begin(); 160 DistanceArray_t::const_iterator seconditer = seconddistances.begin(); 161 for (;(firstiter != firstdistances.end()) && (seconditer != seconddistances.end()); 162 ++firstiter, ++seconditer) { 163 const double gap = *firstiter - *seconditer; 164 // L1 error 165 if (errors.first < gap) 166 errors.first = gap; 167 // L2 error 168 errors.second += gap*gap; 169 } 170 } 171 172 return errors; 173 } 174 175 SphericalPointDistribution::Polygon_t removeMatchingPoints( 176 const SphericalPointDistribution::Polygon_t &_points, 177 const IndexList_t &_matchingindices 178 ) 179 { 180 SphericalPointDistribution::Polygon_t remainingpoints; 181 IndexArray_t indices(_matchingindices.begin(), _matchingindices.end()); 182 std::sort(indices.begin(), indices.end()); 183 IndexArray_t::const_iterator valueiter = indices.begin(); 184 SphericalPointDistribution::Polygon_t::const_iterator pointiter = 185 _points.begin(); 186 for (unsigned int i=0; i< _points.size(); ++i, ++pointiter) { 187 // skip all those in values 188 if (*valueiter == i) 189 ++valueiter; 190 else 191 remainingpoints.push_back(*pointiter); 192 } 193 194 return remainingpoints; 195 } 196 197 /** Rotates a given polygon around x, y, and z axis by the given angles. 198 * 199 * Essentially, we concentrate on the three points of the polygon to rotate 200 * to the correct position. First, we rotate its center via \a angles, 201 * then we rotate the "triangle" around itself/\a _RotationAxis by 202 * \a _RotationAngle. 203 * 204 * \param _polygon polygon whose points to rotate 205 * \param _angles vector with rotation angles for x,y,z axis 206 * \param _RotationAxis 207 * \param _RotationAngle 208 */ 209 SphericalPointDistribution::Polygon_t rotatePolygon( 210 const SphericalPointDistribution::Polygon_t &_polygon, 211 const std::vector<double> &_angles, 212 const Line &_RotationAxis, 213 const double _RotationAngle) 214 { 215 SphericalPointDistribution::Polygon_t rotated_polygon = _polygon; 216 RealSpaceMatrix rotation; 217 ASSERT( _angles.size() == 3, 218 "rotatePolygon() - not exactly "+toString(3)+" angles given."); 219 rotation.setRotation(_angles[0] * M_PI/180., _angles[1] * M_PI/180., _angles[2] * M_PI/180.); 220 221 // apply rotation angles 222 for (SphericalPointDistribution::Polygon_t::iterator iter = rotated_polygon.begin(); 223 iter != rotated_polygon.end(); ++iter) { 224 *iter = rotation * (*iter); 225 _RotationAxis.rotateVector(*iter, _RotationAngle); 226 } 227 228 return rotated_polygon; 229 } 230 231 struct MatchingControlStructure { 232 bool foundflag; 233 double bestL2; 234 IndexList_t bestmatching; 235 VectorArray_t oldpoints; 236 VectorArray_t newpoints; 237 }; 238 239 /** Recursive function to go through all possible matchings. 240 * 241 * \param _MCS structure holding global information to the recursion 242 * \param _matching current matching being build up 243 * \param _indices contains still available indices 244 * \param _matchingsize 245 */ 246 void recurseMatchings( 247 MatchingControlStructure &_MCS, 248 IndexList_t &_matching, 249 IndexList_t _indices, 250 unsigned int _matchingsize) 251 { 252 //!> threshold for L1 error below which matching is immediately acceptable 253 const double L1THRESHOLD = 1e-2; 254 if (!_MCS.foundflag) { 255 if (_matching.size() < _matchingsize) { 256 // go through all indices 257 for (IndexList_t::iterator iter = _indices.begin(); 258 iter != _indices.end();) { 259 // add index to matching 260 _matching.push_back(*iter); 261 // remove index but keep iterator to position (is the next to erase element) 262 IndexList_t::iterator backupiter = _indices.erase(iter); 263 // recurse with decreased _matchingsize 264 recurseMatchings(_MCS, _matching, _indices, _matchingsize-1); 265 // re-add chosen index and reset index to new position 266 _indices.insert(backupiter, _matching.back()); 267 iter = backupiter; 268 // remove index from _matching to make space for the next one 269 _matching.pop_back(); 270 } 271 // gone through all indices then exit recursion 272 _MCS.foundflag = true; 273 } else { 274 // calculate errors 275 std::pair<double, double> errors = calculateErrorOfMatching( 276 _MCS.oldpoints, _MCS.newpoints, _matching); 277 if (errors.first < L1THRESHOLD) { 278 _MCS.bestmatching = _matching; 279 _MCS.foundflag = true; 280 } 281 if (_MCS.bestL2 > errors.second) { 282 _MCS.bestmatching = _matching; 283 _MCS.bestL2 = errors.second; 284 } 285 } 286 } 287 } 288 82 289 SphericalPointDistribution::Polygon_t 83 290 SphericalPointDistribution::matchSphericalPointDistributions( … … 86 293 ) 87 294 { 88 SphericalPointDistribution::Polygon_t remainingpolygon = _newpolygon; 89 90 return remainingpolygon; 295 SphericalPointDistribution::Polygon_t remainingpoints; 296 VectorArray_t remainingold(_polygon.begin(), _polygon.end()); 297 VectorArray_t remainingnew(_newpolygon.begin(), _newpolygon.end()); 298 299 if (_polygon.size() > 0) { 300 MatchingControlStructure MCS; 301 MCS.foundflag = false; 302 MCS.bestL2 = std::numeric_limits<double>::max(); 303 MCS.oldpoints.insert(MCS.oldpoints.begin(), _polygon.begin(),_polygon.end() ); 304 MCS.newpoints.insert(MCS.newpoints.begin(), _newpolygon.begin(),_newpolygon.end() ); 305 306 // search for bestmatching combinatorially 307 { 308 // translate polygon into vector to enable index addressing 309 IndexList_t indices(_newpolygon.size()); 310 std::generate(indices.begin(), indices.end(), UniqueNumber); 311 IndexList_t matching; 312 313 // walk through all matchings 314 const unsigned int matchingsize = _polygon.size(); 315 ASSERT( matchingsize <= indices.size(), 316 "SphericalPointDistribution::matchSphericalPointDistributions() - not enough new points to choose for matching to old ones."); 317 recurseMatchings(MCS, matching, indices, matchingsize); 318 } 319 320 // determine rotation angles to align the two point distributions with 321 // respect to bestmatching 322 std::vector<double> angles(3); 323 Vector newCenter; 324 { 325 // calculate center of triangle/line/point consisting of first points of matching 326 Vector oldCenter; 327 IndexList_t::const_iterator iter = MCS.bestmatching.begin(); 328 unsigned int i = 0; 329 for (; (i<3) && (i<MCS.bestmatching.size()); ++i, ++iter) { 330 oldCenter += remainingold[i]; 331 newCenter += remainingnew[*iter]; 332 } 333 oldCenter *= 1./(double)i; 334 newCenter *= 1./(double)i; 335 336 Vector direction(0.,0.,0.); 337 for(size_t i=0;i<NDIM;++i) { 338 // create new rotation axis 339 direction[i] = 1.; 340 const Line axis (zeroVec, direction); 341 // calculate rotation angle for this axis 342 const double alpha = direction.Angle(oldCenter) - direction.Angle(newCenter); 343 // perform rotation 344 axis.rotateVector(newCenter, alpha); 345 // store angle 346 angles[i] = alpha; 347 // reset direction component for next iteration 348 direction[i] = 0.; 349 } 350 } 351 const Line RotationAxis(zeroVec, newCenter); 352 const double RotationAngle = 353 newCenter.Angle(remainingold[0]) 354 - newCenter.Angle(remainingnew[*MCS.bestmatching.begin()]); 355 356 // rotate _newpolygon 357 SphericalPointDistribution::Polygon_t rotated_newpolygon = 358 rotatePolygon(_newpolygon, angles, RotationAxis, RotationAngle); 359 360 // remove all points in matching and return remaining ones 361 return removeMatchingPoints(rotated_newpolygon, MCS.bestmatching); 362 } else 363 return _newpolygon; 91 364 } 92 365
Note:
See TracChangeset
for help on using the changeset viewer.