ProSHADE  0.7.6.6 (JUL 2022)
Protein Shape Detection
ProSHADE_symmetry.cpp
Go to the documentation of this file.
1 
22 //==================================================== ProSHADE
23 #include "ProSHADE_symmetry.hpp"
24 
25 //==================================================== Local functions prototypes
26 proshade_double determinePeakThreshold ( std::vector < proshade_double > inArr, proshade_double noIQRsFromMedian, proshade_double startMinVal );
27 bool sortProSHADESymmetryByPeak ( proshade_double* a, proshade_double* b );
28 std::vector < std::pair< proshade_unsign, proshade_unsign > > findBestIcosDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr );
29 std::vector < std::pair< proshade_unsign, proshade_unsign > > findBestOctaDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr );
30 std::vector < std::pair< proshade_unsign, proshade_unsign > > findBestTetraDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr );
31 
42 {
43  //================================================ Report progress
44  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting self-rotation function computation.", settings->messageShift );
45 
46  //================================================ Compute un-weighted E matrices and their weights
47  ProSHADE_internal_distances::computeEMatrices ( this, this, settings );
48 
49  //================================================ Normalise E matrices by the magnitudes
50  ProSHADE_internal_distances::normaliseEMatrices ( this, this, settings );
51 
52  //================================================ Generate SO(3) coefficients
54 
55  //================================================ Compute the inverse SO(3) Fourier Transform (SOFT) on the newly computed coefficients
57 
58  //================================================ Report completion
59  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "Self-rotation function obtained.", settings->messageShift );
60 
61  //================================================ Done
62  return ;
63 
64 }
65 
73 proshade_double determinePeakThreshold ( std::vector < proshade_double > inArr, proshade_double noIQRsFromMedian, proshade_double startMinVal )
74 {
75  //================================================ Initialise variables
76  proshade_double ret = 0.0;
77  proshade_double rmsd = 0.0;
78  size_t vecSize = inArr.size();
79  proshade_unsign noVals = 0;
80  proshade_double mean = 0.0;
81 
82  //================================================ Deal with low number of input cases
83  if ( vecSize == 0 ) { return ( ret ); } // Return 0
84  if ( vecSize <= 4 ) { ret = std::accumulate ( inArr.begin(), inArr.end(), 0.0 ) / static_cast< proshade_double > ( vecSize ); return ( ret ); } // Return mean
85 
86  //================================================ Deal with reasonable number in input cases
87  else
88  {
89  //============================================ Find mean
90  mean = std::accumulate ( inArr.begin(), inArr.end(), 0.0 ) / static_cast< proshade_double > ( vecSize );
91 
92  //============================================ Get the RMS distance
93  for ( size_t i = 0; i < vecSize; i++ )
94  {
95  rmsd += std::pow ( ret - inArr.at(i), 2.0 );
96  }
97  rmsd = std::sqrt ( rmsd );
98 
99  //============================================ Get the threshold
100  ret = std::min ( mean + ( noIQRsFromMedian * rmsd ), startMinVal );
101  for ( size_t iter = 0; iter < inArr.size(); iter++ ) { if ( inArr.at(iter) > ret ) { noVals += 1; } }
102  while ( noVals > 1000)
103  {
104  noVals = 0;
105  ret += 0.01;
106  for ( size_t iter = 0; iter < inArr.size(); iter++ ) { if ( inArr.at(iter) > ret ) { noVals += 1; } }
107  }
108  if ( noVals == 0 ) { ret -= 0.01; }
109  }
110 
111  //================================================ Sanity checks
112  if ( ret > *( std::max_element ( inArr.begin(), inArr.end() ) ) )
113  {
114  ret = *( std::max_element ( inArr.begin(), inArr.end() ) );
115  }
116 
117  if ( ret > 0.85 ) { ret = 0.85; }
118 
119  //================================================ Done
120  return ( ret );
121 
122 }
123 
136 {
137  //================================================ Report progress
138  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting self-rotation function conversion to angle-axis representation.", settings->messageShift );
139 
140  //================================================ Initialise variables
141  proshade_double shellSpacing = ( 2.0 * M_PI ) / static_cast<proshade_double> ( this->getMaxBand ( ) ) * 2.0;
142  std::vector< proshade_double > allPeakHeights;
143 
144  //================================================ Initialise the spheres
145  for ( proshade_unsign spIt = 1; spIt < ( this->getMaxBand ( ) * 2 ); spIt++ )
146  {
147  this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere( static_cast<proshade_double> ( spIt ) * shellSpacing,
148  shellSpacing,
149  this->getMaxBand ( ) * 2,
150  this->getEMatDim ( ) * 2,
151  static_cast<proshade_double> ( spIt ) * shellSpacing,
152  spIt - 1 ) );
153  }
154 
155  //================================================ Interpolate the rotation function onto the spheres
156  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( sphereMappedRotFun.size() ); shIt++ )
157  {
158  //============================================ Report progress
159  std::stringstream hlpSS;
160  hlpSS << "Interpolating sphere " << shIt << " ( radius: " << this->sphereMappedRotFun.at(shIt)->getRadius() << " ).";
161  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str(), settings->messageShift );
162 
163  //============================================ Interpolate onto spheres
164  this->sphereMappedRotFun.at(shIt)->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
165  }
166 
167  //================================================ Report completion
168  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "Self-rotation function converted to spherical angle-axis space.", settings->messageShift );
169  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "Started peak detection on the angle-axis spheres.", settings->messageShift );
170 
171  //================================================ Find all peaks in the sphere grids
172  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
173  {
174  this->sphereMappedRotFun.at(shIt)->findAllPeaks ( static_cast< proshade_signed > ( settings->peakNeighbours ), &allPeakHeights );
175  }
176 
177  //================================================ Report progress
178  std::stringstream hlpSS;
179  hlpSS << "Detected " << allPeakHeights.size() << " peaks with any height.";
180  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str(), settings->messageShift );
181 
182  //================================================ Compute threshold for small peaks
183  proshade_double peakThres = std::max ( settings->minSymPeak, determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak, settings->peakThresholdMin ) );
184 
185  //================================================ Report progress
186  std::stringstream hlpSS2;
187  hlpSS2 << "From these peaks, decided the threshold will be " << peakThres << " peak height.";
188  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str(), settings->messageShift );
189 
190  //================================================ Remove too small peaks
191  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
192  {
193  this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( peakThres );
194  }
195 
196  //================================================ Report progress
197  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, "Peaks detected for all spheres.", settings->messageShift );
198 
199  //================================================ Done
200  return ;
201 
202 }
203 
215 bool ProSHADE_internal_symmetry::isSymmetrySame ( std::vector< proshade_double* >* ret, proshade_double* sym, proshade_double simThres, proshade_signed* matchedPos )
216 {
217  //================================================ Initialise variables
218  proshade_double dotProduct = 0.0;
219  *matchedPos = -1;
220 
221  //================================================ Check
222  for ( proshade_unsign symIt = 0; symIt < static_cast<proshade_unsign> ( ret->size() ); symIt++ )
223  {
224  //============================================ Minor speed-up => only test for same folds
225  const FloatingPoint< proshade_double > lhs ( ret->at(symIt)[0] ), rhs ( sym[0] );
226  if ( lhs.AlmostEquals ( rhs ) )
227  {
228  //======================================== Is axis the same?
229  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &ret->at(symIt)[1], &ret->at(symIt)[2],
230  &ret->at(symIt)[3], &sym[1], &sym[2], &sym[3] );
231  if ( ( ( 1.0 > ( dotProduct - simThres ) ) && ( 1.0 < ( dotProduct + simThres ) ) ) || ( ( -1.0 > ( dotProduct - simThres ) ) && ( -1.0 < ( dotProduct + simThres ) ) ) )
232  {
233  //==================================== Matched. Save the index
234  *matchedPos = static_cast< proshade_signed > ( symIt );
235 
236  //==================================== Does the already saved have higher height?
237  if ( ret->at(symIt)[5] >= sym[5] ) { return ( true ); }
238 
239  //==================================== In this case, new is better than old - sort it out
240  ret->at(symIt)[1] = sym[1];
241  ret->at(symIt)[2] = sym[2];
242  ret->at(symIt)[3] = sym[3];
243  ret->at(symIt)[5] = sym[5];
244  return ( true );
245  }
246  }
247  }
248 
249  //================================================ Done - no matches found
250  return ( false );
251 
252 }
253 
266 bool ProSHADE_internal_symmetry::isSymmetrySame ( std::vector< proshade_double* >* ret, proshade_double* sym, proshade_double simThres, proshade_signed* matchedPos, proshade_double fscVal )
267 {
268  //================================================ Initialise variables
269  proshade_double dotProduct = 0.0;
270  *matchedPos = -1;
271 
272  //================================================ Check
273  for ( proshade_unsign symIt = 0; symIt < static_cast<proshade_unsign> ( ret->size() ); symIt++ )
274  {
275  //============================================ Minor speed-up => only test for same folds
276  const FloatingPoint< proshade_double > lhs ( ret->at(symIt)[0] ), rhs ( sym[0] );
277  if ( lhs.AlmostEquals ( rhs ) )
278  {
279  //======================================== Is axis the same?
280  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &ret->at(symIt)[1], &ret->at(symIt)[2],
281  &ret->at(symIt)[3], &sym[1], &sym[2], &sym[3] );
282  if ( ( ( 1.0 > ( dotProduct - simThres ) ) && ( 1.0 < ( dotProduct + simThres ) ) ) || ( ( -1.0 > ( dotProduct - simThres ) ) && ( -1.0 < ( dotProduct + simThres ) ) ) )
283  {
284  //==================================== Matched. Save the index
285  *matchedPos = static_cast< proshade_signed > ( symIt );
286 
287  //==================================== Does the already saved have higher height?
288  if ( ret->at(symIt)[5] >= sym[5] ) { return ( true ); }
289 
290  //==================================== In this case, new is better than old - sort it out
291  ret->at(symIt)[1] = sym[1];
292  ret->at(symIt)[2] = sym[2];
293  ret->at(symIt)[3] = sym[3];
294  ret->at(symIt)[5] = sym[5];
295  ret->at(symIt)[6] = fscVal;
296  return ( true );
297  }
298  }
299  }
300 
301  //================================================ Done - no matches found
302  return ( false );
303 
304 }
305 
317 void ProSHADE_internal_data::ProSHADE_data::getDihedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
318 {
319  //================================================ Initialise variables
320  proshade_double dotProduct;
321 
322  //================================================ Report progress
323  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting D symmetry detection.", settings->messageShift );
324 
325  //================================================If not enough axes, just end here
326  if ( CSymList->size() < 2 ) { return ; }
327 
328  //================================================ For each unique pair of axes
329  for ( proshade_unsign ax1 = 0; ax1 < static_cast<proshade_unsign> ( CSymList->size() ); ax1++ )
330  {
331  //============================================ Ignore small axes
332  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(ax1)[5] ), rhs1 ( -999.9 );
333  if ( ( CSymList->at(ax1)[5] < settings->minSymPeak ) && !( lhs1.AlmostEquals ( rhs1 ) ) ) { continue; }
334 
335  for ( proshade_unsign ax2 = 1; ax2 < static_cast<proshade_unsign> ( CSymList->size() ); ax2++ )
336  {
337  //======================================= Use unique pairs only
338  if ( ax1 >= ax2 ) { continue; }
339 
340  //======================================== Ignore small axes
341  const FloatingPoint< proshade_double > lhs2 ( CSymList->at(ax2)[5] ), rhs2 ( -999.9 );
342  if ( ( CSymList->at(ax2)[5] < settings->minSymPeak ) && !( lhs2.AlmostEquals ( rhs2 ) ) ) { continue; }
343 
344  //======================================= Compute the dot product
345  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(ax1)[1], &CSymList->at(ax1)[2],
346  &CSymList->at(ax1)[3], &CSymList->at(ax2)[1],
347  &CSymList->at(ax2)[2], &CSymList->at(ax2)[3] );
348 
349  //======================================== If close to zero, these two axes are perpendicular
350  if ( std::abs( dotProduct ) < settings->axisErrTolerance )
351  {
352  //==================================== Save
353  if ( CSymList->at(ax1)[0] >= CSymList->at(ax2)[0] )
354  {
355  std::vector< proshade_double* > hlpVec;
356 
357  proshade_double* hlpSym = new proshade_double[7];
358  ProSHADE_internal_misc::checkMemoryAllocation ( hlpSym, __FILE__, __LINE__, __func__ );
359  for ( size_t iter = 0; iter < 7; iter++ ) { hlpSym[iter] = CSymList->at(ax1)[iter]; }
360  ProSHADE_internal_misc::addToDblPtrVector ( &hlpVec, hlpSym );
361 
362  proshade_double* hlpSym2 = new proshade_double[7];
363  ProSHADE_internal_misc::checkMemoryAllocation ( hlpSym2, __FILE__, __LINE__, __func__ );
364  for ( size_t iter = 0; iter < 7; iter++ ) { hlpSym2[iter] = CSymList->at(ax2)[iter]; }
365  ProSHADE_internal_misc::addToDblPtrVector ( &hlpVec, hlpSym2 );
366 
367  ProSHADE_internal_misc::addToDblPtrVectorVector ( &this->dihedralSymmetries, hlpVec );
368  }
369  else
370  {
371  std::vector< proshade_double* > hlpVec;
372 
373  proshade_double* hlpSym = new proshade_double[7];
374  ProSHADE_internal_misc::checkMemoryAllocation ( hlpSym, __FILE__, __LINE__, __func__ );
375  for ( size_t iter = 0; iter < 7; iter++ ) { hlpSym[iter] = CSymList->at(ax2)[iter]; }
376  ProSHADE_internal_misc::addToDblPtrVector ( &hlpVec, hlpSym );
377 
378  proshade_double* hlpSym2 = new proshade_double[7];
379  ProSHADE_internal_misc::checkMemoryAllocation ( hlpSym2, __FILE__, __LINE__, __func__ );
380  for ( size_t iter = 0; iter < 7; iter++ ) { hlpSym2[iter] = CSymList->at(ax1)[iter]; }
381  ProSHADE_internal_misc::addToDblPtrVector ( &hlpVec, hlpSym2 );
382 
383  ProSHADE_internal_misc::addToDblPtrVectorVector ( &this->dihedralSymmetries, hlpVec );
384  }
385  }
386  }
387  }
388 
389  //================================================ Report progress
390  std::stringstream hlpSS;
391  hlpSS << "Detected " << this->dihedralSymmetries.size() << " D symmetries.";
392  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, hlpSS.str(), settings->messageShift );
393 
394  //================================================ Done
395  return ;
396 
397 }
398 
410 bool ProSHADE_internal_symmetry::detectTetrahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
411 {
412  //================================================ Initialise variables
413  std::vector< proshade_unsign > C3List;
414  proshade_double dotProduct;
415 
416  //================================================ Find all C3 symmetries
417  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
418  {
419  const FloatingPoint< proshade_double > lhs ( CSymList->at(cSym)[0] ), rhs ( 3.0 );
420  if ( lhs.AlmostEquals ( rhs ) && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C3List, cSym ); }
421  }
422 
423  //================================================ For each unique pair of C3s
424  for ( proshade_unsign c31 = 0; c31 < static_cast<proshade_unsign> ( C3List.size() ); c31++ )
425  {
426  for ( proshade_unsign c32 = 1; c32 < static_cast<proshade_unsign> ( C3List.size() ); c32++ )
427  {
428  //================================ Unique pairs only
429  if ( c31 >= c32 ) { continue; }
430 
431  //======================================== Check the angle between the C3 axes
432  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C3List.at(c31))[1], &CSymList->at(C3List.at(c31))[2], &CSymList->at(C3List.at(c31))[3], &CSymList->at(C3List.at(c32))[1], &CSymList->at(C3List.at(c32))[2], &CSymList->at(C3List.at(c32))[3] );
433 
434  //================================ Is the angle approximately the dihedral angle
435  if ( ( ( 1.0 / 3.0 ) > ( dotProduct - axErr ) ) && ( ( 1.0 / 3.0 ) < ( dotProduct + axErr ) ) )
436  {
437  return ( true );
438  }
439  }
440  }
441 
442  //================================================ Done
443  return ( false );
444 
445 }
446 
461 void ProSHADE_internal_symmetry::findTetra4C3s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
462 {
463  //================================================ Initialise variables
464  std::vector< proshade_unsign > C3PossibilitiesHlp;
465  std::vector< std::vector< proshade_unsign > > C3Possibilities;
466  bool groupMatched;
467 
468  //================================================ Report progress
469  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of four C3 axes.", messageShift );
470 
471  //================================================ For all symmetries in the C symmetries list
472  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
473  {
474  //============================================ Search only using C3s
475  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
476 
477  //============================================ If this is the first C3, then just save it to the first group of the temporary holder
478  if ( C3Possibilities.size() == 0 ) { ProSHADE_internal_misc::addToUnsignVector ( &C3PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C3Possibilities, C3PossibilitiesHlp ); continue; }
479 
480  //============================================ If second or more C3, check if it has the correct angle to all other already found C3s for each group
481  groupMatched = false;
482  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C3Possibilities.size() ); gIt++ )
483  {
484  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &C3Possibilities.at(gIt), CSymList->at(cIt), axErr, 1.0/3.0, true, cIt ) ) { ProSHADE_internal_misc::addToUnsignVector ( &C3Possibilities.at(gIt), cIt ); groupMatched = true; break; }
485  }
486 
487  //============================================ If no group matched, create a new group
488  if ( !groupMatched ) { C3PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C3PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C3Possibilities, C3PossibilitiesHlp ); continue; }
489  }
490 
491  //================================================ Test for missing symmetry axes, if need be
492  ProSHADE_internal_symmetry::findMissingAxes ( &C3Possibilities, CSymList, 4, axErr, 1.0/3.0, 3, dataObj, minPeakHeight );
493 
494  //================================================ Any group has 4 entries? If more such groups, take the one with highest average height.
495  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
496  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( C3Possibilities.size() ); iter++ ) { if ( C3Possibilities.at(iter).size() == 4 ) { if ( ( ( CSymList->at(C3Possibilities.at(iter).at(0))[5] + CSymList->at(C3Possibilities.at(iter).at(1))[5] + CSymList->at(C3Possibilities.at(iter).at(2))[5] + CSymList->at(C3Possibilities.at(iter).at(3))[5] ) / 4.0 ) > maxHeight ) { maxHeight = ( ( CSymList->at(C3Possibilities.at(iter).at(0))[5] + CSymList->at(C3Possibilities.at(iter).at(1))[5] + CSymList->at(C3Possibilities.at(iter).at(2))[5] + CSymList->at(C3Possibilities.at(iter).at(3))[5] ) / 4.0 ); maxGrp = iter; } } }
497 
498  if ( C3Possibilities.at(maxGrp).size() == 4 )
499  {
500  //============================================ Success! Save and exit
501  for ( proshade_unsign it = 0; it < static_cast<proshade_unsign> ( C3Possibilities.at(maxGrp).size() ); it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C3Possibilities.at(maxGrp).at(it)) ); }
502 
503  //============================================ Report progress
504  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of four C3 axes successfull.", messageShift );
505 
506  //============================================ Done
507  return ;
508  }
509 
510  //================================================ Done
511  return ;
512 
513 }
514 
531 bool ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( std::vector< proshade_double* >* CSymList, std::vector< proshade_unsign >* grp, proshade_double* sym, proshade_double axErr, proshade_double angle, bool improve, proshade_unsign pos )
532 {
533  //================================================ Initialise variables
534  bool allAnglesMet = true;
535  proshade_double dotProduct;
536 
537  //================================================ Improve if required
538  if ( improve )
539  {
540  for ( proshade_unsign mIt = 0; mIt < static_cast<proshade_unsign> ( grp->size() ); mIt++ )
541  {
542  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(grp->at(mIt))[1], &CSymList->at(grp->at(mIt))[2], &CSymList->at(grp->at(mIt))[3], &sym[1], &sym[2], &sym[3] );
543 
544  if ( ( ( 1.0 > ( dotProduct - axErr ) ) && ( 1.0 < ( dotProduct + axErr ) ) ) || ( ( -1.0 > ( dotProduct - axErr ) ) && ( -1.0 < ( dotProduct + axErr ) ) ) )
545  {
546  if ( sym[5] > CSymList->at(grp->at(mIt))[5] )
547  {
548  grp->at(mIt) = pos;
549  }
550  else
551  {
552  allAnglesMet = false;
553  return ( allAnglesMet );
554  }
555  }
556  }
557  }
558 
559  //================================================ For all group members
560  for ( proshade_unsign mIt = 0; mIt < static_cast<proshade_unsign> ( grp->size() ); mIt++ )
561  {
562  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(grp->at(mIt))[1], &CSymList->at(grp->at(mIt))[2], &CSymList->at(grp->at(mIt))[3], &sym[1], &sym[2], &sym[3] );
563 
564  if ( ( angle > ( std::abs ( dotProduct ) - axErr ) ) &&
565  ( angle < ( std::abs ( dotProduct ) + axErr ) ) )
566  {
567  //======================================== Matching group memner - try next one
568  }
569  else
570  {
571  //======================================== Group member not matched - try next group
572  allAnglesMet = false;
573  break;
574  }
575  }
576 
577  //================================================ Done
578  return ( allAnglesMet );
579 
580 }
581 
598 bool ProSHADE_internal_symmetry::findMissingAxes ( std::vector< std::vector< proshade_unsign > >* possibilities, std::vector< proshade_double* >* CSymList, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_double angle, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_double minPeakHeight )
599 {
600  //================================================ Initialise variables
601  std::vector< proshade_double* > hlpVec;
602  bool atLeastOne = false;
603 
604  //================================================ Proceed only if need be
605  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( possibilities->size() ); gIt++ )
606  {
607  if ( static_cast<proshade_unsign> ( possibilities->at(gIt).size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
608  }
609 
610  //================================================ For each possible group
611  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( possibilities->size() ); gIt++ )
612  {
613  //============================================ This will not work for less than two axes in group
614  if ( possibilities->at(gIt).size() < 2 ) { continue; }
615 
616  //============================================ Prepare iteration
617  hlpVec.clear ( );
618 
619  //============================================ Search for missing axes
620  ProSHADE_internal_symmetry::searchMissingSymmetrySpace ( dataObj, CSymList, &possibilities->at(gIt), &hlpVec, axErr, angle, fold, minPeakHeight );
621 
622  //============================================ Add missing axes
623  if ( hlpVec.size() > 0 )
624  {
625  //======================================== Start adding by highest first
626  std::sort ( hlpVec.begin(), hlpVec.end(), ProSHADE_internal_misc::sortSymHlpInv );
627 
628  //======================================== For each missing axis
629  for ( proshade_unsign axIt = 0; axIt < static_cast<proshade_unsign> ( hlpVec.size() ); axIt++ )
630  {
631  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &possibilities->at(gIt), hlpVec.at(axIt), axErr, angle, false ) )
632  {
633  //================================ Check for uniqueness
634  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, hlpVec.at(axIt), axErr ) )
635  {
636  //============================ Add
637  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, hlpVec.at(axIt) );
638  ProSHADE_internal_misc::addToUnsignVector ( &possibilities->at(gIt), static_cast<proshade_unsign> ( CSymList->size()-1 ) );
639  }
640  }
641  }
642  }
643 
644  if ( possibilities->at(gIt).size() == requiredNoAxes ) { atLeastOne = true; }
645  }
646 
647  //================================================ Done
648  return ( atLeastOne );
649 
650 }
651 
658 bool ProSHADE_internal_symmetry::sortArrVecHlp ( const proshade_double* a, const proshade_double* b )
659 {
660  //================================================ Compare
661  return ( a[0] < b[0] );
662 
663 }
664 
679 proshade_double ProSHADE_internal_symmetry::missingAxisHeight ( proshade_double xVal, proshade_double yVal, proshade_double zVal, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_unsign fold, proshade_double axErr )
680 {
681  //================================================ Initialise variables
682  proshade_double ret = 0.0;
683  proshade_double curSum = 0.0;
684  proshade_double maxVal = 0.0;
685  proshade_double angStep = std::acos ( 1.0 - axErr ) / 2;
686  std::vector< proshade_double* > angVec;
687 
688  //================================================ Find map points conforming to the axis
689  angVec = ProSHADE_internal_symmetry::findMissingAxisPoints ( xVal, yVal, zVal, dataObj, axErr );
690 
691  //================================================ Sort points by angle
692  std::sort ( angVec.begin(), angVec.end(), ProSHADE_internal_symmetry::sortArrVecHlp );
693 
694  //================================================ Find the best X peaks with correct distances
695  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( std::floor ( ( 2.0 * M_PI / angStep ) / static_cast< proshade_double > ( fold ) ) ); iter++ )
696  {
697  //============================================ Initialise new ang group iteration
698  curSum = 0.0;
699 
700  //============================================ For each of the fold times
701  for ( proshade_unsign angCmb = 0; angCmb < static_cast<proshade_unsign> ( fold ); angCmb++ )
702  {
703  //======================================== Initialise
704  maxVal = 0.0;
705 
706  //======================================== Search
707  for ( proshade_unsign angIt = 0; angIt < static_cast<proshade_unsign> ( angVec.size() ); angIt++ )
708  {
709  if ( angVec.at(angIt)[0] < ( ( static_cast< proshade_double > ( iter ) * angStep ) +
710  ( ( 2.0 * M_PI / static_cast< proshade_double > ( fold ) ) * static_cast< proshade_double > ( angCmb ) ) ) ) { continue; }
711  if ( angVec.at(angIt)[0] > ( ( ( static_cast< proshade_double > ( iter ) + 1.0 ) * angStep ) +
712  ( ( 2.0 * M_PI / static_cast< proshade_double > ( fold ) ) * static_cast< proshade_double > ( angCmb ) ) ) ) { break; }
713 
714  if ( angVec.at(angIt)[1] > maxVal ) { maxVal = angVec.at(angIt)[1]; }
715  }
716  curSum += maxVal;
717  }
718  curSum /= static_cast<proshade_double> ( fold );
719  if ( ret < curSum ) { ret = curSum; }
720  }
721 
722  //================================================ Release memory
723  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( angVec.size() ); iter++ ) { delete[] angVec.at(iter); }
724 
725  //================================================ Done
726  return ( ret );
727 
728 }
729 
743 std::vector < proshade_double* > ProSHADE_internal_symmetry::findMissingAxisPoints ( proshade_double xVal, proshade_double yVal, proshade_double zVal, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_double axErr )
744 {
745  //================================================ Initialise variables
746  proshade_double euA, euB, euG, xPk, yPk, zPk, anglPk;
747  proshade_double* rotMat = new proshade_double [9];
748  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat, __FILE__, __LINE__, __func__ );
749  proshade_unsign arrIndex;
750  std::vector< proshade_double* > angVec;
751 
752  //================================================ Search the self-rotation map
753  for ( proshade_unsign xIt = 0; xIt < ( dataObj->getMaxBand ( ) * 2 ); xIt++ )
754  {
755  for ( proshade_unsign yIt = 0; yIt < ( dataObj->getMaxBand ( ) * 2 ); yIt++ )
756  {
757  for ( proshade_unsign zIt = 0; zIt < ( dataObj->getMaxBand ( ) * 2 ); zIt++ )
758  {
759  //==================================== Get height and check against threshold
760  arrIndex = zIt + ( dataObj->getMaxBand ( ) * 2 ) * ( yIt + ( dataObj->getMaxBand ( ) * 2 ) * xIt );
761 
762  //==================================== Get angle-axis values
763  ProSHADE_internal_maths::getEulerZYZFromSOFTPosition ( static_cast< proshade_signed > ( dataObj->getMaxBand() ), static_cast< proshade_signed > ( xIt ),
764  static_cast< proshade_signed > ( yIt ), static_cast< proshade_signed > ( zIt ),
765  &euA, &euB, &euG );
767  ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( rotMat, &xPk, &yPk, &zPk, &anglPk );
768 
769  //==================================== Set largest axis element to positive
770  const FloatingPoint< proshade_double > lhs1 ( std::max ( std::abs ( xPk ), std::max( std::abs ( yPk ), std::abs ( zPk ) ) ) );
771  const FloatingPoint< proshade_double > rhs1 ( std::abs ( xPk ));
772  const FloatingPoint< proshade_double > rhs2 ( std::abs ( yPk ) );
773  const FloatingPoint< proshade_double > rhs3 ( std::abs ( zPk ) );
774  if ( ( lhs1.AlmostEquals ( rhs1 ) && ( xPk < 0.0 ) ) ||
775  ( lhs1.AlmostEquals ( rhs2 ) && ( yPk < 0.0 ) ) ||
776  ( lhs1.AlmostEquals ( rhs3 ) && ( zPk < 0.0 ) ) )
777  {
778  xPk *= -1.0;
779  yPk *= -1.0;
780  zPk *= -1.0;
781  anglPk *= -1.0;
782  }
783 
784  //==================================== Does the peak match the required axis?
785  if ( ProSHADE_internal_maths::vectorOrientationSimilarity ( xPk, yPk, zPk, xVal, yVal, zVal, axErr ) )
786  {
787  //================================ Matching map point - save it
788  proshade_double* hlpArr = new proshade_double [2];
789  ProSHADE_internal_misc::checkMemoryAllocation ( hlpArr, __FILE__, __LINE__, __func__ );
790  hlpArr[0] = anglPk + M_PI;
791  hlpArr[1] = pow( dataObj->getInvSO3Coeffs()[arrIndex][0], 2.0 ) +
792  pow( dataObj->getInvSO3Coeffs()[arrIndex][1], 2.0 );
793  ProSHADE_internal_misc::addToDblPtrVector ( &angVec, hlpArr );
794  }
795  }
796  }
797  }
798 
799  //================================================ Release memory
800  delete[] rotMat;
801 
802  //================================================ Done
803  return ( angVec );
804 
805 }
806 
821 void ProSHADE_internal_symmetry::saveMissingAxisNewOnly ( std::vector< proshade_double* >* axVec, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double height, proshade_unsign fold, proshade_double axErr )
822 {
823  //================================================ Create symmetry array from the inputs
824  proshade_double* hlpSym = new proshade_double [6];
825  ProSHADE_internal_misc::checkMemoryAllocation ( hlpSym, __FILE__, __LINE__, __func__ );
826 
827  //================================================ Fill it in
828  hlpSym[0] = static_cast<proshade_double> ( fold );
829  hlpSym[1] = axX;
830  hlpSym[2] = axY;
831  hlpSym[3] = axZ;
832  hlpSym[4] = ( 2.0 * M_PI ) / static_cast<proshade_double> ( fold );
833  hlpSym[5] = height;
834 
835  //================================================ Check if similar symmetry does not exist already
836  for ( proshade_unsign symIt = 0; symIt < static_cast<proshade_unsign> ( axVec->size() ); symIt++ )
837  {
838  //============================================ Minor speed-up => only test for same folds
839  const FloatingPoint< proshade_double > lhs1 ( axVec->at(symIt)[0] ), rhs1 ( hlpSym[0] );
840  if ( lhs1.AlmostEquals ( rhs1 ) )
841  {
842  if ( ProSHADE_internal_maths::vectorOrientationSimilarity ( axVec->at(symIt)[1],
843  axVec->at(symIt)[2],
844  axVec->at(symIt)[3],
845  hlpSym[1],
846  hlpSym[2],
847  hlpSym[3],
848  axErr ) )
849  {
850  //==================================== Almost identical entry
851  if ( axVec->at(symIt)[5] < hlpSym[5] )
852  {
853  //================================ If higher, save
854  delete[] axVec->at(symIt);
855  axVec->at(symIt) = hlpSym;
856  return ;
857  }
858  else
859  {
860  //================================ or just terminate if better is already saved
861  delete[] hlpSym;
862  return ;
863  }
864  }
865  }
866  }
867 
868  //================================================ Not matched to anything
870 
871  //================================================ Done
872  return ;
873 
874 }
875 
890 void ProSHADE_internal_symmetry::searchMissingSymmetrySpace ( ProSHADE_internal_data::ProSHADE_data* dataObj, std::vector< proshade_double* >* CSymList, std::vector< proshade_unsign >* grp, std::vector< proshade_double* >* hlpVec, proshade_double axErr, proshade_double angle, proshade_unsign fold, proshade_double minPeakHeight )
891 {
892  //================================================ Sanity check
893  if ( grp->size() < 2 ) { return; }
894 
895  //================================================ Initialise variables
896  proshade_double axHeight = 0.0;
897  proshade_double* symHlp = new proshade_double[7];
898  ProSHADE_internal_misc::checkMemoryAllocation ( symHlp, __FILE__, __LINE__, __func__ );
899 
900  //================================================ For each axis pair in the group, find the possible solutions
901  for ( proshade_unsign fAx = 0; fAx < static_cast<proshade_unsign> ( grp->size() ); fAx++ )
902  {
903  for ( proshade_unsign sAx = 1; sAx < static_cast<proshade_unsign> ( grp->size() ); sAx++ )
904  {
905  //======================================== Only unique pairs
906  if ( fAx >= sAx ) { continue; }
907 
908  //======================================== Find possible axis having the required angle to this pair ( solution 1 )
909  std::vector< proshade_double > solVec = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( CSymList->at(grp->at(fAx))[1],
910  CSymList->at(grp->at(fAx))[2],
911  CSymList->at(grp->at(fAx))[3],
912  CSymList->at(grp->at(sAx))[1],
913  CSymList->at(grp->at(sAx))[2],
914  CSymList->at(grp->at(sAx))[3], angle, angle );
915 
916  //======================================== Set largest axis element to positive
917  const FloatingPoint< proshade_double > lhs1 ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) );
918  const FloatingPoint< proshade_double > rhs1 ( std::abs ( solVec.at(0) ) );
919  const FloatingPoint< proshade_double > rhs2 ( std::abs ( solVec.at(1) ) );
920  const FloatingPoint< proshade_double > rhs3 ( std::abs ( solVec.at(2) ) );
921  if ( ( lhs1.AlmostEquals ( rhs1 ) && ( solVec.at(0) < 0.0 ) ) ||
922  ( lhs1.AlmostEquals ( rhs2 ) && ( solVec.at(1) < 0.0 ) ) ||
923  ( lhs1.AlmostEquals ( rhs3 ) && ( solVec.at(2) < 0.0 ) ) )
924  {
925  solVec.at(0) *= -1.0;
926  solVec.at(1) *= -1.0;
927  solVec.at(2) *= -1.0;
928  }
929 
930  //======================================== Does the solution fit the whole group?
931  symHlp[1] = solVec.at(0); symHlp[2] = solVec.at(1); symHlp[3] = solVec.at(2); symHlp[6] = -std::numeric_limits < proshade_double >::infinity();
932  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, grp, symHlp, axErr, angle, true ) )
933  {
934  //==================================== Find the height for the axis
935  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( solVec.at(0), solVec.at(1), solVec.at(2), dataObj, fold, axErr );
936 
937  //================================ Save max height result
938  if ( axHeight >= minPeakHeight ) { ProSHADE_internal_symmetry::saveMissingAxisNewOnly ( hlpVec, solVec.at(0), solVec.at(1), solVec.at(2), axHeight, fold, axErr ); }
939  }
940 
941  //======================================== Find possible axis having the required angle to this pair ( solution 2 )
942  solVec = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( CSymList->at(grp->at(fAx))[1],
943  CSymList->at(grp->at(fAx))[2],
944  CSymList->at(grp->at(fAx))[3],
945  CSymList->at(grp->at(sAx))[1],
946  CSymList->at(grp->at(sAx))[2],
947  CSymList->at(grp->at(sAx))[3], -angle, -angle );
948 
949  //======================================== Set largest axis element to positive
950  const FloatingPoint< proshade_double > lhs2 ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) );
951  const FloatingPoint< proshade_double > rhs4 ( std::abs ( solVec.at(0) ) );
952  const FloatingPoint< proshade_double > rhs5 ( std::abs ( solVec.at(1) ) );
953  const FloatingPoint< proshade_double > rhs6 ( std::abs ( solVec.at(2) ) );
954  if ( ( lhs2.AlmostEquals ( rhs4 ) && ( solVec.at(0) < 0.0 ) ) ||
955  ( lhs2.AlmostEquals ( rhs5 ) && ( solVec.at(1) < 0.0 ) ) ||
956  ( lhs2.AlmostEquals ( rhs6 ) && ( solVec.at(2) < 0.0 ) ) )
957  {
958  solVec.at(0) *= -1.0;
959  solVec.at(1) *= -1.0;
960  solVec.at(2) *= -1.0;
961  }
962 
963  //======================================== Does the solution fit the whole group?
964  symHlp[1] = solVec.at(0); symHlp[2] = solVec.at(1); symHlp[3] = solVec.at(2); symHlp[6] = -std::numeric_limits < proshade_double >::infinity();
965  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, grp, symHlp, axErr, angle, true ) )
966  {
967  //==================================== Find the height for the axis
968  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( solVec.at(0), solVec.at(1), solVec.at(2), dataObj, fold, axErr );
969 
970  //================================ Save max height result
971  if ( axHeight >= minPeakHeight ) { ProSHADE_internal_symmetry::saveMissingAxisNewOnly ( hlpVec, solVec.at(0), solVec.at(1), solVec.at(2), axHeight, fold, axErr ); }
972  }
973  }
974  }
975 
976  //================================================ Release memory
977  delete[] symHlp;
978 
979  //================================================ Done
980  return ;
981 
982 }
983 
998 void ProSHADE_internal_symmetry::findTetra3C2s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
999 {
1000  //================================================ Initialise variables
1001  std::vector< proshade_unsign > C3s, prospectiveC2s, C2PossibilitiesHlp;
1002  std::vector< std::vector< proshade_unsign > > C2Possibilities;
1003  proshade_double dotProd;
1004  bool groupMatched;
1005  for ( proshade_unsign iter = 0; iter < 4; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &C3s, iter ); }
1006 
1007  //================================================ Report progress
1008  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of three C2 axes.", messageShift );
1009 
1010  //================================================ For each C3
1011  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( ret->size() ); rIt++ )
1012  {
1013  //============================================ For each C2, check it has angle ( acos(0.5) ) to the tested C3
1014  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1015  {
1016  //======================================== Search only using C2s
1017  const FloatingPoint< proshade_double > lhs999 ( CSymList->at(cIt)[5] ), rhs999 ( static_cast< proshade_double > ( -999.9 ) );
1018  if ( CSymList->at(cIt)[0] != 2.0 || ( ( CSymList->at(cIt)[5] < minPeakHeight ) && !( lhs999.AlmostEquals( rhs999 ) ) ) ) { continue; }
1019 
1020  //======================================== Check the C2 axis to the C3 ( acos ( 0.5 ) )
1021  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1], &ret->at(rIt)[2], &ret->at(rIt)[3],
1022  &CSymList->at(cIt)[1], &CSymList->at(cIt)[2], &CSymList->at(cIt)[3] );
1023 
1024  if ( ( std::abs ( dotProd ) > ( 0.5 - axErr ) ) && ( std::abs ( dotProd ) < ( 0.5 + axErr ) ) ) { ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt ); }
1025  }
1026  }
1027 
1028  //================================================ Group the prospective C2s
1029  C2Possibilities.clear(); C2PossibilitiesHlp.clear();
1030  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( prospectiveC2s.size() ); cIt++ )
1031  {
1032  //============================================ If second or more C2, check if it can be placed in any group with being perpendicular to all its members
1033  groupMatched = false;
1034  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C2Possibilities.size() ); gIt++ )
1035  {
1036  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &C2Possibilities.at(gIt), CSymList->at(prospectiveC2s.at(cIt)), axErr, 0.0, true, prospectiveC2s.at(cIt) ) ) { ProSHADE_internal_misc::addToUnsignVector ( &C2Possibilities.at(gIt), prospectiveC2s.at(cIt) ); groupMatched = true; break; }
1037  }
1038 
1039  //============================================ If no group matched, create a new group
1040  if ( !groupMatched ) { C2PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C2PossibilitiesHlp, prospectiveC2s.at(cIt) ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C2Possibilities, C2PossibilitiesHlp ); continue; }
1041  }
1042 
1043  //================================================ Find the best group or return empty
1044  while ( C2Possibilities.size() != 0 )
1045  {
1046  //============================================ Test for missing symmetry axes, if need be
1047  ProSHADE_internal_symmetry::findMissingAxes ( &C2Possibilities, CSymList, 3, axErr, 0.0, 2, dataObj, minPeakHeight );
1048 
1049  //============================================ Found 3 C2s?
1050  if ( C2Possibilities.at(0).size() == 3 )
1051  {
1052  //======================================== Success! Save and exit
1053  for ( proshade_unsign it = 0; it < 3; it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C2Possibilities.at(0).at(it)) ); }
1054 
1055  //======================================== Report progress
1056  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of three C2 axes successfull.", messageShift );
1057 
1058  //======================================== Done
1059  return ;
1060  }
1061  else { C2Possibilities.erase ( C2Possibilities.begin() ); }
1062  }
1063 
1064  //================================================ Done
1065  return ;
1066 
1067 }
1068 
1083 bool ProSHADE_internal_symmetry::testGroupAgainstGroup ( std::vector< proshade_double* >* GrList1, std::vector< proshade_unsign >* grp1, std::vector< proshade_double* >* GrList2, std::vector< proshade_unsign >* grp2, proshade_double angle, proshade_double axErr )
1084 {
1085  //================================================ Initialise variables
1086  bool ret = false;
1087  proshade_double dotProduct;
1088 
1089  //================================================ For all pairs of axes
1090  for ( proshade_unsign g1It = 0; g1It < static_cast<proshade_unsign> ( grp1->size() ); g1It++ )
1091  {
1092  for ( proshade_unsign g2It = 0; g2It < static_cast<proshade_unsign> ( grp2->size() ); g2It++ )
1093  {
1094  //======================================== Find the angle
1095  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &GrList1->at(grp1->at(g1It))[1],
1096  &GrList1->at(grp1->at(g1It))[2],
1097  &GrList1->at(grp1->at(g1It))[3],
1098  &GrList2->at(grp2->at(g2It))[1],
1099  &GrList2->at(grp2->at(g2It))[2],
1100  &GrList2->at(grp2->at(g2It))[3] );
1101 
1102  //======================================== Check the angle
1103  if ( ( angle > ( dotProduct - axErr ) ) && ( angle < ( dotProduct + axErr ) ) )
1104  {
1105  ret = true;
1106  return ( ret );
1107  }
1108  }
1109  }
1110 
1111  //================================================ Done
1112  return ( ret );
1113 
1114 }
1115 
1127 bool ProSHADE_internal_symmetry::detectOctahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
1128 {
1129  //================================================ Initialise variables
1130  std::vector< proshade_unsign > C4List;
1131  proshade_double dotProduct;
1132 
1133  //================================================ Find all C4 symmetries
1134  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
1135  {
1136  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 4.0 );
1137  if ( lhs1.AlmostEquals ( rhs1 ) && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C4List, cSym ); }
1138  }
1139 
1140  //================================================ For each unique pair of C3s
1141  for ( proshade_unsign c4 = 0; c4 < static_cast<proshade_unsign> ( C4List.size() ); c4++ )
1142  {
1143  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
1144  {
1145  //======================================== Compare only C3s to the C3List
1146  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 3.0 );
1147  if ( !lhs1.AlmostEquals ( rhs1 ) ) { continue; }
1148 
1149  //======================================== Check the angle between the C4 and C3 axes
1150  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C4List.at(c4))[1],
1151  &CSymList->at(C4List.at(c4))[2],
1152  &CSymList->at(C4List.at(c4))[3],
1153  &CSymList->at(cSym)[1],
1154  &CSymList->at(cSym)[2],
1155  &CSymList->at(cSym)[3] );
1156 
1157  //======================================== Is the angle approximately the dihedral angle
1158  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( dotProduct - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( dotProduct + axErr ) ) )
1159  {
1160  return ( true );
1161  }
1162  }
1163  }
1164 
1165  //================================================ Done
1166  return ( false );
1167 
1168 }
1169 
1185 void ProSHADE_internal_symmetry::findOcta3C4s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
1186 {
1187  //================================================ Initialise variables
1188  std::vector< proshade_unsign > C4PossibilitiesHlp;
1189  std::vector< std::vector< proshade_unsign > > C4Possibilities;
1190  bool groupMatched;
1191 
1192  //================================================ Report progress
1193  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of three C4 axes.", messageShift );
1194 
1195  //================================================ For all symmetries in the C symmetries list
1196  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1197  {
1198  //============================================ Search only using C4s
1199  if ( CSymList->at(cIt)[0] != 4.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
1200 
1201  //============================================ If second or more C4, check if it has the correct angle to all other already found C4s for each group
1202  groupMatched = false;
1203  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C4Possibilities.size() ); gIt++ )
1204  {
1205  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &C4Possibilities.at(gIt), CSymList->at(cIt), axErr, 0.0, true, cIt ) ) { ProSHADE_internal_misc::addToUnsignVector ( &C4Possibilities.at(gIt), cIt ); groupMatched = true; break; }
1206  }
1207 
1208  //=========================================== If no group matched, create a new group
1209  if ( !groupMatched ) { C4PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C4PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C4Possibilities, C4PossibilitiesHlp ); continue; }
1210  }
1211 
1212  //================================================ Test for missing symmetry axes, if need be
1213  ProSHADE_internal_symmetry::findMissingAxes ( &C4Possibilities, CSymList, 3, axErr, 0.0, 4, dataObj, minPeakHeight );
1214 
1215  //================================================ Any group has 3 entries? If more such groups, take the one with highest average height.
1216  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
1217  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( C4Possibilities.size() ); iter++ ) { if ( C4Possibilities.at(iter).size() == 3 ) { if ( ( ( CSymList->at(C4Possibilities.at(iter).at(0))[5] + CSymList->at(C4Possibilities.at(iter).at(1))[5] + CSymList->at(C4Possibilities.at(iter).at(2))[5] ) / 3.0 ) > maxHeight ) { maxHeight = ( ( CSymList->at(C4Possibilities.at(iter).at(0))[5] + CSymList->at(C4Possibilities.at(iter).at(1))[5] + CSymList->at(C4Possibilities.at(iter).at(2))[5] ) / 3.0 ); maxGrp = iter; } } }
1218 
1219  if ( C4Possibilities.at(maxGrp).size() == 3 )
1220  {
1221  //============================================ Success! Save and exit
1222  for ( proshade_unsign it = 0; it < static_cast<proshade_unsign> ( C4Possibilities.at(maxGrp).size() ); it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C4Possibilities.at(maxGrp).at(it)) ); }
1223 
1224  //============================================ Report progress
1225  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of three C4 axes successfull.", messageShift );
1226 
1227  //============================================ Done
1228  return ;
1229  }
1230 
1231  //================================================ Done
1232  return ;
1233 
1234 }
1235 
1252 void ProSHADE_internal_symmetry::findOcta4C3s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
1253 {
1254  //================================================ Initialise variables
1255  std::vector< proshade_unsign > C4s, prospectiveC3s, C3PossibilitiesHlp;
1256  std::vector< std::vector< proshade_unsign > > C3Possibilities;
1257  proshade_double dotProd;
1258  bool groupMatched;
1259  for ( proshade_unsign iter = 0; iter < 3; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &C4s, iter ); }
1260 
1261  //================================================ Report progress
1262  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of four C3 axes.", messageShift );
1263 
1264  //================================================ For each C4
1265  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( ret->size() ); rIt++ )
1266  {
1267  //============================================ For each C3, check it has angle ( acos( 1/sqrt(3) ) ) to the tested C4
1268  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1269  {
1270  //======================================== Search only using C3s
1271  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
1272 
1273  //======================================== Check the C3 axis to the C4 ( acos ( 1/sqrt(3) ) )
1274  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1], &ret->at(rIt)[2], &ret->at(rIt)[3], &CSymList->at(cIt)[1], &CSymList->at(cIt)[2], &CSymList->at(cIt)[3] );
1275 
1276  if ( ( std::abs ( dotProd ) > ( ( 1.0 / sqrt(3.0) ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / sqrt(3.0) ) + axErr ) ) ) { ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC3s, cIt ); }
1277  }
1278  }
1279 
1280  //================================================ Group the prospective C3s
1281  C3Possibilities.clear(); C3PossibilitiesHlp.clear();
1282  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( prospectiveC3s.size() ); cIt++ )
1283  {
1284  //============================================ If second or more C3, check if it can be placed in any group with having acos (1/3) to all its members
1285  groupMatched = false;
1286  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C3Possibilities.size() ); gIt++ )
1287  {
1288  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &C3Possibilities.at(gIt), CSymList->at(prospectiveC3s.at(cIt)), axErr, 1.0/3.0, true, prospectiveC3s.at(cIt) ) ) { ProSHADE_internal_misc::addToUnsignVector ( &C3Possibilities.at(gIt), prospectiveC3s.at(cIt) ); groupMatched = true; break; }
1289  }
1290 
1291  //============================================ If no group matched, create a new group
1292  if ( !groupMatched ) { C3PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C3PossibilitiesHlp, prospectiveC3s.at(cIt) ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C3Possibilities, C3PossibilitiesHlp ); continue; }
1293  }
1294 
1295  //================================================ Find the best group or return empty
1296  while ( C3Possibilities.size() != 0 )
1297  {
1298  //============================================ Test for missing symmetry axes, if need be
1299  ProSHADE_internal_symmetry::findMissingAxes ( &C3Possibilities, CSymList, 4, axErr, 1.0/3.0, 3, dataObj, minPeakHeight );
1300 
1301  //============================================ Found four C3s?
1302  if ( C3Possibilities.at(0).size() == 4 )
1303  {
1304  //======================================== Success! Save and exit
1305  for ( proshade_unsign it = 0; it < 4; it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C3Possibilities.at(0).at(it)) ); }
1306 
1307  //======================================== Report progress
1308  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of four C3 axes successfull.", messageShift );
1309 
1310  //======================================== Done
1311  return ;
1312  }
1313  else { C3Possibilities.erase ( C3Possibilities.begin() ); }
1314  }
1315 
1316  //================================================ Done
1317  return ;
1318 
1319 }
1320 
1335 void ProSHADE_internal_symmetry::findOcta6C2s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
1336 {
1337  //================================================ Initialise variables
1338  std::vector< proshade_unsign > prospectiveC2s, retGrp;
1339  proshade_double dotProd;
1340  proshade_unsign noPerpendicular, noSqrtTwo;
1341 
1342  //================================================ Report progress
1343  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of six C2 axes.", messageShift );
1344 
1345  //================================================ For each C2
1346  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1347  {
1348  //============================================ Use only C2s
1349  const FloatingPoint< proshade_double > lhs999 ( CSymList->at(cIt)[5] ), rhs999 ( static_cast< proshade_double > ( -999.9 ) );
1350  if ( CSymList->at(cIt)[0] != 2.0 || ( ( CSymList->at(cIt)[5] < minPeakHeight ) && ! ( lhs999.AlmostEquals( rhs999 ) ) ) ) { continue; }
1351 
1352  //============================================ Check the C2 has acos ( 1/sqrt(2) ) to 2 C4s and acos ( 0.0 ) to the third C4
1353  noPerpendicular = 0; noSqrtTwo = 0;
1354  for ( proshade_unsign rIt = 0; rIt < 3; rIt++ )
1355  {
1356  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
1357  &ret->at(rIt)[2],
1358  &ret->at(rIt)[3],
1359  &CSymList->at(cIt)[1],
1360  &CSymList->at(cIt)[2],
1361  &CSymList->at(cIt)[3] );
1362 
1363  if ( ( std::abs ( dotProd ) > ( ( 1.0 / sqrt(2.0) ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / sqrt(2.0) ) + axErr ) ) ) { noSqrtTwo += 1; continue; }
1364  if ( ( std::abs ( dotProd ) > ( 0.0 - axErr ) ) && ( std::abs ( dotProd ) < ( 0.0 + axErr ) ) ) { noPerpendicular += 1; continue; }
1365  }
1366 
1367  //============================================ If correct angles distribution is found, save the axis
1368  if ( ( noSqrtTwo == 2 ) && ( noPerpendicular == 1 ) )
1369  {
1370  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt );
1371  }
1372  }
1373 
1374  //================================================ Search for missing axes
1375  for ( proshade_unsign iter = 0; iter < 3; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
1376  if ( !ProSHADE_internal_symmetry::findMissingAxesDual ( &prospectiveC2s, CSymList, ret, &retGrp, 6, axErr, 1, 0.0, 2, 1/sqrt(2.0), 2, dataObj ) )
1377  {
1378  return ;
1379  }
1380 
1381  //================================================ Found correct number of axes! Now save the
1382  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC2s.size() ); iter++ )
1383  {
1384  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC2s.at(iter)) );
1385  }
1386 
1387  //================================================ Report progress
1388  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of six C2 axes successfull.", messageShift );
1389 
1390  //================================================ Done
1391  return ;
1392 
1393 }
1394 
1416 bool ProSHADE_internal_symmetry::findMissingAxesDual ( std::vector< proshade_unsign >* possibilities, std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, std::vector< proshade_unsign >* retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data* dataObj )
1417 {
1418  //================================================ Initialise variables
1419  bool atLeastOne = false;
1420  std::vector< proshade_double* > prosp;
1421  std::vector< proshade_double > sol;
1422 
1423  //================================================ Proceed only if need be
1424  if ( static_cast<proshade_unsign> ( possibilities->size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
1425 
1426  //================================================ Copy already found to prospective
1427  for ( proshade_unsign prIt = 0; prIt < static_cast<proshade_unsign> ( possibilities->size() ); prIt++ )
1428  {
1429  ProSHADE_internal_symmetry::addAxisUnlessSame ( static_cast< proshade_unsign > ( CSymList->at(possibilities->at(prIt))[0] ),
1430  CSymList->at(possibilities->at(prIt))[1],
1431  CSymList->at(possibilities->at(prIt))[2],
1432  CSymList->at(possibilities->at(prIt))[3],
1433  CSymList->at(possibilities->at(prIt))[5], &prosp, axErr );
1434  }
1435 
1436  //================================================ Start generating possible solutions
1437  for ( proshade_unsign rgIt1 = 0; rgIt1 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt1++ )
1438  {
1439  for ( proshade_unsign rgIt2 = 0; rgIt2 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt2++ )
1440  {
1441  //======================================== Use unique combinations (order matters here!)
1442  if ( rgIt1 == rgIt2 ) { continue; }
1443 
1444  //======================================== Generate possible solution (1)
1445  sol = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
1446  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3], angle1, angle2 );
1447 
1448  //======================================== Check if solution fits the group completely
1449  ProSHADE_internal_symmetry::checkFittingAxisDualAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, dataObj );
1450  if ( prosp.size() == requiredNoAxes ) { break; }
1451 
1452  //======================================== Generate possible solution (2)
1453  sol = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
1454  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3], -angle1, -angle2 );
1455 
1456  //======================================== Check if solution fits the group completely
1457  ProSHADE_internal_symmetry::checkFittingAxisDualAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, dataObj );
1458  if ( prosp.size() == requiredNoAxes ) { break; }
1459  }
1460 
1461  if ( prosp.size() == requiredNoAxes ) { break; }
1462  }
1463 
1464  //================================================ Found all required axes!
1465  if ( static_cast<proshade_unsign> ( prosp.size() ) == requiredNoAxes )
1466  {
1467  //============================================ Copy the detected axes
1468  for ( proshade_unsign iter = static_cast<proshade_unsign> ( possibilities->size() ); iter < static_cast<proshade_unsign> ( prosp.size() ); iter++ )
1469  {
1470  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, prosp.at(iter), axErr ) )
1471  {
1472  //==================================== Add
1473  ProSHADE_internal_misc::addToUnsignVector ( possibilities, static_cast< proshade_unsign > ( CSymList->size() ) );
1474  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, prosp.at(iter) );
1475  }
1476  }
1477 
1478  //============================================ Done
1479  atLeastOne = true;
1480  return ( atLeastOne );
1481  }
1482  else
1483  {
1484  //============================================ Delete the created, but not used axes
1485  for ( proshade_unsign iter = static_cast<proshade_unsign> ( possibilities->size() ); iter < static_cast<proshade_unsign> ( prosp.size() ); iter++ )
1486  {
1487  delete[] prosp.at(iter);
1488  }
1489  }
1490 
1491  //================================================ Done
1492  return ( atLeastOne );
1493 
1494 }
1495 
1511 proshade_signed ProSHADE_internal_symmetry::addAxisUnlessSame ( proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double axHeight, proshade_double averageFSC, std::vector< proshade_double* >* prosp, proshade_double axErr )
1512 {
1513  //================================================ Initialise variables
1514  proshade_double* symHlp = new proshade_double[7];
1515  ProSHADE_internal_misc::checkMemoryAllocation ( symHlp, __FILE__, __LINE__, __func__ );
1516  proshade_signed ret = -1;
1517 
1518  //================================================ Fill in the prospective axis
1519  symHlp[0] = static_cast<proshade_double> ( fold );
1520  symHlp[1] = axX;
1521  symHlp[2] = axY;
1522  symHlp[3] = axZ;
1523  symHlp[4] = 2.0 * M_PI / symHlp[0];
1524  symHlp[5] = axHeight;
1525  symHlp[6] = averageFSC;
1526 
1527  //================================================ If it is not the same as already saved axes
1528  if ( !ProSHADE_internal_symmetry::isSymmetrySame ( prosp, symHlp, axErr, &ret, averageFSC ) )
1529  {
1531  return ( static_cast< proshade_signed > ( prosp->size() - 1 ) );
1532  }
1533  else
1534  {
1535  delete[] symHlp;
1536  return ( ret );
1537  }
1538 
1539  //================================================ Done
1540 
1541 }
1542 
1557 proshade_signed ProSHADE_internal_symmetry::addAxisUnlessSame ( proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double axHeight, std::vector< proshade_double* >* prosp, proshade_double axErr )
1558 {
1559  //================================================ Initialise variables
1560  proshade_double* symHlp = new proshade_double[7];
1561  ProSHADE_internal_misc::checkMemoryAllocation ( symHlp, __FILE__, __LINE__, __func__ );
1562  proshade_signed ret = -1;
1563 
1564  //================================================ Fill in the prospective axis
1565  symHlp[0] = static_cast<proshade_double> ( fold );
1566  symHlp[1] = axX;
1567  symHlp[2] = axY;
1568  symHlp[3] = axZ;
1569  symHlp[4] = 2.0 * M_PI / symHlp[0];
1570  symHlp[5] = axHeight;
1571  symHlp[6] = -std::numeric_limits < proshade_double >::infinity();
1572 
1573  //================================================ If it is not the same as already saved axes
1574  if ( !ProSHADE_internal_symmetry::isSymmetrySame ( prosp, symHlp, axErr, &ret ) )
1575  {
1577  return ( static_cast< proshade_signed > ( prosp->size() - 1 ) );
1578  }
1579  else
1580  {
1581  delete[] symHlp;
1582  return ( ret );
1583  }
1584 
1585  //================================================ Done
1586 
1587 }
1588 
1610 bool ProSHADE_internal_symmetry::checkFittingAxisDualAndSave ( std::vector< proshade_unsign >* retGroup, std::vector< proshade_double* >* ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double* >* prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, ProSHADE_internal_data::ProSHADE_data* dataObj )
1611 {
1612  //================================================ Initialise variables
1613  proshade_unsign noG1 = 0;
1614  proshade_unsign noG2 = 0;
1615  proshade_double dotProd = 0.0;
1616  proshade_double axHeight = 0.0;
1617 
1618  //================================================ Find the angle and count dual matching frequencies
1619  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( retGroup->size() ); rIt++ )
1620  {
1621  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(retGroup->at(rIt))[1],
1622  &ret->at(retGroup->at(rIt))[2],
1623  &ret->at(retGroup->at(rIt))[3],
1624  &axX, &axY, &axZ );
1625 
1626  if ( ( std::abs ( dotProd ) > ( angle1 - axErr ) ) && ( std::abs ( dotProd ) < ( angle1 + axErr ) ) ) { noG1 += 1; continue; }
1627  if ( ( std::abs ( dotProd ) > ( angle2 - axErr ) ) && ( std::abs ( dotProd ) < ( angle2 + axErr ) ) ) { noG2 += 1; continue; }
1628  }
1629 
1630  //================================================ If correct frequencies are matched, check height.
1631  if ( ( noG1 == noMatchesG1 ) && ( noG2 == noMatchesG2 ) )
1632  {
1633  //============================================ Is the height good enough?
1634  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( axX, axY, axZ, dataObj, fold, axErr );
1635 
1636  //============================================ If so, save
1637  if ( axHeight > 0.1 )
1638  {
1639  proshade_unsign prevProsp = static_cast<proshade_unsign> ( prosp->size() );
1640  ProSHADE_internal_symmetry::addAxisUnlessSame ( fold, axX, axY, axZ, axHeight, prosp, axErr );
1641 
1642  if ( static_cast<proshade_unsign> ( prosp->size() ) > prevProsp ) { return ( true ); }
1643  else { return ( false ); }
1644  }
1645  }
1646 
1647  //================================================ Done
1648  return ( false );
1649 
1650 }
1651 
1659 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::decidePolyFromList ( ProSHADE_settings* settings, std::vector < std::vector< proshade_double* > >* polyList, size_t fullGroupSize, std::vector< proshade_double* >* CSyms, proshade_double tolerance, proshade_signed*& cutIndices, fftw_complex*& fCoeffsCut, proshade_signed noBins, proshade_double**& bindata, proshade_signed*& binCounts, proshade_double*& fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim )
1660 {
1661  //================================================ Initialise local variables
1662  std::vector< proshade_double* > ret;
1663  proshade_double fscVal = 0.0;
1664  proshade_double fscValAvg = 0.0;
1665  proshade_double fscMax = 0.0;
1666  size_t fscMaxInd = 0;
1667  proshade_signed matchedPos = -1;
1668 
1669  //================================================ For each possible polyhedral group
1670  for ( size_t gIt = 0; gIt < polyList->size(); gIt++ )
1671  {
1672  //============================================ Is this a complete group?
1673  if ( polyList->at(gIt).size() != fullGroupSize ) { continue; }
1674 
1675  //============================================ Initialise decision vars
1676  fscVal = 0.0;
1677  fscValAvg = 0.0;
1678 
1679  //============================================ For each axis
1680  for ( size_t aIt = 0; aIt < polyList->at(gIt).size(); aIt++ )
1681  {
1682  //======================================== Match to CSyms
1683  matchedPos = ProSHADE_internal_symmetry::addAxisUnlessSame ( static_cast< proshade_unsign > ( polyList->at(gIt).at(aIt)[0] ), polyList->at(gIt).at(aIt)[1], polyList->at(gIt).at(aIt)[2], polyList->at(gIt).at(aIt)[3], polyList->at(gIt).at(aIt)[5], polyList->at(gIt).at(aIt)[6], CSyms, tolerance );
1684 
1685  //======================================== Compute FSC
1686  fscVal = this->computeFSC ( settings, CSyms, static_cast< size_t > ( matchedPos ), cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, xDim, yDim, zDim );
1687  polyList->at(gIt).at(aIt)[6] = fscVal;
1688  fscValAvg += fscVal;
1689 
1690  //======================================== If not required, do not compute more
1691  if ( settings->fastISearch ) { if ( fscVal < ( settings->fscThreshold / 2.0 ) ) { break; } }
1692  }
1693 
1694  //============================================ If not required, do not compute more
1695  if ( settings->fastISearch ) { if ( fscVal < ( settings->fscThreshold / 2.0 ) ) { continue; } }
1696 
1697  //============================================ Get FSC average over all axes
1698  fscValAvg /= static_cast< proshade_double > ( fullGroupSize );
1699 
1700  //============================================ Is this the best
1701  if ( fscValAvg > fscMax )
1702  {
1703  fscMax = fscValAvg;
1704  fscMaxInd = gIt;
1705  }
1706  }
1707 
1708  //================================================ If at least one poly group was found
1709  if ( fscMax >= settings->fscThreshold )
1710  {
1711  //============================================ Add predicted axes to detected C axes list and also to the settings poly symmetry list
1712  for ( size_t retIt = 0; retIt < polyList->at(fscMaxInd).size(); retIt++ )
1713  {
1714  //======================================== Add the correct index to the settings object
1715  matchedPos = ProSHADE_internal_symmetry::addAxisUnlessSame ( static_cast< proshade_unsign > ( polyList->at(fscMaxInd).at(retIt)[0] ), polyList->at(fscMaxInd).at(retIt)[1], polyList->at(fscMaxInd).at(retIt)[2], polyList->at(fscMaxInd).at(retIt)[3], polyList->at(fscMaxInd).at(retIt)[5], polyList->at(fscMaxInd).at(retIt)[6], CSyms, tolerance );
1716 
1717  //============================================ Set single group list for saving
1718  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, polyList->at(fscMaxInd).at(retIt) );
1719  }
1720  }
1721 
1722  //================================================ Done
1723  return ( ret );
1724 
1725 }
1726 
1749 void ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList, proshade_signed*& cutIndices, fftw_complex*& fCoeffsCut, proshade_signed noBins, proshade_double**& bindata, proshade_signed*& binCounts, proshade_double*& fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim )
1750 {
1751  //================================================ Initialise variables
1752  std::vector< std::vector< proshade_double* > > hlpVec;
1753 
1754  //================================================ Report progress
1755  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting I symmetry prediction.", settings->messageShift );
1756 
1757  //================================================ Are the basic requirements for icosahedral symmetry met?
1759  {
1760  //============================================ Generate the rest of the axes
1761  ProSHADE_internal_symmetry::predictIcosAxes ( CSymList, &hlpVec, settings->axisErrTolerance, settings->minSymPeak );
1762 
1763  //============================================ For each possible axes pair
1764  for ( size_t pIt = 0; pIt < hlpVec.size(); pIt++ )
1765  {
1766  //======================================== Get heights for the predicted axes
1767  ProSHADE_internal_symmetry::findPredictedAxesHeights ( &(hlpVec.at(pIt)), this, settings );
1768  }
1769  }
1770 
1771  //================================================ Sort by best peak height sum
1772  this->icosahedralSymmetries = ProSHADE_data::decidePolyFromList ( settings, &hlpVec, 31, CSymList, settings->axisErrTolerance, cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, xDim, yDim, zDim );
1773 
1774  //================================================ Release hlpVec memory
1775  for ( size_t gIt = 0; gIt < hlpVec.size(); gIt++ ) { for ( size_t aIt = 0; aIt < hlpVec.at(gIt).size(); aIt++ ) { if ( hlpVec.at(gIt).at(aIt) != nullptr ) { delete[] hlpVec.at(gIt).at(aIt); } } }
1776 
1777  //================================================ Report progress
1778  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "I symmetry prediction complete.", settings->messageShift );
1779 
1780  //================================================ Done
1781  return ;
1782 
1783 }
1784 
1807 void ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList, proshade_signed*& cutIndices, fftw_complex*& fCoeffsCut, proshade_signed noBins, proshade_double**& bindata, proshade_signed*& binCounts, proshade_double*& fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim )
1808 {
1809  //================================================ Initialise variables
1810  std::vector< std::vector< proshade_double* > > hlpVec;
1811 
1812  //================================================ Report progress
1813  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting O symmetry prediction.", settings->messageShift );
1814 
1815  //================================================ Are the basic requirements for icosahedral symmetry met?
1816  if ( ProSHADE_internal_symmetry::detectOctahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
1817  {
1818  //============================================ Generate the rest of the axes
1819  ProSHADE_internal_symmetry::predictOctaAxes ( CSymList, &hlpVec, settings->axisErrTolerance, settings->minSymPeak );
1820 
1821  //============================================ For each possible axes pair
1822  for ( size_t pIt = 0; pIt < hlpVec.size(); pIt++ )
1823  {
1824  //======================================== Get heights for the predicted axes
1825  ProSHADE_internal_symmetry::findPredictedAxesHeights ( &(hlpVec.at(pIt)), this, settings );
1826  }
1827  }
1828 
1829  //================================================ Sort by best peak height sum
1830  this->octahedralSymmetries = ProSHADE_data::decidePolyFromList ( settings, &hlpVec, 13, CSymList, settings->axisErrTolerance, cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, xDim, yDim, zDim );
1831 
1832  //================================================ Release hlpVec memory
1833  for ( size_t gIt = 0; gIt < hlpVec.size(); gIt++ ) { for ( size_t aIt = 0; aIt < hlpVec.at(gIt).size(); aIt++ ) { if ( hlpVec.at(gIt).at(aIt) != nullptr ) { delete[] hlpVec.at(gIt).at(aIt); } } }
1834 
1835  //================================================ Report progress
1836  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "O symmetry prediction complete.", settings->messageShift );
1837 
1838  //================================================ Done
1839  return ;
1840 
1841 }
1842 
1854 bool ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
1855 {
1856  //================================================ Initialise variables
1857  std::vector< proshade_unsign > C5List;
1858  proshade_double dotProduct;
1859 
1860  //================================================ Find all C5 symmetries
1861  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
1862  {
1863  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 5.0 );
1864  if ( lhs1.AlmostEquals ( rhs1 ) && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); }
1865  }
1866 
1867  //================================================ For each unique pair of C5 and C3
1868  for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
1869  {
1870  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
1871  {
1872  //======================================== Compare only C3s to the C5List
1873  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 3.0 );
1874  if ( !lhs1.AlmostEquals ( rhs1 ) ) { continue; }
1875 
1876  //======================================== Check the angle between the C5 and C3 axes
1877  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
1878  &CSymList->at(C5List.at(c5))[2],
1879  &CSymList->at(C5List.at(c5))[3],
1880  &CSymList->at(cSym)[1],
1881  &CSymList->at(cSym)[2],
1882  &CSymList->at(cSym)[3] );
1883 
1884  //======================================== Is the angle approximately the dihedral angle
1885  if ( std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) < axErr ) { return ( true ); }
1886  }
1887  }
1888 
1889  //================================================ Done
1890  return ( false );
1891 
1892 }
1893 
1912 void ProSHADE_internal_symmetry::findIcos6C5s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
1913 {
1914  //================================================ Initialise variables
1915  std::vector< proshade_unsign > C5PossibilitiesHlp;
1916  std::vector< std::vector< proshade_unsign > > C5Possibilities;
1917  bool groupMatched;
1918 
1919  //================================================ Report progress
1920  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of six C5 axes.", messageShift );
1921 
1922  //================================================ For all symmetries in the C symmetries list
1923  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1924  {
1925  //============================================ Search only using C5s and check peak height
1926  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cIt)[0] ), rhs1 ( 5.0 );
1927  if ( !lhs1.AlmostEquals ( rhs1 ) || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
1928 
1929  //============================================ If second or more C5, check if it has the correct angle to all other already found C5s for each group
1930  groupMatched = false;
1931  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C5Possibilities.size() ); gIt++ )
1932  {
1933  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &C5Possibilities.at(gIt), CSymList->at(cIt), axErr, 1.0/2.0, true, cIt ) ) { ProSHADE_internal_misc::addToUnsignVector ( &C5Possibilities.at(gIt), cIt ); groupMatched = true; break; }
1934  }
1935 
1936  //============================================ If no group matched, create a new group
1937  if ( !groupMatched ) { C5PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C5PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C5Possibilities, C5PossibilitiesHlp ); continue; }
1938  }
1939 
1940  //================================================ Test for missing symmetry axes, if need be
1941  ProSHADE_internal_symmetry::findMissingAxes ( &C5Possibilities, CSymList, 6, axErr, 1.0 / 2.0, 5, dataObj, minPeakHeight );
1942 
1943  //=================================================Any group has 6 entries? If more such groups, take the one with highest average height.
1944  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
1945  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( C5Possibilities.size() ); iter++ ) { if ( C5Possibilities.at(iter).size() == 6 ) { if ( ( ( CSymList->at(C5Possibilities.at(iter).at(0))[5] + CSymList->at(C5Possibilities.at(iter).at(1))[5] + CSymList->at(C5Possibilities.at(iter).at(2))[5] + CSymList->at(C5Possibilities.at(iter).at(3))[5] + CSymList->at(C5Possibilities.at(iter).at(4))[5] + CSymList->at(C5Possibilities.at(iter).at(5))[5] ) / 6.0 ) > maxHeight ) { maxHeight = ( ( CSymList->at(C5Possibilities.at(iter).at(0))[5] + CSymList->at(C5Possibilities.at(iter).at(1))[5] + CSymList->at(C5Possibilities.at(iter).at(2))[5] + CSymList->at(C5Possibilities.at(iter).at(3))[5] + CSymList->at(C5Possibilities.at(iter).at(4))[5] + CSymList->at(C5Possibilities.at(iter).at(5))[5] ) / 6.0 ); maxGrp = iter; } } }
1946 
1947  if ( C5Possibilities.at(maxGrp).size() == 6 )
1948  {
1949  //============================================ Success! Save and exit
1950  for ( proshade_unsign it = 0; it < static_cast<proshade_unsign> ( C5Possibilities.at(maxGrp).size() ); it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C5Possibilities.at(maxGrp).at(it)) ); }
1951 
1952  //============================================ Report progress
1953  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of six C5 axes successfull.", messageShift );
1954 
1955  //============================================ Done
1956  return ;
1957  }
1958 
1959  //================================================ Done
1960  return ;
1961 
1962 }
1963 
1971 std::vector < std::pair< proshade_unsign, proshade_unsign > > findBestIcosDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
1972 {
1973  //================================================ Initialise variables
1974  std::vector < std::pair< proshade_unsign, proshade_unsign > > ret;
1975  std::vector< proshade_unsign > C5List;
1976  proshade_double dotProduct;
1977 
1978  //================================================ Find all C5 symmetries
1979  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 5.0 ); if ( lhs1.AlmostEquals ( rhs1 ) && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); } }
1980 
1981  //================================================ For each unique pair of C5 and C3
1982  for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
1983  {
1984  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
1985  {
1986  //======================================== Compare only C3s to the C5List and only with decent average peak height
1987  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 3.0 );
1988  if ( !lhs1.AlmostEquals ( rhs1 ) ) { continue; }
1989  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
1990 
1991  //======================================== Check the angle between the C5 and C3 axes
1992  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
1993  &CSymList->at(C5List.at(c5))[2],
1994  &CSymList->at(C5List.at(c5))[3],
1995  &CSymList->at(cSym)[1],
1996  &CSymList->at(cSym)[2],
1997  &CSymList->at(cSym)[3] );
1998 
1999  //======================================== Is the angle approximately the dihedral angle?
2000  if ( std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) < axErr )
2001  {
2002  std::pair< proshade_unsign, proshade_unsign > hlp;
2003  hlp.first = C5List.at(c5);
2004  hlp.second = cSym;
2005  ret.emplace_back ( hlp );
2006  }
2007  }
2008  }
2009 
2010  //================================================ Done
2011  return ( ret );
2012 }
2013 
2034 void ProSHADE_internal_symmetry::predictIcosAxes ( std::vector< proshade_double* >* CSymList, std::vector< std::vector< proshade_double* > >* ret, proshade_double axErr, proshade_double minPeakHeight )
2035 {
2036  //================================================ Find the best axis combination with dihedral angle and correct folds
2037  std::vector < std::pair< proshade_unsign, proshade_unsign > > initAxes = findBestIcosDihedralPair ( CSymList, minPeakHeight, axErr );
2038 
2039  //================================================ For each pair of possible axis combinations
2040  for ( size_t pIt = 0; pIt < initAxes.size(); pIt++ )
2041  {
2042  //============================================ Create the tetrahedronAxes object
2044 
2045  //============================================ Find rotation between the detected C5 and the model C5 axes.
2046  proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( icoAx->getValue ( 0, 1 ),
2047  icoAx->getValue ( 0, 2 ),
2048  icoAx->getValue ( 0, 3 ),
2049  CSymList->at(initAxes.at(pIt).first)[1],
2050  CSymList->at(initAxes.at(pIt).first)[2],
2051  CSymList->at(initAxes.at(pIt).first)[3] );
2052 
2053  //============================================ Rotate the model C3 to the correct orientation relative to the detected C5 axis.
2054  proshade_double* rotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
2055  icoAx->getValue ( 6, 1 ),
2056  icoAx->getValue ( 6, 2 ),
2057  icoAx->getValue ( 6, 3 ) );
2058 
2059  //============================================ Find the angle betwen the rotated model C3 and the detected C3 axes along the detected C5 axis.
2060  proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
2061  proshade_double* rotMatHlp = new proshade_double[9];
2062  ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
2063  for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.002 )
2064  {
2065  //============================================ Compute rotation matrix for this angle value
2066  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.at(pIt).first)[1], CSymList->at(initAxes.at(pIt).first)[2], CSymList->at(initAxes.at(pIt).first)[3], ang );
2067 
2068  //======================================== Rotate the rotated C2 by the matrix
2069  proshade_double* rotRotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
2070  rotModelC3[0],
2071  rotModelC3[1],
2072  rotModelC3[2] );
2073 
2074  //======================================== Find distance
2075  curAngDist = std::sqrt ( std::pow ( rotRotModelC3[0] - CSymList->at(initAxes.at(pIt).second)[1], 2.0 ) +
2076  std::pow ( rotRotModelC3[1] - CSymList->at(initAxes.at(pIt).second)[2], 2.0 ) +
2077  std::pow ( rotRotModelC3[2] - CSymList->at(initAxes.at(pIt).second)[3], 2.0 ) );
2078 
2079  //======================================== Save best angle
2080  if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
2081 
2082  //======================================== Release memory
2083  delete[] rotRotModelC3;
2084  }
2085 
2086  //============================================ Release memory
2087  delete[] rotMatHlp;
2088 
2089  //============================================ For the rotation matrix along the detected C5 axis with the same angle as is between the rotated model C3 and the detected C3 axes.
2090  proshade_double* rotMat2 = new proshade_double[9];
2091  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
2092  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.at(pIt).first)[1], CSymList->at(initAxes.at(pIt).first)[2], CSymList->at(initAxes.at(pIt).first)[3], bestAng );
2093 
2094  //============================================ Combine the two rotation matrices into a single rotation matrix
2095  proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
2096 
2097  //============================================ For each model axis
2098  std::vector< proshade_double* > hlpAxes;
2099  for ( proshade_unsign iter = 0; iter < icoAx->getNoAxes ( ); iter++ )
2100  {
2101  //======================================== Rotate the model axis to fit the detected orientation
2102  proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
2103  icoAx->getValue ( iter, 1 ),
2104  icoAx->getValue ( iter, 2 ),
2105  icoAx->getValue ( iter, 3 ) );
2106 
2107  //======================================== Create ProSHADE symmetry axis representation
2108  proshade_double* axis = new proshade_double[7];
2109  ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
2110 
2111  axis[0] = icoAx->getValue ( iter, 0 );
2112  axis[1] = rotAxis[0];
2113  axis[2] = rotAxis[1];
2114  axis[3] = rotAxis[2];
2115  axis[4] = ( 2.0 * M_PI ) / axis[0];
2116  axis[5] = 0.0;
2117  axis[6] = -std::numeric_limits < proshade_double >::infinity();
2118 
2119  //======================================== Save axis to ret
2121 
2122  //======================================== Release memory
2123  delete[] rotAxis;
2124  delete[] axis;
2125  }
2126 
2127  //============================================ Save to ret
2128  ret->emplace_back ( hlpAxes );
2129 
2130  //============================================ Release memory
2131  delete[] rotMat;
2132  delete[] rotMat2;
2133  delete[] rotMatFin;
2134  delete[] rotModelC3;
2135  delete icoAx;
2136  }
2137 
2138  //================================================ Done
2139  return ;
2140 
2141 }
2142 
2150 std::vector < std::pair< proshade_unsign, proshade_unsign > > findBestOctaDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
2151 {
2152  //================================================ Initialise variables
2153  std::vector < std::pair< proshade_unsign, proshade_unsign > > ret;
2154  std::vector< proshade_unsign > C4List;
2155  proshade_double dotProduct;
2156 
2157  //================================================ Find all C5 symmetries
2158  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 4.0 ); if ( lhs1.AlmostEquals ( rhs1 ) && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C4List, cSym ); } }
2159 
2160  //================================================ For each unique pair of C5 and C3
2161  for ( proshade_unsign c4 = 0; c4 < static_cast<proshade_unsign> ( C4List.size() ); c4++ )
2162  {
2163  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2164  {
2165  //======================================== Compare only C3s to the C5List and only with decent average peak height
2166  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 3.0 );
2167  if ( !lhs1.AlmostEquals ( rhs1 ) ) { continue; }
2168  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
2169 
2170  //======================================== Check the angle between the C5 and C3 axes
2171  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C4List.at(c4))[1],
2172  &CSymList->at(C4List.at(c4))[2],
2173  &CSymList->at(C4List.at(c4))[3],
2174  &CSymList->at(cSym)[1],
2175  &CSymList->at(cSym)[2],
2176  &CSymList->at(cSym)[3] );
2177 
2178  //======================================== Is the angle approximately the dihedral angle?
2179  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
2180  {
2181  std::pair< proshade_unsign, proshade_unsign > hlp;
2182  hlp.first = C4List.at(c4);
2183  hlp.second = cSym;
2184  ret.emplace_back ( hlp );
2185  }
2186  }
2187  }
2188 
2189  //================================================ Done
2190  return ( ret );
2191 
2192 }
2193 
2214 void ProSHADE_internal_symmetry::predictOctaAxes ( std::vector< proshade_double* >* CSymList, std::vector< std::vector< proshade_double* > >* ret, proshade_double axErr, proshade_double minPeakHeight )
2215 {
2216  //================================================ Find the best axis combination with dihedral angle and correct folds
2217  std::vector < std::pair< proshade_unsign, proshade_unsign > > initAxes = findBestOctaDihedralPair ( CSymList, minPeakHeight, axErr );
2218 
2219  //================================================ For each pair of possible axis combinations
2220  for ( size_t pIt = 0; pIt < initAxes.size(); pIt++ )
2221  {
2222  //============================================ Create the tetrahedronAxes object
2224 
2225  //============================================ Find rotation between the detected C4 and the model C4 axes.
2226  proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( octAx->getValue ( 0, 1 ),
2227  octAx->getValue ( 0, 2 ),
2228  octAx->getValue ( 0, 3 ),
2229  CSymList->at(initAxes.at(pIt).first)[1],
2230  CSymList->at(initAxes.at(pIt).first)[2],
2231  CSymList->at(initAxes.at(pIt).first)[3] );
2232 
2233  //============================================ Rotate the model C3 to the correct orientation relative to the detected C4 axis.
2234  proshade_double* rotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
2235  octAx->getValue ( 3, 1 ),
2236  octAx->getValue ( 3, 2 ),
2237  octAx->getValue ( 3, 3 ) );
2238 
2239  //============================================ Find the angle betwen the rotated model C3 and the detected C3 axes along the detected C4 axis.
2240  proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
2241  proshade_double* rotMatHlp = new proshade_double[9];
2242  ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
2243  for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.002 )
2244  {
2245  //======================================== Compute rotation matrix for this angle value
2246  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.at(pIt).first)[1], CSymList->at(initAxes.at(pIt).first)[2], CSymList->at(initAxes.at(pIt).first)[3], ang );
2247 
2248  //======================================== Rotate the rotated C2 by the matrix
2249  proshade_double* rotRotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
2250  rotModelC3[0],
2251  rotModelC3[1],
2252  rotModelC3[2] );
2253 
2254  //======================================== Find distance
2255  curAngDist = std::sqrt ( std::pow ( rotRotModelC3[0] - CSymList->at(initAxes.at(pIt).second)[1], 2.0 ) +
2256  std::pow ( rotRotModelC3[1] - CSymList->at(initAxes.at(pIt).second)[2], 2.0 ) +
2257  std::pow ( rotRotModelC3[2] - CSymList->at(initAxes.at(pIt).second)[3], 2.0 ) );
2258 
2259  //======================================== Save best angle
2260  if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
2261 
2262  //======================================== Release memory
2263  delete[] rotRotModelC3;
2264  }
2265 
2266  //============================================ Release memory
2267  delete[] rotMatHlp;
2268 
2269  //============================================ For the rotation matrix along the detected C5 axis with the same anlge as is between the rotated model C3 and the detected C3 axes.
2270  proshade_double* rotMat2 = new proshade_double[9];
2271  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
2272  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.at(pIt).first)[1], CSymList->at(initAxes.at(pIt).first)[2], CSymList->at(initAxes.at(pIt).first)[3], bestAng );
2273 
2274  //============================================ Combine the two rotation matrices into a single rotation matrix
2275  proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
2276 
2277  //================================================ For each model axis
2278  std::vector< proshade_double* > hlpAxes;
2279  for ( proshade_unsign iter = 0; iter < octAx->getNoAxes ( ); iter++ )
2280  {
2281  //======================================== Rotate the model axis to fit the detected orientation
2282  proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
2283  octAx->getValue ( iter, 1 ),
2284  octAx->getValue ( iter, 2 ),
2285  octAx->getValue ( iter, 3 ) );
2286 
2287  //============================================ Create ProSHADE symmetry axis representation
2288  proshade_double* axis = new proshade_double[7];
2289  ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
2290 
2291  axis[0] = octAx->getValue ( iter, 0 );
2292  axis[1] = rotAxis[0];
2293  axis[2] = rotAxis[1];
2294  axis[3] = rotAxis[2];
2295  axis[4] = ( 2.0 * M_PI ) / axis[0];
2296  axis[5] = 0.0;
2297  axis[6] = -std::numeric_limits < proshade_double >::infinity();
2298 
2299  //======================================== Save axis to ret
2301 
2302  //======================================== Release memory
2303  delete[] rotAxis;
2304  delete[] axis;
2305  }
2306 
2307  //============================================ Save to ret
2308  ret->emplace_back ( hlpAxes );
2309 
2310  //================================================ Release memory
2311  delete[] rotMat;
2312  delete[] rotMat2;
2313  delete[] rotMatFin;
2314  delete[] rotModelC3;
2315  delete octAx;
2316  }
2317 
2318  //================================================ Done
2319  return ;
2320 
2321 }
2322 
2337 void ProSHADE_internal_symmetry::findIcos10C3s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
2338 {
2339  //================================================ Initialise variables
2340  std::vector< proshade_unsign > prospectiveC3s, retGrp;
2341  proshade_double dotProd;
2342  proshade_unsign noClose, noAway;
2343 
2344  //================================================ Report progress
2345  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of ten C3 axes.", messageShift );
2346 
2347  //================================================ For each C3
2348  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2349  {
2350  //============================================ Use only C3s with hight enough average
2351  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
2352 
2353  //============================================ Check the C3 has acos ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) to 3 C5s and acos ( 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) ) to the other three C5s
2354  noClose = 0; noAway = 0;
2355  for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
2356  {
2357  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
2358  &ret->at(rIt)[2],
2359  &ret->at(rIt)[3],
2360  &CSymList->at(cIt)[1],
2361  &CSymList->at(cIt)[2],
2362  &CSymList->at(cIt)[3] );
2363 
2364  if ( ( std::abs ( dotProd ) > ( ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) + axErr ) ) ) { noClose += 1; continue; }
2365  if ( ( std::abs ( dotProd ) > ( 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - axErr ) ) && ( std::abs ( dotProd ) < ( 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) + axErr ) ) ) { noAway += 1; continue; }
2366  }
2367 
2368  //============================================ If correct angles distribution is found, save the axis
2369  if ( ( noClose == 3 ) && ( noAway == 3 ) )
2370  {
2371  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC3s, cIt );
2372  }
2373  }
2374 
2375  //================================================ Search for missing axes
2376  for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
2377  if ( !ProSHADE_internal_symmetry::findMissingAxesDual ( &prospectiveC3s, CSymList, ret, &retGrp, 10, axErr, 3, std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ), 3, 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ), 3, dataObj ) )
2378  {
2379  return ;
2380  }
2381 
2382  //================================================ Found correct number of axes! Now save the
2383  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC3s.size() ); iter++ )
2384  {
2385  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC3s.at(iter)) );
2386  }
2387 
2388  //================================================ Report progress
2389  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of ten C3 axes successfull.", messageShift );
2390 
2391  //================================================ Done
2392  return ;
2393 
2394 }
2395 
2410 void ProSHADE_internal_symmetry::findIcos15C2s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight )
2411 {
2412  //================================================ Initialise variables
2413  std::vector< proshade_unsign > prospectiveC2s, retGrp;
2414  proshade_double dotProd;
2415  proshade_unsign noClose, noMidway, noAway;
2416 
2417  //================================================ Report progress
2418  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of fifteen C2 axes.", messageShift );
2419 
2420  //================================================ For each C2
2421  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2422  {
2423  //============================================ Use only C2s
2424  const FloatingPoint< proshade_double > lhs999 ( CSymList->at(cIt)[5] ), rhs999 ( static_cast< proshade_double > ( -999.9 ) );
2425  if ( CSymList->at(cIt)[0] != 2.0 || ( ( CSymList->at(cIt)[5] < minPeakHeight ) && !( lhs999.AlmostEquals( rhs999 ) ) ) ) { continue; }
2426 
2427  //============================================ Check the C2 has acos ( 0.0 ) to 2 C5s, acos ( 0.5 ) to another 2 C5s and acos ( sqrt ( 3.0 ) / 2.0 ) to the last two C5s
2428  noClose = 0; noMidway = 0; noAway = 0;
2429  for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
2430  {
2431  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
2432  &ret->at(rIt)[2],
2433  &ret->at(rIt)[3],
2434  &CSymList->at(cIt)[1],
2435  &CSymList->at(cIt)[2],
2436  &CSymList->at(cIt)[3] );
2437 
2438  if ( ( std::abs ( dotProd ) > ( ( sqrt ( 3.0 ) / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( sqrt ( 3.0 ) / 2.0 ) + axErr ) ) ) { noAway += 1; continue; }
2439  if ( ( std::abs ( dotProd ) > ( ( 1.0 / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / 2.0 ) + axErr ) ) ) { noMidway += 1; continue; }
2440  if ( ( std::abs ( dotProd ) > ( ( 0.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 0.0 ) + axErr ) ) ) { noClose += 1; continue; }
2441  }
2442 
2443  //============================================ If correct angles distribution is found, save the axis
2444  if ( ( noClose == 2 ) && ( noMidway == 2 ) && ( noAway == 2 ) )
2445  {
2446  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt );
2447  }
2448  }
2449 
2450  //================================================ Search for missing axes
2451  for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
2452  if ( !ProSHADE_internal_symmetry::findMissingAxesTriple ( &prospectiveC2s, CSymList, ret, &retGrp, 15, axErr, 2, 0.0, 2, 1.0/2.0, 2, sqrt ( 3.0 ) / 2.0, 2, dataObj ) )
2453  {
2454  return ;
2455  }
2456 
2457  //================================================ Found correct number of axes! Now save the
2458  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC2s.size() ); iter++ )
2459  {
2460  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC2s.at(iter)) );
2461  }
2462 
2463  //================================================ Report progress
2464  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of fifteen C2 axes successfull.", messageShift );
2465 
2466  //================================================ Done
2467  return ;
2468 
2469 }
2470 
2493 bool ProSHADE_internal_symmetry::findMissingAxesTriple ( std::vector< proshade_unsign >* possibilities, std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, std::vector< proshade_unsign >* retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data* dataObj )
2494 {
2495  //================================================ Initialise variables
2496  bool atLeastOne = false;
2497  std::vector< proshade_double* > prosp;
2498  std::vector< proshade_double > sol;
2499 
2500  //================================================ Proceed only if need be
2501  if ( static_cast<proshade_unsign> ( possibilities->size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
2502 
2503  //================================================ Copy already found to prospective
2504  for ( proshade_unsign prIt = 0; prIt < static_cast<proshade_unsign> ( possibilities->size() ); prIt++ )
2505  {
2506  ProSHADE_internal_symmetry::addAxisUnlessSame ( static_cast< proshade_unsign > ( CSymList->at(possibilities->at(prIt))[0] ),
2507  CSymList->at(possibilities->at(prIt))[1],
2508  CSymList->at(possibilities->at(prIt))[2],
2509  CSymList->at(possibilities->at(prIt))[3],
2510  CSymList->at(possibilities->at(prIt))[5], &prosp, axErr );
2511  }
2512 
2513  //================================================ Start generating possible solutions
2514  for ( proshade_unsign rgIt1 = 0; rgIt1 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt1++ )
2515  {
2516  for ( proshade_unsign rgIt2 = 0; rgIt2 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt2++ )
2517  {
2518  //======================================== Use unique combinations (order matters here!)
2519  if ( rgIt1 == rgIt2 ) { continue; }
2520 
2521  for ( proshade_unsign rgIt3 = 0; rgIt3 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt3++ )
2522  {
2523  //==================================== Use unique combinations (order matters here!)
2524  if ( ( rgIt1 == rgIt3 ) || ( rgIt2 == rgIt3 ) ) { continue; }
2525 
2526  //==================================== Generate possible solution (1)
2527  sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
2528  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
2529  ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], angle1, angle2, angle3 );
2530 
2531  //==================================== Check if solution fits the group completely
2532  ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, noMatchesG3, angle3, dataObj );
2533  if ( prosp.size() == requiredNoAxes ) { break; }
2534 
2535  //==================================== Generate possible solution (2)
2536  sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
2537  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
2538  ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], -angle1, -angle2, -angle3 );
2539 
2540  //==================================== Check if solution fits the group completely
2541  ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, noMatchesG3, angle3, dataObj );
2542  if ( prosp.size() == requiredNoAxes ) { break; }
2543  }
2544 
2545  if ( prosp.size() == requiredNoAxes ) { break; }
2546  }
2547 
2548  if ( prosp.size() == requiredNoAxes ) { break; }
2549  }
2550 
2551  //================================================ Found all required axes
2552  if ( prosp.size() == requiredNoAxes )
2553  {
2554  //============================================ For each found missing axis
2555  for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
2556  {
2557  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, prosp.at(axIt), axErr ) )
2558  {
2559  //======================================== Add
2560  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, prosp.at(axIt) );
2561  ProSHADE_internal_misc::addToUnsignVector ( possibilities, static_cast<proshade_unsign> ( CSymList->size()-1 ) );
2562  }
2563  }
2564 
2565  atLeastOne = true;
2566  return ( atLeastOne );
2567  }
2568  else
2569  {
2570  //============================================ Delete all found, but unnecessary axes
2571  for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
2572  {
2573  delete[] prosp.at(axIt);
2574  }
2575  }
2576 
2577  //================================================ Done
2578  return ( atLeastOne );
2579 
2580 }
2581 
2604 void ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave ( std::vector< proshade_unsign >* retGroup, std::vector< proshade_double* >* ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double* >* prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, ProSHADE_internal_data::ProSHADE_data* dataObj )
2605 {
2606  //================================================ Initialise variables
2607  proshade_unsign noG1 = 0;
2608  proshade_unsign noG2 = 0;
2609  proshade_unsign noG3 = 0;
2610  proshade_double dotProd = 0.0;
2611  proshade_double axHeight = 0.0;
2612 
2613  //================================================ Find the angle and count dual matching frequencies
2614  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( retGroup->size() ); rIt++ )
2615  {
2616  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(retGroup->at(rIt))[1],
2617  &ret->at(retGroup->at(rIt))[2],
2618  &ret->at(retGroup->at(rIt))[3],
2619  &axX, &axY, &axZ );
2620 
2621  if ( ( std::abs ( dotProd ) > ( angle1 - axErr ) ) && ( std::abs ( dotProd ) < ( angle1 + axErr ) ) ) { noG1 += 1; continue; }
2622  if ( ( std::abs ( dotProd ) > ( angle2 - axErr ) ) && ( std::abs ( dotProd ) < ( angle2 + axErr ) ) ) { noG2 += 1; continue; }
2623  if ( ( std::abs ( dotProd ) > ( angle3 - axErr ) ) && ( std::abs ( dotProd ) < ( angle3 + axErr ) ) ) { noG3 += 1; continue; }
2624  }
2625 
2626  //================================================ If correct frequencies are matched, check height.
2627  if ( ( noG1 == noMatchesG1 ) && ( noG2 == noMatchesG2 ) && ( noG3 == noMatchesG3 ) )
2628  {
2629  //============================================ Is the height good enough?
2630  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( axX, axY, axZ, dataObj, fold, axErr );
2631 
2632  //============================================ If so, save
2633  if ( axHeight > 0.1 )
2634  {
2635  ProSHADE_internal_symmetry::addAxisUnlessSame ( fold, axX, axY, axZ, axHeight, prosp, axErr );
2636  }
2637  }
2638 
2639  //================================================ Done
2640  return ;
2641 
2642 }
2643 
2657 {
2658  //================================================ Initialise variables
2659  std::vector< proshade_unsign > primes = ProSHADE_internal_maths::findAllPrimes ( settings->maxSymmetryFold );
2660  std::vector< proshade_double* > ret, tmpHolder;
2661  std::vector< proshade_unsign > testedFolds;
2662  proshade_double symThres;
2663  proshade_unsign foldToTest, rotationNumber;
2664  bool foldDone, anyNewSyms = true;
2665 
2666  //================================================ Prepare FSC computation memory and variables
2667  fftw_complex* fCoeffsCut;
2668  proshade_double **bindata, *fscByBin;
2669  proshade_signed *cutIndices, *binCounts, noBins, cutXDim, cutYDim, cutZDim;
2670  this->prepareFSCFourierMemory ( cutIndices, fCoeffsCut, &noBins, bindata, binCounts, fscByBin, settings->requestedResolution, &cutXDim, &cutYDim, &cutZDim );
2671 
2672  //================================================ For each found prime number in the limit
2673  for ( proshade_unsign prIt = 0; prIt < static_cast< proshade_unsign > ( primes.size() ); prIt++ )
2674  {
2675  //============================================ Report progress
2676  std::stringstream hlpSS;
2677  hlpSS << "Searching for prime fold symmetry C" << primes.at(prIt) << ".";
2678  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str(), settings->messageShift );
2679 
2680  //============================================ If fold is higher than the resolution supports, compute FSC using different than first rotation.
2681  rotationNumber = 1;
2682  if ( primes.at(prIt) >= settings->supportedSymmetryFold )
2683  {
2684  //========================================= Find all prime factors
2685  std::vector< proshade_signed > divisAll;
2686  ProSHADE_internal_misc::addToSignedVector ( &divisAll, static_cast< proshade_signed > ( primes.at(prIt) - 1 ) );
2687  for ( proshade_unsign pfIt = primes.at(prIt) + 4; pfIt >= ( std::max ( primes.at(prIt) - 4, settings->supportedSymmetryFold ) ); pfIt-- )
2688  {
2689  if ( pfIt == primes.at(prIt) ) { continue; }
2690  if ( pfIt < 2 ) { continue; }
2691  std::vector< proshade_signed > divis = ProSHADE_internal_maths::primeFactorsDecomp ( static_cast< proshade_signed > ( pfIt ) );
2692  for ( size_t iter = 0; iter < divis.size(); iter++ ) { ProSHADE_internal_misc::addToSignedVector ( &divisAll, divis.at(iter) ); }
2693  }
2694 
2695  //======================================== Find all rotations of all smaller symmetries
2696  std::vector< proshade_double > prevRots;
2697  for ( size_t fIt = 0; fIt < divisAll.size(); fIt++ ) { for ( size_t rIt = 1; rIt < static_cast< size_t > ( divisAll.at(fIt) - 1 ); rIt++ ) { ProSHADE_internal_misc::addToDoubleVector ( &prevRots, ( ( 2.0 * M_PI ) / static_cast< proshade_double > ( divisAll.at(fIt) ) ) * static_cast< proshade_double > ( rIt ) ); } }
2698 
2699  //======================================== Find most unique rotation
2700  proshade_unsign bestPos = 1;
2701  proshade_double bestDist = 0.0;
2702  proshade_double curPos = 0.0;
2703  proshade_double bestDistHlp;
2704 
2705  //======================================== For each possible rotation
2706  for ( proshade_unsign fIt = 2; fIt <= static_cast< proshade_unsign >( ( primes.at(prIt) + 1 ) / 2 ); fIt++ )
2707  {
2708  //==================================== Set current values
2709  curPos = ( ( 2.0 * M_PI ) / static_cast< proshade_double > ( primes.at(prIt) ) ) * static_cast< proshade_double > ( fIt );
2710  bestDistHlp = std::numeric_limits < proshade_double >::infinity();
2711 
2712  //==================================== Find closest other rotation
2713  for ( proshade_unsign dIt = 0; dIt < static_cast< proshade_unsign > ( prevRots.size() ); dIt++ ) { if ( std::abs ( prevRots.at(dIt) - curPos ) < bestDistHlp ) { bestDistHlp = std::abs ( prevRots.at(dIt) - curPos ); } }
2714 
2715  //==================================== Is this the best
2716  if ( bestDistHlp > bestDist ) { bestDist = bestDistHlp; bestPos = fIt; }
2717  }
2718 
2719  rotationNumber = bestPos;
2720  if ( rotationNumber >= ( primes.at(prIt) - 1 ) ) { rotationNumber = 1; }
2721  }
2722 
2723  //============================================ Get all symmetries for this prime fold
2724  std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, primes.at(prIt), &symThres );
2725 
2726  //============================================ For each detected C symmetry with this fold
2727  for ( size_t axIt = 0; axIt < prSyms.size(); axIt++ )
2728  {
2729  //======================================== Check the peak threshold
2730  if ( prSyms.at(axIt)[5] >= std::max ( symThres, settings->minSymPeak ) )
2731  {
2732  //==================================== Check if the axis and fold are unique
2733  if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(axIt), settings->axisErrTolerance, true ) )
2734  {
2735  //================================ Compute FSC
2736  this->computeFSC ( settings, prSyms.at(axIt), cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, cutXDim, cutYDim, cutZDim, rotationNumber );
2737 
2738  //======================== If high FSC and undersampled map, check the FSC with full computation
2739  if ( ( primes.at(prIt) >= settings->supportedSymmetryFold ) || ( prSyms.at(axIt)[6] >= settings->fscThreshold ) )
2740  {
2741  prSyms.at(axIt)[6] = -std::numeric_limits < proshade_double >::infinity();
2742  this->computeFSC ( settings, prSyms.at(axIt), cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, cutXDim, cutYDim, cutZDim, 0 );
2743  }
2744 
2745  //================================ Save axis
2746  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, prSyms.at(axIt) );
2747  }
2748  }
2749 
2750  //======================================== Release memory
2751  delete[] prSyms.at(axIt);
2752  }
2753  }
2754 
2755  //================================================ Was anything found?
2756  if ( ret.size() < 1 ) { return ( ret ); }
2757 
2758  //================================================ Compute the FSC threshold
2759  proshade_double bestFSCPeakStart = ProSHADE_internal_maths::findTopGroupSmooth ( &ret, 6, 0.01, 0.3, 9 );
2760  proshade_double dotProduct = 0.0;
2761 
2762  //================================================ Check for prime symmetry fold multiples
2763  while ( anyNewSyms )
2764  {
2765  //============================================ Initialise new iteration
2766  anyNewSyms = false;
2767 
2768  //============================================ For each passing symmetry, look if there are any combinations of symmetries that would contain it
2769  for ( proshade_unsign axIt1 = 0; axIt1 < static_cast< proshade_unsign > ( ret.size() ); axIt1++ )
2770  {
2771  //======================================== Do not try axes with low FSC
2772  if ( bestFSCPeakStart > ret.at(axIt1)[6] ) { continue; }
2773 
2774  for ( proshade_unsign axIt2 = 0; axIt2 < static_cast< proshade_unsign > ( ret.size() ); axIt2++ )
2775  {
2776  //==================================== Do not try axes with low FSC
2777  if ( bestFSCPeakStart > ret.at(axIt2)[6] ) { continue; }
2778 
2779  //==================================== At least one fold needs to be prime (to avoid escalation of high symmetries - i.e. C32 would require checking C32 * C32 = C1024 including computing its FSC...)
2780  if ( !ProSHADE_internal_maths::isPrime ( static_cast< proshade_unsign > ( ret.at(axIt1)[0] ) ) &&
2781  !ProSHADE_internal_maths::isPrime ( static_cast< proshade_unsign > ( ret.at(axIt2)[0] ) ) ) { continue; }
2782 
2783  //==================================== Is the axis the same?
2784  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &ret.at(axIt1)[1],
2785  &ret.at(axIt1)[2],
2786  &ret.at(axIt1)[3],
2787  &ret.at(axIt2)[1],
2788  &ret.at(axIt2)[2],
2789  &ret.at(axIt2)[3] );
2790  if ( std::abs ( dotProduct ) < ( 1.0 - settings->axisErrTolerance ) ) { continue; }
2791 
2792  //==================================== Initialise iteration
2793  foldToTest = static_cast< proshade_unsign > ( ret.at(axIt1)[0] * ret.at(axIt2)[0] );
2794 
2795  //==================================== If fold is higher than the resolution supports, compute FSC using different than first rotation.
2796  rotationNumber = 1;
2797  if ( foldToTest >= settings->supportedSymmetryFold )
2798  {
2799  //================================ Find all prime factors
2800  std::vector< proshade_signed > divisAll;
2801  ProSHADE_internal_misc::addToSignedVector ( &divisAll, static_cast< proshade_signed > ( ret.at(axIt1)[0] ) );
2802  ProSHADE_internal_misc::addToSignedVector ( &divisAll, static_cast< proshade_signed > ( ret.at(axIt2)[0] ) );
2803  for ( size_t iter = 0; iter < ret.size(); iter++ ) { if ( ret.at(iter)[6] > ( bestFSCPeakStart * 0.9 ) ) { ProSHADE_internal_misc::addToSignedVector ( &divisAll, static_cast< proshade_signed > ( ret.at(iter)[0] ) ); } }
2804 
2805  //================================ Find all rotations of all smaller symmetries
2806  std::vector< proshade_double > prevRots;
2807  for ( size_t fIt = 0; fIt < divisAll.size(); fIt++ ) { for ( size_t rIt = 1; rIt < static_cast< size_t > ( divisAll.at(fIt) - 1 ); rIt++ ) { ProSHADE_internal_misc::addToDoubleVector ( &prevRots, ( ( 2.0 * M_PI ) / static_cast< proshade_double > ( divisAll.at(fIt) ) ) * static_cast< proshade_double > ( rIt ) ); } }
2808 
2809  //================================ Find most unique rotation
2810  proshade_unsign bestPos = 1;
2811  proshade_double bestDist = 0.0;
2812  proshade_double curPos = 0.0;
2813  proshade_double bestDistHlp;
2814 
2815  //================================ For each possible rotation
2816  for ( proshade_unsign fIt = 2; fIt <= static_cast< proshade_unsign >( foldToTest / 2 ); fIt++ )
2817  {
2818  //============================ Set current values
2819  curPos = ( ( 2.0 * M_PI ) / static_cast< proshade_double > ( foldToTest ) ) * static_cast< proshade_double > ( fIt );
2820  bestDistHlp = std::numeric_limits < proshade_double >::infinity();
2821 
2822  //============================ Find closest other rotation
2823  for ( proshade_unsign dIt = 0; dIt < static_cast< proshade_unsign > ( prevRots.size() ); dIt++ ) { if ( std::abs ( prevRots.at(dIt) - curPos ) < bestDistHlp ) { bestDistHlp = std::abs ( prevRots.at(dIt) - curPos ); } }
2824 
2825  //============================ Is this the best
2826  if ( bestDistHlp > bestDist ) { bestDist = bestDistHlp; bestPos = fIt; }
2827  }
2828 
2829  rotationNumber = bestPos;
2830  if ( rotationNumber >= ( foldToTest - 1 ) ) { rotationNumber = 1; }
2831  }
2832 
2833  //==================================== Was this fold tested already?
2834  foldDone = false;
2835  for ( proshade_unsign fIt = 0; fIt < static_cast< proshade_unsign > ( testedFolds.size() ); fIt++ ) { if ( testedFolds.at(fIt) == foldToTest ) { foldDone = true; break; } }
2836  if ( foldDone ) { continue; }
2837  else { ProSHADE_internal_misc::addToUnsignVector ( &testedFolds, foldToTest ); }
2838 
2839  //==================================== Report progress
2840  std::stringstream hlpSS2;
2841  hlpSS2 << "Searching for fold combination of detected folds " << ret.at(axIt1)[0] << " and " << ret.at(axIt2)[0] << ", i.e. C" << foldToTest << ".";
2842  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS2.str(), settings->messageShift );
2843 
2844  //==================================== Get all symmetries for this fold
2845  std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, foldToTest, &symThres );
2846 
2847  //==================================== For each detected group with the required fold
2848  for ( size_t newAxIt = 0; newAxIt < prSyms.size(); newAxIt++ )
2849  {
2850  //================================ Check peak threshold
2851  if ( prSyms.at(newAxIt)[5] >= symThres )
2852  {
2853  //============================ Check axis and fold are unique
2854  if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(newAxIt), settings->axisErrTolerance, true ) &&
2855  ProSHADE_internal_maths::isAxisUnique ( &tmpHolder, prSyms.at(newAxIt), settings->axisErrTolerance, false ) )
2856  {
2857  //======================== Compute FSC
2858  this->computeFSC ( settings, prSyms.at(newAxIt), cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, cutXDim, cutYDim, cutZDim, rotationNumber );
2859 
2860  //======================== If high FSC and undersampled map, check the FSC with full computation
2861  if ( ( foldToTest >= settings->supportedSymmetryFold ) || ( prSyms.at(newAxIt)[6] >= bestFSCPeakStart ) )
2862  {
2863  prSyms.at(newAxIt)[6] = -std::numeric_limits < proshade_double >::infinity();
2864  this->computeFSC ( settings, prSyms.at(newAxIt), cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, cutXDim, cutYDim, cutZDim, 0 );
2865  }
2866 
2867  //======================== Save axis
2868  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &tmpHolder, prSyms.at(newAxIt) );
2869  }
2870  }
2871 
2872  //================================ Release memory
2873  delete[] prSyms.at(newAxIt);
2874  }
2875 
2876  //==================================== If new axes were found, add them and repeat
2877  if ( tmpHolder.size() != 0 )
2878  {
2879  //================================ Add newly found groups and repeat if need be
2880  for ( proshade_unsign tmpIt = 0; tmpIt < static_cast< proshade_unsign > ( tmpHolder.size() ); tmpIt++ )
2881  {
2882  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, tmpHolder.at(tmpIt) );
2883  delete[] tmpHolder.at(tmpIt);
2884  }
2885 
2886  anyNewSyms = true;
2887  }
2888 
2889  //==================================== Clean up
2890  tmpHolder.clear ( );
2891  }
2892  }
2893  }
2894 
2895  //================================================ Release memory after FSC computation
2896  for (size_t binIt = 0; binIt < static_cast< size_t > ( noBins ); binIt++ ) { delete[] bindata[binIt]; }
2897  delete[] bindata;
2898  delete[] binCounts;
2899  delete[] fscByBin;
2900  delete[] cutIndices;
2901  fftw_free ( fCoeffsCut );
2902 
2903  //================================================ Sort the vector
2904  std::sort ( ret.begin(), ret.end(), ProSHADE_internal_misc::sortSymFSCHlpInv );
2905 
2906  //================================================ Done
2907  return ( ret );
2908 }
2909 
2916 bool sortProSHADESymmetryByPeak ( proshade_double* a, proshade_double* b)
2917 {
2918  //================================================ Done
2919  return ( a[5] > b[5] );
2920 
2921 }
2922 
2939 std::vector < proshade_double* > ProSHADE_internal_data::ProSHADE_data::findRequestedCSymmetryFromAngleAxis ( ProSHADE_settings* settings, proshade_unsign fold, proshade_double* peakThres )
2940 {
2941  //================================================ Initialise variables
2942  proshade_double soughtAngle;
2943  std::vector< proshade_double > allPeakHeights;
2944  std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup* > peakGroups;
2945  std::vector< proshade_double* > ret;
2946  bool newPeak;
2947 
2948  //================================================ Sanity check
2949  if ( ( M_PI / static_cast < proshade_double > ( this->maxShellBand ) ) >= ( M_PI / static_cast< proshade_double > ( fold ) ) )
2950  {
2951  //============================================ Not enough points to produce all the spheres meaningfully. Issue warning and ignore this fold.
2952  std::stringstream hlpSS;
2953  hlpSS << "!!! ProSHADE WARNING !!! Will not search for fold " << fold << " as the map sampling does not support its reliable detection. Increase resolution/bandwidth if you suspect this fold could be present in the structure.";
2954  ProSHADE_internal_messages::printWarningMessage ( settings->verbose, hlpSS.str(), "WS00075" );
2955  return ( ret );
2956  }
2957 
2958  //================================================ Make sure we have a clean start
2959  this->sphereMappedRotFun.clear();
2960 
2961  //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
2962  for ( proshade_double angIt = 1.0; angIt < static_cast < proshade_double > ( fold ); angIt += 1.0 )
2963  {
2964  //============================================ Figure the angles to form the symmetry
2965  soughtAngle = angIt * ( 2.0 * M_PI / static_cast<proshade_double> ( fold ) );
2966 
2967  //============================================ Create the angle-axis sphere with correct radius (angle)
2968  this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( soughtAngle,
2969  M_PI / static_cast < proshade_double > ( this->maxShellBand ),
2970  this->getMaxBand ( ) * 2,
2971  this->getEMatDim ( ) * 2,
2972  soughtAngle,
2973  static_cast < proshade_unsign > ( angIt - 1.0 ) ) );
2974 
2975  //=========================================== Interpolate rotation function onto the sphere
2976  this->sphereMappedRotFun.at(static_cast < size_t > ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
2977 
2978  //============================================ Find all peaks for this sphere
2979  this->sphereMappedRotFun.at(static_cast < size_t > ( angIt - 1.0 ))->findAllPeaks ( static_cast< proshade_signed > ( settings->peakNeighbours ), &allPeakHeights );
2980  }
2981 
2982  //============================================ Report progress
2983  std::stringstream hlpSS;
2984  hlpSS << "Found a total of " << std::pow ( static_cast< proshade_double > ( this->maxShellBand ) * 2.0 * ( static_cast< proshade_double > ( fold ) - 1.0 ), 2.0 ) - static_cast< proshade_double > ( allPeakHeights.size() ) << " non-peaks for thresholding.";
2985  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS.str(), settings->messageShift );
2986 
2987  //================================================ Determine the threshold for significant peaks
2988  *peakThres = determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak, settings->peakThresholdMin );
2989 
2990  //============================================ Report progress
2991  std::stringstream hlpSS2;
2992  hlpSS2 << "Determined peak threshold " << *peakThres << ".";
2993  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str(), settings->messageShift );
2994 
2995  //================================================ Remove small peaks
2996  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
2997  {
2998  this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( *peakThres );
2999  }
3000 
3001  //================================================ Group peaks
3002  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); sphIt++ )
3003  {
3004  //============================================ For each peak
3005  for ( proshade_unsign pkIt = 0; pkIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.at(sphIt)->getPeaks().size() ); pkIt++ )
3006  {
3007  //======================================== Check if peak belongs to an already detected peak group
3008  newPeak = true;
3009  for ( proshade_unsign pkGrpIt = 0; pkGrpIt < static_cast<proshade_unsign> ( peakGroups.size() ); pkGrpIt++ )
3010  {
3011  if ( peakGroups.at(pkGrpIt)->checkIfPeakBelongs ( static_cast< proshade_double > ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first ),
3012  static_cast< proshade_double > ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second ),
3013  sphIt, settings->axisErrTolerance, settings->verbose, settings->messageShift, 0.1 ) ) { newPeak = false; break; }
3014  }
3015 
3016  //======================================== If already added, go to next one
3017  if ( !newPeak ) { continue; }
3018 
3019  //======================================== If not, create a new group with this peak
3020  peakGroups.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( static_cast< proshade_double > ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first ),
3021  static_cast< proshade_double > ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second ),
3022  sphIt,
3023  this->sphereMappedRotFun.at(sphIt)->getAngularDim() ) );
3024  }
3025  }
3026 
3027  //================================================ For each peak group, look for the requested fold
3028  for ( proshade_unsign grIt = 0; grIt < static_cast<proshade_unsign> ( peakGroups.size() ); grIt++ )
3029  {
3030  //============================================ Report progress
3031  std::stringstream hlpSS3;
3032  hlpSS3 << "Now considering group with LAT " << peakGroups.at(grIt)->getLatFromIndices() << " - " << peakGroups.at(grIt)->getLatToIndices() << " and LON " << peakGroups.at(grIt)->getLonFromIndices() << " - " << peakGroups.at(grIt)->getLonToIndices() << " spanning spheres ";
3033  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( peakGroups.at(grIt)->getSpherePositions().size() ); sphIt++ ) { hlpSS3 << peakGroups.at(grIt)->getSpherePositions().at(sphIt) << " ; "; }
3034  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 5, hlpSS3.str(), settings->messageShift );
3035 
3036  //============================================ Find point groups in the peak group
3037  peakGroups.at(grIt)->findCyclicPointGroupsGivenFold ( this->sphereMappedRotFun, &ret, settings->useBiCubicInterpolationOnPeaks, fold, settings->verbose, settings->messageShift );
3038 
3039  //============================================ Release the memory
3040  delete peakGroups.at(grIt);
3041  }
3042 
3043  //================================================ Sort ret by peak height
3044  std::sort ( ret.begin(), ret.end(), sortProSHADESymmetryByPeak );
3045 
3046  //================================================ Done
3047  return ( ret );
3048 
3049 }
3050 
3064 {
3065  //================================================ Initialise variables
3066  std::vector < proshade_unsign > folds;
3067  std::vector < proshade_double > angs, applicableAngs;
3068  bool alreadyFound = false;
3069  size_t corAngIt = 0;
3070  proshade_double lat = 0.0, lon = 0.0, radRange = 0.0, searchRangeInDeg = 0.0, axSum = 0.0, curSum = 0.0, maxSum = 0.0, bestXRot = 0.0, bestYRot = 0.0, bestZRot = 0.0, finXRotChan = 0.0, finYRotChan = 0.0, finZRotChan = 0.0;
3071  proshade_double latSamlUnit = ( 2.0 * M_PI ) / ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 );
3072  proshade_double lonSamlUnit = ( 1.0 * M_PI ) / ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 );
3073 
3074  //================================================ Determine all the folds for which rotation function mapping will be required
3075  for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ret->size() ); iter++ )
3076  {
3077  alreadyFound = false;
3078  for ( proshade_unsign it = 0; it < static_cast < proshade_unsign > ( folds.size() ); it++ ) { const FloatingPoint< proshade_double > lhs1 ( static_cast< proshade_double > ( folds.at(it) ) ), rhs1 ( ret->at(iter)[0] ); if ( lhs1.AlmostEquals ( rhs1 ) ) { alreadyFound = true; break; } }
3079 
3080  if ( !alreadyFound ) { ProSHADE_internal_misc::addToUnsignVector ( &folds, static_cast< proshade_unsign > ( ret->at(iter)[0] ) ); }
3081  }
3082 
3083  //================================================ Generate vector of all angles
3084  for ( proshade_unsign foldIt = 0; foldIt < static_cast < proshade_unsign > ( folds.size() ); foldIt++ ) { for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( folds.at(foldIt) ); angIt += 1.0 ) { ProSHADE_internal_misc::addToDoubleVector ( &angs, angIt * ( 2.0 * M_PI / static_cast<proshade_double> ( folds.at(foldIt) ) ) ); } }
3085  std::sort ( angs.begin(), angs.end() );
3086 
3087  //================================================ Remove redundant angles from the list
3088  for ( int angIt = static_cast< int > ( angs.size() - 2 ); angIt >= 0; angIt-- ) { const FloatingPoint< proshade_double > lhs1 ( angs.at(static_cast< size_t > ( angIt ) ) ), rhs1 ( angs.at(static_cast< size_t > ( angIt + 1 ) ) ); if ( lhs1.AlmostEquals ( rhs1 ) ) { angs.erase ( angs.begin() + (angIt+1) ); } }
3089 
3090  //================================================ Generate all sphere mapped rotation function
3091  dataObj->sphereMappedRotFun.clear();
3092  for ( size_t angIt = 0; angIt < angs.size(); angIt++ )
3093  {
3094  //============================================ Decide the range in which the sphere operates
3095  if ( ( angIt == 0 ) && ( angs.size() > 1 ) ) { radRange = ( angs.at(1) - angs.at(0) ) / 2; }
3096  else { if ( ( angIt == ( angs.size() - 1 ) ) && ( angs.size() > 1 ) ) { radRange = ( angs.at(angIt) - angs.at(angIt-1) ) / 2; }
3097  else { if ( angs.size() > 2 ) { radRange = std::min ( ( angs.at(angIt) - angs.at(angIt-1) ) / 2, ( angs.at(angIt+1) - angs.at(angIt) ) / 2 ); }
3098  else { radRange = 0.5; } } }
3099 
3100  //============================================ Create the sphere
3101  dataObj->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( angs.at(angIt),
3102  radRange,
3103  dataObj->getMaxBand ( ) * 2,
3104  dataObj->getEMatDim ( ) * 2,
3105  angs.at(angIt),
3106  static_cast<proshade_unsign> ( angIt ) ) );
3107 
3108  //=========================================== Interpolate rotation function onto the sphere
3109  dataObj->sphereMappedRotFun.at( static_cast < proshade_unsign > ( angIt ))->interpolateSphereValues ( dataObj->getInvSO3Coeffs ( ) );
3110  }
3111 
3112  //================================================ Check for improved sum
3113  searchRangeInDeg = 360.0 / ( static_cast< proshade_double > ( dataObj->getMaxBand() ) * 2.0 );
3114  proshade_double* rotMat, *newAxis;
3115  while ( searchRangeInDeg > 0.09 )
3116  {
3117  //============================================ For change along each dimension
3118  for ( proshade_double xChan = -searchRangeInDeg; xChan < ( 1.5 * searchRangeInDeg ); xChan += searchRangeInDeg )
3119  {
3120  for ( proshade_double yChan = -searchRangeInDeg; yChan < ( 1.5 * searchRangeInDeg ); yChan += searchRangeInDeg )
3121  {
3122  for ( proshade_double zChan = -searchRangeInDeg; zChan < ( 1.5 * searchRangeInDeg ); zChan += searchRangeInDeg )
3123  {
3124  //================================ Initialise local variables
3125  curSum = 0.0;
3126 
3127  //================================ Find the rotation matrix of the appropriate rotation
3128  rotMat = ProSHADE_internal_maths::build3x3MatrixFromXYZRotations ( xChan + finXRotChan, yChan + finYRotChan, zChan + finZRotChan );
3129 
3130  //================================ For each axis, find new position and its RF value
3131  for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( ret->size() ); axIt++ )
3132  {
3133  //============================ Find rotated axis
3134  newAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat, ret->at(axIt)[1], ret->at(axIt)[2], ret->at(axIt)[3] );
3135 
3136  //============================ Convert XYZ to lat and lon INDICES
3137  lat = ( std::atan2( newAxis[1], newAxis[0] ) / latSamlUnit );
3138  lon = ( std::acos ( newAxis[2] ) / lonSamlUnit );
3139 
3140  if ( lat < 0.0 ) { lat += ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 ); }
3141  if ( lon < 0.0 ) { lon += ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 ); }
3142 
3143  //============================ Generate all angles for this fold
3144  applicableAngs.clear ( );
3145  for ( proshade_double angIt = 1.0; angIt < ret->at(axIt)[0]; angIt += 1.0 ) { ProSHADE_internal_misc::addToDoubleVector ( &applicableAngs, angIt * ( 2.0 * M_PI / ret->at(axIt)[0] ) ); }
3146 
3147  //============================ For each shpere with the correct angle, average the peak heights
3148  axSum = 1.0;
3149  for ( size_t angIt = 0; angIt < angs.size(); angIt++ )
3150  {
3151  //======================== Find the correct sphere
3152  alreadyFound = false;
3153  for ( size_t aIt = 0; aIt < applicableAngs.size(); aIt++ ) { if ( alreadyFound ) { break; } const FloatingPoint< proshade_double > lhs1 ( angs.at(angIt) ), rhs1 ( applicableAngs.at(aIt) ); if ( lhs1.AlmostEquals ( rhs1 ) ) { alreadyFound = true; corAngIt = angIt; } }
3154 
3155  if ( !alreadyFound ) { continue; }
3156 
3157  //======================== Get its peak height for the longitude and latitude
3158  axSum += dataObj->sphereMappedRotFun.at(corAngIt)->getSphereLatLonLinearInterpolationPos ( lat, lon );
3159  }
3160 
3161  //============================ And average the peak heights over the axis
3162  axSum /= ret->at(axIt)[0];
3163  curSum += axSum;
3164 
3165  //============================ Release memory
3166  delete[] newAxis;
3167  }
3168 
3169  //================================ And average the peak heights over all axes
3170  curSum /= static_cast< proshade_double > ( ret->size() );
3171 
3172  //================================ If improved, save
3173  if ( curSum > maxSum )
3174  {
3175  maxSum = curSum;
3176  bestXRot = xChan;
3177  bestYRot = yChan;
3178  bestZRot = zChan;
3179  }
3180 
3181  //================================ Release memory
3182  delete[] rotMat;
3183 
3184 
3185  }
3186  }
3187  }
3188 
3189  //============================================ Prepare for next iteration
3190  searchRangeInDeg /= 2.0;
3191  finXRotChan += bestXRot;
3192  finYRotChan += bestYRot;
3193  finZRotChan += bestZRot;
3194  bestXRot = 0.0;
3195  bestYRot = 0.0;
3196  bestZRot = 0.0;
3197  }
3198 
3199  //================================================ Apply the optimisation
3200  rotMat = ProSHADE_internal_maths::build3x3MatrixFromXYZRotations ( finXRotChan, finYRotChan, finZRotChan );
3201  for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( ret->size() ); axIt++ )
3202  {
3203  //============================================ Find the rotated axis
3204  newAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat, ret->at(axIt)[1], ret->at(axIt)[2], ret->at(axIt)[3] );
3205 
3206  //============================================ Change axes
3207  ret->at(axIt)[1] = newAxis[0];
3208  ret->at(axIt)[2] = newAxis[1];
3209  ret->at(axIt)[3] = newAxis[2];
3210  }
3211 
3212  //================================================ Release memory
3213  delete[] rotMat;
3214 
3215  //================================================ For each ret axis, compute predicted position
3216  for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( ret->size() ); axIt++ )
3217  {
3218  //============================================ Convert XYZ to lat and lon INDICES
3219  lat = std::atan2( ret->at(axIt)[2], ret->at(axIt)[1] ) / latSamlUnit;
3220  lon = std::acos ( ret->at(axIt)[3] ) / lonSamlUnit;
3221 
3222  if ( lat < 0.0 ) { lat += ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 ); }
3223  if ( lon < 0.0 ) { lon += ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 ); }
3224 
3225  //============================================ Generate all angles for this fold
3226  applicableAngs.clear ( );
3227  for ( proshade_double angIt = 1.0; angIt < ret->at(axIt)[0]; angIt += 1.0 ) { ProSHADE_internal_misc::addToDoubleVector ( &applicableAngs, angIt * ( 2.0 * M_PI / ret->at(axIt)[0] ) ); }
3228 
3229  //============================================ For each shpere with the correct angle, average the peak heights
3230  ret->at(axIt)[5] = 0.0;
3231  for ( size_t angIt = 0; angIt < angs.size(); angIt++ )
3232  {
3233  //======================================== Find the correct sphere
3234  alreadyFound = false;
3235  for ( size_t aIt = 0; aIt < applicableAngs.size(); aIt++ ) { if ( alreadyFound ) { break; } const FloatingPoint< proshade_double > lhs1 ( angs.at(angIt) ), rhs1 ( applicableAngs.at(aIt) ); if ( lhs1.AlmostEquals ( rhs1 ) ) { alreadyFound = true; corAngIt = angIt; } }
3236 
3237  if ( !alreadyFound ) { continue; }
3238 
3239  //======================================== Get its peak height for the longitude and latitude
3240  ret->at(axIt)[5] += dataObj->sphereMappedRotFun.at(corAngIt)->getSphereLatLonLinearInterpolationPos ( lat, lon );
3241  }
3242 
3243  //============================================ And average the peak heights over the axis
3244  ret->at(axIt)[5] /= ( ret->at(axIt)[0] - 1.0 );
3245  maxSum += ret->at(axIt)[5];
3246  }
3247 
3248  //================================================ Report progress
3249  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, "Peak height detection and rotation optimisation complete.", settings->messageShift );
3250 
3251  //================================================ Done
3252  return ;
3253 
3254 }
3255 
3269 void ProSHADE_internal_symmetry::optimiseDGroupAngleFromAxesHeights ( std::vector < std::vector< proshade_double > >* ret, ProSHADE_internal_data::ProSHADE_data* dataObj, ProSHADE_settings* settings )
3270 {
3271  //================================================ Sanity check
3272  if ( ret->size() != 2 )
3273  {
3274  throw ProSHADE_exception ( "Attempted to optimise less than two axes for dihedral\n : group.", "ES00070", __FILE__, __LINE__, __func__, "The function for optimisation of dihedral angle of D\n : group was called on group with less than two axes. This\n : seems like a programming bug and should not happen - \n : contact author if you ever see this." );
3275  }
3276 
3277  //================================================ Set the angle to the correct dihedral group position - i.e. 90 deg
3278  proshade_double *crossProd, *perpVec, normFactor;
3279  size_t higherRFIndex = 0;
3280 
3281  //================================================ Find vector perperndicular to the plane given by the two axes
3282  crossProd = ProSHADE_internal_maths::computeCrossProduct ( &ret->at(0).at(1), &ret->at(0).at(2), &ret->at(0).at(3), &ret->at(1).at(1), &ret->at(1).at(2), &ret->at(1).at(3) );
3283 
3284  //================================================ Find a vector perpendicular to the plane between the new vector and the vector with higher rotation function value
3285  if ( ret->at(1).at(5) > ret->at(0).at(5) ) { higherRFIndex = 1; }
3286  perpVec = ProSHADE_internal_maths::computeCrossProduct ( &ret->at(higherRFIndex).at(1), &ret->at(higherRFIndex).at(2), &ret->at(higherRFIndex).at(3), &crossProd[0], &crossProd[1], &crossProd[2] );
3287 
3288  //================================================ Normalise the new vector
3289  normFactor = std::sqrt ( pow ( perpVec[0], 2.0 ) + pow ( perpVec[1], 2.0 ) + pow ( perpVec[2], 2.0 ) );
3290  perpVec[0] /= normFactor; perpVec[1] /= normFactor; perpVec[2] /= normFactor;
3291 
3292  //================================================ Set largest axis element to positive
3293  const FloatingPoint< proshade_double > lhs1 ( std::max ( std::abs ( perpVec[0] ), std::max( std::abs ( perpVec[1] ), std::abs ( perpVec[2] ) ) ) );
3294  const FloatingPoint< proshade_double > rhs1 ( std::abs ( perpVec[0] ));
3295  const FloatingPoint< proshade_double > rhs2 ( std::abs ( perpVec[1] ) );
3296  const FloatingPoint< proshade_double > rhs3 ( std::abs ( perpVec[2] ) );
3297  if ( ( lhs1.AlmostEquals ( rhs1 ) && ( perpVec[0] < 0.0 ) ) ||
3298  ( lhs1.AlmostEquals ( rhs2 ) && ( perpVec[1] < 0.0 ) ) ||
3299  ( lhs1.AlmostEquals ( rhs3 ) && ( perpVec[2] < 0.0 ) ) )
3300  {
3301  perpVec[0] *= -1.0;
3302  perpVec[1] *= -1.0;
3303  perpVec[2] *= -1.0;
3304  }
3305 
3306  //================================================ Which vector are we to over-write?
3307  if ( higherRFIndex == 0 ) { higherRFIndex = 1; }
3308  else { higherRFIndex = 0; }
3309 
3310  //================================================ Over-write the old vector with the better one
3311  ret->at(higherRFIndex).at(1) = perpVec[0]; ret->at(higherRFIndex).at(2) = perpVec[1]; ret->at(higherRFIndex).at(3) = perpVec[2];
3312 
3313  //================================================ Release memory
3314  delete[] perpVec;
3315  delete[] crossProd;
3316 
3317  //================================================ Convert input to pointers
3318  std::vector< proshade_double* > convVec;
3319  for ( size_t axIt = 0; axIt < 2; axIt++ )
3320  {
3321  //============================================ Allocate memory
3322  proshade_double* axVals = new proshade_double[7];
3323  ProSHADE_internal_misc::checkMemoryAllocation ( axVals, __FILE__, __LINE__, __func__ );
3324 
3325  //============================================ Copy values
3326  for ( size_t elIt = 0; elIt < 7; elIt++ )
3327  {
3328  axVals[elIt] = ret->at(axIt).at(elIt);
3329  }
3330 
3331  //============================================ Save
3332  convVec.push_back ( axVals );
3333  }
3334 
3335  //================================================ Run normal optimisation
3336  ProSHADE_internal_symmetry::findPredictedAxesHeights ( &convVec, dataObj, settings );
3337 
3338  //================================================ Convert back and release memory
3339  for ( size_t axIt = 0; axIt < 2; axIt++ )
3340  {
3341  //============================================ Copy values
3342  for ( size_t elIt = 0; elIt < 7; elIt++ )
3343  {
3344  ret->at(axIt).at(elIt) = convVec.at(axIt)[elIt];
3345  }
3346 
3347  //============================================ Release memory
3348  delete[] convVec.at(axIt);
3349  }
3350 
3351  //================================================ Done
3352  return ;
3353 
3354 }
3355 
3366 void ProSHADE_internal_symmetry::optimiseDGroupAngleFromAxesHeights ( std::vector < std::vector< proshade_double > >* allCs, std::vector< proshade_unsign > selection, ProSHADE_internal_data::ProSHADE_data* dataObj, ProSHADE_settings* settings )
3367 {
3368  //================================================ Initialise local variables
3369  std::vector < std::vector< proshade_double > > ortPair;
3370  std::vector< proshade_double > hlpVec;
3371 
3372  //================================================ Convert the indices and the list into a single vector containing only the axes to be optimised.
3373  hlpVec.push_back ( allCs->at(selection.at(0))[0] ); hlpVec.push_back ( allCs->at(selection.at(0))[1] ); hlpVec.push_back ( allCs->at(selection.at(0))[2] );
3374  hlpVec.push_back ( allCs->at(selection.at(0))[3] ); hlpVec.push_back ( allCs->at(selection.at(0))[4] ); hlpVec.push_back ( allCs->at(selection.at(0))[5] );
3375  hlpVec.push_back ( allCs->at(selection.at(0))[6] );
3376  ortPair.push_back ( hlpVec ); hlpVec.clear ( );
3377  hlpVec.push_back ( allCs->at(selection.at(1))[0] ); hlpVec.push_back ( allCs->at(selection.at(1))[1] ); hlpVec.push_back ( allCs->at(selection.at(1))[2] );
3378  hlpVec.push_back ( allCs->at(selection.at(1))[3] ); hlpVec.push_back ( allCs->at(selection.at(1))[4] ); hlpVec.push_back ( allCs->at(selection.at(1))[5] );
3379  hlpVec.push_back ( allCs->at(selection.at(1))[6] );
3380  ortPair.push_back ( hlpVec );
3381 
3382  //================================================ Run the optimisation proper
3383  optimiseDGroupAngleFromAxesHeights ( &ortPair, dataObj, settings );
3384 
3385  //================================================ Save the results back to the vector
3386  allCs->at(selection.at(0))[1] = ortPair.at(0).at(1); allCs->at(selection.at(0))[2] = ortPair.at(0).at(2); allCs->at(selection.at(0))[3] = ortPair.at(0).at(3);
3387  allCs->at(selection.at(0))[5] = ortPair.at(0).at(5); allCs->at(selection.at(0))[6] = ortPair.at(0).at(6);
3388 
3389  allCs->at(selection.at(1))[1] = ortPair.at(1).at(1); allCs->at(selection.at(1))[2] = ortPair.at(1).at(2); allCs->at(selection.at(1))[3] = ortPair.at(1).at(3);
3390  allCs->at(selection.at(1))[5] = ortPair.at(1).at(5); allCs->at(selection.at(1))[6] = ortPair.at(1).at(6);
3391 
3392  //================================================ Done
3393  return ;
3394 
3395 }
3396 
3409 proshade_double ProSHADE_internal_symmetry::findPredictedSingleAxisHeight ( proshade_double* axis, proshade_double fold, ProSHADE_internal_data::ProSHADE_data* dataObj, ProSHADE_settings* settings )
3410 {
3411  //================================================ Initialise variables
3412  proshade_double height = 0.0;
3413  proshade_double lat, lon;
3414  proshade_double latSamlUnit = ( 2.0 * M_PI ) / ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 );
3415  proshade_double lonSamlUnit = ( 1.0 * M_PI ) / ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 );
3416 
3417  //================================================ Make sure we have a clean start
3418  dataObj->sphereMappedRotFun.clear ( );
3419 
3420  //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
3421  for ( proshade_double angIt = 1.0; angIt < fold; angIt += 1.0 )
3422  {
3423  //============================================ Create the angle-axis sphere with correct radius (angle)
3424  dataObj->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( angIt * ( 2.0 * M_PI / fold ),
3425  M_PI / fold,
3426  dataObj->getMaxBand ( ) * 2,
3427  dataObj->getEMatDim ( ) * 2,
3428  angIt * ( 2.0 * M_PI / fold ),
3429  static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3430 
3431  //============================================ Interpolate rotation function onto the sphere
3432  dataObj->sphereMappedRotFun.at( static_cast < proshade_unsign > ( angIt - 1.0 ))->interpolateSphereValues ( dataObj->getInvSO3Coeffs ( ) );
3433  }
3434 
3435  //================================================ Convert XYZ to lat and lon INDICES
3436  lat = std::atan2( axis[1], axis[0] ) / latSamlUnit;
3437  lon = std::acos ( axis[2] ) / lonSamlUnit;
3438 
3439  if ( lat < 0.0 ) { lat += ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 ); }
3440  if ( lon < 0.0 ) { lon += ( static_cast< proshade_double > ( dataObj->maxShellBand ) * 2.0 ); }
3441 
3442  lat = std::round ( lat );
3443  lon = std::round ( lon );
3444 
3445  //================================================ Initialise the peak group
3447 
3448  //================================================ Construct a peak group with entry from each sphere with the axis as the peak
3449  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( dataObj->sphereMappedRotFun.size() ); sphIt++ )
3450  {
3451  if ( sphIt == 0 )
3452  {
3453  //======================================== If first sphere, create the peak group
3454  grp = new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( lat, lon, sphIt, dataObj->sphereMappedRotFun.at(sphIt)->getAngularDim() );
3455  }
3456  else
3457  {
3458  //======================================== Add to the existing object
3459  grp->checkIfPeakBelongs ( lat, lon, sphIt, settings->axisErrTolerance, settings->verbose, settings->messageShift, 0.1 );
3460  }
3461  }
3462 
3463  //================================================ Find the peak height
3464  std::vector < proshade_double* > detectedAxis;
3465  grp->findCyclicPointGroupsGivenFold ( dataObj->sphereMappedRotFun, &detectedAxis, settings->useBiCubicInterpolationOnPeaks, static_cast< proshade_unsign > ( fold ), settings->verbose, settings->messageShift );
3466 
3467  //================================================ Save it!
3468  if ( detectedAxis.size() > 0 ) { height = detectedAxis.at(0)[5]; }
3469  else { height = 0.0; }
3470 
3471  //================================================ Release memory
3472  for ( proshade_unsign i = 0; i < static_cast < proshade_unsign > ( detectedAxis.size() ); i++ ) { delete detectedAxis.at(i); }
3473  delete grp;
3474 
3475  //================================================ Done
3476  return ( height );
3477 
3478 }
3479 
3487 std::vector < std::pair< proshade_unsign, proshade_unsign > > findBestTetraDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
3488 {
3489  //================================================ Initialise variables
3490  std::vector < std::pair< proshade_unsign, proshade_unsign > > ret;
3491  std::vector< proshade_unsign > C3List;
3492  proshade_double dotProduct;
3493 
3494  //================================================ Find all C3 symmetries
3495  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 3.0 ); if ( lhs1.AlmostEquals ( rhs1 ) && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C3List, cSym ); } }
3496 
3497  //================================================ For each unique pair of C3 and C2
3498  for ( proshade_unsign c3 = 0; c3 < static_cast<proshade_unsign> ( C3List.size() ); c3++ )
3499  {
3500  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
3501  {
3502  //======================================== Compare only C2s to the C3List and only with decent average peak height
3503  const FloatingPoint< proshade_double > lhs1 ( CSymList->at(cSym)[0] ), rhs1 ( 2.0 );
3504  if ( !lhs1.AlmostEquals ( rhs1 ) ) { continue; }
3505  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
3506 
3507  //======================================== Check the angle between the C5 and C3 axes
3508  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C3List.at(c3))[1],
3509  &CSymList->at(C3List.at(c3))[2],
3510  &CSymList->at(C3List.at(c3))[3],
3511  &CSymList->at(cSym)[1],
3512  &CSymList->at(cSym)[2],
3513  &CSymList->at(cSym)[3] );
3514 
3515  //======================================== Is the angle approximately the dihedral angle?
3516  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
3517  {
3518  std::pair< proshade_unsign, proshade_unsign > hlp;
3519  hlp.first = C3List.at(c3);
3520  hlp.second = cSym;
3521  ret.emplace_back ( hlp );
3522  }
3523  }
3524  }
3525 
3526  //================================================ Done
3527  return ( ret );
3528 
3529 }
3530 
3553 void ProSHADE_internal_data::ProSHADE_data::getPredictedTetrahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList, proshade_signed*& cutIndices, fftw_complex*& fCoeffsCut, proshade_signed noBins, proshade_double**& bindata, proshade_signed*& binCounts, proshade_double*& fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim )
3554 {
3555  //================================================ Initialise variables
3556  std::vector< std::vector< proshade_double* > > hlpVec;
3557 
3558  //================================================ Report progress
3559  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting T symmetry prediction.", settings->messageShift );
3560 
3561  //================================================ Are the basic requirements for icosahedral symmetry met?
3563  {
3564  //============================================ Generate the rest of the axes
3565  ProSHADE_internal_symmetry::predictTetraAxes ( CSymList, &hlpVec, settings->axisErrTolerance, settings->minSymPeak );
3566 
3567  //============================================ For each possible axes pair
3568  for ( size_t pIt = 0; pIt < hlpVec.size(); pIt++ )
3569  {
3570  //======================================== Get heights for the predicted axes
3571  ProSHADE_internal_symmetry::findPredictedAxesHeights ( &(hlpVec.at(pIt)), this, settings );
3572  }
3573  }
3574 
3575  //================================================ Find the best T group of all detected
3576  this->tetrahedralSymmetries = ProSHADE_data::decidePolyFromList ( settings, &hlpVec, 7, CSymList, settings->axisErrTolerance, cutIndices, fCoeffsCut, noBins, bindata, binCounts, fscByBin, xDim, yDim, zDim );
3577 
3578  //================================================ Release hlpVec memory
3579  for ( size_t gIt = 0; gIt < hlpVec.size(); gIt++ ) { for ( size_t aIt = 0; aIt < hlpVec.at(gIt).size(); aIt++ ) { if ( hlpVec.at(gIt).at(aIt) != nullptr ) { delete[] hlpVec.at(gIt).at(aIt); } } }
3580 
3581  //================================================ Report progress
3582  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "T symmetry prediction complete.", settings->messageShift );
3583 
3584  //================================================ Done
3585  return ;
3586 
3587 }
3588 
3609 void ProSHADE_internal_symmetry::predictTetraAxes ( std::vector< proshade_double* >* CSymList, std::vector< std::vector< proshade_double* > >* ret, proshade_double axErr, proshade_double minPeakHeight )
3610 {
3611  //================================================ Find the best axis combination with dihedral angle and correct folds
3612  std::vector< std::pair< proshade_unsign, proshade_unsign > > initAxes = findBestTetraDihedralPair ( CSymList, minPeakHeight, axErr );
3613 
3614  //================================================ For each pair of possible axis combinations
3615  for ( size_t pIt = 0; pIt < initAxes.size(); pIt++ )
3616  {
3617  //============================================ Create the tetrahedronAxes object
3619 
3620  //============================================ Find rotation between the detected C3 and the model C3 axes.
3621  proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( tetAx->getValue ( 0, 1 ),
3622  tetAx->getValue ( 0, 2 ),
3623  tetAx->getValue ( 0, 3 ),
3624  CSymList->at(initAxes.at(pIt).first)[1],
3625  CSymList->at(initAxes.at(pIt).first)[2],
3626  CSymList->at(initAxes.at(pIt).first)[3] );
3627 
3628  //============================================ Rotate the model C2 to the correct orientation relative to the detected C3 axis.
3629  proshade_double* rotModelC2 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
3630  tetAx->getValue ( 4, 1 ),
3631  tetAx->getValue ( 4, 2 ),
3632  tetAx->getValue ( 4, 3 ) );
3633 
3634  //============================================ Find the angle betwen the rotated model C2 and the detected C2 axes along the detected C3 axis.
3635  proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
3636  proshade_double* rotMatHlp = new proshade_double[9];
3637  ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
3638  for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.002 )
3639  {
3640  //======================================== Compute rotation matrix for this angle value
3641  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.at(pIt).first)[1], CSymList->at(initAxes.at(pIt).first)[2], CSymList->at(initAxes.at(pIt).first)[3], ang );
3642 
3643  //======================================== Rotate the rotated C2 by the matrix
3644  proshade_double* rotRotModelC2 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
3645  rotModelC2[0],
3646  rotModelC2[1],
3647  rotModelC2[2] );
3648 
3649  //======================================== Find distance
3650  curAngDist = std::sqrt ( std::pow ( rotRotModelC2[0] - CSymList->at(initAxes.at(pIt).second)[1], 2.0 ) +
3651  std::pow ( rotRotModelC2[1] - CSymList->at(initAxes.at(pIt).second)[2], 2.0 ) +
3652  std::pow ( rotRotModelC2[2] - CSymList->at(initAxes.at(pIt).second)[3], 2.0 ) );
3653 
3654  //======================================== Save best angle
3655  if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
3656 
3657  //======================================== Release memory
3658  delete[] rotRotModelC2;
3659  }
3660 
3661  //============================================ Release memory
3662  delete[] rotMatHlp;
3663 
3664  //============================================ For the rotation matrix along the detected C5 axis with the same anlge as is between the rotated model C3 and the detected C3 axes.
3665  proshade_double* rotMat2 = new proshade_double[9];
3666  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
3667  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.at(pIt).first)[1], CSymList->at(initAxes.at(pIt).first)[2], CSymList->at(initAxes.at(pIt).first)[3], bestAng );
3668 
3669  //============================================ Combine the two rotation matrices into a single rotation matrix
3670  proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
3671 
3672  //================================================ For each model axis
3673  std::vector< proshade_double* > hlpAxes;
3674  for ( proshade_unsign iter = 0; iter < tetAx->getNoAxes( ); iter++ )
3675  {
3676  //============================================ Rotate the model axis to fit the detected orientation
3677  proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
3678  tetAx->getValue ( iter, 1 ),
3679  tetAx->getValue ( iter, 2 ),
3680  tetAx->getValue ( iter, 3 ) );
3681 
3682  //======================================== Create ProSHADE symmetry axis representation
3683  proshade_double* axis = new proshade_double[7];
3684  ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
3685 
3686  axis[0] = tetAx->getValue ( iter, 0 );
3687  axis[1] = rotAxis[0];
3688  axis[2] = rotAxis[1];
3689  axis[3] = rotAxis[2];
3690  axis[4] = ( 2.0 * M_PI ) / axis[0];
3691  axis[5] = 0.0;
3692  axis[6] = -std::numeric_limits < proshade_double >::infinity();
3693 
3694  //======================================== Save axis to ret
3696 
3697  //======================================== Release memory
3698  delete[] rotAxis;
3699  delete[] axis;
3700  }
3701 
3702  //============================================ Save to ret
3703  ret->emplace_back ( hlpAxes );
3704 
3705  //============================================ Release memory
3706  delete[] rotMat;
3707  delete[] rotMat2;
3708  delete[] rotMatFin;
3709  delete[] rotModelC2;
3710  delete tetAx;
3711  }
3712 
3713  //================================================ Done
3714  return ;
3715 
3716 }
3717 
3735 std::vector< proshade_unsign > ProSHADE_internal_symmetry::findReliableUnphasedSymmetries ( std::vector < proshade_double* >* allCs, std::vector < std::vector < proshade_double* > >* allDs, proshade_signed verbose, proshade_signed messageShift, proshade_double tolerance )
3736 {
3737  //================================================ Report progress
3738  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Deciding whether any axis(es) is/are reliable.", messageShift );
3739 
3740  //================================================ Initialise local variables
3741  std::vector< proshade_unsign > ret;
3742 
3743  //================================================ Find the thresholds
3744  proshade_double bestHistPeakStart = ProSHADE_internal_maths::findTopGroupSmooth ( allCs, 5, 0.01, 0.1, 9 );
3745  proshade_double bestFSCPeakStart = ProSHADE_internal_maths::findTopGroupSmooth ( allCs, 6, 0.01, 0.1, 9 );
3746  if ( bestHistPeakStart > 0.9 ) { bestHistPeakStart = 0.9; }
3747 
3748  //================================================ Are there any dihedral axes?
3749  proshade_double maxOrtSum = 0.0, curOrtSum = 0.0;
3750  proshade_signed maxOrtAx1 = 0, maxOrtAx2 = 0;
3751  for ( size_t relAx = 0; relAx < allDs->size(); relAx++ )
3752  {
3753  //============================================ Check if they are reliable
3754  if ( allDs->at(relAx).at(0)[5] < bestHistPeakStart ) { continue; }
3755  if ( allDs->at(relAx).at(1)[5] < bestHistPeakStart ) { continue; }
3756  if ( allDs->at(relAx).at(0)[6] < bestFSCPeakStart ) { continue; }
3757  if ( allDs->at(relAx).at(1)[6] < bestFSCPeakStart ) { continue; }
3758 
3759  //============================================ They are! Save if the sum is the best
3760  curOrtSum = allDs->at(relAx).at(0)[6] + allDs->at(relAx).at(1)[6];
3761  if ( curOrtSum > maxOrtSum )
3762  {
3763  maxOrtSum = curOrtSum;
3764  maxOrtAx1 = ProSHADE_internal_symmetry::addAxisUnlessSame ( static_cast< proshade_unsign > ( allDs->at(relAx).at(0)[0] ),
3765  allDs->at(relAx).at(0)[1],
3766  allDs->at(relAx).at(0)[2],
3767  allDs->at(relAx).at(0)[3],
3768  allDs->at(relAx).at(0)[5], allCs, tolerance );
3769  maxOrtAx2 = ProSHADE_internal_symmetry::addAxisUnlessSame ( static_cast< proshade_unsign > ( allDs->at(relAx).at(1)[0] ),
3770  allDs->at(relAx).at(1)[1],
3771  allDs->at(relAx).at(1)[2],
3772  allDs->at(relAx).at(1)[3],
3773  allDs->at(relAx).at(1)[5], allCs, tolerance );
3774  }
3775  }
3776 
3777  //================================================ If any orthogonal pair was found, return it
3778  if ( maxOrtAx2 != 0 )
3779  {
3780  //================================================ Report progress
3781  ProSHADE_internal_misc::addToUnsignVector ( &ret, static_cast< proshade_unsign > ( maxOrtAx1 ) );
3782  ProSHADE_internal_misc::addToUnsignVector ( &ret, static_cast< proshade_unsign > ( maxOrtAx2 ) );
3783  return ( ret );
3784  }
3785 
3786  //================================================ Well, no orthogonal axes. Is there at least one good axis?
3787  curOrtSum = 0.0; maxOrtSum = 0.0; maxOrtAx1 = -1;
3788  for ( size_t relAx = 0; relAx < allCs->size(); relAx++ )
3789  {
3790  //============================================ Consider only reliable axes in terms of RF
3791  if ( allCs->at(relAx)[5] < bestHistPeakStart ) { continue; }
3792 
3793  //============================================ Consider only reasonable axes in terms of FSC
3794  if ( allCs->at(relAx)[6] < bestFSCPeakStart ) { continue; }
3795 
3796  //============================================ Get the sum
3797  curOrtSum = allCs->at(relAx)[6];
3798 
3799  //============================================ If highest sum, save
3800  if ( curOrtSum > maxOrtSum )
3801  {
3802  maxOrtSum = curOrtSum;
3803  maxOrtAx1 = static_cast< proshade_signed > ( relAx );
3804  }
3805  }
3806 
3807  //================================================ If anything was found, save it
3808  if ( maxOrtAx1 >= 0 )
3809  {
3810  //============================================ Report progress
3811  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Found single reliable axis.", messageShift );
3812 
3813  ProSHADE_internal_misc::addToUnsignVector ( &ret, static_cast< proshade_unsign > ( maxOrtAx1 ) );
3814  }
3815  else
3816  {
3817  //================================================ Report progress
3818  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Found no reliable axis.", messageShift );
3819  }
3820 
3821  //================================================ Done
3822  return ( ret );
3823 
3824 }
3825 
3841 void ProSHADE_internal_symmetry::allocateCentreOfMapFourierTransforms ( proshade_unsign xDim, proshade_unsign yDim, proshade_unsign zDim, fftw_complex *&origMap, fftw_complex *&origCoeffs, fftw_complex *&rotMapComplex, fftw_complex *&rotCoeffs, fftw_complex *&trFunc, fftw_complex *&trFuncCoeffs, fftw_plan *planForwardFourier, fftw_plan *planForwardFourierRot, fftw_plan *planReverseFourierComb )
3842 {
3843  //================================================ Allocate the memory
3844  origMap = reinterpret_cast< fftw_complex* > ( fftw_malloc ( sizeof ( fftw_complex ) * xDim * yDim * zDim ) );
3845  origCoeffs = reinterpret_cast< fftw_complex* > ( fftw_malloc ( sizeof ( fftw_complex ) * xDim * yDim * zDim ) );
3846  rotMapComplex = reinterpret_cast< fftw_complex* > ( fftw_malloc ( sizeof ( fftw_complex ) * xDim * yDim * zDim ) );
3847  rotCoeffs = reinterpret_cast< fftw_complex* > ( fftw_malloc ( sizeof ( fftw_complex ) * xDim * yDim * zDim ) );
3848  trFunc = reinterpret_cast< fftw_complex* > ( fftw_malloc ( sizeof ( fftw_complex ) * xDim * yDim * zDim ) );
3849  trFuncCoeffs = reinterpret_cast< fftw_complex* > ( fftw_malloc ( sizeof ( fftw_complex ) * xDim * yDim * zDim ) );
3850 
3851  //================================================ Check the memory allocation
3852  ProSHADE_internal_misc::checkMemoryAllocation ( origMap, __FILE__, __LINE__, __func__ );
3853  ProSHADE_internal_misc::checkMemoryAllocation ( origCoeffs, __FILE__, __LINE__, __func__ );
3854  ProSHADE_internal_misc::checkMemoryAllocation ( rotMapComplex, __FILE__, __LINE__, __func__ );
3855  ProSHADE_internal_misc::checkMemoryAllocation ( rotCoeffs, __FILE__, __LINE__, __func__ );
3856  ProSHADE_internal_misc::checkMemoryAllocation ( trFunc, __FILE__, __LINE__, __func__ );
3857  ProSHADE_internal_misc::checkMemoryAllocation ( trFuncCoeffs, __FILE__, __LINE__, __func__ );
3858 
3859  //================================================
3860  *planForwardFourier = fftw_plan_dft_3d ( static_cast< int > ( xDim ), static_cast< int > ( yDim ), static_cast< int > ( zDim ), origMap, origCoeffs, FFTW_FORWARD, FFTW_ESTIMATE );
3861  *planForwardFourierRot = fftw_plan_dft_3d ( static_cast< int > ( xDim ), static_cast< int > ( yDim ), static_cast< int > ( zDim ), rotMapComplex, rotCoeffs, FFTW_FORWARD, FFTW_ESTIMATE );
3862  *planReverseFourierComb = fftw_plan_dft_3d ( static_cast< int > ( xDim ), static_cast< int > ( yDim ), static_cast< int > ( zDim ), trFuncCoeffs, trFunc, FFTW_BACKWARD, FFTW_ESTIMATE );
3863 
3864  //================================================ Done
3865  return ;
3866 
3867 }
3868 
3881 void ProSHADE_internal_symmetry::releaseCentreOfMapFourierTransforms ( fftw_complex *origMap, fftw_complex *origCoeffs, fftw_complex *rotMapComplex, fftw_complex *rotCoeffs, fftw_complex *trFunc, fftw_complex *trFuncCoeffs, fftw_plan planForwardFourier, fftw_plan planForwardFourierRot, fftw_plan planReverseFourierComb )
3882 {
3883  //================================================ Destroy the FFTW3 plans
3884  fftw_destroy_plan ( planReverseFourierComb );
3885  fftw_destroy_plan ( planForwardFourier );
3886  fftw_destroy_plan ( planForwardFourierRot );
3887 
3888  //================================================ Set pointers to NULL
3889  planReverseFourierComb = nullptr;
3890  planForwardFourier = nullptr;
3891  planForwardFourierRot = nullptr;
3892 
3893  //================================================ Release the memory
3894  fftw_free ( origMap );
3895  fftw_free ( origCoeffs );
3896  fftw_free ( rotMapComplex );
3897  fftw_free ( rotCoeffs );
3898  fftw_free ( trFunc );
3899  fftw_free ( trFuncCoeffs );
3900 
3901  //================================================ Done
3902  return ;
3903 
3904 }
3905 
3919 std::vector< proshade_double > ProSHADE_internal_symmetry::findTranslationBetweenRotatedAndOriginalMap ( ProSHADE_internal_data::ProSHADE_data* symStr, std::vector < proshade_double > symElem, fftw_complex *origCoeffs, fftw_complex* rotMapComplex, fftw_complex* rotCoeffs, fftw_plan planForwardFourierRot, fftw_complex* trFuncCoeffs, fftw_complex* trFunc, fftw_plan planReverseFourierComb )
3920 {
3921  //================================================ Initialise local variables
3922  proshade_double axX, axY, axZ, axAng, mapPeak, trsX, trsY, trsZ;
3923  std::vector< proshade_double > trsVec;
3924 
3925  //=============================================== Rotate the map by the rotation matrix
3926  proshade_double *rotMap;
3927  ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( &symElem, &axX, &axY, &axZ, &axAng );
3928  symStr->rotateMapRealSpace ( axX, axY, axZ, axAng, rotMap );
3929 
3930  //================================================ Convert rotated map to Fourier space
3931  for ( size_t it = 0; it < static_cast< size_t > ( symStr->getXDim() * symStr->getYDim() * symStr->getZDim() ); it++ ) { rotMapComplex[it][0] = rotMap[it]; rotMapComplex[it][1] = 0.0; }
3932  fftw_execute ( planForwardFourierRot );
3933 
3934  //================================================ Combine coeffs for translation function
3935  ProSHADE_internal_maths::combineFourierForTranslation ( origCoeffs, rotCoeffs, trFuncCoeffs, symStr->getXDim(), symStr->getYDim(), symStr->getZDim() );
3936 
3937  //================================================ Compute translation function
3938  fftw_execute ( planReverseFourierComb );
3939 
3940  //================================================ Find peak
3941  mapPeak = 0.0;
3942  ProSHADE_internal_maths::findHighestValueInMap ( trFunc, symStr->getXDim(), symStr->getYDim(), symStr->getZDim(), &trsX, &trsY, &trsZ, &mapPeak );
3943 
3944  //================================================ Convert to Angstroms
3945  trsX *= static_cast< proshade_double > ( symStr->getXDimSize() ) / static_cast< proshade_double > ( symStr->getXDim() );
3946  trsY *= static_cast< proshade_double > ( symStr->getYDimSize() ) / static_cast< proshade_double > ( symStr->getYDim() );
3947  trsZ *= static_cast< proshade_double > ( symStr->getZDimSize() ) / static_cast< proshade_double > ( symStr->getZDim() );
3948 
3949  //================================================ Do not translate over half
3950  if ( trsX > ( static_cast< proshade_double > ( symStr->getXDimSize() ) / 2.0 ) ) { trsX = trsX - static_cast< proshade_double > ( symStr->getXDimSize() ); }
3951  if ( trsY > ( static_cast< proshade_double > ( symStr->getYDimSize() ) / 2.0 ) ) { trsY = trsY - static_cast< proshade_double > ( symStr->getYDimSize() ); }
3952  if ( trsZ > ( static_cast< proshade_double > ( symStr->getZDimSize() ) / 2.0 ) ) { trsZ = trsZ - static_cast< proshade_double > ( symStr->getZDimSize() ); }
3953 
3954  //================================================ Save line point
3958 
3959  //================================================ Release memory
3960  delete[] rotMap;
3961 
3962  //================================================ Done
3963  return ( trsVec );
3964 
3965 }
3966 
3985 std::vector< proshade_double > ProSHADE_internal_symmetry::findPointFromTranslations ( ProSHADE_internal_data::ProSHADE_data* symStr, std::vector < std::vector < proshade_double > > symElems, fftw_complex *origCoeffs, fftw_complex* rotMapComplex, fftw_complex* rotCoeffs, fftw_plan planForwardFourierRot, fftw_complex* trFuncCoeffs, fftw_complex* trFunc, fftw_plan planReverseFourierComb )
3986 {
3987  //================================================ Initialise local variables
3988  std::vector< proshade_double > pointOnLine ( 3, 0.0 );
3989  std::vector< proshade_double > identityMat ( 9, 0.0 ); identityMat.at(0) = 1.0; identityMat.at(4) = 1.0; identityMat.at(8) = 1.0;
3990 
3991  //================================================ For each symmetry element in this cyclic group
3992  for ( size_t gEl = 0; gEl < symElems.size(); gEl++ )
3993  {
3994  //============================================ Ignore identity element
3995  if ( ProSHADE_internal_maths::rotationMatrixSimilarity ( &symElems.at(gEl), &identityMat, 0.01 ) ) { continue; }
3996 
3997  //============================================ Find translation difference between rotated and original map
3998  std::vector< proshade_double > trsCenHlp = ProSHADE_internal_symmetry::findTranslationBetweenRotatedAndOriginalMap ( symStr,
3999  symElems.at(gEl),
4000  origCoeffs, rotMapComplex,
4001  rotCoeffs, planForwardFourierRot,
4002  trFuncCoeffs, trFunc,
4003  planReverseFourierComb );
4004 
4005  //============================================ Sum translations over the whole axis
4006  pointOnLine.at(0) += trsCenHlp.at(0);
4007  pointOnLine.at(1) += trsCenHlp.at(1);
4008  pointOnLine.at(2) += trsCenHlp.at(2);
4009  }
4010 
4011  //================================================ Average over all symmetry elements (including the identity one)
4012  pointOnLine.at(0) /= static_cast< proshade_double > ( symElems.size() );
4013  pointOnLine.at(1) /= static_cast< proshade_double > ( symElems.size() );
4014  pointOnLine.at(2) /= static_cast< proshade_double > ( symElems.size() );
4015 
4016  //================================================ Done
4017  return ( pointOnLine );
4018 
4019 }
ProSHADE_internal_misc::addToDblPtrVectorVector
void addToDblPtrVectorVector(std::vector< std::vector< proshade_double * > > *vecToAddTo, std::vector< proshade_double * > elementToAdd)
Adds the element to the vector of vectors of double pointers.
Definition: ProSHADE_misc.cpp:255
ProSHADE_settings::noIQRsFromMedianNaivePeak
proshade_double noIQRsFromMedianNaivePeak
When doing peak searching, how many IQRs from the median the threshold for peak height should be (in ...
Definition: ProSHADE_settings.hpp:124
ProSHADE_internal_distances::normaliseEMatrices
void normaliseEMatrices(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function normalises the E matrices.
Definition: ProSHADE_distances.cpp:604
sortProSHADESymmetryByPeak
bool sortProSHADESymmetryByPeak(proshade_double *a, proshade_double *b)
This function allows using std::sort to sort vectors of ProSHADE symmetry format..
Definition: ProSHADE_symmetry.cpp:2916
ProSHADE_internal_symmetry::findTetra3C2s
void findTetra3C2s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 3 C2 symmetries with correct angles requir...
Definition: ProSHADE_symmetry.cpp:998
ProSHADE_internal_symmetry::detectOctahedralSymmetry
bool detectOctahedralSymmetry(std::vector< proshade_double * > *CSymList, proshade_double axErr, proshade_double minPeakHeight)
This function takes the list of C symmetries and decides whether basic requirements for octahhedral s...
Definition: ProSHADE_symmetry.cpp:1127
ProSHADE_internal_maths::findVectorFromThreeVAndThreeD
std::vector< proshade_double > findVectorFromThreeVAndThreeD(proshade_double x1, proshade_double y1, proshade_double z1, proshade_double x2, proshade_double y2, proshade_double z2, proshade_double x3, proshade_double y3, proshade_double z3, proshade_double dot1, proshade_double dot2, proshade_double dot3)
Function for finding a vector which would have a given three dot products to three other vectors.
Definition: ProSHADE_maths.cpp:2479
ProSHADE_internal_data::ProSHADE_data::getZDimSize
proshade_single getZDimSize(void)
This function allows access to the map size in angstroms along the Z axis.
Definition: ProSHADE_data.cpp:4064
ProSHADE_internal_maths::getEulerZYZFromSOFTPosition
void getEulerZYZFromSOFTPosition(proshade_signed band, proshade_signed x, proshade_signed y, proshade_signed z, proshade_double *eulerAlpha, proshade_double *eulerBeta, proshade_double *eulerGamma)
Function to find Euler angles (ZYZ convention) from index position in the inverse SOFT map.
Definition: ProSHADE_maths.cpp:966
ProSHADE_internal_precomputedVals::octahedronAxes
Definition: ProSHADE_precomputedValues.hpp:55
ProSHADE_internal_symmetry::findMissingAxes
bool findMissingAxes(std::vector< std::vector< proshade_unsign > > *possibilities, std::vector< proshade_double * > *CSymList, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_double angle, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_double minPeakHeight)
This function tries to find an axis which would complete a particular group of axes for polyhedral sy...
Definition: ProSHADE_symmetry.cpp:598
determinePeakThreshold
proshade_double determinePeakThreshold(std::vector< proshade_double > inArr, proshade_double noIQRsFromMedian, proshade_double startMinVal)
This function takes a vector of values and determines the threshold for removing noise from it.
Definition: ProSHADE_symmetry.cpp:73
ProSHADE_internal_misc::addToDblPtrVector
void addToDblPtrVector(std::vector< proshade_double * > *vecToAddTo, proshade_double *elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:143
ProSHADE_internal_maths::isPrime
bool isPrime(proshade_unsign toCheck)
This function check is the supplied number is prime or not.
Definition: ProSHADE_maths.cpp:3259
ProSHADE_settings::maxSymmetryFold
proshade_unsign maxSymmetryFold
The highest symmetry fold to search for.
Definition: ProSHADE_settings.hpp:138
ProSHADE_internal_data::ProSHADE_data::computeRotationFunction
void computeRotationFunction(ProSHADE_settings *settings)
This function computes the self-rotation function for this structure.
Definition: ProSHADE_symmetry.cpp:41
ProSHADE_internal_symmetry::sortArrVecHlp
bool sortArrVecHlp(const proshade_double *a, const proshade_double *b)
This function compares two arrays of two based on the first number.
Definition: ProSHADE_symmetry.cpp:658
findBestOctaDihedralPair
std::vector< std::pair< proshade_unsign, proshade_unsign > > findBestOctaDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds the best pair of axes conforming to the octahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:2150
ProSHADE_internal_symmetry::findIcos6C5s
void findIcos6C5s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the six C5 symmetries with given angles requir...
Definition: ProSHADE_symmetry.cpp:1912
ProSHADE_internal_symmetry::testGroupAgainstGroup
bool testGroupAgainstGroup(std::vector< proshade_double * > *CSymList, std::vector< proshade_unsign > *grp1, std::vector< proshade_double * > *RetList, std::vector< proshade_unsign > *grp2, proshade_double angle, proshade_double axErr)
This function compares two groups of axes for a single pair having the required angle.
Definition: ProSHADE_symmetry.cpp:1083
ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList
void getPredictedIcosahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList, proshade_signed *&cutIndices, fftw_complex *&fCoeffsCut, proshade_signed noBins, proshade_double **&bindata, proshade_signed *&binCounts, proshade_double *&fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim)
This function predicts a list of all I symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:1749
ProSHADE_internal_data::ProSHADE_data::decidePolyFromList
std::vector< proshade_double * > decidePolyFromList(ProSHADE_settings *settings, std::vector< std::vector< proshade_double * > > *polyList, size_t fullGroupSize, std::vector< proshade_double * > *CSyms, proshade_double tolerance, proshade_signed *&cutIndices, fftw_complex *&fCoeffsCut, proshade_signed noBins, proshade_double **&bindata, proshade_signed *&binCounts, proshade_double *&fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim)
This function takes a list of predicted polyheral groups and decides which is most likely using the F...
Definition: ProSHADE_symmetry.cpp:1659
ProSHADE_internal_maths::computeCrossProduct
proshade_double * computeCrossProduct(proshade_double *x1, proshade_double *y1, proshade_double *z1, proshade_double *x2, proshade_double *y2, proshade_double *z2)
Simple 3D vector cross product computation.
Definition: ProSHADE_maths.cpp:1817
ProSHADE_internal_maths::computeFSC
proshade_double computeFSC(fftw_complex *fCoeffs1, fftw_complex *fCoeffs2, proshade_signed xInds, proshade_signed yInds, proshade_signed zInds, proshade_signed noBins, proshade_signed *binIndexing, proshade_double **&binData, proshade_signed *&binCounts, proshade_double *&fscByBin, bool averageByBinSize=false)
This function computes the FSC.
Definition: ProSHADE_maths.cpp:3693
ProSHADE_internal_symmetry::addAxisUnlessSame
proshade_signed addAxisUnlessSame(proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double axHeight, proshade_double averageFSC, std::vector< proshade_double * > *prosp, proshade_double axErr)
This function simply creates a new axis from information in aruments and tests if no such axis alread...
Definition: ProSHADE_symmetry.cpp:1511
ProSHADE_internal_spheres::ProSHADE_rotFun_sphere
This class contains all inputed data for the rotation function angle-axis converted spheres.
Definition: ProSHADE_maths.hpp:58
ProSHADE_internal_precomputedVals::icosahedronAxes::getValue
proshade_double getValue(proshade_unsign axis, proshade_unsign element)
Accessor for the icosahedronAxesVals variable.
Definition: ProSHADE_precomputedValues.cpp:223
ProSHADE_exception
This class is the representation of ProSHADE exception.
Definition: ProSHADE_exceptions.hpp:37
ProSHADE_internal_symmetry::findPredictedAxesHeights
void findPredictedAxesHeights(std::vector< proshade_double * > *ret, ProSHADE_internal_data::ProSHADE_data *dataObj, ProSHADE_settings *settings)
This function finds the rotation function value for all axes supplied in the ret parameter.
Definition: ProSHADE_symmetry.cpp:3063
ProSHADE_internal_symmetry::findPointFromTranslations
std::vector< proshade_double > findPointFromTranslations(ProSHADE_internal_data::ProSHADE_data *symStr, std::vector< std::vector< proshade_double > > symElems, fftw_complex *origCoeffs, fftw_complex *rotMapComplex, fftw_complex *rotCoeffs, fftw_plan planForwardFourierRot, fftw_complex *trFuncCoeffs, fftw_complex *trFunc, fftw_plan planReverseFourierComb)
This function computes the average of optimal translations for a cyclic point group.
Definition: ProSHADE_symmetry.cpp:3985
ProSHADE_internal_maths::getRotationMatrixFromEulerZYZAngles
void getRotationMatrixFromEulerZYZAngles(proshade_double eulerAlpha, proshade_double eulerBeta, proshade_double eulerGamma, proshade_double *matrix)
Function to find the rotation matrix from Euler angles (ZYZ convention).
Definition: ProSHADE_maths.cpp:1019
findBestTetraDihedralPair
std::vector< std::pair< proshade_unsign, proshade_unsign > > findBestTetraDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds the best pair of axes conforming to the tetrahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:3487
ProSHADE_internal_symmetry::allocateCentreOfMapFourierTransforms
void allocateCentreOfMapFourierTransforms(proshade_unsign xDim, proshade_unsign yDim, proshade_unsign zDim, fftw_complex *&origMap, fftw_complex *&origCoeffs, fftw_complex *&rotMapComplex, fftw_complex *&rotCoeffs, fftw_complex *&trFunc, fftw_complex *&trFuncCoeffs, fftw_plan *planForwardFourier, fftw_plan *planForwardFourierRot, fftw_plan *planReverseFourierComb)
This function allocates the required memory for the Fourier transforms required to find the centre of...
Definition: ProSHADE_symmetry.cpp:3841
ProSHADE_internal_data::ProSHADE_data::getXDimSize
proshade_single getXDimSize(void)
This function allows access to the map size in angstroms along the X axis.
Definition: ProSHADE_data.cpp:4044
ProSHADE_internal_misc::addToUnsignVectorVector
void addToUnsignVectorVector(std::vector< std::vector< proshade_unsign > > *vecToAddTo, std::vector< proshade_unsign > elementToAdd)
Adds the element to the vector of vectors.
Definition: ProSHADE_misc.cpp:211
ProSHADE_internal_data::ProSHADE_data
This class contains all inputed and derived data for a single structure.
Definition: ProSHADE_data.hpp:49
ProSHADE_internal_data::ProSHADE_data::getYDimSize
proshade_single getYDimSize(void)
This function allows access to the map size in angstroms along the Y axis.
Definition: ProSHADE_data.cpp:4054
ProSHADE_internal_symmetry::missingAxisHeight
proshade_double missingAxisHeight(proshade_double xVal, proshade_double yVal, proshade_double zVal, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign fold, proshade_double axErr)
This function searches for the highest peaks average that would produce the required axis and fold.
Definition: ProSHADE_symmetry.cpp:679
ProSHADE_settings::requestedResolution
proshade_single requestedResolution
The resolution to which the calculations are to be done.
Definition: ProSHADE_settings.hpp:50
ProSHADE_internal_symmetry::searchMissingSymmetrySpace
void searchMissingSymmetrySpace(ProSHADE_internal_data::ProSHADE_data *dataObj, std::vector< proshade_double * > *CSymList, std::vector< proshade_unsign > *grp, std::vector< proshade_double * > *hlpVec, proshade_double axErr, proshade_double angle, proshade_unsign fold, proshade_double minPeakHeight)
This function tests feasible axes against the missing axis criteria, returning a set of matching axes...
Definition: ProSHADE_symmetry.cpp:890
ProSHADE_settings::minSymPeak
proshade_double minSymPeak
Minimum average peak for symmetry axis to be considered as "real".
Definition: ProSHADE_settings.hpp:134
ProSHADE_internal_symmetry::findMissingAxesDual
bool findMissingAxesDual(std::vector< proshade_unsign > *possibilities, std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, std::vector< proshade_unsign > *retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function tries to find a particular symmetry axes which would complete a group of symmetries wit...
Definition: ProSHADE_symmetry.cpp:1416
ProSHADE_internal_data::ProSHADE_data::maxShellBand
proshade_unsign maxShellBand
The maximum band for any shell of the object.
Definition: ProSHADE_data.hpp:123
ProSHADE_symmetry.hpp
This header file declares all the functions required for symmetry detection and construction.
ProSHADE_internal_symmetry::optimiseDGroupAngleFromAxesHeights
void optimiseDGroupAngleFromAxesHeights(std::vector< std::vector< proshade_double > > *ret, ProSHADE_internal_data::ProSHADE_data *dataObj, ProSHADE_settings *settings)
This function takes two axes with almost dihedral angle and optimises their relative positions as wel...
Definition: ProSHADE_symmetry.cpp:3269
ProSHADE_internal_messages::printWarningMessage
void printWarningMessage(proshade_signed verbose, std::string message, std::string warnCode)
General stderr message printing (used for warnings).
Definition: ProSHADE_messages.cpp:102
ProSHADE_settings::verbose
proshade_signed verbose
Should the software report on the progress, or just be quiet? Value between -1 (nothing) and 4 (loud)
Definition: ProSHADE_settings.hpp:152
ProSHADE_internal_symmetry::isSymmetrySame
bool isSymmetrySame(std::vector< proshade_double * > *ret, proshade_double *sym, proshade_double simThres, proshade_signed *matchedPos)
This function checks if a very similar symmetry is not already saved.
Definition: ProSHADE_symmetry.cpp:215
ProSHADE_internal_maths::vectorOrientationSimilarity
bool vectorOrientationSimilarity(proshade_double a1, proshade_double a2, proshade_double a3, proshade_double b1, proshade_double b2, proshade_double b3, proshade_double tolerance=0.1)
This function compares two vectors using cosine distance and decides if they are similar using tolera...
Definition: ProSHADE_maths.cpp:2680
ProSHADE_settings::messageShift
proshade_signed messageShift
This value allows shifting the messages to create more readable log for sub-processes.
Definition: ProSHADE_settings.hpp:153
ProSHADE_internal_misc::addToDoubleVector
void addToDoubleVector(std::vector< proshade_double > *vecToAddTo, proshade_double elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:77
ProSHADE_internal_precomputedVals::octahedronAxes::getValue
proshade_double getValue(proshade_unsign axis, proshade_unsign element)
Accessor for the octahedronAxesVals variable.
Definition: ProSHADE_precomputedValues.cpp:136
ProSHADE_internal_data::ProSHADE_data::getXDim
proshade_unsign getXDim(void)
This function allows access to the map size in indices along the X axis.
Definition: ProSHADE_data.cpp:4074
ProSHADE_internal_precomputedVals::tetrahedronAxes::getValue
proshade_double getValue(proshade_unsign axis, proshade_unsign element)
Accessor for the tetrahedronAxesVals variable.
Definition: ProSHADE_precomputedValues.cpp:68
ProSHADE_settings::useBiCubicInterpolationOnPeaks
bool useBiCubicInterpolationOnPeaks
This variable switch decides whether best symmetry is detected from peak indices, or whether bicubic ...
Definition: ProSHADE_settings.hpp:137
ProSHADE_internal_symmetry::saveMissingAxisNewOnly
void saveMissingAxisNewOnly(std::vector< proshade_double * > *axVec, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double height, proshade_unsign fold, proshade_double axErr)
This function saves the recovered information about missing axis into a full symmetry,...
Definition: ProSHADE_symmetry.cpp:821
ProSHADE_settings::peakThresholdMin
proshade_double peakThresholdMin
The threshold for peak height above which axes are considered possible.
Definition: ProSHADE_settings.hpp:141
ProSHADE_internal_distances::generateSO3CoeffsFromEMatrices
void generateSO3CoeffsFromEMatrices(ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function converts the E matrices to SO(3) coefficients.
Definition: ProSHADE_distances.cpp:732
ProSHADE_internal_symmetry::findIcos10C3s
void findIcos10C3s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the ten C3 symmetries with correct angles requ...
Definition: ProSHADE_symmetry.cpp:2337
ProSHADE_internal_maths::computeDotProduct
proshade_double computeDotProduct(proshade_double *x1, proshade_double *y1, proshade_double *z1, proshade_double *x2, proshade_double *y2, proshade_double *z2)
Simple 3D vector dot product computation.
Definition: ProSHADE_maths.cpp:1785
ProSHADE_internal_misc::addToSignedVector
void addToSignedVector(std::vector< proshade_signed > *vecToAddTo, proshade_signed elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:121
ProSHADE_internal_data::ProSHADE_data::getPredictedTetrahedralSymmetriesList
void getPredictedTetrahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList, proshade_signed *&cutIndices, fftw_complex *&fCoeffsCut, proshade_signed noBins, proshade_double **&bindata, proshade_signed *&binCounts, proshade_double *&fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim)
This function predicts a list of all T symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:3553
ProSHADE_internal_misc::deepCopyAxisToDblPtrVector
void deepCopyAxisToDblPtrVector(std::vector< proshade_double * > *dblPtrVec, proshade_double *axis)
Does a deep copy of a double array to a vector of double arrays.
Definition: ProSHADE_misc.cpp:433
ProSHADE_internal_precomputedVals::tetrahedronAxes::getNoAxes
proshade_unsign getNoAxes()
Accessor for the tetrahedronAxesVals variable number of axes.
Definition: ProSHADE_precomputedValues.cpp:79
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup::checkIfPeakBelongs
bool checkIfPeakBelongs(proshade_double lat, proshade_double lon, proshade_unsign sphPos, proshade_double cosTol, proshade_signed verbose, proshade_signed messageShift, proshade_double allowedAngle)
This function takes a new prospective peak and tests if it belongs to this peak group or not.
Definition: ProSHADE_spheres.cpp:1180
ProSHADE_internal_maths::getRotationMatrixFromAngleAxis
void getRotationMatrixFromAngleAxis(proshade_double *rotMat, proshade_double x, proshade_double y, proshade_double z, proshade_double ang)
This function converts the axis-angle representation to the rotation matrix representation.
Definition: ProSHADE_maths.cpp:1458
ProSHADE_internal_data::ProSHADE_data::getMaxBand
proshade_unsign getMaxBand(void)
This function returns the maximum band value for the object.
Definition: ProSHADE_data.cpp:3748
ProSHADE_internal_symmetry::detectIcosahedralSymmetry
bool detectIcosahedralSymmetry(std::vector< proshade_double * > *CSymList, proshade_double axErr, proshade_double minPeakHeight)
This function takes the list of C symmetries and decides whether basic requirements for isosahedral s...
Definition: ProSHADE_symmetry.cpp:1854
ProSHADE_internal_data::ProSHADE_data::getYDim
proshade_unsign getYDim(void)
This function allows access to the map size in indices along the Y axis.
Definition: ProSHADE_data.cpp:4084
ProSHADE_internal_maths::findHighestValueInMap
void findHighestValueInMap(fftw_complex *resIn, proshade_unsign xD, proshade_unsign yD, proshade_unsign zD, proshade_double *trsX, proshade_double *trsY, proshade_double *trsZ, proshade_double *mapPeak)
This function simply finds the highest value in fftw_complex map and returns its position and value.
Definition: ProSHADE_maths.cpp:4289
ProSHADE_settings
This class stores all the settings and is passed to the executive classes instead of a multitude of p...
Definition: ProSHADE_settings.hpp:37
ProSHADE_internal_maths::getAxisAngleFromRotationMatrix
void getAxisAngleFromRotationMatrix(proshade_double *rotMat, proshade_double *x, proshade_double *y, proshade_double *z, proshade_double *ang, proshade_signed verbose=1)
This function converts rotation matrix to the axis-angle representation.
Definition: ProSHADE_maths.cpp:1083
ProSHADE_internal_maths::build3x3MatrixFromXYZRotations
proshade_double * build3x3MatrixFromXYZRotations(proshade_double xRot, proshade_double yRot, proshade_double zRot)
Function for building a 3x3 rotation matrix from the x, y and z rotations in degrees.
Definition: ProSHADE_maths.cpp:2057
ProSHADE_settings::fscThreshold
proshade_double fscThreshold
The threshold for FSC value under which the axis is considered to be likely noise.
Definition: ProSHADE_settings.hpp:140
ProSHADE_internal_symmetry::findOcta4C3s
void findOcta4C3s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the four C3 symmetries with correct angles req...
Definition: ProSHADE_symmetry.cpp:1252
ProSHADE_settings::supportedSymmetryFold
proshade_unsign supportedSymmetryFold
Maximum supported fold by the map.
Definition: ProSHADE_settings.hpp:139
ProSHADE_internal_distances::computeEMatrices
void computeEMatrices(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function computes the complete E matrices and their weights between any two objects.
Definition: ProSHADE_distances.cpp:540
ProSHADE_internal_maths::combineFourierForTranslation
void combineFourierForTranslation(fftw_complex *tmpOut1, fftw_complex *tmpOut2, fftw_complex *&resOut, proshade_unsign xD, proshade_unsign yD, proshade_unsign zD)
This function combines Fourier coefficients of two structures in a way, so that inverse Fourier of th...
Definition: ProSHADE_maths.cpp:4242
ProSHADE_internal_symmetry::findReliableUnphasedSymmetries
std::vector< proshade_unsign > findReliableUnphasedSymmetries(std::vector< proshade_double * > *allCs, std::vector< std::vector< proshade_double * > > *allDs, proshade_signed verbose, proshade_signed messageShift, proshade_double tolerance)
This function checks the list of detected axes (presumably from phaseless symmetry detection) and ret...
Definition: ProSHADE_symmetry.cpp:3735
ProSHADE_internal_data::ProSHADE_data::getZDim
proshade_unsign getZDim(void)
This function allows access to the map size in indices along the Z axis.
Definition: ProSHADE_data.cpp:4094
ProSHADE_internal_symmetry::findTranslationBetweenRotatedAndOriginalMap
std::vector< proshade_double > findTranslationBetweenRotatedAndOriginalMap(ProSHADE_internal_data::ProSHADE_data *symStr, std::vector< proshade_double > symElem, fftw_complex *origCoeffs, fftw_complex *rotMapComplex, fftw_complex *rotCoeffs, fftw_plan planForwardFourierRot, fftw_complex *trFuncCoeffs, fftw_complex *trFunc, fftw_plan planReverseFourierComb)
This function takes a single rotation matrix and procceds to compute the optimal translation between ...
Definition: ProSHADE_symmetry.cpp:3919
ProSHADE_internal_data::ProSHADE_data::convertRotationFunction
void convertRotationFunction(ProSHADE_settings *settings)
This function converts the self-rotation function of this structure to angle-axis representation.
Definition: ProSHADE_symmetry.cpp:135
ProSHADE_internal_maths::primeFactorsDecomp
std::vector< proshade_signed > primeFactorsDecomp(proshade_signed number)
Function to find prime factors of an integer.
Definition: ProSHADE_maths.cpp:1719
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup
This class contains peak groups detected in the rotation function mapped spheres.
Definition: ProSHADE_spheres.hpp:129
ProSHADE_internal_maths::isAxisUnique
bool isAxisUnique(std::vector< proshade_double * > *CSymList, proshade_double *axis, proshade_double tolerance=0.1, bool improve=false)
This function checks if new axis is unique, or already detected.
Definition: ProSHADE_maths.cpp:3064
ProSHADE_internal_symmetry::releaseCentreOfMapFourierTransforms
void releaseCentreOfMapFourierTransforms(fftw_complex *origMap, fftw_complex *origCoeffs, fftw_complex *rotMapComplex, fftw_complex *rotCoeffs, fftw_complex *trFunc, fftw_complex *trFuncCoeffs, fftw_plan planForwardFourier, fftw_plan planForwardFourierRot, fftw_plan planReverseFourierComb)
This function releases the allocated memory for the Fourier transforms used to find the centre of the...
Definition: ProSHADE_symmetry.cpp:3881
ProSHADE_internal_symmetry::findMissingAxesTriple
bool findMissingAxesTriple(std::vector< proshade_unsign > *possibilities, std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, std::vector< proshade_unsign > *retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function tries to find a particular symmetry axis which would complete a group of symmetries wit...
Definition: ProSHADE_symmetry.cpp:2493
ProSHADE_internal_maths::findRotMatMatchingVectors
proshade_double * findRotMatMatchingVectors(proshade_double x1, proshade_double y1, proshade_double z1, proshade_double x2, proshade_double y2, proshade_double z2)
Computation of rotation matrix rotating one vector onto the other.
Definition: ProSHADE_maths.cpp:2119
ProSHADE_internal_distances::computeInverseSOFTTransform
void computeInverseSOFTTransform(ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function computes the inverse SO(3) transform.
Definition: ProSHADE_distances.cpp:885
findBestIcosDihedralPair
std::vector< std::pair< proshade_unsign, proshade_unsign > > findBestIcosDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds all the pairs of axes conforming to the icosahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:1971
ProSHADE_settings::peakNeighbours
proshade_unsign peakNeighbours
Number of points in any direction that have to be lower than the considered index in order to conside...
Definition: ProSHADE_settings.hpp:123
ProSHADE_internal_data::ProSHADE_data::rotateMapRealSpace
std::vector< proshade_double > rotateMapRealSpace(proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double axAng, proshade_double *&map)
This function rotates a map based on the given angle-axis rotation.
Definition: ProSHADE_overlay.cpp:632
ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication
proshade_double * compute3x3MatrixVectorMultiplication(proshade_double *mat, proshade_double x, proshade_double y, proshade_double z)
Function for computing a 3x3 matrix to 3x1 vector multiplication.
Definition: ProSHADE_maths.cpp:1895
ProSHADE_internal_symmetry::predictOctaAxes
void predictOctaAxes(std::vector< proshade_double * > *CSymList, std::vector< std::vector< proshade_double * > > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all octahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:2214
ProSHADE_internal_precomputedVals::tetrahedronAxes
Definition: ProSHADE_precomputedValues.hpp:69
ProSHADE_internal_symmetry::findOcta6C2s
void findOcta6C2s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the six C2 symmetries with correct angles requ...
Definition: ProSHADE_symmetry.cpp:1335
ProSHADE_internal_maths::rotationMatrixSimilarity
bool rotationMatrixSimilarity(std::vector< proshade_double > *mat1, std::vector< proshade_double > *mat2, proshade_double tolerance=0.1)
This function compares the distance between two rotation matrices and decides if they are similar usi...
Definition: ProSHADE_maths.cpp:2576
ProSHADE_internal_symmetry::testGroupAgainstSymmetry
bool testGroupAgainstSymmetry(std::vector< proshade_double * > *CSymList, std::vector< proshade_unsign > *grp, proshade_double *sym, proshade_double axErr, proshade_double angle, bool improve, proshade_unsign pos=0)
This function tests whether a symmetry has particular angle to all members of a group.
Definition: ProSHADE_symmetry.cpp:531
ProSHADE_internal_precomputedVals::icosahedronAxes
Definition: ProSHADE_precomputedValues.hpp:41
ProSHADE_internal_misc::checkMemoryAllocation
void checkMemoryAllocation(chVar checkVar, std::string fileP, unsigned int lineP, std::string funcP, std::string infoP="This error may occurs when ProSHADE requests memory to be\n : allocated to it and this operation fails. This could\n : happen when not enough memory is available, either due to\n : other processes using a lot of memory, or when the machine\n : does not have sufficient memory available. Re-run to see\n : if this problem persists.")
Checks if memory was allocated properly.
Definition: ProSHADE_misc.hpp:73
ProSHADE_internal_symmetry::findOcta3C4s
void findOcta3C4s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 3 C4 symmetries with perpendicular angles ...
Definition: ProSHADE_symmetry.cpp:1185
ProSHADE_internal_symmetry::predictTetraAxes
void predictTetraAxes(std::vector< proshade_double * > *CSymList, std::vector< std::vector< proshade_double * > > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all tetrahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:3609
ProSHADE_internal_precomputedVals::octahedronAxes::getNoAxes
proshade_unsign getNoAxes()
Accessor for the octahedronAxesVals variable number of axes.
Definition: ProSHADE_precomputedValues.cpp:147
ProSHADE_internal_symmetry::findTetra4C3s
void findTetra4C3s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 4 C3 symmetries with correct angles requir...
Definition: ProSHADE_symmetry.cpp:461
ProSHADE_internal_data::ProSHADE_data::getInvSO3Coeffs
proshade_complex * getInvSO3Coeffs(void)
This function allows access to the inverse SO(3) coefficients array.
Definition: ProSHADE_data.cpp:3992
ProSHADE_internal_data::ProSHADE_data::getEMatDim
proshade_unsign getEMatDim(void)
This function allows access to the maximum band for the E matrix.
Definition: ProSHADE_data.cpp:4014
ProSHADE_internal_symmetry::findPredictedSingleAxisHeight
proshade_double findPredictedSingleAxisHeight(proshade_double *axis, proshade_double fold, ProSHADE_internal_data::ProSHADE_data *dataObj, ProSHADE_settings *settings)
This function finds the rotation function value for a single axis.
Definition: ProSHADE_symmetry.cpp:3409
ProSHADE_internal_misc::addToUnsignVector
void addToUnsignVector(std::vector< proshade_unsign > *vecToAddTo, proshade_unsign elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:99
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup::findCyclicPointGroupsGivenFold
void findCyclicPointGroupsGivenFold(std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_sphere * > sphereVals, std::vector< proshade_double * > *detectedCs, bool bicubicInterp, proshade_unsign fold, proshade_signed verbose, proshade_signed messageShift)
Function detecting cyclic point groups with a particular fold in a peak group.
Definition: ProSHADE_spheres.cpp:1427
ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesListFromAngleAxis
std::vector< proshade_double * > getCyclicSymmetriesListFromAngleAxis(ProSHADE_settings *settings)
This function obtains a list of all C symmetries from the angle-axis space mapped rotation function v...
Definition: ProSHADE_symmetry.cpp:2656
ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave
void checkFittingAxisTripleAndSave(std::vector< proshade_unsign > *retGroup, std::vector< proshade_double * > *ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double * > *prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function takes a newly detected "missing" axis and tests it for belonging to the group,...
Definition: ProSHADE_symmetry.cpp:2604
ProSHADE_internal_misc::sortSymFSCHlpInv
bool sortSymFSCHlpInv(const proshade_double *a, const proshade_double *b)
This function compares two arrays of two based on the sixth number, sorting highest first.
Definition: ProSHADE_misc.cpp:314
ProSHADE_internal_maths::findVectorFromTwoVAndTwoD
std::vector< proshade_double > findVectorFromTwoVAndTwoD(proshade_double x1, proshade_double y1, proshade_double z1, proshade_double x2, proshade_double y2, proshade_double z2, proshade_double dot1, proshade_double dot2)
Function for finding a vector which would have a given two dot products to two other vectors.
Definition: ProSHADE_maths.cpp:2333
ProSHADE_internal_symmetry::findIcos15C2s
void findIcos15C2s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_signed verbose, proshade_signed messageShift, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the fifteen C3 symmetries with correct angles ...
Definition: ProSHADE_symmetry.cpp:2410
ProSHADE_settings::axisErrTolerance
proshade_double axisErrTolerance
Allowed error on vector axis in in dot product ( acos ( 1 - axErr ) is the allowed difference in radi...
Definition: ProSHADE_settings.hpp:132
ProSHADE_internal_precomputedVals::icosahedronAxes::getNoAxes
proshade_unsign getNoAxes()
Accessor for the octahedronAxesVals variable number of axes.
Definition: ProSHADE_precomputedValues.cpp:234
ProSHADE_internal_data::ProSHADE_data::getDihedralSymmetriesList
void getDihedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all D symmetries from already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:317
ProSHADE_internal_maths::findTopGroupSmooth
proshade_double findTopGroupSmooth(std::vector< proshade_double * > *CSym, size_t peakPos, proshade_double step, proshade_double sigma, proshade_signed windowSize, proshade_double maxLim=1.0)
This function finds a subgroup of axes with distinctly higher correlation value.
Definition: ProSHADE_maths.cpp:4113
ProSHADE_internal_data::ProSHADE_data::findRequestedCSymmetryFromAngleAxis
std::vector< proshade_double * > findRequestedCSymmetryFromAngleAxis(ProSHADE_settings *settings, proshade_unsign fold, proshade_double *peakThres)
This function searches the angle-axis representation of the rotation function for a cyclic point grou...
Definition: ProSHADE_symmetry.cpp:2939
ProSHADE_internal_maths::findAllPrimes
std::vector< proshade_unsign > findAllPrimes(proshade_unsign upTo)
This function finds all prime numbers up to the supplied limit.
Definition: ProSHADE_maths.cpp:3218
ProSHADE_internal_messages::printProgressMessage
void printProgressMessage(proshade_signed verbose, proshade_signed messageLevel, std::string message, proshade_signed messageShift=0)
General stdout message printing.
Definition: ProSHADE_messages.cpp:71
ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList
void getPredictedOctahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList, proshade_signed *&cutIndices, fftw_complex *&fCoeffsCut, proshade_signed noBins, proshade_double **&bindata, proshade_signed *&binCounts, proshade_double *&fscByBin, proshade_signed xDim, proshade_signed yDim, proshade_signed zDim)
This function predicts a list of all O symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:1807
ProSHADE_settings::fastISearch
bool fastISearch
Should FSC be computed for all possible I matches, or just for the best one according to FR?
Definition: ProSHADE_settings.hpp:142
ProSHADE_internal_symmetry::predictIcosAxes
void predictIcosAxes(std::vector< proshade_double * > *CSymList, std::vector< std::vector< proshade_double * > > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all possible icosahedral point groups symmetry axes from the cyclic point grou...
Definition: ProSHADE_symmetry.cpp:2034
ProSHADE_internal_symmetry::detectTetrahedralSymmetry
bool detectTetrahedralSymmetry(std::vector< proshade_double * > *CSymList, proshade_double axErr, proshade_double minPeakHeight)
This function takes the list of C symmetries and decides whether basic requirements for tetrahedral s...
Definition: ProSHADE_symmetry.cpp:410
ProSHADE_internal_misc::sortSymHlpInv
bool sortSymHlpInv(const proshade_double *a, const proshade_double *b)
This function compares two arrays of two based on the fifth number, sorting highest first.
Definition: ProSHADE_misc.cpp:288
ProSHADE_internal_symmetry::checkFittingAxisDualAndSave
bool checkFittingAxisDualAndSave(std::vector< proshade_unsign > *retGroup, std::vector< proshade_double * > *ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double * > *prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function takes a newly detected "missing" axis and tests it for belonging to the group,...
Definition: ProSHADE_symmetry.cpp:1610
ProSHADE_internal_symmetry::findMissingAxisPoints
std::vector< proshade_double * > findMissingAxisPoints(proshade_double xVal, proshade_double yVal, proshade_double zVal, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_double axErr)
This function searches for all the self-rotation map points conforming to the axis,...
Definition: ProSHADE_symmetry.cpp:743
ProSHADE_internal_maths::compute3x3MatrixMultiplication
proshade_double * compute3x3MatrixMultiplication(proshade_double *mat1, proshade_double *mat2)
Function for computing a 3x3 matrix multiplication.
Definition: ProSHADE_maths.cpp:1865