ProSHADE  0.7.5.3 (FEB 2021)
Protein Shape Detection
ProSHADE_symmetry.cpp
Go to the documentation of this file.
1 
22 //==================================================== ProSHADE
23 #include "ProSHADE_symmetry.hpp"
24 
35 {
36  //================================================ Report progress
37  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting self-rotation function computation." );
38 
39  //================================================ Compute un-weighted E matrices and their weights
40  ProSHADE_internal_distances::computeEMatrices ( this, this, settings );
41 
42  //================================================ Normalise E matrices by the magnitudes
43  ProSHADE_internal_distances::normaliseEMatrices ( this, this, settings );
44 
45  //================================================ Generate SO(3) coefficients
47 
48  //================================================ Compute the inverse SO(3) Fourier Transform (SOFT) on the newly computed coefficients
50 
51  //================================================ Report completion
52  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "Self-rotation function obtained." );
53 
54  //================================================ Done
55  return ;
56 
57 }
58 
64 proshade_double determinePeakThreshold ( std::vector < proshade_double > inArr, proshade_double noIQRsFromMedian )
65 {
66  //================================================ Initialise variables
67  proshade_double ret = 0.0;
68  proshade_unsign vecSize = static_cast< proshade_unsign > ( inArr.size() );
69  proshade_double* meadianAndIQR = new proshade_double[2];
70 
71  //================================================ Deal with low number of input cases
72  if ( vecSize == 0 ) { delete[] meadianAndIQR; return ( ret ); } // Return 0
73  if ( vecSize <= 4 ) { ret = std::accumulate ( inArr.begin(), inArr.end(), 0.0 ) / static_cast< proshade_double > ( vecSize ); } // Return mean
74 
75  //================================================ Deal with reasonable number in input cases
76  else
77  {
78  //============================================ Allocate memory for median and IQR computation
79  ProSHADE_internal_misc::checkMemoryAllocation ( meadianAndIQR, __FILE__, __LINE__, __func__ );
80 
81  //============================================ Find median and IQR
82  ProSHADE_internal_maths::vectorMedianAndIQR ( &inArr, meadianAndIQR );
83 
84  //============================================ Get the threshold
85  ret = meadianAndIQR[0] + ( meadianAndIQR[1] * noIQRsFromMedian );
86  }
87 
88  //================================================ Sanity checks
89  if ( ret > *( std::max_element ( inArr.begin(), inArr.end() ) ) )
90  {
91  ret = *( std::max_element ( inArr.begin(), inArr.end() ) );
92  }
93 
94  //================================================ Release memory
95  delete[] meadianAndIQR;
96 
97  //================================================ Done
98  return ( ret );
99 
100 }
101 
114 {
115  //================================================ Report progress
116  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting self-rotation function conversion to angle-axis representation." );
117 
118  //================================================ Initialise variables
119  proshade_double shellSpacing = ( 2.0 * M_PI ) / static_cast<proshade_double> ( this->maxShellBand * 2.0 );
120  std::vector< proshade_double > allPeakHeights;
121 
122  //================================================ Initialise the spheres
123  for ( proshade_unsign spIt = 1; spIt < ( this->maxShellBand * 2 ); spIt++ )
124  {
125  this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere( static_cast<proshade_double> ( spIt ) * shellSpacing,
126  shellSpacing,
127  this->maxShellBand * 2.0,
128  static_cast<proshade_double> ( spIt ) * shellSpacing,
129  spIt - 1 ) );
130  }
131 
132  //================================================ Interpolate the rotation function onto the spheres
133  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( sphereMappedRotFun.size() ); shIt++ )
134  {
135  //============================================ Report progress
136  std::stringstream hlpSS;
137  hlpSS << "Interpolating sphere " << shIt << " ( radius: " << this->sphereMappedRotFun.at(shIt)->getRadius() << " ).";
138  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str() );
139 
140  //============================================ Interpolate onto spheres
141  this->sphereMappedRotFun.at(shIt)->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
142  }
143 
144  //================================================ Report completion
145  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "Self-rotation function converted to spherical angle-axis space." );
146  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "Started peak detection on the angle-axis spheres." );
147 
148  //================================================ Find all peaks in the sphere grids
149  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
150  {
151  this->sphereMappedRotFun.at(shIt)->findAllPeaks ( settings->peakNeighbours, &allPeakHeights );
152  }
153 
154  //================================================ Report progress
155  std::stringstream hlpSS;
156  hlpSS << "Detected " << allPeakHeights.size() << " peaks with any height.";
157  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str() );
158 
159  //================================================ Compute threshold for small peaks
160  proshade_double peakThres = std::max ( settings->minSymPeak, determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak ) );
161 
162  //================================================ Report progress
163  std::stringstream hlpSS2;
164  hlpSS2 << "From these peaks, decided the threshold will be " << peakThres << " peak height.";
165  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str() );
166 
167  //================================================ Remove too small peaks
168  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
169  {
170  this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( peakThres );
171  }
172 
173  //================================================ Report progress
174  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, "Peaks detected for all spheres." );
175 
176  //================================================ Done
177  return ;
178 
179 }
180 
192 {
193  //================================================ Initialise variables
194  std::vector< proshade_double* > ret;
195 
196  //================================================ Report progress
197  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting C symmetry detection." );
198 
199  //================================================ Get list of peaks in the self-rotation map
200  std::vector< proshade_double* > allPeaks = ProSHADE_internal_peakSearch::getAllPeaksNaive ( this->getInvSO3Coeffs (), this->getMaxBand() * 2,
201  settings->peakNeighbours,
202  settings->noIQRsFromMedianNaivePeak );
203 
204  //================================================ Convert peaks to angle-axis
205  std::vector< proshade_double* > peaksAA = ProSHADE_internal_symmetry::getPeaksAngleAxisPositions ( allPeaks, settings->verbose );
206 
207  //================================================ Sort peaks by height groups
208  std::vector< proshade_double > peakGroupsBoundaries = ProSHADE_internal_symmetry::findPeaksByHeightBoundaries ( peaksAA, settings->smoothingFactor );
209 
210  //================================================ Get symmetry per group
211  std::vector< std::vector< proshade_unsign > > detectedCSymmetries;
212  for ( proshade_signed iter = static_cast<proshade_unsign> ( peakGroupsBoundaries.size() - 1 ); iter >= 0; iter-- )
213  {
214  //============================================ Get peaks group peaks only
215  std::vector< proshade_double* > symPeaks;
216  for ( proshade_unsign it = 0; it < static_cast<proshade_unsign> ( peaksAA.size() ); it++ )
217  {
218  if ( peaksAA.at(it)[4] > peakGroupsBoundaries.at(iter) ) { ProSHADE_internal_misc::addToDblPtrVector ( &symPeaks, peaksAA.at(it) ); }
219  }
220 
221  //============================================ Search for symmetry in these peaks
222  detectedCSymmetries = ProSHADE_internal_symmetry::findPeaksCSymmetry ( &symPeaks, settings->verbose,
223  this->getMaxBand(),
224  settings->symMissPeakThres,
225  settings->axisErrTolerance,
226  settings->axisErrToleranceDefault,
227  this );
228 
229  //============================================ Print detected symmetries
230  for ( proshade_unsign detIt = 0; detIt < static_cast<proshade_unsign> ( detectedCSymmetries.size() ); detIt++ ) { ProSHADE_internal_symmetry::printSymmetryGroup ( detectedCSymmetries.at(detIt), symPeaks, settings->verbose ); }
231 
232  //============================================ Save detected
233  ProSHADE_internal_symmetry::saveAllCSymmetries ( detectedCSymmetries, symPeaks, &ret, settings->axisErrTolerance );
234  }
235 
236  //================================================ Release memory
237  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( peaksAA.size() ); iter++ ) { delete[] allPeaks.at(iter); delete[] peaksAA.at(iter); }
238 
239  //================================================ Report completion
240  ProSHADE_internal_symmetry::printSymmetryCompletion ( static_cast<proshade_unsign>( ret.size() ), settings->verbose );
241 
242  //================================================ Done
243  return ( ret );
244 
245 }
246 
256 std::vector< proshade_double* > ProSHADE_internal_symmetry::getPeaksAngleAxisPositions ( std::vector< proshade_double* > allPeaks, proshade_unsign verbose )
257 {
258  //================================================ Initialise variables
259  std::vector< proshade_double* > ret;
260  proshade_double* hlpP = NULL;
261  proshade_double* rotMat = new proshade_double [9];
262  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat, __FILE__, __LINE__, __func__ );
263 
264  //================================================ For each peak
265  for ( proshade_unsign peakIter = 0; peakIter < static_cast<proshade_unsign> ( allPeaks.size() ); peakIter++ )
266  {
267  //============================================ Convert Euler ZXZ angles to rotation matrix
268  ProSHADE_internal_maths::getRotationMatrixFromEulerZXZAngles ( allPeaks.at(peakIter)[0], allPeaks.at(peakIter)[1], allPeaks.at(peakIter)[2], rotMat );
269 
270  //============================================ Allocate pointer to results
271  hlpP = new proshade_double [5];
272  ProSHADE_internal_misc::checkMemoryAllocation ( hlpP, __FILE__, __LINE__, __func__ );
273 
274  //============================================ Convert rotation matrix to Angle-axis reporesentation
275  ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( rotMat, &hlpP[0], &hlpP[1], &hlpP[2], &hlpP[3] );
276  hlpP[4] = allPeaks.at(peakIter)[3];
277 
278  //============================================ Save results
280  }
281 
282  //================================================ Release memory
283  delete[] rotMat;
284 
285  //================================================ Report progress
286  std::stringstream hlpSSP;
287  hlpSSP << "Found " << ret.size() << " possible peaks.";
288  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, hlpSSP.str() );
289 
290  //================================================ Warning if no peaks!
291  if ( ret.size() < 1 )
292  {
293  ProSHADE_internal_messages::printWarningMessage ( verbose, "!!! ProSHADE WARNING !!! Failed to detect any symmetries. There are no reasonable peaks in the self-rotation map. If you believe there should be some symmetry, you can try decreasing the resolution or changing the peak IQR threshold.", "WS00029" );
294  }
295 
296  //================================================ Done
297  return ( ret );
298 
299 }
300 
310 std::vector< proshade_double > ProSHADE_internal_symmetry::findPeaksByHeightBoundaries ( std::vector< proshade_double* > allPeaks, proshade_double smoothing )
311 {
312  //================================================ Initialise variables
313  std::vector< proshade_double > boundaries;
314  ProSHADE_internal_misc::addToDoubleVector ( &boundaries, 0.0 );
315  proshade_double peakContribution = 0.0;
316 
317  //================================================ Generate Probability Density function (PDF)
318  std::vector< proshade_double > pdf;
319  for ( proshade_double iter = 0.0; iter <= 1.0; iter += 0.01 )
320  {
321  //============================================ Initialise point
322  peakContribution = 0.0;
323 
324  //============================================ Sum peak contributions
325  for ( proshade_unsign it = 0; it < static_cast<proshade_unsign> ( allPeaks.size() ); it++ )
326  {
327  peakContribution += ProSHADE_internal_maths::normalDistributionValue ( allPeaks.at(it)[4], smoothing, iter );
328  }
329 
330  //============================================ Save result
331  ProSHADE_internal_misc::addToDoubleVector ( &pdf, peakContribution );
332  }
333 
334  //================================================ Find boundaries
335  proshade_double prev = pdf.at(0);
336  for ( proshade_unsign iter = 1; iter < static_cast<proshade_unsign> ( pdf.size() - 1 ); iter ++ )
337  {
338  //============================================ Check for local minima
339  if ( ( prev > pdf.at(iter) ) && ( pdf.at(iter+1) > pdf.at(iter) ) )
340  {
341  ProSHADE_internal_misc::addToDoubleVector ( &boundaries, iter * 0.01 );
342  }
343 
344  //============================================ Prepare next iteration
345  prev = pdf.at(iter);
346  }
347 
348  //================================================ Done
349  return ( boundaries );
350 
351 }
352 
368 std::vector< std::vector< proshade_unsign > > ProSHADE_internal_symmetry::findPeaksCSymmetry ( std::vector< proshade_double* >* peaks, proshade_signed verbose, proshade_unsign band, proshade_double missPeakThres, proshade_double axisErrTolerance, bool axisErrToleranceDef, ProSHADE_internal_data::ProSHADE_data* dataObj )
369 {
370  //======================================== Initialise variables
371  std::vector< std::vector< proshade_unsign > > ret;
372  std::vector< proshade_double > triedAlready;
373  std::vector< proshade_unsign > angsToTry, testedAlready;
374  proshade_double angDist, angDivisionRemainder, angDivisionBasis, nextSymmetryError, nextPeakError = ( M_PI * 2.0 ) / ( static_cast<proshade_double> ( band ) * 2.0 );
375 
376  //================================================ Sanity check
377  if ( peaks->size() < 1 ) { return ( ret ); }
378 
379  //================================================ Group peaks by axes
380  std::vector< std::vector< proshade_unsign > > sameAxesGroups = ProSHADE_internal_symmetry::groupSameAxes ( *peaks, axisErrTolerance );
381 
382  //================================================ For each axis group
383  for ( proshade_unsign grpIt = 0; grpIt < static_cast<proshade_unsign> ( sameAxesGroups.size() ); grpIt++ )
384  {
385  //============================================ Print axis group if need be
386  ProSHADE_internal_symmetry::printSymmetryPeaks ( sameAxesGroups.at(grpIt), *peaks, verbose, grpIt );
387 
388  //============================================ While there are distances between rotation angles in the group
389  triedAlready.clear ( );
390  testedAlready.clear ( );
391  while ( ProSHADE_internal_symmetry::smallestDistanceBetweenAngles ( sameAxesGroups.at(grpIt), *peaks, &triedAlready, &angDist ) )
392  {
393  //======================================== Check if testable fold value exists, otherwise test other distances
394  angsToTry.clear ( );
395  if ( !ProSHADE_internal_symmetry::determineFoldToTry ( angDist, &angDivisionBasis, &angDivisionRemainder, nextPeakError, &nextSymmetryError, &angsToTry ) ) { continue; }
396 
397  //======================================== If reasonable folds are found, test these for being complete symmetries
398  ProSHADE_internal_symmetry::findSymmetryUsingFold ( dataObj, &angsToTry, &sameAxesGroups.at(grpIt), peaks, &ret, &testedAlready, axisErrTolerance, axisErrToleranceDef, missPeakThres, verbose );
399  }
400 
401  }
402 
403  //================================================ Done
404  return ( ret );
405 
406 }
407 
419 std::vector< std::vector< proshade_unsign > > ProSHADE_internal_symmetry::groupSameAxes ( std::vector< proshade_double* >& peaks, proshade_double errTolerance )
420 {
421  //================================================ Initialise variables
422  std::vector< std::vector< proshade_unsign > > ret;
423  bool sameAxisFound = false;
424  proshade_double angTolerance = std::acos ( 1.0 - errTolerance );
425 
426  //================================================ Set all largest axis value to positive (this will make the 0,0,1 and 0,0,-1 axes the same)
428 
429  //================================================ For each axis
430  for ( proshade_unsign peakIter = 0; peakIter < static_cast<proshade_unsign> ( peaks.size() ); peakIter++ )
431  {
432  //============================================ Initialise variables for next peak
433  sameAxisFound = false;
434 
435  //============================================ Ignore zero angle peaks
436  if ( ( peaks.at(peakIter)[3] - angTolerance <= 0.0 ) && ( peaks.at(peakIter)[3] + angTolerance > 0.0 ) ) { continue; }
437 
438  //============================================ Ignore very small axis peaks - the axis may be wrong here.
439  // !! The value of 0.1 is hardcoded, but arbitrary
440  if ( ( ( peaks.at(peakIter)[0] - 0.1 <= 0.0 ) && ( peaks.at(peakIter)[0] + 0.1 > 0.0 ) ) &&
441  ( ( peaks.at(peakIter)[1] - 0.1 <= 0.0 ) && ( peaks.at(peakIter)[1] + 0.1 > 0.0 ) ) &&
442  ( ( peaks.at(peakIter)[2] - 0.1 <= 0.0 ) && ( peaks.at(peakIter)[2] + 0.1 > 0.0 ) ) ) { continue; }
443 
444  //============================================ Compare to all already detected axes groups
445  for ( proshade_unsign sameAxisGrp = 0; sameAxisGrp < static_cast<proshade_unsign> ( ret.size() ); sameAxisGrp++ )
446  {
447  //======================================== and all their members
448  for ( proshade_unsign sameAxis = 0; sameAxis < static_cast<proshade_unsign> ( ret.at(sameAxisGrp).size() ); sameAxis++ )
449  {
450  //==================================== Is this identical axis to the tested one?
451  if ( ProSHADE_internal_maths::vectorOrientationSimilarity ( peaks.at(ret.at(sameAxisGrp).at(sameAxis))[0],
452  peaks.at(ret.at(sameAxisGrp).at(sameAxis))[1],
453  peaks.at(ret.at(sameAxisGrp).at(sameAxis))[2],
454  peaks.at(peakIter)[0],
455  peaks.at(peakIter)[1],
456  peaks.at(peakIter)[2],
457  errTolerance ) )
458  {
459  sameAxisFound = true;
460  ProSHADE_internal_misc::addToUnsignVector ( &ret.at(sameAxisGrp), peakIter );
461  break;
462  }
463  }
464  }
465 
466  //============================================ If same axis was found, do nothing
467  if ( sameAxisFound ) { continue; }
468 
469  //============================================ No similar axis was found
470  std::vector<proshade_unsign> hlpVec;
471  ProSHADE_internal_misc::addToUnsignVector ( &hlpVec, peakIter );
473  }
474 
475  //================================================ Add zero peak to all axes
477 
478  //================================================ Done
479  return ( ret );
480 
481 }
482 
491 void ProSHADE_internal_symmetry::giveOppositeAxesSameDirection ( std::vector< proshade_double* > peaks )
492 {
493  //================================================ Apply to all peaks
494  for ( proshade_unsign i = 0; i < static_cast<proshade_unsign> ( peaks.size() ); i++ )
495  {
496  if ( ( ( std::max ( std::abs ( peaks.at(i)[0] ), std::max( std::abs ( peaks.at(i)[1] ), std::abs ( peaks.at(i)[2] ) ) ) == std::abs ( peaks.at(i)[0] ) ) && ( peaks.at(i)[0] < 0.0 ) ) ||
497  ( ( std::max ( std::abs ( peaks.at(i)[0] ), std::max( std::abs ( peaks.at(i)[1] ), std::abs ( peaks.at(i)[2] ) ) ) == std::abs ( peaks.at(i)[1] ) ) && ( peaks.at(i)[1] < 0.0 ) ) ||
498  ( ( std::max ( std::abs ( peaks.at(i)[0] ), std::max( std::abs ( peaks.at(i)[1] ), std::abs ( peaks.at(i)[2] ) ) ) == std::abs ( peaks.at(i)[2] ) ) && ( peaks.at(i)[2] < 0.0 ) ) )
499  {
500  peaks.at(i)[0] *= -1.0;
501  peaks.at(i)[1] *= -1.0;
502  peaks.at(i)[2] *= -1.0;
503  peaks.at(i)[3] *= -1.0;
504  }
505  }
506 
507  //================================================ Done
508  return ;
509 
510 }
511 
518 void ProSHADE_internal_symmetry::printSymmetryPeaks ( std::vector< proshade_unsign > grp, std::vector< proshade_double* > peaks, proshade_signed verbose, proshade_unsign groupNo )
519 {
520  //================================================ Symmetry group output header
521  std::stringstream hlpSS;
522  hlpSS << "Symmetry axis group " << groupNo;
523  ProSHADE_internal_messages::printProgressMessage ( verbose, 6, hlpSS.str() );
524  ProSHADE_internal_messages::printProgressMessage ( verbose, 6, "Peak index\t\tx\t y\t z\tAngle\tPeak heiht" );
525 
526  //================================================ Print the symmetry group
527  for ( proshade_unsign axIt = 0; axIt < static_cast<proshade_unsign> ( grp.size() ); axIt++ )
528  {
529  std::stringstream SS;
530  SS << " " << axIt << "\t " << static_cast<int>( peaks.at(grp.at(axIt))[0] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(axIt))[1] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(axIt))[2] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(axIt))[3] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(axIt))[4] * 100.0 ) / 100.0;
531  ProSHADE_internal_messages::printProgressMessage ( verbose, 6, SS.str() );
532  }
533 
534  //================================================ Done
535  return ;
536 
537 }
538 
551 bool ProSHADE_internal_symmetry::smallestDistanceBetweenAngles ( std::vector< proshade_unsign > grp, std::vector< proshade_double* > peaks, std::vector< proshade_double >* tried, proshade_double* dist )
552 {
553  //================================================ Initialise variables
554  bool ret = false;
555  bool skip = false;
556  proshade_unsign g1 = 0, g2 = 0;
557  *dist = 999.9;
558 
559  //================================================ For each group pair
560  for ( proshade_unsign gr1It = 0; gr1It < static_cast<proshade_unsign> ( grp.size() ); gr1It++ )
561  {
562  for ( proshade_unsign gr2It = 1; gr2It < static_cast<proshade_unsign> ( grp.size() ); gr2It++ )
563  {
564  //======================================== Unique pairs only
565  if ( gr1It >= gr2It ) { continue; }
566 
567  //======================================== Have we tried this already?
568  skip = false;
569  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( tried->size() ); iter += 3 )
570  {
571  //==================================== Avoid already tested combinations
572  if ( ( gr2It == tried->at( iter + 1 ) ) && ( gr1It == tried->at( iter ) ) ) { skip = true; }
573 
574  //==================================== Also avoid distances very close to already tested distances (no problem until approx C700)
575  if ( !skip &&
576  ( ( std::abs( std::abs ( peaks.at(grp.at(gr1It))[3] ) - std::abs ( peaks.at(grp.at(gr2It))[3] ) ) - 0.01 ) < tried->at( iter + 2 ) ) &&
577  ( ( std::abs( std::abs ( peaks.at(grp.at(gr1It))[3] ) - std::abs ( peaks.at(grp.at(gr2It))[3] ) ) + 0.01 ) > tried->at( iter + 2 ) ) )
578  {
579  skip = true;
580  }
581  }
582  if ( skip ) { continue; }
583 
584  //======================================== Is this the smallest distance?
585  if ( std::abs( std::abs ( peaks.at(grp.at(gr1It))[3] ) - std::abs ( peaks.at(grp.at(gr2It))[3] ) ) < (*dist) )
586  {
587  //==================================== Avoid very small angle distances as they would just take time (the hardcoded value would only be a problem for C700 and larger symmetries...
588  if ( std::abs( std::abs ( peaks.at(grp.at(gr1It))[3] ) - std::abs ( peaks.at(grp.at(gr2It))[3] ) ) > 0.01 )
589  {
590  g1 = gr1It;
591  g2 = gr2It;
592  *dist = std::abs( std::abs ( peaks.at(grp.at(gr1It))[3] ) - std::abs ( peaks.at(grp.at(gr2It))[3] ) );
593  }
594  }
595  }
596  }
597 
598  //================================================ If new dist found, save to tried and return success
599  if ( *dist != 999.9 )
600  {
601  ret = true;
605  }
606 
607  //================================================ Done
608  return ( ret );
609 
610 }
611 
621 void ProSHADE_internal_symmetry::addZeroPeakToGroups ( std::vector< std::vector< proshade_unsign > >& grpsVec, std::vector< proshade_double* >& peaks )
622 {
623  //================================================ Do your job
624  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( grpsVec.size() ); iter++ )
625  {
626  proshade_double* hlpP = new proshade_double [5];
627  ProSHADE_internal_misc::checkMemoryAllocation ( hlpP, __FILE__, __LINE__, __func__ );
628  hlpP[0] = peaks.at(grpsVec.at(iter).at(0))[0];
629  hlpP[1] = peaks.at(grpsVec.at(iter).at(0))[1];
630  hlpP[2] = peaks.at(grpsVec.at(iter).at(0))[2];
631  hlpP[3] = 0.0;
632  hlpP[4] = peaks.at(grpsVec.at(iter).at(0))[4];
633  ProSHADE_internal_misc::addToUnsignVector ( &grpsVec.at(iter), static_cast<proshade_unsign> ( peaks.size() ) );
635  }
636 
637  //================================================ Done
638  return ;
639 
640 }
641 
658 bool ProSHADE_internal_symmetry::determineFoldToTry ( proshade_double dist, proshade_double* divBasis, proshade_double* divRem, proshade_double peakErr, proshade_double* symmErr, std::vector< proshade_unsign >* angsToTry )
659 {
660  //================================================ Initialise variables
661  bool ret = false;
662 
663  //================================================ Find the basis and remainder of the 2pi/dist equation
664  *divRem = std::modf ( static_cast<proshade_double> ( ( 2.0 * M_PI ) / std::abs ( dist ) ), divBasis );
665 
666  //================================================ If the remainder would be smaller for larger basis, so this basis
667  if ( *divRem > 0.5 )
668  {
669  *divRem -= 1.0;
670  *divBasis += 1.0;
671  }
672 
673  //================================================ Determine errors on peaks and on folds
674  *symmErr = ( M_PI * 2.0 / *divBasis ) - ( M_PI * 2.0 / ( *divBasis + 1.0 ) );
675  proshade_double angTolerance = ( peakErr / *symmErr );
676 
677  //================================================ Is remainder small enough?
678  if ( ( *divRem < ( 0.0 + angTolerance ) ) && ( *divRem > ( 0.0 - angTolerance ) ) )
679  {
680  //============================================ Are we sure about the fold determination accuracy
681  proshade_signed angTolRound = std::min ( ProSHADE_internal_mapManip::myRound ( angTolerance ), static_cast<proshade_signed> ( 10 ) );
682  for ( proshade_signed iter = -angTolRound; iter <= angTolRound; iter++ )
683  {
684  ProSHADE_internal_misc::addToUnsignVector ( angsToTry, static_cast<proshade_unsign> ( std::max ( *divBasis + iter, 2.0 ) ) );
685  }
686  }
687 
688  //================================================ Return indication of whether testable fold value(s) was found.
689  if ( angsToTry->size() == 0 ) { ret = false; }
690  else { ret = true; }
691 
692  //================================================ Done
693  return ( ret );
694 
695 }
696 
705 void ProSHADE_internal_symmetry::findExpectedPeakRotations ( proshade_unsign fold, std::vector< proshade_double >* expAngs )
706 {
707  //================================================ Initialise variables
708  proshade_double groupAngle = ( 2.0 * M_PI ) / static_cast<proshade_double> ( fold );
709 
710  //================================================ Generate expected angles
711  for ( proshade_signed iter = static_cast<proshade_signed> ( -( static_cast<proshade_double> ( fold ) / 2.0 + 1.0) ); iter <= static_cast<proshade_signed> ( static_cast<proshade_double> ( fold )/2.0 + 1.0 ); iter++ )
712  {
713  ProSHADE_internal_misc::addToDoubleVector ( expAngs, iter * groupAngle );
714  }
715 
716  //================================================ Done
717  return ;
718 
719 }
720 
734 proshade_unsign ProSHADE_internal_symmetry::checkExpectedAgainstFound ( std::vector< proshade_unsign > grp, std::vector< proshade_double* > peaks, std::vector< proshade_double >* expAngs, std::vector< proshade_unsign >* matchedAngs, std::vector< proshade_unsign >* missingAngs, proshade_double angTol )
735 {
736  //================================================ Initialise variables
737  proshade_unsign ret = 0;
738  proshade_unsign retHlp = 0;
739  proshade_double groupAngle = expAngs->at(1) - expAngs->at(0);
740  bool matchedThisPeak = false;
741  bool noDoubleMatches = false;
742  std::vector < proshade_unsign > matchedAlready;
743 
744  //================================================ For each expected peak rotation angle value
745  for ( proshade_unsign expAngIt = 0; expAngIt < static_cast<proshade_unsign> ( expAngs->size() ); expAngIt++ )
746  {
747  //============================================ For each peak in the group
748  matchedThisPeak = false;
749  for ( proshade_unsign peakIt = 0; peakIt < static_cast<proshade_unsign> ( grp.size() ); peakIt++ )
750  {
751  if ( ( expAngs->at(expAngIt) < ( peaks.at(grp.at(peakIt))[3] + angTol ) ) &&
752  ( expAngs->at(expAngIt) > ( peaks.at(grp.at(peakIt))[3] - angTol ) ) )
753  {
754  noDoubleMatches = false;
755  for ( proshade_unsign ndm = 0; ndm < static_cast<proshade_unsign> ( matchedAlready.size() ); ndm++ )
756  {
757  if ( matchedAlready.at(ndm) == grp.at(peakIt) ) { noDoubleMatches = true; break; }
758  }
759 
760  if ( !noDoubleMatches )
761  {
762  ProSHADE_internal_misc::addToUnsignVector ( matchedAngs, grp.at(peakIt) );
763  ProSHADE_internal_misc::addToUnsignVector ( &matchedAlready, grp.at(peakIt) );
764  matchedThisPeak = true;
765  break;
766  }
767  }
768  }
769 
770  //============================================ If not matched, add to missing
771  if ( !matchedThisPeak )
772  {
773  ProSHADE_internal_misc::addToUnsignVector ( missingAngs, expAngIt );
774  }
775  }
776 
777  //================================================ Find the number of consecutive matches
778  if ( matchedAngs->size () > 1 )
779  {
780  for ( proshade_unsign iter = 1; iter < static_cast<unsigned int> ( matchedAngs->size () ); iter++ )
781  {
782  if ( ( ( peaks.at(matchedAngs->at(iter-1))[3] + groupAngle ) < ( peaks.at(matchedAngs->at(iter))[3] + angTol ) ) &&
783  ( ( peaks.at(matchedAngs->at(iter-1))[3] + groupAngle ) > ( peaks.at(matchedAngs->at(iter))[3] - angTol ) ) )
784  {
785  retHlp += 1;
786  }
787  else
788  {
789  retHlp = 0;
790  }
791  if ( retHlp > ret ) { ret = retHlp; }
792  }
793  }
794 
795  //================================================ Done
796  return ( ret + 1 ); // This is because the count of matches is the count of intervals between numbers, so +1 to get the count of matched numbers.
797 
798 }
799 
816 proshade_double ProSHADE_internal_symmetry::checkForMissingPeak ( ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_double x, proshade_double y, proshade_double z, proshade_double angle, proshade_double heightThres, proshade_double axTol )
817 {
818  //================================================ Initialise variables
819  proshade_double ret = 0.0;
820  proshade_unsign arrIndex = 0;
821  proshade_double* rotMat = new proshade_double [9];
822  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat, __FILE__, __LINE__, __func__ );
823  proshade_double pointHeight, euA, euB, euG, xPk, yPk, zPk, anglPk;
824  proshade_double angTol = std::acos ( 1.0 - axTol );
825 
826  //================================================ Search the self-rotation map
827  for ( proshade_unsign xIt = 0; xIt < ( dataObj->getMaxBand() * 2 ); xIt++ )
828  {
829  for ( proshade_unsign yIt = 0; yIt < ( dataObj->getMaxBand() * 2 ); yIt++ )
830  {
831  for ( proshade_unsign zIt = 0; zIt < ( dataObj->getMaxBand() * 2 ); zIt++ )
832  {
833  //==================================== Get height and check against threshold
834  arrIndex = zIt + ( dataObj->getMaxBand() * 2 ) * ( yIt + ( dataObj->getMaxBand() * 2 ) * xIt );
835  pointHeight = pow( dataObj->getInvSO3Coeffs()[arrIndex][0], 2.0 ) + pow( dataObj->getInvSO3Coeffs()[arrIndex][1], 2.0 );
836  if ( pointHeight < heightThres ) { continue; }
837 
838  //==================================== Get angle-axis values
839  ProSHADE_internal_maths::getEulerZXZFromSOFTPosition ( dataObj->getMaxBand(), static_cast<proshade_signed> ( xIt ),
840  static_cast<proshade_signed> ( yIt ), static_cast<proshade_signed> ( zIt ),
841  &euA, &euB, &euG );
843  ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( rotMat, &xPk, &yPk, &zPk, &anglPk );
844 
845  //==================================== Check for matching angle
846  if ( ( ( std::abs( anglPk ) - angTol ) < std::abs ( angle ) ) && ( ( std::abs( anglPk ) + angTol ) > std::abs ( angle ) ) )
847  {
848  //================================ Make sure vector direction is the same
849  if ( ( ( std::max( std::abs( xPk ), std::max( std::abs( yPk ), std::abs( zPk ) ) ) == std::abs( xPk ) ) && ( xPk < 0.0 ) ) ||
850  ( ( std::max( std::abs( xPk ), std::max( std::abs( yPk ), std::abs( zPk ) ) ) == std::abs( yPk ) ) && ( yPk < 0.0 ) ) ||
851  ( ( std::max( std::abs( xPk ), std::max( std::abs( yPk ), std::abs( zPk ) ) ) == std::abs( zPk ) ) && ( zPk < 0.0 ) ) )
852  {
853  xPk *= -1.0;
854  yPk *= -1.0;
855  zPk *= -1.0;
856  anglPk *= -1.0;
857  }
858 
859  //================================ Compare axis elements
860  if ( ProSHADE_internal_maths::vectorOrientationSimilarity ( xPk, yPk, zPk, x, y, z, axTol ) )
861  {
862  if ( ret < pointHeight ) { ret = pointHeight; }
863  }
864  }
865  }
866  }
867  }
868 
869  //================================================ Done
870  return ( ret );
871 
872 }
873 
884 void ProSHADE_internal_symmetry::saveDetectedCSymmetry ( proshade_unsign fold, std::vector< proshade_unsign >* matchedPeaks, std::vector< std::vector< proshade_unsign > >* ret, proshade_signed verbose )
885 {
886  //================================================ Save fold as first vector value
887  std::vector< proshade_unsign > hlpVec;
889 
890  //================================================ and follow it with indices of all symmetry forming peaks
891  for ( proshade_unsign pIt = 0; pIt < static_cast<proshade_unsign> ( matchedPeaks->size() ); pIt++ )
892  {
893  ProSHADE_internal_misc::addToUnsignVector ( &hlpVec, matchedPeaks->at(pIt) );
894  }
896 
897  //================================================ Report finding symmetry
898  std::stringstream hlpS;
899  hlpS << "Found symmetry C" << fold;
900  ProSHADE_internal_messages::printProgressMessage ( verbose, 5, hlpS.str() );
901 
902  //================================================ Done
903  return ;
904 
905 }
906 
924 bool ProSHADE_internal_symmetry::completeMissingCSymmetry ( ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_unsign fold, std::vector< proshade_unsign >* grp, std::vector< proshade_double* >* peaks, std::vector< proshade_unsign >* missingPeaks, std::vector< proshade_double >* expectedAngles, std::vector< proshade_unsign >* matchedPeaks, proshade_double axErrTolerance, proshade_unsign verbose )
925 {
926  //================================================ Initialise variables
927  bool ret = true;
928 
929  //================================================ Report searching for missing peaks
930  std::stringstream hlpSSP;
931  hlpSSP << "Searching for missing peaks for symmetry C" << fold;
932  ProSHADE_internal_messages::printProgressMessage ( verbose, 4, hlpSSP.str() );
933 
934  //================================================ Height threshold for missing peak
935  proshade_double heightThreshold = 0.0;
936  for ( proshade_unsign grIt = 0; grIt < static_cast<proshade_unsign> ( grp->size() ); grIt++ ) { heightThreshold += peaks->at(grp->at(grIt))[4]; }
937  heightThreshold /= static_cast<proshade_double> ( grp->size() );
938  heightThreshold *= 0.5;
939 
940  //================================================ For each missing value
941  for ( proshade_unsign misPkIt = 0; misPkIt < static_cast<proshade_unsign> ( missingPeaks->size() ); misPkIt++ )
942  {
943  //============================================ Ignore the extra values in the expected values
944  if ( expectedAngles->at(missingPeaks->at(misPkIt)) > M_PI ) { continue; }
945  if ( expectedAngles->at(missingPeaks->at(misPkIt)) < -M_PI ) { continue; }
946 
947  //============================================ Search for the missing peak
948  proshade_double misHeight = ProSHADE_internal_symmetry::checkForMissingPeak ( dataObj, peaks->at(grp->at(0))[0], peaks->at(grp->at(0))[1], peaks->at(grp->at(0))[2], expectedAngles->at(missingPeaks->at(misPkIt)), heightThreshold, axErrTolerance );
949  if ( misHeight != 0.0 )
950  {
951  //======================================== Missing peak detected - save it to the group
952  proshade_double* hlpP = new proshade_double [5];
953  ProSHADE_internal_misc::checkMemoryAllocation ( hlpP, __FILE__, __LINE__, __func__ );
954  hlpP[0] = peaks->at(grp->at(0))[0];
955  hlpP[1] = peaks->at(grp->at(0))[1];
956  hlpP[2] = peaks->at(grp->at(0))[2];
957  hlpP[3] = expectedAngles->at(missingPeaks->at(misPkIt));
958  hlpP[4] = misHeight;
959  ProSHADE_internal_misc::addToUnsignVector ( matchedPeaks, static_cast<proshade_unsign> ( peaks->size() ) );
961  }
962  else
963  {
964  ret = false;
965  }
966  }
967 
968  //================================================ Done
969  return ( ret );
970 
971 }
972 
989 void ProSHADE_internal_symmetry::findSymmetryUsingFold ( ProSHADE_internal_data::ProSHADE_data* dataObj, std::vector< proshade_unsign >* angsToTry, std::vector< proshade_unsign >* grp, std::vector< proshade_double* >* peaks, std::vector< std::vector< proshade_unsign > >* ret, std::vector< proshade_unsign >* testedAlready, proshade_double axErrTolerance, bool axErrToleranceDefault, proshade_double missPeakThres, proshade_unsign verbose )
990 {
991  //================================================ Initialise variables
992  bool skipFold = false;
993  std::vector< proshade_unsign > matchedPeaks, missingPeaks;
994  std::vector< proshade_double > expectedAngles;
995  proshade_double angTolerance = std::acos ( 1.0 - axErrTolerance );
996 
997  //================================================ Testing folds for being supported by peaks
998  for ( proshade_unsign fIt = 0; fIt < static_cast<proshade_unsign> ( angsToTry->size() ); fIt++ )
999  {
1000  //============================================ Was this fold already found?
1001  skipFold = false;
1002  for ( proshade_unsign ftIt = 0; ftIt < static_cast<proshade_unsign> ( testedAlready->size() ); ftIt++ ) { if ( testedAlready->at(ftIt) == angsToTry->at(fIt) ) { skipFold = true; } }
1003  if ( skipFold ) { continue; }
1004  else { ProSHADE_internal_misc::addToUnsignVector( testedAlready, angsToTry->at(fIt) ); }
1005 
1006  //============================================ Set axis tolerance based on fold (if required)
1007  if ( axErrToleranceDefault )
1008  {
1009  angTolerance = std::max ( std::min ( angTolerance, ( ( (M_PI * 2.0) / static_cast<double> ( angsToTry->at(fIt) ) ) -
1010  ( (M_PI * 2.0) / static_cast<double> ( angsToTry->at(fIt) + 1 ) ) ) * 2.0 ), 0.02 );
1011  axErrTolerance = std::max ( 1.0 - std::cos ( angTolerance ), 0.0008 );
1012  }
1013 
1014  //============================================ Find expected peak rotation angles
1015  expectedAngles.clear ( );
1016  ProSHADE_internal_symmetry::findExpectedPeakRotations ( angsToTry->at(fIt), &expectedAngles );
1017 
1018  //============================================ Compare group to expected angles
1019  matchedPeaks.clear ( );
1020  missingPeaks.clear ( );
1021  proshade_unsign consecMatches = ProSHADE_internal_symmetry::checkExpectedAgainstFound ( *grp, *peaks, &expectedAngles,
1022  &matchedPeaks, &missingPeaks, angTolerance );
1023 
1024  //============================================ If enough consecutive matches, symmetry was found. Save it
1025  if ( consecMatches >= angsToTry->at(fIt) )
1026  {
1027  ProSHADE_internal_symmetry::saveDetectedCSymmetry ( angsToTry->at(fIt), &matchedPeaks, ret, verbose );
1028  }
1029  else
1030  {
1031  if ( ( static_cast<proshade_double> ( matchedPeaks.size() ) / static_cast<proshade_double> ( angsToTry->at(fIt) ) ) >= ( 1.0 - missPeakThres ) )
1032  {
1033  //==================================== Attempt completing the symmetry using missing peaks
1034  if ( ProSHADE_internal_symmetry::completeMissingCSymmetry ( dataObj, angsToTry->at(fIt), grp, peaks, &missingPeaks,
1035  &expectedAngles, &matchedPeaks, axErrTolerance, verbose ) )
1036  {
1037  ProSHADE_internal_symmetry::saveDetectedCSymmetry ( angsToTry->at(fIt), &matchedPeaks, ret, verbose );
1038  }
1039  }
1040  else
1041  {
1042  //=================================== Symmetry not detected
1043  continue;
1044  }
1045  }
1046  }
1047 
1048  //=============================================== Done
1049  return ;
1050 
1051 }
1052 
1059 void ProSHADE_internal_symmetry::printSymmetryGroup ( std::vector< proshade_unsign > grp, std::vector< proshade_double* > peaks, proshade_signed verbose )
1060 {
1061  //================================================ Detected symmetry table header
1062  std::stringstream ss;
1063  ss << "Detected C" << grp.at(0) << " symmetry with following peaks:";
1064  ProSHADE_internal_messages::printProgressMessage ( verbose, 5, ss.str() );
1065  ProSHADE_internal_messages::printProgressMessage ( verbose, 5, "\tx\t y\t z\tAngle\tPeak height" );
1066 
1067  //================================================ Now print all supporting peaks
1068  for ( proshade_unsign pkIt = 1; pkIt < static_cast<proshade_unsign> ( grp.size() ); pkIt++ )
1069  {
1070  std::stringstream SS;
1071  SS << " " << static_cast<int>( peaks.at(grp.at(pkIt))[0] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(pkIt))[1] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(pkIt))[2] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(pkIt))[3] * 100.0 ) / 100.0 << "\t" << static_cast<int>( peaks.at(grp.at(pkIt))[4] * 100.0 ) / 100.0;
1072  ProSHADE_internal_messages::printProgressMessage ( verbose, 5, SS.str() );
1073  }
1074 
1075  //================================================ Done
1076  return ;
1077 
1078 }
1079 
1085 void ProSHADE_internal_symmetry::printSymmetryCompletion ( proshade_unsign noSyms, proshade_unsign verbose )
1086 {
1087  //================================================ Report completion of symmetry detection
1088  std::stringstream ss;
1089  ss << "Detected " << noSyms << " Cyclic symmetries.";
1090  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, ss.str() );
1091 
1092  //================================================ If no symmetries were found, print warning
1093  if ( noSyms < 1 )
1094  {
1095  ProSHADE_internal_messages::printWarningMessage ( verbose, "!!! ProSHADE WARNING !!! Failed to detect any symmetries. If you believe there should be one, you can try decreasing the resolution or checking that the map is centred on the centry of symmetry (or use map centering option in ProSHADE).", "WS00030" );
1096  }
1097 
1098  //================================================ Done
1099  return ;
1100 
1101 }
1102 
1115 void ProSHADE_internal_symmetry::saveAllCSymmetries ( std::vector< std::vector< proshade_unsign > > detected, std::vector< proshade_double* > peaks, std::vector< proshade_double* >* ret, proshade_double axErr )
1116 {
1117  //================================================ Initialise variables
1118  proshade_double sumX, sumY, sumZ, sumH;
1119 
1120  //================================================ Start saving
1121  for ( proshade_unsign symIt = 0; symIt < static_cast<proshade_unsign> ( detected.size() ); symIt++ )
1122  {
1123  //============================================ Allocate the memory
1124  proshade_double* hlpP = new proshade_double [6];
1125  ProSHADE_internal_misc::checkMemoryAllocation ( hlpP, __FILE__, __LINE__, __func__ );
1126 
1127  //============================================ Set obvious values
1128  hlpP[0] = static_cast<proshade_double> ( detected.at(symIt).at(0) );
1129  hlpP[4] = static_cast<proshade_double> ( ( 2.0 * M_PI ) / hlpP[0] );
1130 
1131  //============================================ Compute peak averages for rest
1132  sumX = 0.0; sumY = 0.0; sumZ = 0.0; sumH = 0.0;
1133  for ( proshade_unsign pkIt = 1; pkIt < static_cast<proshade_unsign> ( detected.at(symIt).size() ); pkIt++ )
1134  {
1135  sumX += peaks.at(detected.at(symIt).at(pkIt))[0];
1136  sumY += peaks.at(detected.at(symIt).at(pkIt))[1];
1137  sumZ += peaks.at(detected.at(symIt).at(pkIt))[2];
1138  sumH += peaks.at(detected.at(symIt).at(pkIt))[4];
1139  }
1140  sumX /= static_cast<proshade_double> ( detected.at(symIt).size() - 1 );
1141  sumY /= static_cast<proshade_double> ( detected.at(symIt).size() - 1 );
1142  sumZ /= static_cast<proshade_double> ( detected.at(symIt).size() - 1 );
1143  sumH /= static_cast<proshade_double> ( detected.at(symIt).size() - 1 );
1144 
1145  //============================================ And add these as well
1146  hlpP[1] = sumX;
1147  hlpP[2] = sumY;
1148  hlpP[3] = sumZ;
1149  hlpP[5] = sumH;
1150 
1151  //============================================ Save the complete symmetry description to the vector, unless already there
1152  if ( !ProSHADE_internal_symmetry::isSymmetrySame ( ret, hlpP, axErr ) )
1153  {
1155  }
1156  else
1157  {
1158  delete[] hlpP;
1159  }
1160  }
1161 
1162  //================================================ Done
1163  return ;
1164 
1165 }
1166 
1177 bool ProSHADE_internal_symmetry::isSymmetrySame ( std::vector< proshade_double* >* ret, proshade_double* sym, proshade_double simThres )
1178 {
1179  //================================================ Initialise variables
1180  proshade_double dotProduct = 0.0;
1181 
1182  //================================================ Check
1183  for ( proshade_unsign symIt = 0; symIt < static_cast<proshade_unsign> ( ret->size() ); symIt++ )
1184  {
1185  //============================================ Minor speed-up => only test for same folds
1186  if ( ret->at(symIt)[0] == sym[0] )
1187  {
1188  //======================================== Is axis the same?
1189  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &ret->at(symIt)[1], &ret->at(symIt)[2],
1190  &ret->at(symIt)[3], &sym[1], &sym[2], &sym[3] );
1191  if ( ( ( 1.0 > ( dotProduct - simThres ) ) && ( 1.0 < ( dotProduct + simThres ) ) ) || ( ( -1.0 > ( dotProduct - simThres ) ) && ( -1.0 < ( dotProduct + simThres ) ) ) )
1192  {
1193  //==================================== Does the already saved have higher height?
1194  if ( ret->at(symIt)[5] >= sym[5] ) { return ( true ); }
1195 
1196  //==================================== In this case, new is better than old - sort it out
1197  ret->at(symIt)[1] = sym[1];
1198  ret->at(symIt)[2] = sym[2];
1199  ret->at(symIt)[3] = sym[3];
1200  ret->at(symIt)[5] = sym[5];
1201  return ( true );
1202  }
1203  }
1204  }
1205 
1206  //================================================ Done - no matches found
1207  return ( false );
1208 
1209 }
1210 
1222 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getDihedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
1223 {
1224  //================================================ Initialise variables
1225  std::vector< proshade_double* > ret;
1226  proshade_double dotProduct;
1227 
1228  //================================================ Report progress
1229  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting D symmetry detection." );
1230 
1231  //================================================If not enough axes, just end here
1232  if ( CSymList->size() < 2 ) { return ( ret ); }
1233 
1234  //================================================ For each unique pair of axes
1235  for ( proshade_unsign ax1 = 0; ax1 < static_cast<proshade_unsign> ( CSymList->size() ); ax1++ )
1236  {
1237  //============================================ Ignore small axes
1238  if ( CSymList->at(ax1)[5] < settings->minSymPeak ) { continue; }
1239 
1240  for ( proshade_unsign ax2 = 1; ax2 < static_cast<proshade_unsign> ( CSymList->size() ); ax2++ )
1241  {
1242  //======================================= Use unique pairs only
1243  if ( ax1 >= ax2 ) { continue; }
1244 
1245  //======================================== Ignore small axes
1246  if ( CSymList->at(ax2)[5] < settings->minSymPeak ) { continue; }
1247 
1248  //======================================= Compute the dot product
1249  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(ax1)[1], &CSymList->at(ax1)[2],
1250  &CSymList->at(ax1)[3], &CSymList->at(ax2)[1],
1251  &CSymList->at(ax2)[2], &CSymList->at(ax2)[3] );
1252 
1253  //======================================== If close to zero, these two axes are perpendicular
1254  if ( std::abs( dotProduct ) < settings->axisErrTolerance )
1255  {
1256  //==================================== Save
1257  if ( CSymList->at(ax1)[0] >= CSymList->at(ax2)[0] )
1258  {
1259  ProSHADE_internal_symmetry::saveDSymmetry ( &ret, CSymList, ax1, ax2 );
1260 
1261  std::vector< proshade_unsign > DSymInd;
1265 
1266  }
1267  else
1268  {
1269  ProSHADE_internal_symmetry::saveDSymmetry ( &ret, CSymList, ax2, ax1 );
1270 
1271  std::vector< proshade_unsign > DSymInd;
1275  }
1276  }
1277  }
1278  }
1279 
1280  //================================================ Report progress
1281  std::stringstream hlpSS;
1282  hlpSS << "Detected " << ret.size() << " D symmetries.";
1283  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, hlpSS.str() );
1284 
1285  //================================================ Done
1286  return ( ret );
1287 
1288 }
1289 
1301 void ProSHADE_internal_symmetry::saveDSymmetry ( std::vector< proshade_double* >* ret, std::vector< proshade_double* >* CSymList, proshade_unsign axisOne, proshade_unsign axisTwo )
1302 {
1303  //================================================ Allocate the memory
1304  proshade_double* hlpP = new proshade_double [12];
1305  ProSHADE_internal_misc::checkMemoryAllocation ( hlpP, __FILE__, __LINE__, __func__ );
1306 
1307  //================================================ Set obvious values
1308  hlpP[0] = static_cast<proshade_double> ( CSymList->at(axisOne)[0] );
1309  hlpP[4] = static_cast<proshade_double> ( ( 2.0 * M_PI ) / hlpP[0] );
1310  hlpP[6] = static_cast<proshade_double> ( CSymList->at(axisTwo)[0] );
1311  hlpP[10] = static_cast<proshade_double> ( ( 2.0 * M_PI ) / hlpP[6] );
1312 
1313  //================================================ Set the axis and heights
1314  hlpP[1] = CSymList->at(axisOne)[1];
1315  hlpP[2] = CSymList->at(axisOne)[2];
1316  hlpP[3] = CSymList->at(axisOne)[3];
1317  hlpP[5] = CSymList->at(axisOne)[5];
1318  hlpP[7] = CSymList->at(axisTwo)[1];
1319  hlpP[8] = CSymList->at(axisTwo)[2];
1320  hlpP[9] = CSymList->at(axisTwo)[3];
1321  hlpP[11] = CSymList->at(axisTwo)[5];
1322 
1323  //================================================ Save to ret
1325 
1326  //================================================ Done
1327  return ;
1328 
1329 }
1330 
1340 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getTetrahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
1341 {
1342  //================================================ Initialise variables
1343  std::vector< proshade_double* > ret;
1344 
1345  //================================================ Report progress
1346  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting T symmetry detection." );
1347 
1348  //================================================ Are the basic requirements for tetrahedral symmetry met?
1350  {
1351  //============================================ Search for all the symmetry axes
1352  ProSHADE_internal_symmetry::findTetra4C3s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
1353  if ( ret.size() != 4 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
1354 
1355  ProSHADE_internal_symmetry::findTetra3C2s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
1356  if ( ret.size() != 7 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
1357  else
1358  {
1359  for ( proshade_unsign csIt = 0; csIt < static_cast<proshade_unsign> ( CSymList->size() ); csIt++ )
1360  {
1361  for ( proshade_unsign retIt = 0; retIt < static_cast<proshade_unsign> ( ret.size() ); retIt++ )
1362  {
1363  if ( ( CSymList->at(csIt)[0] == ret.at(retIt)[0] ) &&
1364  ( CSymList->at(csIt)[1] == ret.at(retIt)[1] ) &&
1365  ( CSymList->at(csIt)[2] == ret.at(retIt)[2] ) &&
1366  ( CSymList->at(csIt)[3] == ret.at(retIt)[3] ) &&
1367  ( CSymList->at(csIt)[4] == ret.at(retIt)[4] ) &&
1368  ( CSymList->at(csIt)[5] == ret.at(retIt)[5] ) )
1369  {
1371  }
1372  }
1373  }
1374  }
1375  }
1376 
1377  //================================================ Report progress
1378  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "T symmetry detection complete." );
1379 
1380  //================================================ Done
1381  return ( ret );
1382 
1383 }
1384 
1396 bool ProSHADE_internal_symmetry::detectTetrahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
1397 {
1398  //================================================ Initialise variables
1399  std::vector< proshade_unsign > C3List;
1400  proshade_double dotProduct;
1401 
1402  //================================================ Find all C3 symmetries
1403  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
1404  {
1405  if ( CSymList->at(cSym)[0] == 3 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C3List, cSym ); }
1406  }
1407 
1408  //================================================ For each unique pair of C3s
1409  for ( proshade_unsign c31 = 0; c31 < static_cast<proshade_unsign> ( C3List.size() ); c31++ )
1410  {
1411  for ( proshade_unsign c32 = 1; c32 < static_cast<proshade_unsign> ( C3List.size() ); c32++ )
1412  {
1413  //================================ Unique pairs only
1414  if ( c31 >= c32 ) { continue; }
1415 
1416  //======================================== Check the angle between the C3 axes
1417  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] );
1418 
1419  //================================ Is the angle approximately the dihedral angle
1420  if ( ( ( 1.0 / 3.0 ) > ( dotProduct - axErr ) ) && ( ( 1.0 / 3.0 ) < ( dotProduct + axErr ) ) )
1421  {
1422  return ( true );
1423  }
1424  }
1425  }
1426 
1427  //================================================ Done
1428  return ( false );
1429 
1430 }
1431 
1445 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_unsign verbose, proshade_double minPeakHeight )
1446 {
1447  //================================================ Initialise variables
1448  std::vector< proshade_unsign > C3PossibilitiesHlp;
1449  std::vector< std::vector< proshade_unsign > > C3Possibilities;
1450  bool groupMatched;
1451 
1452  //================================================ Report progress
1453  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of four C3 axes." );
1454 
1455  //================================================ For all symmetries in the C symmetries list
1456  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1457  {
1458  //============================================ Search only using C3s
1459  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
1460 
1461  //============================================ If this is the first C3, then just save it to the first group of the temporary holder
1462  if ( C3Possibilities.size() == 0 ) { ProSHADE_internal_misc::addToUnsignVector ( &C3PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C3Possibilities, C3PossibilitiesHlp ); continue; }
1463 
1464  //============================================ If second or more C3, check if it has the correct angle to all other already found C3s for each group
1465  groupMatched = false;
1466  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C3Possibilities.size() ); gIt++ )
1467  {
1468  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; }
1469  }
1470 
1471  //============================================ If no group matched, create a new group
1472  if ( !groupMatched ) { C3PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C3PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C3Possibilities, C3PossibilitiesHlp ); continue; }
1473  }
1474 
1475  //================================================ Test for missing symmetry axes, if need be
1476  ProSHADE_internal_symmetry::findMissingAxes ( &C3Possibilities, CSymList, 4, axErr, 1.0/3.0, 3, dataObj, minPeakHeight );
1477 
1478  //================================================ Any group has 4 entries? If more such groups, take the one with highest average height.
1479  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
1480  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; } } }
1481 
1482  if ( C3Possibilities.at(maxGrp).size() == 4 )
1483  {
1484  //============================================ Success! Save and exit
1485  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)) ); }
1486 
1487  //============================================ Report progress
1488  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of four C3 axes successfull." );
1489 
1490  //============================================ Done
1491  return ;
1492  }
1493 
1494  //================================================ Done
1495  return ;
1496 
1497 }
1498 
1515 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 )
1516 {
1517  //================================================ Initialise variables
1518  bool allAnglesMet = true;
1519  proshade_double dotProduct;
1520 
1521  //================================================ Improve if required
1522  if ( improve )
1523  {
1524  for ( proshade_unsign mIt = 0; mIt < static_cast<proshade_unsign> ( grp->size() ); mIt++ )
1525  {
1526  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] );
1527 
1528  if ( ( ( 1.0 > ( dotProduct - axErr ) ) && ( 1.0 < ( dotProduct + axErr ) ) ) || ( ( -1.0 > ( dotProduct - axErr ) ) && ( -1.0 < ( dotProduct + axErr ) ) ) )
1529  {
1530  if ( sym[5] > CSymList->at(grp->at(mIt))[5] )
1531  {
1532  grp->at(mIt) = pos;
1533  }
1534  else
1535  {
1536  allAnglesMet = false;
1537  return ( allAnglesMet );
1538  }
1539  }
1540  }
1541  }
1542 
1543  //================================================ For all group members
1544  for ( proshade_unsign mIt = 0; mIt < static_cast<proshade_unsign> ( grp->size() ); mIt++ )
1545  {
1546  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] );
1547 
1548  if ( ( angle > ( std::abs ( dotProduct ) - axErr ) ) &&
1549  ( angle < ( std::abs ( dotProduct ) + axErr ) ) )
1550  {
1551  //======================================== Matching group memner - try next one
1552  }
1553  else
1554  {
1555  //======================================== Group member not matched - try next group
1556  allAnglesMet = false;
1557  break;
1558  }
1559  }
1560 
1561  //================================================ Done
1562  return ( allAnglesMet );
1563 
1564 }
1565 
1582 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 )
1583 {
1584  //================================================ Initialise variables
1585  std::vector< proshade_double* > hlpVec;
1586  bool atLeastOne = false;
1587 
1588  //================================================ Proceed only if need be
1589  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( possibilities->size() ); gIt++ )
1590  {
1591  if ( static_cast<proshade_unsign> ( possibilities->at(gIt).size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
1592  }
1593 
1594  //================================================ For each possible group
1595  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( possibilities->size() ); gIt++ )
1596  {
1597  //============================================ This will not work for less than two axes in group
1598  if ( possibilities->at(gIt).size() < 2 ) { continue; }
1599 
1600  //============================================ Prepare iteration
1601  hlpVec.clear ( );
1602 
1603  //============================================ Search for missing axes
1604  ProSHADE_internal_symmetry::searchMissingSymmetrySpace ( dataObj, CSymList, &possibilities->at(gIt), &hlpVec, axErr, angle, fold, minPeakHeight );
1605 
1606  //============================================ Add missing axes
1607  if ( hlpVec.size() > 0 )
1608  {
1609  //======================================== Start adding by highest first
1610  std::sort ( hlpVec.begin(), hlpVec.end(), ProSHADE_internal_misc::sortSymHlpInv );
1611 
1612  //======================================== For each missing axis
1613  for ( proshade_unsign axIt = 0; axIt < static_cast<proshade_unsign> ( hlpVec.size() ); axIt++ )
1614  {
1615  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &possibilities->at(gIt), hlpVec.at(axIt), axErr, angle, false ) )
1616  {
1617  //================================ Check for uniqueness
1618  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, hlpVec.at(axIt), axErr ) )
1619  {
1620  //============================ Add
1621  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, hlpVec.at(axIt) );
1622  ProSHADE_internal_misc::addToUnsignVector ( &possibilities->at(gIt), static_cast<proshade_unsign> ( CSymList->size()-1 ) );
1623  }
1624  }
1625  }
1626  }
1627 
1628  if ( possibilities->at(gIt).size() == requiredNoAxes ) { atLeastOne = true; }
1629  }
1630 
1631  //================================================ Done
1632  return ( atLeastOne );
1633 
1634 }
1635 
1642 bool ProSHADE_internal_symmetry::sortArrVecHlp ( const proshade_double* a, const proshade_double* b )
1643 {
1644  //================================================ Compare
1645  return ( a[0] < b[0] );
1646 
1647 }
1648 
1663 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 )
1664 {
1665  //================================================ Initialise variables
1666  proshade_double ret = 0.0;
1667  proshade_double curSum = 0.0;
1668  proshade_double maxVal = 0.0;
1669  proshade_double angStep = std::acos ( 1.0 - axErr ) / 2;
1670  std::vector< proshade_double* > angVec;
1671 
1672  //================================================ Find map points conforming to the axis
1673  angVec = ProSHADE_internal_symmetry::findMissingAxisPoints ( xVal, yVal, zVal, dataObj, axErr );
1674 
1675  //================================================ Sort points by angle
1676  std::sort ( angVec.begin(), angVec.end(), ProSHADE_internal_symmetry::sortArrVecHlp );
1677 
1678  //================================================ Find the best X peaks with correct distances
1679  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( std::floor ( ( 2.0 * M_PI / angStep ) / fold ) ); iter++ )
1680  {
1681  //============================================ Initialise new ang group iteration
1682  curSum = 0.0;
1683 
1684  //============================================ For each of the fold times
1685  for ( proshade_unsign angCmb = 0; angCmb < static_cast<proshade_unsign> ( fold ); angCmb++ )
1686  {
1687  //======================================== Initialise
1688  maxVal = 0.0;
1689 
1690  //======================================== Search
1691  for ( proshade_unsign angIt = 0; angIt < static_cast<proshade_unsign> ( angVec.size() ); angIt++ )
1692  {
1693  if ( angVec.at(angIt)[0] < ( ( iter*angStep ) + ( ( 2.0 * M_PI / fold ) * angCmb ) ) ) { continue; }
1694  if ( angVec.at(angIt)[0] > ( ( (iter+1)*angStep ) + ( ( 2.0 * M_PI / fold ) * angCmb ) ) ) { break; }
1695 
1696  if ( angVec.at(angIt)[1] > maxVal ) { maxVal = angVec.at(angIt)[1]; }
1697  }
1698  curSum += maxVal;
1699  }
1700  curSum /= static_cast<proshade_double> ( fold );
1701  if ( ret < curSum ) { ret = curSum; }
1702  }
1703 
1704  //================================================ Release memory
1705  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( angVec.size() ); iter++ ) { delete[] angVec.at(iter); }
1706 
1707  //================================================ Done
1708  return ( ret );
1709 
1710 }
1711 
1725 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 )
1726 {
1727  //================================================ Initialise variables
1728  proshade_double euA, euB, euG, xPk, yPk, zPk, anglPk;
1729  proshade_double* rotMat = new proshade_double [9];
1730  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat, __FILE__, __LINE__, __func__ );
1731  proshade_unsign arrIndex;
1732  std::vector< proshade_double* > angVec;
1733 
1734  //================================================ Search the self-rotation map
1735  for ( proshade_unsign xIt = 0; xIt < ( dataObj->getMaxBand() * 2 ); xIt++ )
1736  {
1737  for ( proshade_unsign yIt = 0; yIt < ( dataObj->getMaxBand() * 2 ); yIt++ )
1738  {
1739  for ( proshade_unsign zIt = 0; zIt < ( dataObj->getMaxBand() * 2 ); zIt++ )
1740  {
1741  //==================================== Get height and check against threshold
1742  arrIndex = zIt + ( dataObj->getMaxBand() * 2 ) * ( yIt + ( dataObj->getMaxBand() * 2 ) * xIt );
1743 
1744  //==================================== Get angle-axis values
1745  ProSHADE_internal_maths::getEulerZXZFromSOFTPosition ( dataObj->getMaxBand(), static_cast<proshade_signed> ( xIt ),
1746  static_cast<proshade_signed> ( yIt ), static_cast<proshade_signed> ( zIt ),
1747  &euA, &euB, &euG );
1749  ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( rotMat, &xPk, &yPk, &zPk, &anglPk );
1750 
1751  //==================================== Set largest axis element to positive
1752  if ( ( ( std::max ( std::abs ( xPk ), std::max( std::abs ( yPk ), std::abs ( zPk ) ) ) == std::abs ( xPk ) ) && ( xPk < 0.0 ) ) ||
1753  ( ( std::max ( std::abs ( xPk ), std::max( std::abs ( yPk ), std::abs ( zPk ) ) ) == std::abs ( yPk ) ) && ( yPk < 0.0 ) ) ||
1754  ( ( std::max ( std::abs ( xPk ), std::max( std::abs ( yPk ), std::abs ( zPk ) ) ) == std::abs ( zPk ) ) && ( zPk < 0.0 ) ) )
1755  {
1756  xPk *= -1.0;
1757  yPk *= -1.0;
1758  zPk *= -1.0;
1759  anglPk *= -1.0;
1760  }
1761 
1762  //==================================== Does the peak match the required axis?
1763  if ( ProSHADE_internal_maths::vectorOrientationSimilarity ( xPk, yPk, zPk, xVal, yVal, zVal, axErr ) )
1764  {
1765  //================================ Matching map point - save it
1766  proshade_double* hlpArr = new proshade_double [2];
1767  ProSHADE_internal_misc::checkMemoryAllocation ( hlpArr, __FILE__, __LINE__, __func__ );
1768  hlpArr[0] = anglPk + M_PI;
1769  hlpArr[1] = pow( dataObj->getInvSO3Coeffs()[arrIndex][0], 2.0 ) +
1770  pow( dataObj->getInvSO3Coeffs()[arrIndex][1], 2.0 );
1771  ProSHADE_internal_misc::addToDblPtrVector ( &angVec, hlpArr );
1772  }
1773  }
1774  }
1775  }
1776 
1777  //================================================ Release memory
1778  delete[] rotMat;
1779 
1780  //================================================ Done
1781  return ( angVec );
1782 
1783 }
1784 
1799 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 )
1800 {
1801  //================================================ Create symmetry array from the inputs
1802  proshade_double* hlpSym = new proshade_double [6];
1803  ProSHADE_internal_misc::checkMemoryAllocation ( hlpSym, __FILE__, __LINE__, __func__ );
1804 
1805  //================================================ Fill it in
1806  hlpSym[0] = static_cast<proshade_double> ( fold );
1807  hlpSym[1] = axX;
1808  hlpSym[2] = axY;
1809  hlpSym[3] = axZ;
1810  hlpSym[4] = ( 2.0 * M_PI ) / static_cast<proshade_double> ( fold );
1811  hlpSym[5] = height;
1812 
1813  //================================================ Check if similar symmetry does not exist already
1814  for ( proshade_unsign symIt = 0; symIt < static_cast<proshade_unsign> ( axVec->size() ); symIt++ )
1815  {
1816  //============================================ Minor speed-up => only test for same folds
1817  if ( axVec->at(symIt)[0] == hlpSym[0] )
1818  {
1819  if ( ProSHADE_internal_maths::vectorOrientationSimilarity ( axVec->at(symIt)[1],
1820  axVec->at(symIt)[2],
1821  axVec->at(symIt)[3],
1822  hlpSym[1],
1823  hlpSym[2],
1824  hlpSym[3],
1825  axErr ) )
1826  {
1827  //==================================== Almost identical entry
1828  if ( axVec->at(symIt)[5] < hlpSym[5] )
1829  {
1830  //================================ If higher, save
1831  delete[] axVec->at(symIt);
1832  axVec->at(symIt) = hlpSym;
1833  return ;
1834  }
1835  else
1836  {
1837  //================================ or just terminate if better is already saved
1838  delete[] hlpSym;
1839  return ;
1840  }
1841  }
1842  }
1843  }
1844 
1845  //================================================ Not matched to anything
1847 
1848  //================================================ Done
1849  return ;
1850 
1851 }
1852 
1867 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 )
1868 {
1869  //================================================ Sanity check
1870  if ( grp->size() < 2 ) { return; }
1871 
1872  //================================================ Initialise variables
1873  proshade_double axHeight = 0.0;
1874  proshade_double* symHlp = new proshade_double[6];
1875  ProSHADE_internal_misc::checkMemoryAllocation ( symHlp, __FILE__, __LINE__, __func__ );
1876 
1877  //================================================ For each axis pair in the group, find the possible solutions
1878  for ( proshade_unsign fAx = 0; fAx < static_cast<proshade_unsign> ( grp->size() ); fAx++ )
1879  {
1880  for ( proshade_unsign sAx = 1; sAx < static_cast<proshade_unsign> ( grp->size() ); sAx++ )
1881  {
1882  //======================================== Only unique pairs
1883  if ( fAx >= sAx ) { continue; }
1884 
1885  //======================================== Find possible axis having the required angle to this pair ( solution 1 )
1886  std::vector< proshade_double > solVec = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( CSymList->at(grp->at(fAx))[1],
1887  CSymList->at(grp->at(fAx))[2],
1888  CSymList->at(grp->at(fAx))[3],
1889  CSymList->at(grp->at(sAx))[1],
1890  CSymList->at(grp->at(sAx))[2],
1891  CSymList->at(grp->at(sAx))[3], angle, angle );
1892 
1893  //======================================== Set largest axis element to positive
1894  if ( ( ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) == std::abs ( solVec.at(0) ) ) && ( solVec.at(0) < 0.0 ) ) ||
1895  ( ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) == std::abs ( solVec.at(1) ) ) && ( solVec.at(1) < 0.0 ) ) ||
1896  ( ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) == std::abs ( solVec.at(2) ) ) && ( solVec.at(2) < 0.0 ) ) )
1897  {
1898  solVec.at(0) *= -1.0;
1899  solVec.at(1) *= -1.0;
1900  solVec.at(2) *= -1.0;
1901  }
1902 
1903  //======================================== Does the solution fit the whole group?
1904  symHlp[1] = solVec.at(0); symHlp[2] = solVec.at(1); symHlp[3] = solVec.at(2);
1905  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, grp, symHlp, axErr, angle, true ) )
1906  {
1907  //==================================== Find the height for the axis
1908  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( solVec.at(0), solVec.at(1), solVec.at(2), dataObj, fold, axErr );
1909 
1910  //================================ Save max height result
1911  if ( axHeight >= minPeakHeight ) { ProSHADE_internal_symmetry::saveMissingAxisNewOnly ( hlpVec, solVec.at(0), solVec.at(1), solVec.at(2), axHeight, fold, axErr ); }
1912  }
1913 
1914  //======================================== Find possible axis having the required angle to this pair ( solution 2 )
1915  solVec = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( CSymList->at(grp->at(fAx))[1],
1916  CSymList->at(grp->at(fAx))[2],
1917  CSymList->at(grp->at(fAx))[3],
1918  CSymList->at(grp->at(sAx))[1],
1919  CSymList->at(grp->at(sAx))[2],
1920  CSymList->at(grp->at(sAx))[3], -angle, -angle );
1921 
1922  //======================================== Set largest axis element to positive
1923  if ( ( ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) == std::abs ( solVec.at(0) ) ) && ( solVec.at(0) < 0.0 ) ) ||
1924  ( ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) == std::abs ( solVec.at(1) ) ) && ( solVec.at(1) < 0.0 ) ) ||
1925  ( ( std::max ( std::abs ( solVec.at(0) ), std::max( std::abs ( solVec.at(1) ), std::abs ( solVec.at(2) ) ) ) == std::abs ( solVec.at(2) ) ) && ( solVec.at(2) < 0.0 ) ) )
1926  {
1927  solVec.at(0) *= -1.0;
1928  solVec.at(1) *= -1.0;
1929  solVec.at(2) *= -1.0;
1930  }
1931 
1932  //======================================== Does the solution fit the whole group?
1933  symHlp[1] = solVec.at(0); symHlp[2] = solVec.at(1); symHlp[3] = solVec.at(2);
1934  if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, grp, symHlp, axErr, angle, true ) )
1935  {
1936  //==================================== Find the height for the axis
1937  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( solVec.at(0), solVec.at(1), solVec.at(2), dataObj, fold, axErr );
1938 
1939  //================================ Save max height result
1940  if ( axHeight >= minPeakHeight ) { ProSHADE_internal_symmetry::saveMissingAxisNewOnly ( hlpVec, solVec.at(0), solVec.at(1), solVec.at(2), axHeight, fold, axErr ); }
1941  }
1942  }
1943  }
1944 
1945  //================================================ Release memory
1946  delete[] symHlp;
1947 
1948  //================================================ Done
1949  return ;
1950 
1951 }
1952 
1966 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_unsign verbose, proshade_double minPeakHeight )
1967 {
1968  //================================================ Initialise variables
1969  std::vector< proshade_unsign > C3s, prospectiveC2s, C2PossibilitiesHlp;
1970  std::vector< std::vector< proshade_unsign > > C2Possibilities;
1971  proshade_double dotProd;
1972  bool groupMatched;
1973  for ( proshade_unsign iter = 0; iter < 4; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &C3s, iter ); }
1974 
1975  //================================================ Report progress
1976  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of three C2 axes." );
1977 
1978  //================================================ For each C3
1979  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( ret->size() ); rIt++ )
1980  {
1981  //============================================ For each C2, check it has angle ( acos(0.5) ) to the tested C3
1982  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
1983  {
1984  //======================================== Search only using C2s
1985  if ( CSymList->at(cIt)[0] != 2.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
1986 
1987  //======================================== Check the C2 axis to the C3 ( acos ( 0.5 ) )
1988  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1], &ret->at(rIt)[2], &ret->at(rIt)[3],
1989  &CSymList->at(cIt)[1], &CSymList->at(cIt)[2], &CSymList->at(cIt)[3] );
1990 
1991  if ( ( std::abs ( dotProd ) > ( 0.5 - axErr ) ) && ( std::abs ( dotProd ) < ( 0.5 + axErr ) ) ) { ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt ); }
1992  }
1993  }
1994 
1995  //================================================ Group the prospective C2s
1996  C2Possibilities.clear(); C2PossibilitiesHlp.clear();
1997  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( prospectiveC2s.size() ); cIt++ )
1998  {
1999  //============================================ If second or more C2, check if it can be placed in any group with being perpendicular to all its members
2000  groupMatched = false;
2001  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C2Possibilities.size() ); gIt++ )
2002  {
2003  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; }
2004  }
2005 
2006  //============================================ If no group matched, create a new group
2007  if ( !groupMatched ) { C2PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C2PossibilitiesHlp, prospectiveC2s.at(cIt) ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C2Possibilities, C2PossibilitiesHlp ); continue; }
2008  }
2009 
2010  //================================================ Find the best group or return empty
2011  while ( C2Possibilities.size() != 0 )
2012  {
2013  //============================================ Test for missing symmetry axes, if need be
2014  ProSHADE_internal_symmetry::findMissingAxes ( &C2Possibilities, CSymList, 3, axErr, 0.0, 2, dataObj, minPeakHeight );
2015 
2016  //============================================ Found 3 C2s?
2017  if ( C2Possibilities.at(0).size() == 3 )
2018  {
2019  //======================================== Success! Save and exit
2020  for ( proshade_unsign it = 0; it < 3; it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C2Possibilities.at(0).at(it)) ); }
2021 
2022  //======================================== Report progress
2023  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of three C2 axes successfull." );
2024 
2025  //======================================== Done
2026  return ;
2027  }
2028  else { C2Possibilities.erase ( C2Possibilities.begin() ); }
2029  }
2030 
2031  //================================================ Done
2032  return ;
2033 
2034 }
2035 
2050 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 )
2051 {
2052  //================================================ Initialise variables
2053  bool ret = false;
2054  proshade_double dotProduct;
2055 
2056  //================================================ For all pairs of axes
2057  for ( proshade_unsign g1It = 0; g1It < static_cast<proshade_unsign> ( grp1->size() ); g1It++ )
2058  {
2059  for ( proshade_unsign g2It = 0; g2It < static_cast<proshade_unsign> ( grp2->size() ); g2It++ )
2060  {
2061  //======================================== Find the angle
2062  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &GrList1->at(grp1->at(g1It))[1],
2063  &GrList1->at(grp1->at(g1It))[2],
2064  &GrList1->at(grp1->at(g1It))[3],
2065  &GrList2->at(grp2->at(g2It))[1],
2066  &GrList2->at(grp2->at(g2It))[2],
2067  &GrList2->at(grp2->at(g2It))[3] );
2068 
2069  //======================================== Check the angle
2070  if ( ( angle > ( dotProduct - axErr ) ) && ( angle < ( dotProduct + axErr ) ) )
2071  {
2072  ret = true;
2073  return ( ret );
2074  }
2075  }
2076  }
2077 
2078  //================================================ Done
2079  return ( ret );
2080 
2081 }
2082 
2093 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getOctahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
2094 {
2095  //================================================ Initialise variables
2096  std::vector< proshade_double* > ret;
2097 
2098  //================================================ Report progress
2099  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting O symmetry detection." );
2100 
2101  //================================================ Are the basic requirements for tetrahedral symmetry met?
2102  if ( ProSHADE_internal_symmetry::detectOctahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
2103  {
2104  //============================================ Search for all the symmetry axes
2105  ProSHADE_internal_symmetry::findOcta3C4s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
2106  if ( ret.size() != 3 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
2107 
2108  ProSHADE_internal_symmetry::findOcta4C3s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
2109  if ( ret.size() != 7 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
2110 
2111  ProSHADE_internal_symmetry::findOcta6C2s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
2112  if ( ret.size() != 13 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
2113  else
2114  {
2115  for ( proshade_unsign csIt = 0; csIt < static_cast<proshade_unsign> ( CSymList->size() ); csIt++ )
2116  {
2117  for ( proshade_unsign retIt = 0; retIt < static_cast<proshade_unsign> ( ret.size() ); retIt++ )
2118  {
2119  if ( ( CSymList->at(csIt)[0] == ret.at(retIt)[0] ) &&
2120  ( CSymList->at(csIt)[1] == ret.at(retIt)[1] ) &&
2121  ( CSymList->at(csIt)[2] == ret.at(retIt)[2] ) &&
2122  ( CSymList->at(csIt)[3] == ret.at(retIt)[3] ) &&
2123  ( CSymList->at(csIt)[4] == ret.at(retIt)[4] ) &&
2124  ( CSymList->at(csIt)[5] == ret.at(retIt)[5] ) )
2125  {
2127  }
2128  }
2129  }
2130  }
2131  }
2132 
2133  //================================================ Report progress
2134  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "O symmetry detection complete." );
2135 
2136  //================================================ Done
2137  return ( ret );
2138 
2139 }
2140 
2152 bool ProSHADE_internal_symmetry::detectOctahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
2153 {
2154  //================================================ Initialise variables
2155  std::vector< proshade_unsign > C4List;
2156  proshade_double dotProduct;
2157 
2158  //================================================ Find all C4 symmetries
2159  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2160  {
2161  if ( CSymList->at(cSym)[0] == 4 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C4List, cSym ); }
2162  }
2163 
2164  //================================================ For each unique pair of C3s
2165  for ( proshade_unsign c4 = 0; c4 < static_cast<proshade_unsign> ( C4List.size() ); c4++ )
2166  {
2167  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2168  {
2169  //======================================== Compare only C3s to the C3List
2170  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2171 
2172  //======================================== Check the angle between the C4 and C3 axes
2173  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C4List.at(c4))[1],
2174  &CSymList->at(C4List.at(c4))[2],
2175  &CSymList->at(C4List.at(c4))[3],
2176  &CSymList->at(cSym)[1],
2177  &CSymList->at(cSym)[2],
2178  &CSymList->at(cSym)[3] );
2179 
2180  //======================================== Is the angle approximately the dihedral angle
2181  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( dotProduct - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( dotProduct + axErr ) ) )
2182  {
2183  return ( true );
2184  }
2185  }
2186  }
2187 
2188  //================================================ Done
2189  return ( false );
2190 
2191 }
2192 
2206 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_unsign verbose, proshade_double minPeakHeight )
2207 {
2208  //================================================ Initialise variables
2209  std::vector< proshade_unsign > C4PossibilitiesHlp;
2210  std::vector< std::vector< proshade_unsign > > C4Possibilities;
2211  bool groupMatched;
2212 
2213  //================================================ Report progress
2214  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of three C4 axes." );
2215 
2216  //================================================ For all symmetries in the C symmetries list
2217  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2218  {
2219  //============================================ Search only using C4s
2220  if ( CSymList->at(cIt)[0] != 4.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
2221 
2222  //============================================ If second or more C4, check if it has the correct angle to all other already found C4s for each group
2223  groupMatched = false;
2224  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C4Possibilities.size() ); gIt++ )
2225  {
2226  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; }
2227  }
2228 
2229  //=========================================== If no group matched, create a new group
2230  if ( !groupMatched ) { C4PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C4PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C4Possibilities, C4PossibilitiesHlp ); continue; }
2231  }
2232 
2233  //================================================ Test for missing symmetry axes, if need be
2234  ProSHADE_internal_symmetry::findMissingAxes ( &C4Possibilities, CSymList, 3, axErr, 0.0, 4, dataObj, minPeakHeight );
2235 
2236  //================================================ Any group has 3 entries? If more such groups, take the one with highest average height.
2237  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
2238  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; } } }
2239 
2240  if ( C4Possibilities.at(maxGrp).size() == 3 )
2241  {
2242  //============================================ Success! Save and exit
2243  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)) ); }
2244 
2245  //============================================ Report progress
2246  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of three C4 axes successfull." );
2247 
2248  //============================================ Done
2249  return ;
2250  }
2251 
2252  //================================================ Done
2253  return ;
2254 
2255 }
2256 
2272 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_unsign verbose, proshade_double minPeakHeight )
2273 {
2274  //================================================ Initialise variables
2275  std::vector< proshade_unsign > C4s, prospectiveC3s, C3PossibilitiesHlp;
2276  std::vector< std::vector< proshade_unsign > > C3Possibilities;
2277  proshade_double dotProd;
2278  bool groupMatched;
2279  for ( proshade_unsign iter = 0; iter < 3; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &C4s, iter ); }
2280 
2281  //================================================ Report progress
2282  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of four C3 axes." );
2283 
2284  //================================================ For each C4
2285  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( ret->size() ); rIt++ )
2286  {
2287  //============================================ For each C3, check it has angle ( acos( 1/sqrt(3) ) ) to the tested C4
2288  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2289  {
2290  //======================================== Search only using C3s
2291  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
2292 
2293  //======================================== Check the C3 axis to the C4 ( acos ( 1/sqrt(3) ) )
2294  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] );
2295 
2296  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 ); }
2297  }
2298  }
2299 
2300  //================================================ Group the prospective C3s
2301  C3Possibilities.clear(); C3PossibilitiesHlp.clear();
2302  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( prospectiveC3s.size() ); cIt++ )
2303  {
2304  //============================================ If second or more C3, check if it can be placed in any group with having acos (1/3) to all its members
2305  groupMatched = false;
2306  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C3Possibilities.size() ); gIt++ )
2307  {
2308  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; }
2309  }
2310 
2311  //============================================ If no group matched, create a new group
2312  if ( !groupMatched ) { C3PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C3PossibilitiesHlp, prospectiveC3s.at(cIt) ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C3Possibilities, C3PossibilitiesHlp ); continue; }
2313  }
2314 
2315  //================================================ Find the best group or return empty
2316  while ( C3Possibilities.size() != 0 )
2317  {
2318  //============================================ Test for missing symmetry axes, if need be
2319  ProSHADE_internal_symmetry::findMissingAxes ( &C3Possibilities, CSymList, 4, axErr, 1.0/3.0, 3, dataObj, minPeakHeight );
2320 
2321  //============================================ Found four C3s?
2322  if ( C3Possibilities.at(0).size() == 4 )
2323  {
2324  //======================================== Success! Save and exit
2325  for ( proshade_unsign it = 0; it < 4; it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C3Possibilities.at(0).at(it)) ); }
2326 
2327  //======================================== Report progress
2328  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of four C3 axes successfull." );
2329 
2330  //======================================== Done
2331  return ;
2332  }
2333  else { C3Possibilities.erase ( C3Possibilities.begin() ); }
2334  }
2335 
2336  //================================================ Done
2337  return ;
2338 
2339 }
2340 
2354 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_unsign verbose, proshade_double minPeakHeight )
2355 {
2356  //================================================ Initialise variables
2357  std::vector< proshade_unsign > prospectiveC2s, retGrp;
2358  proshade_double dotProd;
2359  proshade_unsign noPerpendicular, noSqrtTwo;
2360 
2361  //================================================ Report progress
2362  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of six C2 axes." );
2363 
2364  //================================================ For each C2
2365  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2366  {
2367  //============================================ Use only C2s
2368  if ( CSymList->at(cIt)[0] != 2.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
2369 
2370  //============================================ Check the C2 has acos ( 1/sqrt(2) ) to 2 C4s and acos ( 0.0 ) to the third C4
2371  noPerpendicular = 0; noSqrtTwo = 0;
2372  for ( proshade_unsign rIt = 0; rIt < 3; rIt++ )
2373  {
2374  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
2375  &ret->at(rIt)[2],
2376  &ret->at(rIt)[3],
2377  &CSymList->at(cIt)[1],
2378  &CSymList->at(cIt)[2],
2379  &CSymList->at(cIt)[3] );
2380 
2381  if ( ( std::abs ( dotProd ) > ( ( 1.0 / sqrt(2.0) ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / sqrt(2.0) ) + axErr ) ) ) { noSqrtTwo += 1; continue; }
2382  if ( ( std::abs ( dotProd ) > ( 0.0 - axErr ) ) && ( std::abs ( dotProd ) < ( 0.0 + axErr ) ) ) { noPerpendicular += 1; continue; }
2383  }
2384 
2385  //============================================ If correct angles distribution is found, save the axis
2386  if ( ( noSqrtTwo == 2 ) && ( noPerpendicular == 1 ) )
2387  {
2388  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt );
2389  }
2390  }
2391 
2392  //================================================ Search for missing axes
2393  for ( proshade_unsign iter = 0; iter < 3; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
2394  if ( !ProSHADE_internal_symmetry::findMissingAxesDual ( &prospectiveC2s, CSymList, ret, &retGrp, 6, axErr, 1, 0.0, 2, 1/sqrt(2.0), 2, dataObj ) )
2395  {
2396  return ;
2397  }
2398 
2399  //================================================ Found correct number of axes! Now save the
2400  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC2s.size() ); iter++ )
2401  {
2402  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC2s.at(iter)) );
2403  }
2404 
2405  //================================================ Report progress
2406  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of six C2 axes successfull." );
2407 
2408  //================================================ Done
2409  return ;
2410 
2411 }
2412 
2434 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 )
2435 {
2436  //================================================ Initialise variables
2437  bool atLeastOne = false;
2438  std::vector< proshade_double* > prosp;
2439  std::vector< proshade_double > sol;
2440 
2441  //================================================ Proceed only if need be
2442  if ( static_cast<proshade_unsign> ( possibilities->size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
2443 
2444  //================================================ Copy already found to prospective
2445  for ( proshade_unsign prIt = 0; prIt < static_cast<proshade_unsign> ( possibilities->size() ); prIt++ )
2446  {
2447  ProSHADE_internal_symmetry::addAxisUnlessSame ( CSymList->at(possibilities->at(prIt))[0],
2448  CSymList->at(possibilities->at(prIt))[1],
2449  CSymList->at(possibilities->at(prIt))[2],
2450  CSymList->at(possibilities->at(prIt))[3],
2451  CSymList->at(possibilities->at(prIt))[5], &prosp, axErr );
2452  }
2453 
2454  //================================================ Start generating possible solutions
2455  for ( proshade_unsign rgIt1 = 0; rgIt1 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt1++ )
2456  {
2457  for ( proshade_unsign rgIt2 = 0; rgIt2 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt2++ )
2458  {
2459  //======================================== Use unique combinations (order matters here!)
2460  if ( rgIt1 == rgIt2 ) { continue; }
2461 
2462  //======================================== Generate possible solution (1)
2463  sol = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
2464  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3], angle1, angle2 );
2465 
2466  //======================================== Check if solution fits the group completely
2467  ProSHADE_internal_symmetry::checkFittingAxisDualAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, dataObj );
2468  if ( prosp.size() == requiredNoAxes ) { break; }
2469 
2470  //======================================== Generate possible solution (2)
2471  sol = ProSHADE_internal_maths::findVectorFromTwoVAndTwoD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
2472  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3], -angle1, -angle2 );
2473 
2474  //======================================== Check if solution fits the group completely
2475  ProSHADE_internal_symmetry::checkFittingAxisDualAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, dataObj );
2476  if ( prosp.size() == requiredNoAxes ) { break; }
2477  }
2478 
2479  if ( prosp.size() == requiredNoAxes ) { break; }
2480  }
2481 
2482  //================================================ Found all required axes!
2483  if ( static_cast<proshade_unsign> ( prosp.size() ) == requiredNoAxes )
2484  {
2485  //============================================ Copy the detected axes
2486  for ( proshade_unsign iter = static_cast<proshade_unsign> ( possibilities->size() ); iter < static_cast<proshade_unsign> ( prosp.size() ); iter++ )
2487  {
2488  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, prosp.at(iter), axErr ) )
2489  {
2490  //==================================== Add
2491  ProSHADE_internal_misc::addToUnsignVector ( possibilities, CSymList->size() );
2492  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, prosp.at(iter) );
2493  }
2494  }
2495 
2496  //============================================ Done
2497  atLeastOne = true;
2498  return ( atLeastOne );
2499  }
2500  else
2501  {
2502  //============================================ Delete the created, but not used axes
2503  for ( proshade_unsign iter = static_cast<proshade_unsign> ( possibilities->size() ); iter < static_cast<proshade_unsign> ( prosp.size() ); iter++ )
2504  {
2505  delete[] prosp.at(iter);
2506  }
2507  }
2508 
2509  //================================================ Done
2510  return ( atLeastOne );
2511 
2512 }
2513 
2527 void 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 )
2528 {
2529  //================================================ Initialise variables
2530  proshade_double* symHlp = new proshade_double[6];
2531  ProSHADE_internal_misc::checkMemoryAllocation ( symHlp, __FILE__, __LINE__, __func__ );
2532 
2533  //================================================ Fill in the prospective axis
2534  symHlp[0] = static_cast<proshade_double> ( fold );
2535  symHlp[1] = axX;
2536  symHlp[2] = axY;
2537  symHlp[3] = axZ;
2538  symHlp[4] = 2.0 * M_PI / symHlp[0];
2539  symHlp[5] = axHeight;
2540 
2541  //================================================ If it is not the same as already saved axes
2542  if ( !ProSHADE_internal_symmetry::isSymmetrySame ( prosp, symHlp, axErr ) )
2543  {
2545  }
2546  else
2547  {
2548  delete[] symHlp;
2549  }
2550 
2551  //================================================ Done
2552  return ;
2553 
2554 }
2555 
2577 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 )
2578 {
2579  //================================================ Initialise variables
2580  proshade_unsign noG1 = 0;
2581  proshade_unsign noG2 = 0;
2582  proshade_double dotProd = 0.0;
2583  proshade_double axHeight = 0.0;
2584 
2585  //================================================ Find the angle and count dual matching frequencies
2586  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( retGroup->size() ); rIt++ )
2587  {
2588  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(retGroup->at(rIt))[1],
2589  &ret->at(retGroup->at(rIt))[2],
2590  &ret->at(retGroup->at(rIt))[3],
2591  &axX, &axY, &axZ );
2592 
2593  if ( ( std::abs ( dotProd ) > ( angle1 - axErr ) ) && ( std::abs ( dotProd ) < ( angle1 + axErr ) ) ) { noG1 += 1; continue; }
2594  if ( ( std::abs ( dotProd ) > ( angle2 - axErr ) ) && ( std::abs ( dotProd ) < ( angle2 + axErr ) ) ) { noG2 += 1; continue; }
2595  }
2596 
2597  //================================================ If correct frequencies are matched, check height.
2598  if ( ( noG1 == noMatchesG1 ) && ( noG2 == noMatchesG2 ) )
2599  {
2600  //============================================ Is the height good enough?
2601  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( axX, axY, axZ, dataObj, fold, axErr );
2602 
2603  //============================================ If so, save
2604  if ( axHeight > 0.1 )
2605  {
2606  proshade_unsign prevProsp = static_cast<proshade_unsign> ( prosp->size() );
2607  ProSHADE_internal_symmetry::addAxisUnlessSame ( fold, axX, axY, axZ, axHeight, prosp, axErr );
2608 
2609  if ( static_cast<proshade_unsign> ( prosp->size() ) > prevProsp ) { return ( true ); }
2610  else { return ( false ); }
2611  }
2612  }
2613 
2614  //================================================ Done
2615  return ( false );
2616 
2617 }
2618 
2629 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getIcosahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
2630 {
2631  //================================================ Initialise variables
2632  std::vector< proshade_double* > ret;
2633 
2634  //================================================ Report progress
2635  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting I symmetry detection." );
2636 
2637  //================================================ Are the basic requirements for icosahedral symmetry met?
2639  {
2640  //============================================ Search for all the symmetry axes
2641  ProSHADE_internal_symmetry::findIcos6C5s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
2642  if ( ret.size() != 6 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
2643 
2644  ProSHADE_internal_symmetry::findIcos10C3s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
2645  if ( ret.size() != 16 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
2646 
2647  ProSHADE_internal_symmetry::findIcos15C2s ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak );
2648  if ( ret.size() != 31 ) { ProSHADE_internal_messages::printWarningMessage ( settings->verbose, "!!! ProSHADE WARNING !!! Failed to detect some of the polyhedral symmetries, while detecting the correct dihedral angles.", "WS00031" ); return ( ret ); }
2649  else
2650  {
2651  for ( proshade_unsign csIt = 0; csIt < static_cast<proshade_unsign> ( CSymList->size() ); csIt++ )
2652  {
2653  for ( proshade_unsign retIt = 0; retIt < static_cast<proshade_unsign> ( ret.size() ); retIt++ )
2654  {
2655  if ( ( CSymList->at(csIt)[0] == ret.at(retIt)[0] ) &&
2656  ( CSymList->at(csIt)[1] == ret.at(retIt)[1] ) &&
2657  ( CSymList->at(csIt)[2] == ret.at(retIt)[2] ) &&
2658  ( CSymList->at(csIt)[3] == ret.at(retIt)[3] ) &&
2659  ( CSymList->at(csIt)[4] == ret.at(retIt)[4] ) &&
2660  ( CSymList->at(csIt)[5] == ret.at(retIt)[5] ) )
2661  {
2663  }
2664  }
2665  }
2666  }
2667  }
2668 
2669  //================================================ Report progress
2670  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "I symmetry detection complete." );
2671 
2672  //================================================ Done
2673  return ( ret );
2674 
2675 }
2676 
2691 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
2692 {
2693  //================================================ Initialise variables
2694  std::vector< proshade_double* > ret;
2695 
2696  //================================================ Report progress
2697  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting I symmetry prediction." );
2698 
2699  //================================================ Are the basic requirements for icosahedral symmetry met?
2701  {
2702  //============================================ Generate the rest of the axes
2703  ProSHADE_internal_symmetry::predictIcosAxes ( CSymList, &ret, settings->axisErrTolerance, settings->minSymPeak );
2704 
2705  //============================================ Get heights for the predicted axes
2707 
2708  //============================================ Add predicted axes to detected C axes list and also to the settings Icosahedral symmetry list
2709  for ( proshade_unsign retIt = 0; retIt < static_cast < proshade_unsign > ( ret.size() ); retIt++ )
2710  {
2711  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, ret.at(retIt) );
2712  ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, static_cast < proshade_unsign > ( CSymList->size() ) );
2713  }
2714  }
2715 
2716 
2717  //================================================ Report progress
2718  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "I symmetry prediction complete." );
2719 
2720  //================================================ Done
2721  return ( ret );
2722 
2723 }
2724 
2739 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
2740 {
2741  //================================================ Initialise variables
2742  std::vector< proshade_double* > ret;
2743 
2744  //================================================ Report progress
2745  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting O symmetry prediction." );
2746 
2747  //================================================ Are the basic requirements for icosahedral symmetry met?
2748  if ( ProSHADE_internal_symmetry::detectOctahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
2749  {
2750  //============================================ Generate the rest of the axes
2751  ProSHADE_internal_symmetry::predictOctaAxes ( CSymList, &ret, settings->axisErrTolerance, settings->minSymPeak );
2752 
2753  //============================================ Get heights for the predicted axes
2755 
2756  //============================================ Add predicted axes to detected C axes list and also to the settings Icosahedral symmetry list
2757  for ( proshade_unsign retIt = 0; retIt < static_cast < proshade_unsign > ( ret.size() ); retIt++ )
2758  {
2759  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, ret.at(retIt) );
2760  ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, static_cast < proshade_unsign > ( CSymList->size() ) );
2761  }
2762  }
2763 
2764  //================================================ Report progress
2765  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "O symmetry prediction complete." );
2766 
2767  //================================================ Done
2768  return ( ret );
2769 
2770 }
2771 
2783 bool ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
2784 {
2785  //================================================ Initialise variables
2786  std::vector< proshade_unsign > C5List;
2787  proshade_double dotProduct;
2788 
2789  //================================================ Find all C5 symmetries
2790  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2791  {
2792  if ( CSymList->at(cSym)[0] == 5 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); }
2793  }
2794 
2795  //================================================ For each unique pair of C5 and C3
2796  for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
2797  {
2798  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2799  {
2800  //======================================== Compare only C3s to the C5List
2801  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2802 
2803  //======================================== Check the angle between the C5 and C3 axes
2804  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
2805  &CSymList->at(C5List.at(c5))[2],
2806  &CSymList->at(C5List.at(c5))[3],
2807  &CSymList->at(cSym)[1],
2808  &CSymList->at(cSym)[2],
2809  &CSymList->at(cSym)[3] );
2810 
2811  //======================================== Is the angle approximately the dihedral angle
2812  if ( std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) < axErr )
2813  {
2814  return ( true );
2815  }
2816  }
2817  }
2818 
2819  //================================================ Done
2820  return ( false );
2821 
2822 }
2823 
2841 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_unsign verbose, proshade_double minPeakHeight )
2842 {
2843  //================================================ Initialise variables
2844  std::vector< proshade_unsign > C5PossibilitiesHlp;
2845  std::vector< std::vector< proshade_unsign > > C5Possibilities;
2846  bool groupMatched;
2847 
2848  //================================================ Report progress
2849  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of six C5 axes." );
2850 
2851  //================================================ For all symmetries in the C symmetries list
2852  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2853  {
2854  //============================================ Search only using C5s and check peak height
2855  if ( CSymList->at(cIt)[0] != 5.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
2856 
2857  //============================================ If second or more C5, check if it has the correct angle to all other already found C5s for each group
2858  groupMatched = false;
2859  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C5Possibilities.size() ); gIt++ )
2860  {
2861  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; }
2862  }
2863 
2864  //============================================ If no group matched, create a new group
2865  if ( !groupMatched ) { C5PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C5PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C5Possibilities, C5PossibilitiesHlp ); continue; }
2866  }
2867 
2868  //================================================ Test for missing symmetry axes, if need be
2869  ProSHADE_internal_symmetry::findMissingAxes ( &C5Possibilities, CSymList, 6, axErr, 1.0 / 2.0, 5, dataObj, minPeakHeight );
2870 
2871  //=================================================Any group has 6 entries? If more such groups, take the one with highest average height.
2872  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
2873  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; } } }
2874 
2875  if ( C5Possibilities.at(maxGrp).size() == 6 )
2876  {
2877  //============================================ Success! Save and exit
2878  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)) ); }
2879 
2880  //============================================ Report progress
2881  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of six C5 axes successfull." );
2882 
2883  //============================================ Done
2884  return ;
2885  }
2886 
2887  //================================================ Done
2888  return ;
2889 
2890 }
2891 
2899 std::pair< proshade_unsign, proshade_unsign > findBestIcosDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
2900 {
2901  //================================================ Initialise variables
2902  std::pair< proshade_unsign, proshade_unsign > ret;
2903  std::vector< proshade_unsign > C5List;
2904  proshade_double bestDihedralAngle = 999.9;
2905  proshade_double dotProduct;
2906 
2907  //================================================ Find all C5 symmetries
2908  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { if ( CSymList->at(cSym)[0] == 5 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); } }
2909 
2910  //================================================ For each unique pair of C5 and C3
2911  for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
2912  {
2913  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2914  {
2915  //======================================== Compare only C3s to the C5List and only with decent average peak height
2916  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2917  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
2918 
2919  //======================================== Check the angle between the C5 and C3 axes
2920  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
2921  &CSymList->at(C5List.at(c5))[2],
2922  &CSymList->at(C5List.at(c5))[3],
2923  &CSymList->at(cSym)[1],
2924  &CSymList->at(cSym)[2],
2925  &CSymList->at(cSym)[3] );
2926 
2927  //======================================== Is the angle approximately the dihedral angle?
2928  if ( std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) < axErr )
2929  {
2930  if ( bestDihedralAngle > std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) )
2931  {
2932  bestDihedralAngle = std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) );
2933  ret.first = C5List.at(c5);
2934  ret.second = cSym;
2935  }
2936  }
2937  }
2938  }
2939 
2940  //================================================ Done
2941  return ( ret );
2942 }
2943 
2964 void ProSHADE_internal_symmetry::predictIcosAxes ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, proshade_double minPeakHeight )
2965 {
2966  //================================================ Find the best axis combination with dihedral angle and correct folds
2967  std::pair< proshade_unsign, proshade_unsign > initAxes = findBestIcosDihedralPair ( CSymList, minPeakHeight, axErr );
2968 
2969  //================================================ Find rotation between the detected C5 and the model C5 axes.
2970  proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( ProSHADE_internal_precomputedVals::icosahedronAxes.at(0).at(1),
2971  ProSHADE_internal_precomputedVals::icosahedronAxes.at(0).at(2),
2972  ProSHADE_internal_precomputedVals::icosahedronAxes.at(0).at(3),
2973  CSymList->at(initAxes.first)[1],
2974  CSymList->at(initAxes.first)[2],
2975  CSymList->at(initAxes.first)[3] );
2976 
2977  //================================================ Rotate the model C3 to the correct orientation relative to the detected C5 axis.
2978  proshade_double* rotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
2979  ProSHADE_internal_precomputedVals::icosahedronAxes.at(6).at(1),
2980  ProSHADE_internal_precomputedVals::icosahedronAxes.at(6).at(2),
2981  ProSHADE_internal_precomputedVals::icosahedronAxes.at(6).at(3) );
2982 
2983  //================================================ Find the angle betwen the rotated model C3 and the detected C3 axes along the detected C5 axis.
2984  proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
2985  for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.01 )
2986  {
2987  //============================================ Compute rotation matrix for this angle value
2988  proshade_double* rotMatHlp = new proshade_double[9];
2989  ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
2990  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], ang );
2991 
2992  //============================================ Rotate the rotated C2 by the matrix
2993  proshade_double* rotRotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
2994  rotModelC3[0],
2995  rotModelC3[1],
2996  rotModelC3[2] );
2997 
2998  //============================================ Find distance
2999  curAngDist = std::sqrt ( std::pow ( rotRotModelC3[0] - CSymList->at(initAxes.second)[1], 2.0 ) +
3000  std::pow ( rotRotModelC3[1] - CSymList->at(initAxes.second)[2], 2.0 ) +
3001  std::pow ( rotRotModelC3[2] - CSymList->at(initAxes.second)[3], 2.0 ) );
3002 
3003  //============================================ Save best angle
3004  if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
3005 
3006  //============================================ Release memory
3007  delete[] rotMatHlp;
3008  delete[] rotRotModelC3;
3009  }
3010 
3011  //================================================ 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.
3012  proshade_double* rotMat2 = new proshade_double[9];
3013  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
3014  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], bestAng );
3015 
3016  //================================================ Combine the two rotation matrices into a single rotation matrix
3017  proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
3018 
3019  //================================================ For each model axis
3020  for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ProSHADE_internal_precomputedVals::icosahedronAxes.size() ); iter++ )
3021  {
3022  //============================================ Rotate the model axis to fit the detected orientation
3023  proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
3024  ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(1),
3025  ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(2),
3026  ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(3) );
3027 
3028  //============================================ Create ProSHADE symmetry axis representation
3029  proshade_double* axis = new proshade_double[6];
3030  ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
3031 
3032  axis[0] = ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(0);
3033  axis[1] = rotAxis[0];
3034  axis[2] = rotAxis[1];
3035  axis[3] = rotAxis[2];
3036  axis[4] = ( 2.0 * M_PI ) / axis[0];
3037  axis[5] = 0.0;
3038 
3039  //============================================ Save axis to ret
3041 
3042  //============================================ Release memory
3043  delete[] rotAxis;
3044  }
3045 
3046  //================================================ Release memory
3047  delete[] rotMat;
3048  delete[] rotMat2;
3049  delete[] rotMatFin;
3050  delete[] rotModelC3;
3051 
3052  //================================================ Done
3053  return ;
3054 
3055 }
3056 
3064 std::pair< proshade_unsign, proshade_unsign > findBestOctaDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
3065 {
3066  //================================================ Initialise variables
3067  std::pair< proshade_unsign, proshade_unsign > ret;
3068  std::vector< proshade_unsign > C4List;
3069  proshade_double bestDihedralAngle = 999.9;
3070  proshade_double dotProduct;
3071 
3072  //================================================ Find all C5 symmetries
3073  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { if ( CSymList->at(cSym)[0] == 4 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C4List, cSym ); } }
3074 
3075  //================================================ For each unique pair of C5 and C3
3076  for ( proshade_unsign c4 = 0; c4 < static_cast<proshade_unsign> ( C4List.size() ); c4++ )
3077  {
3078  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
3079  {
3080  //======================================== Compare only C3s to the C5List and only with decent average peak height
3081  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
3082  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
3083 
3084  //======================================== Check the angle between the C5 and C3 axes
3085  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C4List.at(c4))[1],
3086  &CSymList->at(C4List.at(c4))[2],
3087  &CSymList->at(C4List.at(c4))[3],
3088  &CSymList->at(cSym)[1],
3089  &CSymList->at(cSym)[2],
3090  &CSymList->at(cSym)[3] );
3091 
3092  //======================================== Is the angle approximately the dihedral angle?
3093  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
3094  {
3095  if ( bestDihedralAngle > std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) ) )
3096  {
3097  bestDihedralAngle = std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) );
3098  ret.first = C4List.at(c4);
3099  ret.second = cSym;
3100  }
3101  }
3102  }
3103  }
3104 
3105  //================================================ Done
3106  return ( ret );
3107 
3108 }
3109 
3130 void ProSHADE_internal_symmetry::predictOctaAxes ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, proshade_double minPeakHeight )
3131 {
3132  //================================================ Find the best axis combination with dihedral angle and correct folds
3133  std::pair< proshade_unsign, proshade_unsign > initAxes = findBestOctaDihedralPair ( CSymList, minPeakHeight, axErr );
3134 
3135  //================================================ Find rotation between the detected C4 and the model C4 axes.
3136  proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( ProSHADE_internal_precomputedVals::octahedronAxes.at(0).at(1),
3137  ProSHADE_internal_precomputedVals::octahedronAxes.at(0).at(2),
3138  ProSHADE_internal_precomputedVals::octahedronAxes.at(0).at(3),
3139  CSymList->at(initAxes.first)[1],
3140  CSymList->at(initAxes.first)[2],
3141  CSymList->at(initAxes.first)[3] );
3142 
3143  //================================================ Rotate the model C3 to the correct orientation relative to the detected C4 axis.
3144  proshade_double* rotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
3145  ProSHADE_internal_precomputedVals::octahedronAxes.at(3).at(1),
3146  ProSHADE_internal_precomputedVals::octahedronAxes.at(3).at(2),
3147  ProSHADE_internal_precomputedVals::octahedronAxes.at(3).at(3) );
3148 
3149  //================================================ Find the angle betwen the rotated model C3 and the detected C3 axes along the detected C4 axis.
3150  proshade_double bestAng, curAngDist, bestAngDist = 999.9;
3151  for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.01 )
3152  {
3153  //============================================ Compute rotation matrix for this angle value
3154  proshade_double* rotMatHlp = new proshade_double[9];
3155  ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
3156  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], ang );
3157 
3158  //============================================ Rotate the rotated C2 by the matrix
3159  proshade_double* rotRotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
3160  rotModelC3[0],
3161  rotModelC3[1],
3162  rotModelC3[2] );
3163 
3164  //============================================ Find distance
3165  curAngDist = std::sqrt ( std::pow ( rotRotModelC3[0] - CSymList->at(initAxes.second)[1], 2.0 ) +
3166  std::pow ( rotRotModelC3[1] - CSymList->at(initAxes.second)[2], 2.0 ) +
3167  std::pow ( rotRotModelC3[2] - CSymList->at(initAxes.second)[3], 2.0 ) );
3168 
3169  //============================================ Save best angle
3170  if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
3171 
3172  //============================================ Release memory
3173  delete[] rotMatHlp;
3174  delete[] rotRotModelC3;
3175  }
3176 
3177  //================================================ 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.
3178  proshade_double* rotMat2 = new proshade_double[9];
3179  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
3180  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], bestAng );
3181 
3182  //================================================ Combine the two rotation matrices into a single rotation matrix
3183  proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
3184 
3185  //================================================ For each model axis
3186  for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ProSHADE_internal_precomputedVals::octahedronAxes.size() ); iter++ )
3187  {
3188  //============================================ Rotate the model axis to fit the detected orientation
3189  proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
3190  ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(1),
3191  ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(2),
3192  ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(3) );
3193 
3194  //============================================ Create ProSHADE symmetry axis representation
3195  proshade_double* axis = new proshade_double[6];
3196  ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
3197 
3198  axis[0] = ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(0);
3199  axis[1] = rotAxis[0];
3200  axis[2] = rotAxis[1];
3201  axis[3] = rotAxis[2];
3202  axis[4] = ( 2.0 * M_PI ) / axis[0];
3203  axis[5] = 0.0;
3204 
3205  //============================================ Save axis to ret
3207 
3208  //============================================ Release memory
3209  delete[] rotAxis;
3210  }
3211 
3212  //================================================ Release memory
3213  delete[] rotMat;
3214  delete[] rotMat2;
3215  delete[] rotMatFin;
3216  delete[] rotModelC3;
3217 
3218  //================================================ Done
3219  return ;
3220 
3221 }
3222 
3236 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_unsign verbose, proshade_double minPeakHeight )
3237 {
3238  //================================================ Initialise variables
3239  std::vector< proshade_unsign > prospectiveC3s, retGrp;
3240  proshade_double dotProd;
3241  proshade_unsign noClose, noAway;
3242 
3243  //================================================ Report progress
3244  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of ten C3 axes." );
3245 
3246  //================================================ For each C3
3247  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
3248  {
3249  //============================================ Use only C3s with hight enough average
3250  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
3251 
3252  //============================================ 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
3253  noClose = 0; noAway = 0;
3254  for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
3255  {
3256  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
3257  &ret->at(rIt)[2],
3258  &ret->at(rIt)[3],
3259  &CSymList->at(cIt)[1],
3260  &CSymList->at(cIt)[2],
3261  &CSymList->at(cIt)[3] );
3262 
3263  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; }
3264  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; }
3265  }
3266 
3267  //============================================ If correct angles distribution is found, save the axis
3268  if ( ( noClose == 3 ) && ( noAway == 3 ) )
3269  {
3270  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC3s, cIt );
3271  }
3272  }
3273 
3274  //================================================ Search for missing axes
3275  for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
3276  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 ) )
3277  {
3278  return ;
3279  }
3280 
3281  //================================================ Found correct number of axes! Now save the
3282  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC3s.size() ); iter++ )
3283  {
3284  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC3s.at(iter)) );
3285  }
3286 
3287  //================================================ Report progress
3288  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of ten C3 axes successfull." );
3289 
3290  //================================================ Done
3291  return ;
3292 
3293 }
3294 
3308 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_unsign verbose, proshade_double minPeakHeight )
3309 {
3310  //================================================ Initialise variables
3311  std::vector< proshade_unsign > prospectiveC2s, retGrp;
3312  proshade_double dotProd;
3313  proshade_unsign noClose, noMidway, noAway;
3314 
3315  //================================================ Report progress
3316  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of fifteen C2 axes." );
3317 
3318  //================================================ For each C2
3319  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
3320  {
3321  //============================================ Use only C2s
3322  if ( CSymList->at(cIt)[0] != 2.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
3323 
3324  //============================================ 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
3325  noClose = 0; noMidway = 0; noAway = 0;
3326  for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
3327  {
3328  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
3329  &ret->at(rIt)[2],
3330  &ret->at(rIt)[3],
3331  &CSymList->at(cIt)[1],
3332  &CSymList->at(cIt)[2],
3333  &CSymList->at(cIt)[3] );
3334 
3335  if ( ( std::abs ( dotProd ) > ( ( sqrt ( 3.0 ) / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( sqrt ( 3.0 ) / 2.0 ) + axErr ) ) ) { noAway += 1; continue; }
3336  if ( ( std::abs ( dotProd ) > ( ( 1.0 / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / 2.0 ) + axErr ) ) ) { noMidway += 1; continue; }
3337  if ( ( std::abs ( dotProd ) > ( ( 0.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 0.0 ) + axErr ) ) ) { noClose += 1; continue; }
3338  }
3339 
3340  //============================================ If correct angles distribution is found, save the axis
3341  if ( ( noClose == 2 ) && ( noMidway == 2 ) && ( noAway == 2 ) )
3342  {
3343  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt );
3344  }
3345  }
3346 
3347  //================================================ Search for missing axes
3348  for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
3349  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 ) )
3350  {
3351  return ;
3352  }
3353 
3354  //================================================ Found correct number of axes! Now save the
3355  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC2s.size() ); iter++ )
3356  {
3357  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC2s.at(iter)) );
3358  }
3359 
3360  //================================================ Report progress
3361  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of fifteen C2 axes successfull." );
3362 
3363  //================================================ Done
3364  return ;
3365 
3366 }
3367 
3390 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 )
3391 {
3392  //================================================ Initialise variables
3393  bool atLeastOne = false;
3394  std::vector< proshade_double* > prosp;
3395  std::vector< proshade_double > sol;
3396 
3397  //================================================ Proceed only if need be
3398  if ( static_cast<proshade_unsign> ( possibilities->size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
3399 
3400  //================================================ Copy already found to prospective
3401  for ( proshade_unsign prIt = 0; prIt < static_cast<proshade_unsign> ( possibilities->size() ); prIt++ )
3402  {
3403  ProSHADE_internal_symmetry::addAxisUnlessSame ( CSymList->at(possibilities->at(prIt))[0],
3404  CSymList->at(possibilities->at(prIt))[1],
3405  CSymList->at(possibilities->at(prIt))[2],
3406  CSymList->at(possibilities->at(prIt))[3],
3407  CSymList->at(possibilities->at(prIt))[5], &prosp, axErr );
3408  }
3409 
3410  //================================================ Start generating possible solutions
3411  for ( proshade_unsign rgIt1 = 0; rgIt1 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt1++ )
3412  {
3413  for ( proshade_unsign rgIt2 = 0; rgIt2 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt2++ )
3414  {
3415  //======================================== Use unique combinations (order matters here!)
3416  if ( rgIt1 == rgIt2 ) { continue; }
3417 
3418  for ( proshade_unsign rgIt3 = 0; rgIt3 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt3++ )
3419  {
3420  //==================================== Use unique combinations (order matters here!)
3421  if ( ( rgIt1 == rgIt3 ) || ( rgIt2 == rgIt3 ) ) { continue; }
3422 
3423  //==================================== Generate possible solution (1)
3424  sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
3425  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
3426  ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], angle1, angle2, angle3 );
3427 
3428  //==================================== Check if solution fits the group completely
3429  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 );
3430  if ( prosp.size() == requiredNoAxes ) { break; }
3431 
3432  //==================================== Generate possible solution (2)
3433  sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
3434  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
3435  ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], -angle1, -angle2, -angle3 );
3436 
3437  //==================================== Check if solution fits the group completely
3438  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 );
3439  if ( prosp.size() == requiredNoAxes ) { break; }
3440  }
3441 
3442  if ( prosp.size() == requiredNoAxes ) { break; }
3443  }
3444 
3445  if ( prosp.size() == requiredNoAxes ) { break; }
3446  }
3447 
3448  //================================================ Found all required axes
3449  if ( prosp.size() == requiredNoAxes )
3450  {
3451  //============================================ For each found missing axis
3452  for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
3453  {
3454  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, prosp.at(axIt), axErr ) )
3455  {
3456  //======================================== Add
3457  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, prosp.at(axIt) );
3458  ProSHADE_internal_misc::addToUnsignVector ( possibilities, static_cast<proshade_unsign> ( CSymList->size()-1 ) );
3459  }
3460  }
3461 
3462  atLeastOne = true;
3463  return ( atLeastOne );
3464  }
3465  else
3466  {
3467  //============================================ Delete all found, but unnecessary axes
3468  for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
3469  {
3470  delete[] prosp.at(axIt);
3471  }
3472  }
3473 
3474  //================================================ Done
3475  return ( atLeastOne );
3476 
3477 }
3478 
3501 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 )
3502 {
3503  //================================================ Initialise variables
3504  proshade_unsign noG1 = 0;
3505  proshade_unsign noG2 = 0;
3506  proshade_unsign noG3 = 0;
3507  proshade_double dotProd = 0.0;
3508  proshade_double axHeight = 0.0;
3509 
3510  //================================================ Find the angle and count dual matching frequencies
3511  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( retGroup->size() ); rIt++ )
3512  {
3513  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(retGroup->at(rIt))[1],
3514  &ret->at(retGroup->at(rIt))[2],
3515  &ret->at(retGroup->at(rIt))[3],
3516  &axX, &axY, &axZ );
3517 
3518  if ( ( std::abs ( dotProd ) > ( angle1 - axErr ) ) && ( std::abs ( dotProd ) < ( angle1 + axErr ) ) ) { noG1 += 1; continue; }
3519  if ( ( std::abs ( dotProd ) > ( angle2 - axErr ) ) && ( std::abs ( dotProd ) < ( angle2 + axErr ) ) ) { noG2 += 1; continue; }
3520  if ( ( std::abs ( dotProd ) > ( angle3 - axErr ) ) && ( std::abs ( dotProd ) < ( angle3 + axErr ) ) ) { noG3 += 1; continue; }
3521  }
3522 
3523  //================================================ If correct frequencies are matched, check height.
3524  if ( ( noG1 == noMatchesG1 ) && ( noG2 == noMatchesG2 ) && ( noG3 == noMatchesG3 ) )
3525  {
3526  //============================================ Is the height good enough?
3527  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( axX, axY, axZ, dataObj, fold, axErr );
3528 
3529  //============================================ If so, save
3530  if ( axHeight > 0.1 )
3531  {
3532  ProSHADE_internal_symmetry::addAxisUnlessSame ( fold, axX, axY, axZ, axHeight, prosp, axErr );
3533  }
3534  }
3535 
3536  //================================================ Done
3537  return ;
3538 
3539 }
3540 
3554 {
3555  //================================================ Initialise variables
3556  std::vector< proshade_unsign > primes = ProSHADE_internal_maths::findAllPrimes ( settings->maxSymmetryFold );
3557  std::vector< proshade_double* > ret, tmpHolder;
3558  std::vector< proshade_unsign > testedFolds;
3559  proshade_double symThres;
3560  proshade_unsign foldToTest;
3561  bool foldDone, anyNewSyms = true;
3562 
3563  //================================================ For each found prime number in the limit
3564  for ( proshade_unsign prIt = 0; prIt < static_cast< proshade_unsign > ( primes.size() ); prIt++ )
3565  {
3566  //============================================ Report progress
3567  std::stringstream hlpSS;
3568  hlpSS << "Searching for prime fold symmetry C" << primes.at(prIt) << ".";
3569  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str() );
3570 
3571  //============================================ Get all symmetries for this prime fold
3572  std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, primes.at(prIt), &symThres );
3573 
3574  //============================================ Save the detected C symmetries
3575  for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( prSyms.size() ); axIt++ )
3576  {
3577  //======================================== Is this symmetry passing the threshold?
3578  if ( prSyms.at(axIt)[5] >= symThres )
3579  {
3580  //==================================== Add this symmetry to final list
3581  if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(axIt), settings->axisErrTolerance, true ) )
3582  {
3583  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, prSyms.at(axIt) );
3584  }
3585  }
3586 
3587  //======================================== Release memory
3588  delete[] prSyms.at(axIt);
3589  }
3590  }
3591 
3592  //================================================ Was anything found?
3593  if ( ret.size() < 1 ) { return ( ret ); }
3594 
3595  //================================================ Check for prime symmetry fold multiples
3596  while ( anyNewSyms )
3597  {
3598  //============================================ Initialise new iteration
3599  anyNewSyms = false;
3600 
3601  //============================================ For each passing symmetry, look if there are any combinations of symmetries that would contain it
3602  for ( proshade_unsign axIt1 = 0; axIt1 < static_cast< proshade_unsign > ( ret.size() ); axIt1++ )
3603  {
3604  for ( proshade_unsign axIt2 = 0; axIt2 < static_cast< proshade_unsign > ( ret.size() ); axIt2++ )
3605  {
3606  //==================================== Initialise iteration
3607  foldToTest = ret.at(axIt1)[0] * ret.at(axIt2)[0];
3608  if ( foldToTest > settings->maxSymmetryFold ) { continue; }
3609 
3610  //==================================== Was this fold tested already?
3611  foldDone = false;
3612  for ( proshade_unsign fIt = 0; fIt < static_cast< proshade_unsign > ( testedFolds.size() ); fIt++ ) { if ( testedFolds.at(fIt) == foldToTest ) { foldDone = true; break; } }
3613  if ( foldDone ) { continue; }
3614  else { ProSHADE_internal_misc::addToUnsignVector ( &testedFolds, foldToTest ); }
3615 
3616  //==================================== Report progress
3617  std::stringstream hlpSS2;
3618  hlpSS2 << "Searching for fold combination of detected folds " << ret.at(axIt1)[0] << " and " << ret.at(axIt2)[0] << ", i.e. C" << foldToTest << ".";
3619  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS2.str() );
3620 
3621  //==================================== Get all symmetries for this fold
3622  std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, foldToTest, &symThres );
3623 
3624  //==================================== For each detected group with the required fold
3625  for ( proshade_unsign newAxIt = 0; newAxIt < static_cast< proshade_unsign > ( prSyms.size() ); newAxIt++ )
3626  {
3627  if ( prSyms.at(newAxIt)[5] >= symThres )
3628  {
3629  //================================ Add to detected axes
3630  if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(newAxIt), settings->axisErrTolerance, true ) )
3631  {
3632  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &tmpHolder, prSyms.at(newAxIt) );
3633  }
3634  }
3635 
3636  //==================================== Release memory
3637  delete[] prSyms.at(newAxIt);
3638  }
3639  }
3640  }
3641 
3642  //============================================ Add newly found groups and repeat if need be
3643  if ( tmpHolder.size() > 0 )
3644  {
3645  for ( proshade_unsign tmpIt = 0; tmpIt < static_cast< proshade_unsign > ( tmpHolder.size() ); tmpIt++ )
3646  {
3647  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, tmpHolder.at(tmpIt) );
3648  delete[] tmpHolder.at(tmpIt);
3649  }
3650 
3651  anyNewSyms = true;
3652  tmpHolder.clear ( );
3653  }
3654  }
3655 
3656  //================================================ Sort the vector
3657  std::sort ( ret.begin(), ret.end(), ProSHADE_internal_misc::sortSymHlpInv );
3658 
3659  //================================================ Done
3660  return ( ret );
3661 }
3662 
3669 bool sortProSHADESymmetryByPeak ( proshade_double* a, proshade_double* b)
3670 {
3671  //================================================ Done
3672  return ( a[5] > b[5] );
3673 
3674 }
3675 
3692 std::vector < proshade_double* > ProSHADE_internal_data::ProSHADE_data::findRequestedCSymmetryFromAngleAxis ( ProSHADE_settings* settings, proshade_unsign fold, proshade_double* peakThres )
3693 {
3694  //================================================ Initialise variables
3695  proshade_double soughtAngle;
3696  std::vector< proshade_double > allPeakHeights;
3697  std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup* > peakGroups;
3698  std::vector< proshade_double* > ret;
3699  bool newPeak;
3700 
3701  //================================================ Make sure we have a clean start
3702  this->sphereMappedRotFun.clear();
3703 
3704  //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
3705  for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( fold ); angIt += 1.0 )
3706  {
3707  //============================================ Figure the angles to form the symmetry
3708  soughtAngle = angIt * ( 2.0 * M_PI / static_cast<proshade_double> ( fold ) );
3709 
3710  //============================================ Create the angle-axis sphere with correct radius (angle)
3711  this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( soughtAngle,
3712  M_PI / static_cast<proshade_double> ( fold ),
3713  this->maxShellBand * 2.0,
3714  soughtAngle,
3715  static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3716 
3717  //=========================================== Interpolate rotation function onto the sphere
3718  this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
3719 
3720  //============================================ Find all peaks for this sphere
3721  this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->findAllPeaks ( settings->peakNeighbours, &allPeakHeights );
3722  }
3723 
3724  //============================================ Report progress
3725  std::stringstream hlpSS;
3726  hlpSS << "Found a total of " << std::pow ( this->maxShellBand * 2.0 * (fold-1), 2.0 ) - allPeakHeights.size() << " non-peaks for thresholding.";
3727  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS.str() );
3728 
3729  //================================================ Determine the threshold for significant peaks
3730  *peakThres = std::max ( settings->minSymPeak, determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak ) );
3731 
3732  //============================================ Report progress
3733  std::stringstream hlpSS2;
3734  hlpSS2 << "Determined peak threshold " << *peakThres << ".";
3735  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str() );
3736 
3737  //================================================ Remove small peaks
3738  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
3739  {
3740  this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( *peakThres );
3741  }
3742 
3743  //================================================ Group peaks
3744  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); sphIt++ )
3745  {
3746  //============================================ For each peak
3747  for ( proshade_unsign pkIt = 0; pkIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.at(sphIt)->getPeaks().size() ); pkIt++ )
3748  {
3749  //======================================== Check if peak belongs to an already detected peak group
3750  newPeak = true;
3751  for ( proshade_unsign pkGrpIt = 0; pkGrpIt < static_cast<proshade_unsign> ( peakGroups.size() ); pkGrpIt++ )
3752  {
3753  if ( peakGroups.at(pkGrpIt)->checkIfPeakBelongs ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first, this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second, sphIt, settings->axisErrTolerance, settings->verbose ) ) { newPeak = false; break; }
3754  }
3755 
3756  //======================================== If already added, go to next one
3757  if ( !newPeak ) { continue; }
3758 
3759  //======================================== If not, create a new group with this peak
3760  peakGroups.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first,
3761  this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second,
3762  sphIt,
3763  this->sphereMappedRotFun.at(sphIt)->getAngularDim() ) );
3764  }
3765  }
3766 
3767  //================================================ For each peak group, look for the requested fold
3768  for ( proshade_unsign grIt = 0; grIt < static_cast<proshade_unsign> ( peakGroups.size() ); grIt++ )
3769  {
3770  //============================================ Report progress
3771  std::stringstream hlpSS3;
3772  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 ";
3773  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( peakGroups.at(grIt)->getSpherePositions().size() ); sphIt++ ) { hlpSS3 << peakGroups.at(grIt)->getSpherePositions().at(sphIt) << " ; "; }
3774  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 5, hlpSS3.str() );
3775 
3776  //============================================ Find point groups in the peak group
3777  peakGroups.at(grIt)->findCyclicPointGroupsGivenFold ( this->sphereMappedRotFun, settings->axisErrTolerance, &ret, settings->useBiCubicInterpolationOnPeaks, fold, settings->verbose );
3778 
3779  //============================================ Release the memory
3780  delete peakGroups.at(grIt);
3781  }
3782 
3783  //================================================ Sort ret by peak height
3784  std::sort ( ret.begin(), ret.end(), sortProSHADESymmetryByPeak );
3785 
3786  //================================================ Done
3787  return ( ret );
3788 
3789 }
3790 
3804 {
3805  //================================================ Initialise variables
3806  std::vector < proshade_unsign > folds;
3807  bool alreadyFound;
3808  proshade_double lat, lon;
3809  proshade_double latSamlUnit = ( 2.0 * M_PI ) / ( dataObj->maxShellBand * 2.0 );
3810  proshade_double lonSamlUnit = ( 1.0 * M_PI ) / ( dataObj->maxShellBand * 2.0 );
3811 
3812  //================================================ Determine all the folds for which rotation function mapping will be required
3813  for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ret->size() ); iter++ )
3814  {
3815  alreadyFound = false;
3816  for ( proshade_unsign it = 0; it < static_cast < proshade_unsign > ( folds.size() ); it++ ) { if ( folds.at(it) == ret->at(iter)[0] ) { alreadyFound = true; break; } }
3817 
3818  if ( !alreadyFound ) { ProSHADE_internal_misc::addToUnsignVector ( &folds, ret->at(iter)[0] ); }
3819  }
3820 
3821  //================================================ For each fold which needs rotation function mapping
3822  for ( proshade_unsign foldIt = 0; foldIt < static_cast < proshade_unsign > ( folds.size() ); foldIt++ )
3823  {
3824  //============================================ Make sure we have a clean start
3825  dataObj->sphereMappedRotFun.clear();
3826 
3827  //============================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
3828  for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( folds.at(foldIt) ); angIt += 1.0 )
3829  {
3830  //======================================== Create the angle-axis sphere with correct radius (angle)
3831  dataObj->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( ( 2.0 * M_PI ) / static_cast < proshade_double > ( folds.at(foldIt) ),
3832  M_PI / static_cast < proshade_double > ( folds.at(foldIt) ),
3833  dataObj->maxShellBand * 2.0,
3834  ( 2.0 * M_PI ) / static_cast < proshade_double > ( folds.at(foldIt) ),
3835  static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3836 
3837  //=========================================== Interpolate rotation function onto the sphere
3838  dataObj->sphereMappedRotFun.at( static_cast < proshade_unsign > ( angIt - 1.0 ))->interpolateSphereValues ( dataObj->getInvSO3Coeffs ( ) );
3839  }
3840 
3841  //============================================ For each ret axis with this fold
3842  for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( ret->size() ); axIt++ )
3843  {
3844  //======================================== Ignore different folds
3845  if ( ret->at(axIt)[0] != folds.at(foldIt) ) { continue; }
3846 
3847  //======================================== Convert XYZ to lat and lon INDICES
3848  lat = std::atan2( ret->at(axIt)[2], ret->at(axIt)[1] ) / latSamlUnit;
3849  lon = std::acos ( ret->at(axIt)[3] ) / lonSamlUnit;
3850 
3851  if ( lat < 0.0 ) { lat += ( dataObj->maxShellBand * 2.0 ); }
3852  if ( lon < 0.0 ) { lon += ( dataObj->maxShellBand * 2.0 ); }
3853 
3854  lat = std::round ( lat );
3855  lon = std::round ( lon );
3856 
3857  //======================================== Initialise the peak group
3859 
3860  //======================================== Construct a peak group with entry from each sphere with the axis as the peak
3861  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( dataObj->sphereMappedRotFun.size() ); sphIt++ )
3862  {
3863  if ( sphIt == 0 )
3864  {
3865  //================================ If first sphere, create the peak group
3866  grp = new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( lat, lon, sphIt, dataObj->sphereMappedRotFun.at(sphIt)->getAngularDim() );
3867  }
3868  else
3869  {
3870  //================================ Add to the existing object
3871  grp->checkIfPeakBelongs ( lat, lon, sphIt, settings->axisErrTolerance, settings->verbose );
3872  }
3873  }
3874 
3875  //======================================== Find the peak height
3876  std::vector < proshade_double* > detectedAxis;
3877  grp->findCyclicPointGroupsGivenFold ( dataObj->sphereMappedRotFun, settings->axisErrTolerance, &detectedAxis, settings->useBiCubicInterpolationOnPeaks, folds.at(foldIt), settings->verbose );
3878 
3879  //======================================== Save it!
3880  ret->at(axIt)[5] = detectedAxis.at(0)[5];
3881 
3882  //======================================== Release memory
3883  for ( proshade_unsign i = 0; i < static_cast < proshade_unsign > ( detectedAxis.size() ); i++ ) { delete detectedAxis.at(i); }
3884  delete grp;
3885  }
3886  }
3887 
3888  //================================================ Done
3889  return ;
3890 
3891 }
3892 
3900 std::pair< proshade_unsign, proshade_unsign > findBestTetraDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
3901 {
3902  //================================================ Initialise variables
3903  std::pair< proshade_unsign, proshade_unsign > ret;
3904  std::vector< proshade_unsign > C3List;
3905  proshade_double bestDihedralAngle = 999.9;
3906  proshade_double dotProduct;
3907 
3908  //================================================ Find all C5 symmetries
3909  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { if ( CSymList->at(cSym)[0] == 3 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C3List, cSym ); } }
3910 
3911  //================================================ For each unique pair of C3 and C2
3912  for ( proshade_unsign c3 = 0; c3 < static_cast<proshade_unsign> ( C3List.size() ); c3++ )
3913  {
3914  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
3915  {
3916  //======================================== Compare only C3s to the C5List and only with decent average peak height
3917  if ( CSymList->at(cSym)[0] != 2 ) { continue; }
3918  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
3919 
3920  //======================================== Check the angle between the C5 and C3 axes
3921  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C3List.at(c3))[1],
3922  &CSymList->at(C3List.at(c3))[2],
3923  &CSymList->at(C3List.at(c3))[3],
3924  &CSymList->at(cSym)[1],
3925  &CSymList->at(cSym)[2],
3926  &CSymList->at(cSym)[3] );
3927 
3928  //======================================== Is the angle approximately the dihedral angle?
3929  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
3930  {
3931  if ( bestDihedralAngle > std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) ) )
3932  {
3933  bestDihedralAngle = std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) );
3934  ret.first = C3List.at(c3);
3935  ret.second = cSym;
3936  }
3937  }
3938  }
3939  }
3940 
3941  //================================================ Done
3942  return ( ret );
3943 
3944 }
3945 
3960 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedTetrahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
3961 {
3962  //================================================ Initialise variables
3963  std::vector< proshade_double* > ret;
3964 
3965  //================================================ Report progress
3966  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting T symmetry prediction." );
3967 
3968  //================================================ Are the basic requirements for icosahedral symmetry met?
3970  {
3971  //============================================ Generate the rest of the axes
3972  ProSHADE_internal_symmetry::predictTetraAxes ( CSymList, &ret, settings->axisErrTolerance, settings->minSymPeak );
3973 
3974  //============================================ Get heights for the predicted axes
3976 
3977  //============================================ Add predicted axes to detected C axes list and also to the settings Icosahedral symmetry list
3978  for ( proshade_unsign retIt = 0; retIt < static_cast < proshade_unsign > ( ret.size() ); retIt++ )
3979  {
3980  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, ret.at(retIt) );
3981  ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, static_cast < proshade_unsign > ( CSymList->size() ) );
3982  }
3983  }
3984 
3985  //================================================ Report progress
3986  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "T symmetry prediction complete." );
3987 
3988  //================================================ Done
3989  return ( ret );
3990 
3991 }
3992 
4013 void ProSHADE_internal_symmetry::predictTetraAxes ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, proshade_double minPeakHeight )
4014 {
4015  //================================================ Find the best axis combination with dihedral angle and correct folds
4016  std::pair< proshade_unsign, proshade_unsign > initAxes = findBestTetraDihedralPair ( CSymList, minPeakHeight, axErr );
4017 
4018  //================================================ Find rotation between the detected C3 and the model C3 axes.
4019  proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( ProSHADE_internal_precomputedVals::tetrahedronAxes.at(0).at(1),
4020  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(0).at(2),
4021  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(0).at(3),
4022  CSymList->at(initAxes.first)[1],
4023  CSymList->at(initAxes.first)[2],
4024  CSymList->at(initAxes.first)[3] );
4025 
4026  //================================================ Rotate the model C2 to the correct orientation relative to the detected C3 axis.
4027  proshade_double* rotModelC2 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
4028  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(4).at(1),
4029  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(4).at(2),
4030  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(4).at(3) );
4031 
4032  //================================================ Find the angle betwen the rotated model C2 and the detected C2 axes along the detected C3 axis.
4033  proshade_double bestAng, curAngDist, bestAngDist = 999.9;
4034  for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.01 )
4035  {
4036  //============================================ Compute rotation matrix for this angle value
4037  proshade_double* rotMatHlp = new proshade_double[9];
4038  ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
4039  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], ang );
4040 
4041  //============================================ Rotate the rotated C2 by the matrix
4042  proshade_double* rotRotModelC2 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
4043  rotModelC2[0],
4044  rotModelC2[1],
4045  rotModelC2[2] );
4046 
4047  //============================================ Find distance
4048  curAngDist = std::sqrt ( std::pow ( rotRotModelC2[0] - CSymList->at(initAxes.second)[1], 2.0 ) +
4049  std::pow ( rotRotModelC2[1] - CSymList->at(initAxes.second)[2], 2.0 ) +
4050  std::pow ( rotRotModelC2[2] - CSymList->at(initAxes.second)[3], 2.0 ) );
4051 
4052  //============================================ Save best angle
4053  if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
4054 
4055  //============================================ Release memory
4056  delete[] rotMatHlp;
4057  delete[] rotRotModelC2;
4058  }
4059 
4060  //================================================ 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.
4061  proshade_double* rotMat2 = new proshade_double[9];
4062  ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
4063  ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], bestAng );
4064 
4065  //================================================ Combine the two rotation matrices into a single rotation matrix
4066  proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
4067 
4068  //================================================ For each model axis
4069  for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ProSHADE_internal_precomputedVals::tetrahedronAxes.size() ); iter++ )
4070  {
4071  //============================================ Rotate the model axis to fit the detected orientation
4072  proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
4073  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(1),
4074  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(2),
4075  ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(3) );
4076 
4077  //============================================ Create ProSHADE symmetry axis representation
4078  proshade_double* axis = new proshade_double[6];
4079  ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
4080 
4081  axis[0] = ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(0);
4082  axis[1] = rotAxis[0];
4083  axis[2] = rotAxis[1];
4084  axis[3] = rotAxis[2];
4085  axis[4] = ( 2.0 * M_PI ) / axis[0];
4086  axis[5] = 0.0;
4087 
4088  //============================================ Save axis to ret
4090 
4091  //============================================ Release memory
4092  delete[] rotAxis;
4093  }
4094 
4095  //================================================ Release memory
4096  delete[] rotMat;
4097  delete[] rotMat2;
4098  delete[] rotMatFin;
4099  delete[] rotModelC2;
4100 
4101  //================================================ Done
4102  return ;
4103 
4104 }
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:169
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:577
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:3669
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:2152
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:2101
ProSHADE_internal_symmetry::findExpectedPeakRotations
void findExpectedPeakRotations(proshade_unsign fold, std::vector< proshade_double > *expAngs)
This function computes the expected peak rotations for given fold.
Definition: ProSHADE_symmetry.cpp:705
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:1582
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_settings::maxSymmetryFold
proshade_unsign maxSymmetryFold
The highest symmetry fold to search for.
Definition: ProSHADE_settings.hpp:185
ProSHADE_internal_symmetry::groupSameAxes
std::vector< std::vector< proshade_unsign > > groupSameAxes(std::vector< proshade_double * > &peaks, proshade_double errTolerance)
This function groups the peaks by their axes of rotation.
Definition: ProSHADE_symmetry.cpp:419
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:34
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:1642
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_unsign verbose, 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:1966
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:2050
ProSHADE_internal_symmetry::addAxisUnlessSame
void addAxisUnlessSame(proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double axHeight, 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:2527
ProSHADE_internal_symmetry::predictOctaAxes
void predictOctaAxes(std::vector< proshade_double * > *CSymList, 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:3130
ProSHADE_internal_symmetry::smallestDistanceBetweenAngles
bool smallestDistanceBetweenAngles(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, std::vector< proshade_double > *tried, proshade_double *dist)
This function finds the smallest distance between the rotation angles within a group.
Definition: ProSHADE_symmetry.cpp:551
ProSHADE_internal_symmetry::predictTetraAxes
void predictTetraAxes(std::vector< proshade_double * > *CSymList, 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:4013
ProSHADE_internal_spheres::ProSHADE_rotFun_sphere
This class contains all inputed data for the rotation function angle-axis converted spheres.
Definition: ProSHADE_maths.hpp:51
ProSHADE_internal_symmetry::saveAllCSymmetries
void saveAllCSymmetries(std::vector< std::vector< proshade_unsign > > detected, std::vector< proshade_double * > peaks, std::vector< proshade_double * > *ret, proshade_double axErr)
This function takes the detected symmetries indices and peaks and saves these in the main cyclic symm...
Definition: ProSHADE_symmetry.cpp:1115
ProSHADE_internal_maths::normalDistributionValue
proshade_double normalDistributionValue(proshade_double mean, proshade_double standardDev, proshade_double value)
Function to the heiht of normal distribution given by mean and standard deviation for a given value.
Definition: ProSHADE_maths.cpp:1688
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:3803
ProSHADE_settings::allDetectedDAxes
std::vector< std::vector< proshade_unsign > > allDetectedDAxes
The vector of all detected dihedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:198
ProSHADE_internal_symmetry::isSymmetrySame
bool isSymmetrySame(std::vector< proshade_double * > *ret, proshade_double *sym, proshade_double simThres)
This function checks if a very similar symmetry is not already saved.
Definition: ProSHADE_symmetry.cpp:1177
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:188
ProSHADE_internal_peakSearch::getAllPeaksNaive
std::vector< proshade_double * > getAllPeaksNaive(proshade_complex *map, proshade_unsign dim, proshade_signed peakSize, proshade_double noIQRs)
This function finds peaks in the 3D map using the "naive" approach.
Definition: ProSHADE_peakSearch.cpp:315
ProSHADE_internal_data::ProSHADE_data
This class contains all inputed and derived data for a single structure.
Definition: ProSHADE_data.hpp:49
ProSHADE_internal_symmetry::findPeaksByHeightBoundaries
std::vector< proshade_double > findPeaksByHeightBoundaries(std::vector< proshade_double * > allPeaks, proshade_double smoothing)
This function groups the peaks by height and returns the boundaries between such groups.
Definition: ProSHADE_symmetry.cpp:310
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:1663
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:1867
ProSHADE_settings::minSymPeak
proshade_double minSymPeak
Minimum average peak for symmetry axis to be considered as "real".
Definition: ProSHADE_settings.hpp:178
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:2434
ProSHADE_internal_data::ProSHADE_data::maxShellBand
proshade_unsign maxShellBand
The maximum band for any shell of the object.
Definition: ProSHADE_data.hpp:123
ProSHADE_settings::symMissPeakThres
proshade_double symMissPeakThres
Percentage of peaks that could be missing that would warrant starting the missing peaks search proced...
Definition: ProSHADE_settings.hpp:175
ProSHADE_internal_maths::getRotationMatrixFromEulerZXZAngles
void getRotationMatrixFromEulerZXZAngles(proshade_double eulerAlpha, proshade_double eulerBeta, proshade_double eulerGamma, proshade_double *matrix)
Function to find the rotation matrix from Euler angles (ZXZ convention).
Definition: ProSHADE_maths.cpp:1005
ProSHADE_internal_data::ProSHADE_data::getDihedralSymmetriesList
std::vector< proshade_double * > 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:1222
ProSHADE_internal_symmetry::addZeroPeakToGroups
void addZeroPeakToGroups(std::vector< std::vector< proshade_unsign > > &grpsVec, std::vector< proshade_double * > &peaks)
This function takes the peak groups and adds zero peak to each of them.
Definition: ProSHADE_symmetry.cpp:621
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_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 3 C4 symmetries with perpendicular angles ...
Definition: ProSHADE_symmetry.cpp:2206
ProSHADE_symmetry.hpp
This header file declares all the functions required for symmetry detection and construction.
ProSHADE_settings::allDetectedTAxes
std::vector< proshade_unsign > allDetectedTAxes
The vector of all detected tetrahedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:199
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:101
ProSHADE_internal_symmetry::findPeaksCSymmetry
std::vector< std::vector< proshade_unsign > > findPeaksCSymmetry(std::vector< proshade_double * > *peaks, proshade_signed verbose, proshade_unsign band, proshade_double missPeakThres, proshade_double axisErrTolerance, bool axisErrToleranceDef, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function searches the list of peaks for presence of cyclic symmetry.
Definition: ProSHADE_symmetry.cpp:368
ProSHADE_settings::allDetectedOAxes
std::vector< proshade_unsign > allDetectedOAxes
The vector of all detected octahedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:200
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:192
ProSHADE_internal_symmetry::printSymmetryGroup
void printSymmetryGroup(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, proshade_signed verbose)
This function simply prints the detected symmetry and all its supporting peaks.
Definition: ProSHADE_symmetry.cpp:1059
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:2232
ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList
std::vector< proshade_double * > getPredictedIcosahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function predicts a list of all I symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2691
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_symmetry::checkExpectedAgainstFound
proshade_unsign checkExpectedAgainstFound(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, std::vector< proshade_double > *expAngs, std::vector< proshade_unsign > *matchedAngs, std::vector< proshade_unsign > *missingAngs, proshade_double axisTol)
This function computes the expected peak rotations for given fold.
Definition: ProSHADE_symmetry.cpp:734
ProSHADE_settings::useBiCubicInterpolationOnPeaks
bool useBiCubicInterpolationOnPeaks
This variable switch decides whether best symmetry is detected from peak indices, or whether bicubic ...
Definition: ProSHADE_settings.hpp:184
determinePeakThreshold
proshade_double determinePeakThreshold(std::vector< proshade_double > inArr, proshade_double noIQRsFromMedian)
This function takes a vector of values and determines the threshold for removing noise from it.
Definition: ProSHADE_symmetry.cpp:64
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:1799
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:1705
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_unsign verbose, 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:2841
ProSHADE_internal_symmetry::findSymmetryUsingFold
void findSymmetryUsingFold(ProSHADE_internal_data::ProSHADE_data *dataObj, std::vector< proshade_unsign > *angsToTry, std::vector< proshade_unsign > *grp, std::vector< proshade_double * > *peaks, std::vector< std::vector< proshade_unsign > > *ret, std::vector< proshade_unsign > *testedAlready, proshade_double axErrTolerance, bool axErrToleranceDefault, proshade_double missPeakThres, proshade_unsign verbose)
This function tests all supplied folds for being supported by the peaks (i.e. and being complete pres...
Definition: ProSHADE_symmetry.cpp:989
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:287
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:1343
ProSHADE_internal_data::ProSHADE_data::getMaxBand
proshade_unsign getMaxBand(void)
This function returns the maximum band value for the object.
Definition: ProSHADE_data.cpp:2939
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:2783
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup::findCyclicPointGroupsGivenFold
void findCyclicPointGroupsGivenFold(std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_sphere * > sphereVals, proshade_double axisTolerance, std::vector< proshade_double * > *detectedCs, bool bicubicInterp, proshade_unsign fold, proshade_unsign verbose)
Function detecting cyclic point groups with a particular fold in a peak group.
Definition: ProSHADE_spheres.cpp:1366
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup::checkIfPeakBelongs
bool checkIfPeakBelongs(proshade_double lat, proshade_double lon, proshade_unsign sphPos, proshade_double cosTol, proshade_signed verbose)
This function takes a new prospective peak and tests if it belongs to this peak group or not.
Definition: ProSHADE_spheres.cpp:1137
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_unsign verbose, 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:1445
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:93
ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesList
std::vector< proshade_double * > getCyclicSymmetriesList(ProSHADE_settings *settings)
This function obtains a list of all C symmetries from already computed self-rotation map.
Definition: ProSHADE_symmetry.cpp:191
ProSHADE_internal_data::ProSHADE_data::getPredictedTetrahedralSymmetriesList
std::vector< proshade_double * > getPredictedTetrahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function predicts a list of all T symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:3960
ProSHADE_internal_symmetry::completeMissingCSymmetry
bool completeMissingCSymmetry(ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign fold, std::vector< proshade_unsign > *grp, std::vector< proshade_double * > *peaks, std::vector< proshade_unsign > *missingPeaks, std::vector< proshade_double > *expectedAngles, std::vector< proshade_unsign > *matchedPeaks, proshade_double axErrTolerance, proshade_unsign verbose)
This function does the complete missing peak searching and filling in the missing peaks.
Definition: ProSHADE_symmetry.cpp:924
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:515
ProSHADE_internal_symmetry::giveOppositeAxesSameDirection
void giveOppositeAxesSameDirection(std::vector< proshade_double * > peaks)
This function modifiest the axes so that the highest vector element is always positive.
Definition: ProSHADE_symmetry.cpp:491
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:113
ProSHADE_internal_symmetry::printSymmetryPeaks
void printSymmetryPeaks(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, proshade_signed verbose, proshade_unsign groupNo)
This function simply prints the symmetry axis group supplied in the first parameter from the second p...
Definition: ProSHADE_symmetry.cpp:518
ProSHADE_internal_distances::computeInverseSOFTTransform
void computeInverseSOFTTransform(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function computes the inverse SO(3) transform.
Definition: ProSHADE_distances.cpp:859
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup
This class contains peak groups detected in the rotation function mapped spheres.
Definition: ProSHADE_spheres.hpp:130
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:2613
findBestIcosDihedralPair
std::pair< proshade_unsign, proshade_unsign > findBestIcosDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds the best pair of axes conforming to the icosahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:2899
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:3390
ProSHADE_internal_maths::getAxisAngleFromRotationMatrix
void getAxisAngleFromRotationMatrix(proshade_double *rotMat, proshade_double *x, proshade_double *y, proshade_double *z, proshade_double *ang)
This function converts rotation matrix to the axis-angle representation.
Definition: ProSHADE_maths.cpp:1039
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:1883
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:168
ProSHADE_internal_symmetry::getPeaksAngleAxisPositions
std::vector< proshade_double * > getPeaksAngleAxisPositions(std::vector< proshade_double * > allPeaks, proshade_unsign verbose)
This function converts peaks ZXZ Euler anles to angle-axis representation for further processing.
Definition: ProSHADE_symmetry.cpp:256
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_unsign verbose, 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:3236
ProSHADE_settings::smoothingFactor
proshade_double smoothingFactor
This factor decides how small the group sizes should be - larger factor means more smaller groups.
Definition: ProSHADE_settings.hpp:172
ProSHADE_internal_symmetry::predictIcosAxes
void predictIcosAxes(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all icosahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:2964
ProSHADE_internal_mapManip::myRound
proshade_signed myRound(proshade_double x)
Calls the appropriate version of round function depending on compiler version.
Definition: ProSHADE_mapManip.cpp:31
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:1789
findBestTetraDihedralPair
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:3900
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_unsign verbose, 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:2272
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:1515
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:65
ProSHADE_internal_symmetry::saveDetectedCSymmetry
void saveDetectedCSymmetry(proshade_unsign fold, std::vector< proshade_unsign > *matchedPeaks, std::vector< std::vector< proshade_unsign > > *ret, proshade_signed verbose)
This function saves a detected symmetry for reporting to the user.
Definition: ProSHADE_symmetry.cpp:884
ProSHADE_internal_maths::getEulerZXZFromSOFTPosition
void getEulerZXZFromSOFTPosition(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 (ZXZ convention) from index position in the inverse SOFT map.
Definition: ProSHADE_maths.cpp:961
ProSHADE_internal_symmetry::saveDSymmetry
void saveDSymmetry(std::vector< proshade_double * > *ret, std::vector< proshade_double * > *CSymList, proshade_unsign axisOne, proshade_unsign axisTwo)
This function saves a detected dihedral symmetry to the dihedral symmetries list.
Definition: ProSHADE_symmetry.cpp:1301
ProSHADE_internal_maths::vectorMedianAndIQR
void vectorMedianAndIQR(std::vector< proshade_double > *vec, proshade_double *&ret)
Function to get vector median and inter-quartile range.
Definition: ProSHADE_maths.cpp:147
ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList
std::vector< proshade_double * > getPredictedOctahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function predicts a list of all O symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2739
ProSHADE_internal_symmetry::checkForMissingPeak
proshade_double checkForMissingPeak(ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_double x, proshade_double y, proshade_double z, proshade_double angle, proshade_double heightThres, proshade_double axTol)
This function checks for the high of the correlation for particular rotation angle and axis.
Definition: ProSHADE_symmetry.cpp:816
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:3166
ProSHADE_settings::allDetectedIAxes
std::vector< proshade_unsign > allDetectedIAxes
The vector of all detected icosahedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:201
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_data::ProSHADE_data::getIcosahedralSymmetriesList
std::vector< proshade_double * > getIcosahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all I symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2629
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:3553
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:3501
findBestOctaDihedralPair
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:3064
ProSHADE_internal_data::ProSHADE_data::getTetrahedralSymmetriesList
std::vector< proshade_double * > getTetrahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all T symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:1340
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:1959
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_unsign verbose, 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:2354
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:176
ProSHADE_internal_symmetry::determineFoldToTry
bool determineFoldToTry(proshade_double dist, proshade_double *divBasis, proshade_double *divRem, proshade_double peakErr, proshade_double *symmErr, std::vector< proshade_unsign > *angsToTry)
This function determines the symmetry fold to be searched for.
Definition: ProSHADE_symmetry.cpp:658
ProSHADE_internal_distances::generateSO3CoeffsFromEMatrices
void generateSO3CoeffsFromEMatrices(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function converts the E matrices to SO(3) coefficients.
Definition: ProSHADE_distances.cpp:706
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:3692
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:2693
ProSHADE_internal_data::ProSHADE_data::getOctahedralSymmetriesList
std::vector< proshade_double * > getOctahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all O symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2093
ProSHADE_internal_messages::printProgressMessage
void printProgressMessage(proshade_signed verbose, proshade_signed messageLevel, std::string message)
General stdout message printing.
Definition: ProSHADE_messages.cpp:70
ProSHADE_internal_symmetry::printSymmetryCompletion
void printSymmetryCompletion(proshade_unsign noSyms, proshade_unsign verbose)
This function simply prints the summary and warnings for cyclic symmetries detection completion.
Definition: ProSHADE_symmetry.cpp:1085
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:1396
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:243
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:2577
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:1725
ProSHADE_internal_maths::compute3x3MatrixMultiplication
proshade_double * compute3x3MatrixMultiplication(proshade_double *mat1, proshade_double *mat2)
Function for computing a 3x3 matrix multiplication.
Definition: ProSHADE_maths.cpp:1759
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_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the fifteen C3 symmetries with correct angles ...
Definition: ProSHADE_symmetry.cpp:3308