ProSHADE: src/proshade/ProSHADE_symmetry.cpp Source File

| 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
34 void ProSHADE_internal_data::ProSHADE_data::computeRotationFunction ( ProSHADE_settings* settings )
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
46 ProSHADE_internal_distances::generateSO3CoeffsFromEMatrices ( this, this, settings );
47
48 //================================================ Compute the inverse SO(3) Fourier Transform (SOFT) on the newly computed coefficients
49 ProSHADE_internal_distances::computeInverseSOFTTransform ( this, this, settings );
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
113 void ProSHADE_internal_data::ProSHADE_data::convertRotationFunction ( ProSHADE_settings* settings )
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
191 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesList ( ProSHADE_settings* settings )
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
279 ProSHADE_internal_misc::addToDblPtrVector ( &ret, hlpP );
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)
427 ProSHADE_internal_symmetry::giveOppositeAxesSameDirection ( peaks );
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 );
472 ProSHADE_internal_misc::addToUnsignVectorVector ( &ret, hlpVec );
473 }
474
475 //================================================ Add zero peak to all axes
476 ProSHADE_internal_symmetry::addZeroPeakToGroups ( ret, peaks );
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;
602 ProSHADE_internal_misc::addToDoubleVector ( tried, g1 );
603 ProSHADE_internal_misc::addToDoubleVector ( tried, g2 );
604 ProSHADE_internal_misc::addToDoubleVector ( tried, *dist );
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() ) );
634 ProSHADE_internal_misc::addToDblPtrVector ( &peaks, hlpP );
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 );
842 ProSHADE_internal_maths::getRotationMatrixFromEulerZXZAngles ( euA, euB, euG, rotMat );
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;
888 ProSHADE_internal_misc::addToUnsignVector ( &hlpVec, fold );
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 }
895 ProSHADE_internal_misc::addToUnsignVectorVector ( ret, hlpVec );
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() ) );
960 ProSHADE_internal_misc::addToDblPtrVector ( peaks, hlpP );
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 {
1154 ProSHADE_internal_misc::addToDblPtrVector ( ret, hlpP );
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;
1262 ProSHADE_internal_misc::addToUnsignVector ( &DSymInd, ax1 );
1263 ProSHADE_internal_misc::addToUnsignVector ( &DSymInd, ax2 );
1264 ProSHADE_internal_misc::addToUnsignVectorVector ( &settings->allDetectedDAxes, DSymInd );
1265
1266 }
1267 else
1268 {
1269 ProSHADE_internal_symmetry::saveDSymmetry ( &ret, CSymList, ax2, ax1 );
1270
1271 std::vector< proshade_unsign > DSymInd;
1272 ProSHADE_internal_misc::addToUnsignVector ( &DSymInd, ax2 );
1273 ProSHADE_internal_misc::addToUnsignVector ( &DSymInd, ax1 );
1274 ProSHADE_internal_misc::addToUnsignVectorVector ( &settings->allDetectedDAxes, 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
1324 ProSHADE_internal_misc::addToDblPtrVector ( ret, hlpP );
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?
1349 if ( ProSHADE_internal_symmetry::detectTetrahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
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 {
1370 ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedTAxes, csIt );
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 );
1748 ProSHADE_internal_maths::getRotationMatrixFromEulerZXZAngles ( euA, euB, euG, rotMat );
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
1846 ProSHADE_internal_misc::addToDblPtrVector ( axVec, hlpSym );
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 {
2126 ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedOAxes, csIt );
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 {
2544 ProSHADE_internal_misc::addToDblPtrVector ( prosp, symHlp );
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?
2638 if ( ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
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 {
2662 ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, csIt );
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?
2701 if ( ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
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 );
3048 ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( ret, 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
3574 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesListFromAngleAxis ( ProSHADE_settings* settings )
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