ProSHADE  0.7.5.1 (JAN 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 
2692 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList, proshade_double matrixTolerance )
2693 {
2694  //================================================ Initialise variables
2695  std::vector< proshade_double* > ret;
2696 
2697  //================================================ Report progress
2698  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting I symmetry prediction." );
2699 
2700  //================================================ Are the basic requirements for icosahedral symmetry met?
2702  {
2703  //============================================ Generate the rest of the axes
2704  ProSHADE_internal_symmetry::predictIcosAxes ( settings, CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak, matrixTolerance );
2705  }
2706  std::cout << "The Icos search is now complete..." << std::endl;
2707  exit(0);
2708  //================================================ Report progress
2709  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "I symmetry prediction complete." );
2710 
2711  //================================================ Done
2712  return ( ret );
2713 
2714 }
2715 
2731 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList, proshade_double matrixTolerance )
2732 {
2733  //================================================ Initialise variables
2734  std::vector< proshade_double* > ret;
2735 
2736  //================================================ Report progress
2737  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting O symmetry prediction." );
2738 
2739  //================================================ Are the basic requirements for icosahedral symmetry met?
2740  if ( ProSHADE_internal_symmetry::detectOctahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
2741  {
2742  //============================================ Generate the rest of the axes
2743  ProSHADE_internal_symmetry::predictOctaAxes ( CSymList, &ret, settings->axisErrTolerance, this, settings->verbose, settings->minSymPeak, matrixTolerance );
2744  }
2745 
2746  //================================================ Report progress
2747  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "O symmetry prediction complete." );
2748 
2749  //================================================ Done
2750  return ( ret );
2751 
2752 }
2753 
2765 bool ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
2766 {
2767  //================================================ Initialise variables
2768  std::vector< proshade_unsign > C5List;
2769  proshade_double dotProduct;
2770 
2771  //================================================ Find all C5 symmetries
2772  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2773  {
2774  if ( CSymList->at(cSym)[0] == 5 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); }
2775  }
2776 
2777  //================================================ For each unique pair of C5 and C3
2778  for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
2779  {
2780  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2781  {
2782  //======================================== Compare only C3s to the C5List
2783  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2784 
2785  //======================================== Check the angle between the C5 and C3 axes
2786  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
2787  &CSymList->at(C5List.at(c5))[2],
2788  &CSymList->at(C5List.at(c5))[3],
2789  &CSymList->at(cSym)[1],
2790  &CSymList->at(cSym)[2],
2791  &CSymList->at(cSym)[3] );
2792 
2793  //======================================== Is the angle approximately the dihedral angle
2794  if ( std::abs ( std::abs( -sqrt ( 5.0 ) / 3.0 ) - std::abs( dotProduct ) ) < axErr )
2795  {
2796  return ( true );
2797  }
2798  }
2799  }
2800 
2801  //================================================ Done
2802  return ( false );
2803 
2804 }
2805 
2823 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 )
2824 {
2825  //================================================ Initialise variables
2826  std::vector< proshade_unsign > C5PossibilitiesHlp;
2827  std::vector< std::vector< proshade_unsign > > C5Possibilities;
2828  bool groupMatched;
2829 
2830  //================================================ Report progress
2831  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of six C5 axes." );
2832 
2833  //================================================ For all symmetries in the C symmetries list
2834  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2835  {
2836  //============================================ Search only using C5s and check peak height
2837  if ( CSymList->at(cIt)[0] != 5.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
2838 
2839  //============================================ If second or more C5, check if it has the correct angle to all other already found C5s for each group
2840  groupMatched = false;
2841  for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C5Possibilities.size() ); gIt++ )
2842  {
2843  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; }
2844  }
2845 
2846  //============================================ If no group matched, create a new group
2847  if ( !groupMatched ) { C5PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C5PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C5Possibilities, C5PossibilitiesHlp ); continue; }
2848  }
2849 
2850  //================================================ Test for missing symmetry axes, if need be
2851  ProSHADE_internal_symmetry::findMissingAxes ( &C5Possibilities, CSymList, 6, axErr, 1.0 / 2.0, 5, dataObj, minPeakHeight );
2852 
2853  //=================================================Any group has 6 entries? If more such groups, take the one with highest average height.
2854  proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
2855  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; } } }
2856 
2857  if ( C5Possibilities.at(maxGrp).size() == 6 )
2858  {
2859  //============================================ Success! Save and exit
2860  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)) ); }
2861 
2862  //============================================ Report progress
2863  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of six C5 axes successfull." );
2864 
2865  //============================================ Done
2866  return ;
2867  }
2868 
2869  //================================================ Done
2870  return ;
2871 
2872 }
2873 
2881 std::pair< proshade_unsign, proshade_unsign > findBestIcosDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
2882 {
2883  //================================================ Initialise variables
2884  std::pair< proshade_unsign, proshade_unsign > ret;
2885  std::vector< proshade_unsign > C5List;
2886  proshade_double bestDihedralAngle = 999.9;
2887  proshade_double dotProduct;
2888 
2889  //================================================ Find all C5 symmetries
2890  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 ); } }
2891 
2892  //================================================ For each unique pair of C5 and C3
2893  for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
2894  {
2895  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2896  {
2897  //======================================== Compare only C3s to the C5List and only with decent average peak height
2898  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2899  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
2900 
2901  //======================================== Check the angle between the C5 and C3 axes
2902  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
2903  &CSymList->at(C5List.at(c5))[2],
2904  &CSymList->at(C5List.at(c5))[3],
2905  &CSymList->at(cSym)[1],
2906  &CSymList->at(cSym)[2],
2907  &CSymList->at(cSym)[3] );
2908 
2909  //======================================== Is the angle approximately the dihedral angle?
2910  if ( std::abs ( std::abs( -sqrt ( 5.0 ) / 3.0 ) - std::abs( dotProduct ) ) < axErr )
2911  {
2912  if ( bestDihedralAngle > std::abs ( std::abs( -sqrt ( 5.0 ) / 3.0 ) - std::abs( dotProduct ) ) )
2913  {
2914  bestDihedralAngle = std::abs ( std::abs( -sqrt ( 5.0 ) / 3.0 ) - std::abs( dotProduct ) );
2915  ret.first = C5List.at(c5);
2916  ret.second = cSym;
2917  }
2918  }
2919  }
2920  }
2921 
2922  //================================================ Done
2923  return ( ret );
2924 }
2925 
2944 void ProSHADE_internal_symmetry::predictIcosAxes ( ProSHADE_settings* settings, 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, proshade_double matrixTolerance )
2945 {
2946  //================================================ Initialise variables
2947  std::vector< proshade_double* > newAxes;
2948  proshade_double axX, axY, axZ, axAng, foldTolerance = 0.1; // dotProd
2949  proshade_unsign determinedFold, prevComboSize = 0; // bestC5, bestC3
2950  std::vector< proshade_unsign > c5s, c3s, c2s;
2951 // bool anglesPassed;
2952 
2953  //================================================ Find the best axis combination with dihedral angle and correct folds
2954  std::pair< proshade_unsign, proshade_unsign > initAxes = findBestIcosDihedralPair ( CSymList, minPeakHeight, axErr );
2955  dataObj->optimiseDihedralAngleFromAngleAxis ( settings, - sqrt ( 5.0 ) / 3.0, CSymList->at(initAxes.first), CSymList->at(initAxes.second) );
2956 
2957  //================================================ Save detected axes to ret
2958  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( ret, CSymList->at(initAxes.first) );
2959  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( ret, CSymList->at(initAxes.second) );
2960 
2961  //================================================ Generate initial group elements
2962  std::vector<std::vector< proshade_double > > elsC5 = dataObj->computeGroupElementsForGroup ( CSymList, initAxes.first );
2963  std::vector<std::vector< proshade_double > > elsC3 = dataObj->computeGroupElementsForGroup ( CSymList, initAxes.second );
2964  std::vector<std::vector< proshade_double > > combo = ProSHADE_internal_data::joinElementsFromDifferentGroups ( &elsC5, &elsC3, matrixTolerance, true );
2965  std::cout << " !@!@ Allowed axis error: " << axErr << std::endl;
2966  //================================================ Iteratively find all remanining axes by multiplying the group elements
2967  while ( prevComboSize < combo.size() )
2968  {
2969  //============================================ Sanity check
2970  if ( prevComboSize > 60 ) { break; }
2971 
2972  //============================================ Initialise iteration
2973  prevComboSize = combo.size ( );
2974  newAxes.clear ( );
2975 
2976  //============================================ For each group element (this is a bit repetitive, but not slow enough to deal with right now)
2977  for ( proshade_unsign cIt = 0; cIt < static_cast< proshade_unsign > ( combo.size() ); cIt++ )
2978  {
2979  //======================================== Find the axis of the element
2980  ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( &combo.at(cIt), &axX, &axY, &axZ, &axAng );
2981  std::cout << "Combo " << cIt << " has axis " << axX << " ; " << axY << " ; " << axZ << " WITH ANGLE: " << axAng << std::endl;
2982 
2983  //======================================== Determine the fold (in terms of icosahedral symmetry fold options)
2984  if ( ( ( axAng - foldTolerance ) < 0.0 ) && ( ( axAng + foldTolerance ) > 0.0 ) ) { continue; } // Zero angle - identity element
2985  else if ( ( ( axAng - foldTolerance ) < M_PI ) && ( ( axAng + foldTolerance ) > M_PI ) ) { determinedFold = 2; } // Fold 2 ( angle = pi )
2986  else if ( ( ( -axAng - foldTolerance ) < -M_PI ) && ( ( -axAng + foldTolerance ) > -M_PI ) ) { determinedFold = 2; } // Fold 2 ( angle = -pi )
2987  else if ( ( ( axAng - foldTolerance ) < ( 2.0 * M_PI / 3.0 ) ) && ( ( axAng + foldTolerance ) > ( 2.0 * M_PI / 3.0 ) ) ) { determinedFold = 3; } // Fold 3 ( angle = 2pi/3 )
2988  else if ( ( ( -axAng - foldTolerance ) < ( -2.0 * M_PI / 3.0 ) ) && ( ( -axAng + foldTolerance ) > ( -2.0 * M_PI / 3.0 ) ) ) { determinedFold = 3; } // Fold 3 ( angle = -2pi/3 )
2989  else if ( ( ( axAng - foldTolerance ) < ( 2.0 * M_PI / 5.0 ) ) && ( ( axAng + foldTolerance ) > ( 2.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = 2pi/5 )
2990  else if ( ( ( axAng - foldTolerance ) < ( 4.0 * M_PI / 5.0 ) ) && ( ( axAng + foldTolerance ) > ( 4.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = 4pi/5 )
2991  else if ( ( ( -axAng - foldTolerance ) < ( -2.0 * M_PI / 5.0 ) ) && ( ( -axAng + foldTolerance ) > ( -2.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = -2pi/5 )
2992  else if ( ( ( -axAng - foldTolerance ) < ( -4.0 * M_PI / 5.0 ) ) && ( ( -axAng + foldTolerance ) > ( -4.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = -4pi/5 )
2993  else { continue; } // Failed to find matching fold.
2994 
2995 
2996 
2997  //======================================== Is this a new axis?
2998  if ( ProSHADE_internal_maths::isAxisUnique ( ret, axX, axY, axZ, determinedFold, axErr ) )
2999  {
3000  //==================================== Create the new axis array
3001 // anglesPassed = true;
3002  proshade_double* newAx = new proshade_double[6];
3003  ProSHADE_internal_misc::checkMemoryAllocation ( newAx, __FILE__, __LINE__, __func__ );
3004 
3005  newAx[0] = determinedFold;
3006  newAx[1] = axX;
3007  newAx[2] = axY;
3008  newAx[3] = axZ;
3009  newAx[4] = ( 2.0 * M_PI ) / determinedFold;
3010  newAx[5] = 0.0;
3011 
3012  //==================================== Check for it having the correct angles to other detected axes
3013 // if ( determinedFold == 5 )
3014 // {
3015 // for ( proshade_unsign c5It = 0; c5It < static_cast< proshade_unsign > ( c5s.size() ); c5It++ )
3016 // {
3017 // dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(c5s.at(c5It))[1], &ret->at(c5s.at(c5It))[2], &ret->at(c5s.at(c5It))[3], &newAx[1], &newAx[2], &newAx[3] );
3018 // std::cout << "C5 to C5 dot prod: " << dotProd << std::endl;
3019 // if ( ( ( std::abs ( dotProd ) - axErr ) < 0.5 ) && ( ( std::abs ( dotProd ) + axErr ) > 0.5 ) ) { continue; }
3020 // else { anglesPassed = false; break; }
3021 // }
3022 //
3023 // if ( anglesPassed ) { ProSHADE_internal_misc::addToUnsignVector ( &c5s, static_cast<proshade_unsign> ( ret->size() ) ); }
3024 // }
3025 //
3026 // if ( determinedFold == 3 )
3027 // {
3028 // for ( proshade_unsign c3It = 0; c3It < static_cast< proshade_unsign > ( c3s.size() ); c3It++ )
3029 // {
3030 // dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(c3s.at(c3It))[1], &ret->at(c3s.at(c3It))[2], &ret->at(c3s.at(c3It))[3], &newAx[1], &newAx[2], &newAx[3] );
3031 //
3035 // }
3036 //
3037 // if ( anglesPassed ) { ProSHADE_internal_misc::addToUnsignVector ( &c3s, static_cast<proshade_unsign> ( ret->size() ) ); }
3038 // }
3039 // if ( determinedFold == 2 )
3040 // {
3041 // if ( anglesPassed ) { ProSHADE_internal_misc::addToUnsignVector ( &c2s, static_cast<proshade_unsign> ( ret->size() ) ); }
3042 // }
3043 
3044  //==================================== If all good, save
3045 // if ( anglesPassed )
3046  {
3047  ProSHADE_internal_misc::addToDblPtrVector ( &newAxes, newAx );
3049  }
3050  }
3051  }
3052 
3053 // for ( int i = 0; i < combo.size(); i++ )
3054 // {
3055 // for ( int j = 0; j < combo.at(i).size(); j++ )
3056 // {
3057 // if ( j % 3 == 0 ) { std::cout << std::endl; }
3058 // std::cout << combo.at(i).at(j) << "\t";
3059 // }
3060 // std::cout << std::endl;
3061 // }
3062 
3063  //============================================ Generate new group elements from the new axes and folds
3064  for ( proshade_unsign aIt = 0; aIt < static_cast< proshade_unsign > ( newAxes.size() ); aIt++ )
3065  {
3066  std::vector< std::vector< proshade_double > > newEls = dataObj->computeGroupElementsForGroup ( &newAxes, aIt );
3067  combo = ProSHADE_internal_data::joinElementsFromDifferentGroups ( &newEls, &combo, matrixTolerance, true );
3068  delete[] newAxes.at(aIt);
3069  }
3070 //
3071 // for ( int i = 0; i < ret->size(); i++ )
3072 // {
3073 // std::cout << i << " || " << ret->at(i)[1] << " ; " << ret->at(i)[2] << " ; " << ret->at(i)[3] << std::endl;
3074 // }
3075 //
3076 // exit(0);
3077  }
3078 
3079  //================================================ Sort the axes by fold
3080  std::sort ( ret->begin(), ret->end(), ProSHADE_internal_misc::sortSymInvFoldHlp );
3081 
3082 // bool thr = true, two = true;
3083 // for ( int i = 0; i < ret->size(); i++ ) { if ( ret->at(i)[0] == 3 && thr ) { std::cout << std::endl; thr = false; } if ( ret->at(i)[0] == 2 && two ) { std::cout << std::endl; two = false; } std::cout << i << ": " << ret->at(i)[0] << " | " << ret->at(i)[1] << " ; " << ret->at(i)[2] << " ; " << ret->at(i)[3] << std::endl; }
3084 //
3085 // std::cout << "Found " << ret->size() << " icos syms and " << combo.size ( ) << " group elements." << std::endl; exit(0);
3086  //================================================ Done
3087  return ;
3088 
3089 }
3090 
3098 std::pair< proshade_unsign, proshade_unsign > findBestOctaDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
3099 {
3100  //================================================ Initialise variables
3101  std::pair< proshade_unsign, proshade_unsign > ret;
3102  std::vector< proshade_unsign > C4List;
3103  proshade_double bestDihedralAngle = 999.9;
3104  proshade_double dotProduct;
3105 
3106  //================================================ Find all C5 symmetries
3107  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 ); } }
3108 
3109  //================================================ For each unique pair of C5 and C3
3110  for ( proshade_unsign c4 = 0; c4 < static_cast<proshade_unsign> ( C4List.size() ); c4++ )
3111  {
3112  for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
3113  {
3114  //======================================== Compare only C3s to the C5List and only with decent average peak height
3115  if ( CSymList->at(cSym)[0] != 3 ) { continue; }
3116  if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
3117 
3118  //======================================== Check the angle between the C5 and C3 axes
3119  dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C4List.at(c4))[1],
3120  &CSymList->at(C4List.at(c4))[2],
3121  &CSymList->at(C4List.at(c4))[3],
3122  &CSymList->at(cSym)[1],
3123  &CSymList->at(cSym)[2],
3124  &CSymList->at(cSym)[3] );
3125 
3126  //======================================== Is the angle approximately the dihedral angle?
3127  if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
3128  {
3129  if ( bestDihedralAngle > std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) ) )
3130  {
3131  bestDihedralAngle = std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) );
3132  ret.first = C4List.at(c4);
3133  ret.second = cSym;
3134  }
3135  }
3136  }
3137  }
3138 
3139  //================================================ Done
3140  return ( ret );
3141 }
3142 
3161 void ProSHADE_internal_symmetry::predictOctaAxes ( 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, proshade_double matrixTolerance )
3162 {
3163 // //================================================ Initialise variables
3164 // std::vector< proshade_double* > newAxes;
3165 // proshade_double axX, axY, axZ, axAng, foldTolerance = 0.1;
3166 // proshade_unsign bestC5, bestC3, determinedFold, prevComboSize = 0;
3167 //
3168 // //================================================ Find the best axis combination with dihedral angle and correct folds
3169 // std::pair< proshade_unsign, proshade_unsign > initAxes = findBestOctaDihedralPair ( CSymList, minPeakHeight, axErr );
3170 //
3171 // //================================================ Save detected axes to ret
3172 // ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( ret, CSymList->at(initAxes.first) );
3173 // ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( ret, CSymList->at(initAxes.second) );
3174 //
3175 // //================================================ Generate initial group elements
3176 // std::vector<std::vector< proshade_double > > elsC4 = dataObj->computeGroupElementsForGroup ( CSymList, initAxes.first );
3177 // std::vector<std::vector< proshade_double > > elsC3 = dataObj->computeGroupElementsForGroup ( CSymList, initAxes.second );
3178 // std::vector<std::vector< proshade_double > > combo = ProSHADE_internal_data::joinElementsFromDifferentGroups ( &elsC4, &elsC3, matrixTolerance, true );
3179 //
3180 // //================================================ Iteratively find all remanining axes by multiplying the group elements
3181 // while ( prevComboSize < combo.size() )
3182 // {
3183 // //============================================ Sanity check
3184 // if ( prevComboSize > 24 ) { break; }
3185 //
3186 // //============================================ Initialise iteration
3187 // prevComboSize = combo.size ( );
3188 // newAxes.clear ( );
3189 //
3190 // //============================================ For each group element (this is a bit repetitive, but not slow enough to deal with right now)
3191 // for ( proshade_unsign cIt = 0; cIt < static_cast< proshade_unsign > ( combo.size() ); cIt++ )
3192 // {
3193 // //======================================== Find the axis of the element
3194 // ProSHADE_internal_maths::getAxisAngleFromRotationMatrix ( &combo.at(cIt), &axX, &axY, &axZ, &axAng );
3195 //
3196 // //======================================== Determine the fold (in terms of icosahedral symmetry fold options)
3197 // if ( ( ( axAng - foldTolerance ) < 0.0 ) && ( ( axAng + foldTolerance ) > 0.0 ) ) { continue; } // Zero angle - identity element
3198 // else if ( ( ( axAng - foldTolerance ) < M_PI ) && ( ( axAng + foldTolerance ) > M_PI ) ) { determinedFold = 2; } // Fold 2 ( angle = pi )
3199 // else if ( ( ( axAng - foldTolerance ) < ( 2.0 * M_PI / 3.0 ) ) && ( ( axAng + foldTolerance ) > ( 2.0 * M_PI / 3.0 ) ) ) { determinedFold = 3; } // Fold 3 ( angle = 2pi/3 )
3200 // else if ( ( ( axAng - foldTolerance ) < ( 2.0 * M_PI / 1.5 ) ) && ( ( axAng + foldTolerance ) > ( 2.0 * M_PI / 1.5 ) ) ) { determinedFold = 3; } // Fold 3 ( angle = 4pi/3 )
3201 // else if ( ( ( axAng - foldTolerance ) < ( 2.0 * M_PI / 5.0 ) ) && ( ( axAng + foldTolerance ) > ( 2.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = 2pi/5 )
3202 // else if ( ( ( axAng - foldTolerance ) < ( 4.0 * M_PI / 5.0 ) ) && ( ( axAng + foldTolerance ) > ( 4.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = 4pi/5 )
3203 // else if ( ( ( axAng - foldTolerance ) < ( 6.0 * M_PI / 5.0 ) ) && ( ( axAng + foldTolerance ) > ( 6.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = 6pi/5 )
3204 // else if ( ( ( axAng - foldTolerance ) < ( 8.0 * M_PI / 5.0 ) ) && ( ( axAng + foldTolerance ) > ( 8.0 * M_PI / 5.0 ) ) ) { determinedFold = 5; } // Fold 5 ( angle = 8pi/5 )
3205 // else { continue; } // Failed to find matching fold.
3206 //
3207 // //======================================== Is this a new axis?
3208 // if ( ProSHADE_internal_maths::isAxisUnique ( ret, axX, axY, axZ, determinedFold, axErr ) )
3209 // {
3210 // proshade_double* newAx = new proshade_double[6];
3211 // ProSHADE_internal_misc::checkMemoryAllocation ( newAx, __FILE__, __LINE__, __func__ );
3212 //
3213 // newAx[0] = determinedFold;
3214 // newAx[1] = axX;
3215 // newAx[2] = axY;
3216 // newAx[3] = axZ;
3217 // newAx[4] = ( 2.0 * M_PI ) / determinedFold;
3218 // newAx[5] = 0.0;
3219 //
3220 // ProSHADE_internal_misc::addToDblPtrVector ( &newAxes, newAx );
3221 // ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( ret, newAx );
3222 // }
3223 // }
3224 //
3225 // //============================================ Generate new group elements from the new axes and folds
3226 // for ( proshade_unsign aIt = 0; aIt < static_cast< proshade_unsign > ( newAxes.size() ); aIt++ )
3227 // {
3228 // std::vector< std::vector< proshade_double > > newEls = dataObj->computeGroupElementsForGroup ( &newAxes, aIt );
3229 // combo = ProSHADE_internal_data::joinElementsFromDifferentGroups ( &newEls, &combo, matrixTolerance, true );
3230 // delete[] newAxes.at(aIt);
3231 // }
3232 // }
3233 //
3234 // //================================================ Sort the axes by fold
3235 // std::sort ( ret->begin(), ret->end(), ProSHADE_internal_misc::sortSymInvFoldHlp );
3236 //
3237 // std::cout << "Found " << ret->size() << " octa syms and " << combo.size ( ) << " group elements." << std::endl; exit(0);
3238 
3239  //================================================ Done
3240  return ;
3241 
3242 }
3243 
3257 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 )
3258 {
3259  //================================================ Initialise variables
3260  std::vector< proshade_unsign > prospectiveC3s, retGrp;
3261  proshade_double dotProd;
3262  proshade_unsign noClose, noAway;
3263 
3264  //================================================ Report progress
3265  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of ten C3 axes." );
3266 
3267  //================================================ For each C3
3268  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
3269  {
3270  //============================================ Use only C3s with hight enough average
3271  if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
3272 
3273  //============================================ Check the C3 has acos ( sqrt ( 5.0 ) / 3.0 ) to 3 C5s and acos ( 1.0 - ( sqrt ( 5.0 ) / 3.0 ) ) to the other three C5s
3274  noClose = 0; noAway = 0;
3275  for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
3276  {
3277  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
3278  &ret->at(rIt)[2],
3279  &ret->at(rIt)[3],
3280  &CSymList->at(cIt)[1],
3281  &CSymList->at(cIt)[2],
3282  &CSymList->at(cIt)[3] );
3283 
3284  if ( ( std::abs ( dotProd ) > ( ( sqrt ( 5.0 ) / 3.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( sqrt ( 5.0 ) / 3.0 ) + axErr ) ) ) { noClose += 1; continue; }
3285  if ( ( std::abs ( dotProd ) > ( 1.0 - ( sqrt ( 5.0 ) / 3.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( 1.0 - ( sqrt ( 5.0 ) / 3.0 ) + axErr ) ) ) { noAway += 1; continue; }
3286  }
3287 
3288  //============================================ If correct angles distribution is found, save the axis
3289  if ( ( noClose == 3 ) && ( noAway == 3 ) )
3290  {
3291  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC3s, cIt );
3292  }
3293  }
3294 
3295  //================================================ Search for missing axes
3296  for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
3297  if ( !ProSHADE_internal_symmetry::findMissingAxesDual ( &prospectiveC3s, CSymList, ret, &retGrp, 10, axErr, 3, sqrt ( 5.0 ) / 3.0, 3, 1.0 - ( sqrt ( 5.0 ) / 3.0 ), 3, dataObj ) )
3298  {
3299  return ;
3300  }
3301 
3302  //================================================ Found correct number of axes! Now save the
3303  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC3s.size() ); iter++ )
3304  {
3305  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC3s.at(iter)) );
3306  }
3307 
3308  //================================================ Report progress
3309  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of ten C3 axes successfull." );
3310 
3311  //================================================ Done
3312  return ;
3313 
3314 }
3315 
3329 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 )
3330 {
3331  //================================================ Initialise variables
3332  std::vector< proshade_unsign > prospectiveC2s, retGrp;
3333  proshade_double dotProd;
3334  proshade_unsign noClose, noMidway, noAway;
3335 
3336  //================================================ Report progress
3337  ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of fifteen C2 axes." );
3338 
3339  //================================================ For each C2
3340  for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
3341  {
3342  //============================================ Use only C2s
3343  if ( CSymList->at(cIt)[0] != 2.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
3344 
3345  //============================================ 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
3346  noClose = 0; noMidway = 0; noAway = 0;
3347  for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
3348  {
3349  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
3350  &ret->at(rIt)[2],
3351  &ret->at(rIt)[3],
3352  &CSymList->at(cIt)[1],
3353  &CSymList->at(cIt)[2],
3354  &CSymList->at(cIt)[3] );
3355 
3356  if ( ( std::abs ( dotProd ) > ( ( sqrt ( 3.0 ) / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( sqrt ( 3.0 ) / 2.0 ) + axErr ) ) ) { noAway += 1; continue; }
3357  if ( ( std::abs ( dotProd ) > ( ( 1.0 / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / 2.0 ) + axErr ) ) ) { noMidway += 1; continue; }
3358  if ( ( std::abs ( dotProd ) > ( ( 0.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 0.0 ) + axErr ) ) ) { noClose += 1; continue; }
3359  }
3360 
3361  //============================================ If correct angles distribution is found, save the axis
3362  if ( ( noClose == 2 ) && ( noMidway == 2 ) && ( noAway == 2 ) )
3363  {
3364  ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt );
3365  }
3366  }
3367 
3368  //================================================ Search for missing axes
3369  for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
3370  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 ) )
3371  {
3372  return ;
3373  }
3374 
3375  //================================================ Found correct number of axes! Now save the
3376  for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC2s.size() ); iter++ )
3377  {
3378  ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC2s.at(iter)) );
3379  }
3380 
3381  //================================================ Report progress
3382  ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of fifteen C2 axes successfull." );
3383 
3384  //================================================ Done
3385  return ;
3386 
3387 }
3388 
3411 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 )
3412 {
3413  //================================================ Initialise variables
3414  bool atLeastOne = false;
3415  std::vector< proshade_double* > prosp;
3416  std::vector< proshade_double > sol;
3417 
3418  //================================================ Proceed only if need be
3419  if ( static_cast<proshade_unsign> ( possibilities->size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
3420 
3421  //================================================ Copy already found to prospective
3422  for ( proshade_unsign prIt = 0; prIt < static_cast<proshade_unsign> ( possibilities->size() ); prIt++ )
3423  {
3424  ProSHADE_internal_symmetry::addAxisUnlessSame ( CSymList->at(possibilities->at(prIt))[0],
3425  CSymList->at(possibilities->at(prIt))[1],
3426  CSymList->at(possibilities->at(prIt))[2],
3427  CSymList->at(possibilities->at(prIt))[3],
3428  CSymList->at(possibilities->at(prIt))[5], &prosp, axErr );
3429  }
3430 
3431  //================================================ Start generating possible solutions
3432  for ( proshade_unsign rgIt1 = 0; rgIt1 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt1++ )
3433  {
3434  for ( proshade_unsign rgIt2 = 0; rgIt2 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt2++ )
3435  {
3436  //======================================== Use unique combinations (order matters here!)
3437  if ( rgIt1 == rgIt2 ) { continue; }
3438 
3439  for ( proshade_unsign rgIt3 = 0; rgIt3 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt3++ )
3440  {
3441  //==================================== Use unique combinations (order matters here!)
3442  if ( ( rgIt1 == rgIt3 ) || ( rgIt2 == rgIt3 ) ) { continue; }
3443 
3444  //==================================== Generate possible solution (1)
3445  sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
3446  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
3447  ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], angle1, angle2, angle3 );
3448 
3449  //==================================== Check if solution fits the group completely
3450  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 );
3451  if ( prosp.size() == requiredNoAxes ) { break; }
3452 
3453  //==================================== Generate possible solution (2)
3454  sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
3455  ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
3456  ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], -angle1, -angle2, -angle3 );
3457 
3458  //==================================== Check if solution fits the group completely
3459  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 );
3460  if ( prosp.size() == requiredNoAxes ) { break; }
3461  }
3462 
3463  if ( prosp.size() == requiredNoAxes ) { break; }
3464  }
3465 
3466  if ( prosp.size() == requiredNoAxes ) { break; }
3467  }
3468 
3469  //================================================ Found all required axes
3470  if ( prosp.size() == requiredNoAxes )
3471  {
3472  //============================================ For each found missing axis
3473  for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
3474  {
3475  if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, prosp.at(axIt), axErr ) )
3476  {
3477  //======================================== Add
3478  ProSHADE_internal_misc::addToDblPtrVector ( CSymList, prosp.at(axIt) );
3479  ProSHADE_internal_misc::addToUnsignVector ( possibilities, static_cast<proshade_unsign> ( CSymList->size()-1 ) );
3480  }
3481  }
3482 
3483  atLeastOne = true;
3484  return ( atLeastOne );
3485  }
3486  else
3487  {
3488  //============================================ Delete all found, but unnecessary axes
3489  for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
3490  {
3491  delete[] prosp.at(axIt);
3492  }
3493  }
3494 
3495  //================================================ Done
3496  return ( atLeastOne );
3497 
3498 }
3499 
3522 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 )
3523 {
3524  //================================================ Initialise variables
3525  proshade_unsign noG1 = 0;
3526  proshade_unsign noG2 = 0;
3527  proshade_unsign noG3 = 0;
3528  proshade_double dotProd = 0.0;
3529  proshade_double axHeight = 0.0;
3530 
3531  //================================================ Find the angle and count dual matching frequencies
3532  for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( retGroup->size() ); rIt++ )
3533  {
3534  dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(retGroup->at(rIt))[1],
3535  &ret->at(retGroup->at(rIt))[2],
3536  &ret->at(retGroup->at(rIt))[3],
3537  &axX, &axY, &axZ );
3538 
3539  if ( ( std::abs ( dotProd ) > ( angle1 - axErr ) ) && ( std::abs ( dotProd ) < ( angle1 + axErr ) ) ) { noG1 += 1; continue; }
3540  if ( ( std::abs ( dotProd ) > ( angle2 - axErr ) ) && ( std::abs ( dotProd ) < ( angle2 + axErr ) ) ) { noG2 += 1; continue; }
3541  if ( ( std::abs ( dotProd ) > ( angle3 - axErr ) ) && ( std::abs ( dotProd ) < ( angle3 + axErr ) ) ) { noG3 += 1; continue; }
3542  }
3543 
3544  //================================================ If correct frequencies are matched, check height.
3545  if ( ( noG1 == noMatchesG1 ) && ( noG2 == noMatchesG2 ) && ( noG3 == noMatchesG3 ) )
3546  {
3547  //============================================ Is the height good enough?
3548  axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( axX, axY, axZ, dataObj, fold, axErr );
3549 
3550  //============================================ If so, save
3551  if ( axHeight > 0.1 )
3552  {
3553  ProSHADE_internal_symmetry::addAxisUnlessSame ( fold, axX, axY, axZ, axHeight, prosp, axErr );
3554  }
3555  }
3556 
3557  //================================================ Done
3558  return ;
3559 
3560 }
3561 
3575 {
3576  //================================================ Initialise variables
3577  std::vector< proshade_unsign > primes = ProSHADE_internal_maths::findAllPrimes ( settings->maxSymmetryFold );
3578  std::vector< proshade_double* > ret, tmpHolder;
3579  std::vector< proshade_unsign > testedFolds;
3580  proshade_double symThres;
3581  proshade_unsign foldToTest;
3582  bool foldDone, anyNewSyms = true;
3583 
3584  //================================================ For each found prime number in the limit
3585  for ( proshade_unsign prIt = 0; prIt < static_cast< proshade_unsign > ( primes.size() ); prIt++ )
3586  {
3587  //============================================ Report progress
3588  std::stringstream hlpSS;
3589  hlpSS << "Searching for prime fold symmetry C" << primes.at(prIt) << ".";
3590  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str() );
3591 
3592  //============================================ Get all symmetries for this prime fold
3593  std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, primes.at(prIt), &symThres );
3594 
3595  //============================================ Save the detected C symmetries
3596  for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( prSyms.size() ); axIt++ )
3597  {
3598  //======================================== Is this symmetry passing the threshold?
3599  if ( prSyms.at(axIt)[5] >= symThres )
3600  {
3601  //==================================== Add this symmetry to final list
3602  if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(axIt), settings->axisErrTolerance, true ) )
3603  {
3604  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, prSyms.at(axIt) );
3605  }
3606  }
3607 
3608  //======================================== Release memory
3609  delete[] prSyms.at(axIt);
3610  }
3611  }
3612 
3613  //================================================ Was anything found?
3614  if ( ret.size() < 1 ) { return ( ret ); }
3615 
3616  //================================================ Check for prime symmetry fold multiples
3617  while ( anyNewSyms )
3618  {
3619  //============================================ Initialise new iteration
3620  anyNewSyms = false;
3621 
3622  //============================================ For each passing symmetry, look if there are any combinations of symmetries that would contain it
3623  for ( proshade_unsign axIt1 = 0; axIt1 < static_cast< proshade_unsign > ( ret.size() ); axIt1++ )
3624  {
3625  for ( proshade_unsign axIt2 = 0; axIt2 < static_cast< proshade_unsign > ( ret.size() ); axIt2++ )
3626  {
3627  //==================================== Initialise iteration
3628  foldToTest = ret.at(axIt1)[0] * ret.at(axIt2)[0];
3629  if ( foldToTest > settings->maxSymmetryFold ) { continue; }
3630 
3631  //==================================== Was this fold tested already?
3632  foldDone = false;
3633  for ( proshade_unsign fIt = 0; fIt < static_cast< proshade_unsign > ( testedFolds.size() ); fIt++ ) { if ( testedFolds.at(fIt) == foldToTest ) { foldDone = true; break; } }
3634  if ( foldDone ) { continue; }
3635  else { ProSHADE_internal_misc::addToUnsignVector ( &testedFolds, foldToTest ); }
3636 
3637  //==================================== Report progress
3638  std::stringstream hlpSS2;
3639  hlpSS2 << "Searching for fold combination of detected folds " << ret.at(axIt1)[0] << " and " << ret.at(axIt2)[0] << ", i.e. C" << foldToTest << ".";
3640  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS2.str() );
3641 
3642  //==================================== Get all symmetries for this fold
3643  std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, foldToTest, &symThres );
3644 
3645  //==================================== For each detected group with the required fold
3646  for ( proshade_unsign newAxIt = 0; newAxIt < static_cast< proshade_unsign > ( prSyms.size() ); newAxIt++ )
3647  {
3648  if ( prSyms.at(newAxIt)[5] >= symThres )
3649  {
3650  //================================ Add to detected axes
3651  if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(newAxIt), settings->axisErrTolerance, true ) )
3652  {
3653  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &tmpHolder, prSyms.at(newAxIt) );
3654  }
3655  }
3656 
3657  //==================================== Release memory
3658  delete[] prSyms.at(newAxIt);
3659  }
3660  }
3661  }
3662 
3663  //============================================ Add newly found groups and repeat if need be
3664  if ( tmpHolder.size() > 0 )
3665  {
3666  for ( proshade_unsign tmpIt = 0; tmpIt < static_cast< proshade_unsign > ( tmpHolder.size() ); tmpIt++ )
3667  {
3668  ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, tmpHolder.at(tmpIt) );
3669  delete[] tmpHolder.at(tmpIt);
3670  }
3671 
3672  anyNewSyms = true;
3673  tmpHolder.clear ( );
3674  }
3675  }
3676 
3677  //================================================ Sort the vector
3678  std::sort ( ret.begin(), ret.end(), ProSHADE_internal_misc::sortSymHlpInv );
3679 
3680  //================================================ Done
3681  return ( ret );
3682 }
3683 
3690 bool sortProSHADESymmetryByPeak ( proshade_double* a, proshade_double* b)
3691 {
3692  //================================================ Done
3693  return ( a[5] > b[5] );
3694 
3695 }
3696 
3713 std::vector < proshade_double* > ProSHADE_internal_data::ProSHADE_data::findRequestedCSymmetryFromAngleAxis ( ProSHADE_settings* settings, proshade_unsign fold, proshade_double* peakThres )
3714 {
3715  //================================================ Initialise variables
3716  proshade_double soughtAngle;
3717  std::vector< proshade_double > allPeakHeights;
3718  std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup* > peakGroups;
3719  std::vector< proshade_double* > ret;
3720  bool newPeak;
3721 
3722  //================================================ Make sure we have a clean start
3723  this->sphereMappedRotFun.clear();
3724 
3725  //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
3726  for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( fold ); angIt += 1.0 )
3727  {
3728  //============================================ Figure the angles to form the symmetry
3729  soughtAngle = angIt * ( 2.0 * M_PI / static_cast<proshade_double> ( fold ) );
3730 
3731  //============================================ Create the angle-axis sphere with correct radius (angle)
3732  this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( soughtAngle,
3733  M_PI / static_cast<proshade_double> ( fold ),
3734  this->maxShellBand * 2.0,
3735  soughtAngle,
3736  static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3737 
3738  //=========================================== Interpolate rotation function onto the sphere
3739  this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
3740 
3741  //============================================ Find all peaks for this sphere
3742  this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->findAllPeaks ( settings->peakNeighbours, &allPeakHeights );
3743  }
3744 
3745  //============================================ Report progress
3746  std::stringstream hlpSS;
3747  hlpSS << "Found a total of " << std::pow ( this->maxShellBand * 2.0 * (fold-1), 2.0 ) - allPeakHeights.size() << " non-peaks for thresholding.";
3748  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS.str() );
3749 
3750  //================================================ Determine the threshold for significant peaks
3751  *peakThres = std::max ( settings->minSymPeak, determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak ) );
3752 
3753  //============================================ Report progress
3754  std::stringstream hlpSS2;
3755  hlpSS2 << "Determined peak threshold " << *peakThres << ".";
3756  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str() );
3757 
3758  //================================================ Remove small peaks
3759  for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
3760  {
3761  this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( *peakThres );
3762  }
3763 
3764  //================================================ Group peaks
3765  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); sphIt++ )
3766  {
3767  //============================================ For each peak
3768  for ( proshade_unsign pkIt = 0; pkIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.at(sphIt)->getPeaks().size() ); pkIt++ )
3769  {
3770  //======================================== Check if peak belongs to an already detected peak group
3771  newPeak = true;
3772  for ( proshade_unsign pkGrpIt = 0; pkGrpIt < static_cast<proshade_unsign> ( peakGroups.size() ); pkGrpIt++ )
3773  {
3774  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; }
3775  }
3776 
3777  //======================================== If already added, go to next one
3778  if ( !newPeak ) { continue; }
3779 
3780  //======================================== If not, create a new group with this peak
3781  peakGroups.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first,
3782  this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second,
3783  sphIt,
3784  this->sphereMappedRotFun.at(sphIt)->getAngularDim() ) );
3785  }
3786  }
3787 
3788  //================================================ For each peak group, look for the requested fold
3789  for ( proshade_unsign grIt = 0; grIt < static_cast<proshade_unsign> ( peakGroups.size() ); grIt++ )
3790  {
3791  //============================================ Report progress
3792  std::stringstream hlpSS3;
3793  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 ";
3794  for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( peakGroups.at(grIt)->getSpherePositions().size() ); sphIt++ ) { hlpSS3 << peakGroups.at(grIt)->getSpherePositions().at(sphIt) << " ; "; }
3795  ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 5, hlpSS3.str() );
3796 
3797  //============================================ Find point groups in the peak group
3798  peakGroups.at(grIt)->findCyclicPointGroupsGivenFold ( this->sphereMappedRotFun, settings->axisErrTolerance, &ret, settings->useBiCubicInterpolationOnPeaks, fold, settings->verbose );
3799 
3800  //============================================ Release the memory
3801  delete peakGroups.at(grIt);
3802  }
3803 
3804  //================================================ Sort ret by peak height
3805  std::sort ( ret.begin(), ret.end(), sortProSHADESymmetryByPeak );
3806 
3807  //================================================ Done
3808  return ( ret );
3809 
3810 }
3811 
3822 std::vector < proshade_double* > ProSHADE_internal_data::ProSHADE_data::optimiseDihedralAngleFromAngleAxis ( ProSHADE_settings* settings, proshade_double angle, proshade_double* axis1, proshade_double* axis2 )
3823 {
3824  //================================================ Initialise variables
3825  std::vector< proshade_double* > ret;
3826  std::vector<ProSHADE_internal_spheres::ProSHADE_rotFun_sphere*> ax1SphereMappedRotFun, ax2SphereMappedRotFun;
3827  std::cout << "ANGLE: " << angle << std::endl;
3828 
3829  //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks for first axis
3830  for ( proshade_double angIt = 1.0; angIt < axis1[0]; angIt += 1.0 )
3831  {
3832  //============================================ Create the angle-axis sphere with correct radius (angle)
3833  ax1SphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( axis1[4],
3834  M_PI / static_cast<proshade_double> ( axis1[0] ),
3835  this->maxShellBand * 2.0,
3836  axis1[4],
3837  static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3838 
3839  //=========================================== Interpolate rotation function onto the sphere
3840  ax1SphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
3841  }
3842 
3843  //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks for second axis
3844  for ( proshade_double angIt = 1.0; angIt < axis2[0]; angIt += 1.0 )
3845  {
3846  //============================================ Create the angle-axis sphere with correct radius (angle)
3847  ax2SphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( axis2[4],
3848  M_PI / static_cast<proshade_double> ( axis2[0] ),
3849  this->maxShellBand * 2.0,
3850  axis2[4],
3851  static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3852 
3853  //=========================================== Interpolate rotation function onto the sphere
3854  ax2SphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
3855  }
3856 
3857  //================================================ Change axis1 to improve the angle
3858  std::cout << "Original axes : " << axis1[1] << " ; " << axis1[2] << " ; " << axis1[3] << " AND " << axis2[1] << " ; " << axis2[2] << " ; " << axis2[3] << std::endl;
3859  proshade_double angErr = 0.05;
3860  proshade_double dotProd, dotProdHlp, bestAngErr, axNorm, bestX = 0.0, bestY = 0.0, bestZ = 0.0;
3861  bool axisImproved = true;
3862  while ( axisImproved && angErr > 0.0001 )
3863  {
3864  //============================================ Find current error
3865  dotProd = ProSHADE_internal_maths::computeDotProduct ( &axis1[1], &axis1[2], &axis1[3], &axis2[1], &axis2[2], &axis2[3] );
3866  angErr = std::abs ( std::abs ( angle ) - std::abs ( dotProd ) );
3867  std::cout << " !!! Angle error : " << angErr << std::endl;
3868 
3869  //============================================ Can we improve by moving a bit?
3870  axisImproved = false;
3871  bestAngErr = angErr;
3872  for ( proshade_double xCh = -1.0; xCh < 1.1; xCh += 1.0 )
3873  {
3874  for ( proshade_double yCh = -1.0; yCh < 1.1; yCh += 1.0 )
3875  {
3876  for ( proshade_double zCh = -1.0; zCh < 1.1; zCh += 1.0 )
3877  {
3878  //================================ Test changed axis
3879  axNorm = std::sqrt ( std::pow( axis1[1] + (xCh * angErr), 2.0 ) + std::pow( axis1[2] + (yCh * angErr), 2.0 ) + std::pow ( axis1[3] + (zCh * angErr), 2.0 ) );
3880  dotProdHlp = ProSHADE_internal_maths::computeDotProduct ( ( axis1[1] + (xCh * angErr) ) / axNorm,
3881  ( axis1[2] + (yCh * angErr) ) / axNorm,
3882  ( axis1[3] + (zCh * angErr) ) / axNorm, axis2[1], axis2[2], axis2[3] );
3883 
3884  if ( bestAngErr > std::abs ( std::abs ( angle ) - std::abs ( dotProdHlp ) ) )
3885  {
3886  //============================ Improvement!
3887  bestX = ( axis1[1] + (xCh * angErr) ) / axNorm;
3888  bestY = ( axis1[2] + (yCh * angErr) ) / axNorm;
3889  bestZ = ( axis1[3] + (zCh * angErr) ) / axNorm;
3890  axisImproved = true;
3891  bestAngErr = std::abs ( std::abs ( angle ) - std::abs ( dotProdHlp ) );
3892  }
3893  }
3894  }
3895  }
3896 
3897  //============================================ If improved, try again
3898  if ( axisImproved ) { axis1[1] = bestX; axis1[2] = bestY; axis1[3] = bestZ; }
3899  }
3900 
3901  std::cout << "Angle improved axes: " << axis1[1] << " ; " << axis1[2] << " ; " << axis1[3] << " AND " << axis2[1] << " ; " << axis2[2] << " ; " << axis2[3] << std::endl;
3902 
3903  //================================================ Keeping the angle, search for highest peak
3904  proshade_double curSum = 0.0;
3905  proshade_double lat, lon, bestSumChange, bestSum = 0.0, sumChange = 0.005;
3906  bool sumImproved = true;
3907  while ( sumImproved && sumChange > 0.00001 )
3908  {
3909  std::cout << std::endl << " !!! New iteration axes: " << axis1[1] << " ; " << axis1[2] << " ; " << axis1[3] << " AND " << axis2[1] << " ; " << axis2[2] << " ; " << axis2[3] << std::endl;
3910  sumImproved = false;
3911  bestSum = 0.0;
3912 
3913  lon = atan2 ( axis1[2], axis1[1] );
3914  lat = asin ( axis1[3] );
3915  for ( proshade_unsign sph1It = 0; sph1It < static_cast< proshade_unsign > ( ax1SphereMappedRotFun.size() ); sph1It++ )
3916  {
3917  bestSum += ax1SphereMappedRotFun.at(sph1It)->getSphereLatLonLinearInterpolationPos ( lat, lon );
3918  }
3919 
3920  lon = atan2 ( axis2[2], axis2[1] );
3921  lat = asin ( axis2[3] );
3922  for ( proshade_unsign sph2It = 0; sph2It < static_cast< proshade_unsign > ( ax2SphereMappedRotFun.size() ); sph2It++ )
3923  {
3924  bestSum += ax2SphereMappedRotFun.at(sph2It)->getSphereLatLonLinearInterpolationPos ( lat, lon );
3925  }
3926 
3927  std::cout << " !!! !!! Current best sum is: " << bestSum << std::endl;
3928 
3929  bestSumChange = bestSum;
3930  for ( proshade_double xCh = -1.0; xCh < 1.1; xCh += 1.0 )
3931  {
3932  for ( proshade_double yCh = -1.0; yCh < 1.1; yCh += 1.0 )
3933  {
3934  for ( proshade_double zCh = -1.0; zCh < 1.1; zCh += 1.0 )
3935  {
3936  curSum = 0.0;
3937 
3938  axNorm = std::sqrt ( std::pow( axis1[1] + ( xCh * sumChange ), 2.0 ) + std::pow( axis1[2] + ( yCh * sumChange ), 2.0 ) + std::pow ( axis1[3] + ( zCh * sumChange ), 2.0 ) );
3939  lon = atan2 ( ( axis1[2] + ( yCh * sumChange ) ) / axNorm, ( axis1[1] + ( xCh * sumChange ) ) / axNorm );
3940  lat = asin ( ( axis1[3] + ( zCh * sumChange ) ) / axNorm );
3941  for ( proshade_unsign sph1It = 0; sph1It < static_cast< proshade_unsign > ( ax1SphereMappedRotFun.size() ); sph1It++ )
3942  {
3943  curSum += ax1SphereMappedRotFun.at(sph1It)->getSphereLatLonLinearInterpolationPos ( lat, lon );
3944  }
3945 
3946  axNorm = std::sqrt ( std::pow( axis2[1] + ( xCh * sumChange ), 2.0 ) + std::pow( axis2[2] + ( yCh * sumChange ), 2.0 ) + std::pow ( axis2[3] + ( zCh * sumChange ), 2.0 ) );
3947  lon = atan2 ( ( axis2[2] + ( yCh * sumChange ) ) / axNorm, ( axis2[1] + ( xCh * sumChange ) ) / axNorm );
3948  lat = asin ( ( axis2[3] + ( zCh * sumChange ) ) / axNorm );
3949  for ( proshade_unsign sph2It = 0; sph2It < static_cast< proshade_unsign > ( ax2SphereMappedRotFun.size() ); sph2It++ )
3950  {
3951  curSum += ax2SphereMappedRotFun.at(sph2It)->getSphereLatLonLinearInterpolationPos ( lat, lon );
3952  }
3953 
3954  if ( curSum > bestSumChange )
3955  {
3956  sumImproved = true;
3957  bestX = ( xCh * sumChange );
3958  bestY = ( yCh * sumChange );
3959  bestZ = ( zCh * sumChange );
3960  bestSumChange = curSum;
3961  }
3962  }
3963  }
3964  }
3965 
3966  if ( sumImproved )
3967  {
3968  axis1[1] += bestX;
3969  axis1[2] += bestY;
3970  axis1[3] += bestZ;
3971  axNorm = std::sqrt ( std::pow( axis1[1], 2.0 ) + std::pow( axis1[2], 2.0 ) + std::pow ( axis1[3], 2.0 ) );
3972  axis1[1] /= axNorm;
3973  axis1[2] /= axNorm;
3974  axis1[3] /= axNorm;
3975 
3976 
3977  axis2[1] += bestX;
3978  axis2[2] += bestY;
3979  axis2[3] += bestZ;
3980  axNorm = std::sqrt ( std::pow( axis2[1], 2.0 ) + std::pow( axis2[2], 2.0 ) + std::pow ( axis2[3], 2.0 ) );
3981  axis2[1] /= axNorm;
3982  axis2[2] /= axNorm;
3983  axis2[3] /= axNorm;
3984 
3985 
3986  sumChange = bestSumChange - bestSum;
3987  std::cout << " !!! END iteration axes: " << axis1[1] << " ; " << axis1[2] << " ; " << axis1[3] << " AND " << axis2[1] << " ; " << axis2[2] << " ; " << axis2[3] << std::endl;
3988  }
3989  }
3990 
3991  std::cout << "Sum improved axes: " << axis1[1] << " ; " << axis1[2] << " ; " << axis1[3] << " AND " << axis2[1] << " ; " << axis2[2] << " ; " << axis2[3] << std::endl;
3992 
3993  //================================================
3994  std::cout << "Finally, the best sum on changed axes is " << bestSum << " ." << std::endl;
3995 
3996 
3997 // //================================================ Initialise variables
3998 // proshade_double soughtAngle;
3999 // std::vector< proshade_double > allPeakHeights;
4000 // std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup* > peakGroups;
4001 // std::vector< proshade_double* > ret;
4002 // bool newPeak;
4003 //
4004 // //================================================ Make sure we have a clean start
4005 // this->sphereMappedRotFun.clear();
4006 //
4007 // //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
4008 // for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( fold ); angIt += 1.0 )
4009 // {
4010 // //============================================ Figure the angles to form the symmetry
4011 // soughtAngle = angIt * ( 2.0 * M_PI / static_cast<proshade_double> ( fold ) );
4012 //
4013 // //============================================ Create the angle-axis sphere with correct radius (angle)
4014 // this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( soughtAngle,
4015 // M_PI / static_cast<proshade_double> ( fold ),
4016 // this->maxShellBand * 2.0,
4017 // soughtAngle,
4018 // static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
4019 //
4020 // //=========================================== Interpolate rotation function onto the sphere
4021 // this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
4022 //
4023 // //============================================ Find all peaks for this sphere
4024 // this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->findAllPeaks ( settings->peakNeighbours, &allPeakHeights );
4025 // }
4026 //
4027 // //============================================ Report progress
4028 // std::stringstream hlpSS;
4029 // hlpSS << "Found a total of " << std::pow ( this->maxShellBand * 2.0 * (fold-1), 2.0 ) - allPeakHeights.size() << " non-peaks for thresholding.";
4030 // ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS.str() );
4031 //
4032 // //================================================ Determine the threshold for significant peaks
4033 // *peakThres = std::max ( settings->minSymPeak, determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak ) );
4034 //
4035 // //============================================ Report progress
4036 // std::stringstream hlpSS2;
4037 // hlpSS2 << "Determined peak threshold " << *peakThres << ".";
4038 // ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str() );
4039 //
4040 // //================================================ Remove small peaks
4041 // for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
4042 // {
4043 // this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( *peakThres );
4044 // }
4045 //
4046 // proshade_unsign tc = 0;
4047 // for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ ) { tc += this->sphereMappedRotFun.at(shIt)->getPeaks().size(); }
4048 //
4049 //
4050 // //================================================ Group peaks
4051 // for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); sphIt++ )
4052 // {
4053 // //============================================ For each peak
4054 // for ( proshade_unsign pkIt = 0; pkIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.at(sphIt)->getPeaks().size() ); pkIt++ )
4055 // {
4056 // //======================================== Check if peak belongs to an already detected peak group
4057 // newPeak = true;
4058 // for ( proshade_unsign pkGrpIt = 0; pkGrpIt < static_cast<proshade_unsign> ( peakGroups.size() ); pkGrpIt++ )
4059 // {
4060 // 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; }
4061 // }
4062 //
4063 // //======================================== If already added, go to next one
4064 // if ( !newPeak ) { continue; }
4065 //
4066 // //======================================== If not, create a new group with this peak
4067 // peakGroups.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first,
4068 // this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second,
4069 // sphIt,
4070 // this->sphereMappedRotFun.at(sphIt)->getAngularDim() ) );
4071 // }
4072 // }
4073 //
4074 // //================================================ For each peak group, look for the requested fold
4075 // for ( proshade_unsign grIt = 0; grIt < static_cast<proshade_unsign> ( peakGroups.size() ); grIt++ )
4076 // {
4077 // //============================================ Report progress
4078 // std::stringstream hlpSS3;
4079 // 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 ";
4080 // for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( peakGroups.at(grIt)->getSpherePositions().size() ); sphIt++ ) { hlpSS3 << peakGroups.at(grIt)->getSpherePositions().at(sphIt) << " ; "; }
4081 // ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 5, hlpSS3.str() );
4082 //
4083 // //============================================ Find point groups in the peak group
4084 // peakGroups.at(grIt)->findCyclicPointGroupsGivenFold ( this->sphereMappedRotFun, settings->axisErrTolerance, &ret, settings->useBiCubicInterpolationOnPeaks, fold, settings->verbose );
4085 //
4086 // //============================================ Release the memory
4087 // delete peakGroups.at(grIt);
4088 // }
4089 //
4090 // //================================================ Sort ret by peak height
4091 // std::sort ( ret.begin(), ret.end(), sortProSHADESymmetryByPeak );
4092 
4093  //================================================ Done
4094  return ( ret );
4095 
4096 }
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:162
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:3690
ProSHADE_internal_data::ProSHADE_data::optimiseDihedralAngleFromAngleAxis
std::vector< proshade_double * > optimiseDihedralAngleFromAngleAxis(ProSHADE_settings *settings, proshade_double angle, proshade_double *axis1, proshade_double *axis2)
...
Definition: ProSHADE_symmetry.cpp:3822
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:1885
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_misc::sortSymInvFoldHlp
bool sortSymInvFoldHlp(const proshade_double *a, const proshade_double *b)
This function compares two arrays of two based on the first number, sorting highest first.
Definition: ProSHADE_misc.cpp:273
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:178
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_data::joinElementsFromDifferentGroups
std::vector< std::vector< proshade_double > > joinElementsFromDifferentGroups(std::vector< std::vector< proshade_double > > *first, std::vector< std::vector< proshade_double > > *second, proshade_double matrixTolerance, bool combine)
This function joins two group element lists using only unique elements.
Definition: ProSHADE_data.cpp:2486
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::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_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:1685
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:191
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:171
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_symmetry::predictIcosAxes
void predictIcosAxes(ProSHADE_settings *settings, 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, proshade_double matrixTolerance)
This function predicts all icosahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:2944
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:168
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_data::ProSHADE_data::computeGroupElementsForGroup
std::vector< std::vector< proshade_double > > computeGroupElementsForGroup(std::vector< std::vector< proshade_double > > *allCSyms, proshade_unsign grPosition)
This function computes the group elements as rotation matrices (except for the identity element) for ...
Definition: ProSHADE_data.cpp:2263
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:192
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:193
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:185
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:2016
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:177
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_data::ProSHADE_data::getPredictedOctahedralSymmetriesList
std::vector< proshade_double * > getPredictedOctahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList, proshade_double matrixTolerance)
This function predicts a list of all O symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2731
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:1701
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:2823
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_data::ProSHADE_data::getMaxBand
proshade_unsign getMaxBand(void)
This function returns the maximum band value for the object.
Definition: ProSHADE_data.cpp:2986
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:2765
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:86
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::getPredictedIcosahedralSymmetriesList
std::vector< proshade_double * > getPredictedIcosahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList, proshade_double matrixTolerance)
This function predicts a list of all I symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2692
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:2397
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:2881
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:3411
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_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:161
ProSHADE_internal_symmetry::predictOctaAxes
void predictOctaAxes(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, proshade_double matrixTolerance)
This function predicts all octahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:3161
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:3257
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:165
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_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_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:3213
ProSHADE_settings::allDetectedIAxes
std::vector< proshade_unsign > allDetectedIAxes
The vector of all detected icosahedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:194
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:3574
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:3522
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:3098
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:1743
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:169
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:3713
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:2476
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_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:3329