ProSHADE: src/proshade/ProSHADE_symmetry.cpp Source File

| ProSHADE 0.7.5.4 (MAR 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
2691 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
2692 {
2693 //================================================ Initialise variables
2694 std::vector< proshade_double* > ret;
2695
2696 //================================================ Report progress
2697 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting I symmetry prediction." );
2698
2699 //================================================ Are the basic requirements for icosahedral symmetry met?
2700 if ( ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
2701 {
2702 //============================================ Generate the rest of the axes
2703 ProSHADE_internal_symmetry::predictIcosAxes ( CSymList, &ret, settings->axisErrTolerance, settings->minSymPeak );
2704
2705 //============================================ Get heights for the predicted axes
2706 ProSHADE_internal_symmetry::findPredictedAxesHeights ( &ret, this, settings );
2707
2708 //============================================ Add predicted axes to detected C axes list and also to the settings Icosahedral symmetry list
2709 for ( proshade_unsign retIt = 0; retIt < static_cast < proshade_unsign > ( ret.size() ); retIt++ )
2710 {
2711 ProSHADE_internal_misc::addToDblPtrVector ( CSymList, ret.at(retIt) );
2712 ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, static_cast < proshade_unsign > ( CSymList->size() ) );
2713 }
2714 }
2715
2716
2717 //================================================ Report progress
2718 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "I symmetry prediction complete." );
2719
2720 //================================================ Done
2721 return ( ret );
2722
2723 }
2724
2739 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
2740 {
2741 //================================================ Initialise variables
2742 std::vector< proshade_double* > ret;
2743
2744 //================================================ Report progress
2745 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting O symmetry prediction." );
2746
2747 //================================================ Are the basic requirements for icosahedral symmetry met?
2748 if ( ProSHADE_internal_symmetry::detectOctahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
2749 {
2750 //============================================ Generate the rest of the axes
2751 ProSHADE_internal_symmetry::predictOctaAxes ( CSymList, &ret, settings->axisErrTolerance, settings->minSymPeak );
2752
2753 //============================================ Get heights for the predicted axes
2754 ProSHADE_internal_symmetry::findPredictedAxesHeights ( &ret, this, settings );
2755
2756 //============================================ Add predicted axes to detected C axes list and also to the settings Icosahedral symmetry list
2757 for ( proshade_unsign retIt = 0; retIt < static_cast < proshade_unsign > ( ret.size() ); retIt++ )
2758 {
2759 ProSHADE_internal_misc::addToDblPtrVector ( CSymList, ret.at(retIt) );
2760 ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, static_cast < proshade_unsign > ( CSymList->size() ) );
2761 }
2762 }
2763
2764 //================================================ Report progress
2765 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "O symmetry prediction complete." );
2766
2767 //================================================ Done
2768 return ( ret );
2769
2770 }
2771
2783 bool ProSHADE_internal_symmetry::detectIcosahedralSymmetry ( std::vector< proshade_double* >* CSymList, proshade_double axErr, proshade_double minPeakHeight )
2784 {
2785 //================================================ Initialise variables
2786 std::vector< proshade_unsign > C5List;
2787 proshade_double dotProduct;
2788
2789 //================================================ Find all C5 symmetries
2790 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2791 {
2792 if ( CSymList->at(cSym)[0] == 5 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); }
2793 }
2794
2795 //================================================ For each unique pair of C5 and C3
2796 for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
2797 {
2798 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2799 {
2800 //======================================== Compare only C3s to the C5List
2801 if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2802
2803 //======================================== Check the angle between the C5 and C3 axes
2804 dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
2805 &CSymList->at(C5List.at(c5))[2],
2806 &CSymList->at(C5List.at(c5))[3],
2807 &CSymList->at(cSym)[1],
2808 &CSymList->at(cSym)[2],
2809 &CSymList->at(cSym)[3] );
2810
2811 //======================================== Is the angle approximately the dihedral angle
2812 if ( std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) < axErr )
2813 {
2814 return ( true );
2815 }
2816 }
2817 }
2818
2819 //================================================ Done
2820 return ( false );
2821
2822 }
2823
2841 void ProSHADE_internal_symmetry::findIcos6C5s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_unsign verbose, proshade_double minPeakHeight )
2842 {
2843 //================================================ Initialise variables
2844 std::vector< proshade_unsign > C5PossibilitiesHlp;
2845 std::vector< std::vector< proshade_unsign > > C5Possibilities;
2846 bool groupMatched;
2847
2848 //================================================ Report progress
2849 ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of six C5 axes." );
2850
2851 //================================================ For all symmetries in the C symmetries list
2852 for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
2853 {
2854 //============================================ Search only using C5s and check peak height
2855 if ( CSymList->at(cIt)[0] != 5.0 || CSymList->at(cIt)[5] < minPeakHeight ) { continue; }
2856
2857 //============================================ If second or more C5, check if it has the correct angle to all other already found C5s for each group
2858 groupMatched = false;
2859 for ( proshade_unsign gIt = 0; gIt < static_cast<proshade_unsign> ( C5Possibilities.size() ); gIt++ )
2860 {
2861 if ( ProSHADE_internal_symmetry::testGroupAgainstSymmetry ( CSymList, &C5Possibilities.at(gIt), CSymList->at(cIt), axErr, 1.0/2.0, true, cIt ) ) { ProSHADE_internal_misc::addToUnsignVector ( &C5Possibilities.at(gIt), cIt ); groupMatched = true; break; }
2862 }
2863
2864 //============================================ If no group matched, create a new group
2865 if ( !groupMatched ) { C5PossibilitiesHlp.clear(); ProSHADE_internal_misc::addToUnsignVector ( &C5PossibilitiesHlp, cIt ); ProSHADE_internal_misc::addToUnsignVectorVector ( &C5Possibilities, C5PossibilitiesHlp ); continue; }
2866 }
2867
2868 //================================================ Test for missing symmetry axes, if need be
2869 ProSHADE_internal_symmetry::findMissingAxes ( &C5Possibilities, CSymList, 6, axErr, 1.0 / 2.0, 5, dataObj, minPeakHeight );
2870
2871 //=================================================Any group has 6 entries? If more such groups, take the one with highest average height.
2872 proshade_double maxHeight = 0.0; proshade_unsign maxGrp = 0;
2873 for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( C5Possibilities.size() ); iter++ ) { if ( C5Possibilities.at(iter).size() == 6 ) { if ( ( ( CSymList->at(C5Possibilities.at(iter).at(0))[5] + CSymList->at(C5Possibilities.at(iter).at(1))[5] + CSymList->at(C5Possibilities.at(iter).at(2))[5] + CSymList->at(C5Possibilities.at(iter).at(3))[5] + CSymList->at(C5Possibilities.at(iter).at(4))[5] + CSymList->at(C5Possibilities.at(iter).at(5))[5] ) / 6.0 ) > maxHeight ) { maxHeight = ( ( CSymList->at(C5Possibilities.at(iter).at(0))[5] + CSymList->at(C5Possibilities.at(iter).at(1))[5] + CSymList->at(C5Possibilities.at(iter).at(2))[5] + CSymList->at(C5Possibilities.at(iter).at(3))[5] + CSymList->at(C5Possibilities.at(iter).at(4))[5] + CSymList->at(C5Possibilities.at(iter).at(5))[5] ) / 6.0 ); maxGrp = iter; } } }
2874
2875 if ( C5Possibilities.at(maxGrp).size() == 6 )
2876 {
2877 //============================================ Success! Save and exit
2878 for ( proshade_unsign it = 0; it < static_cast<proshade_unsign> ( C5Possibilities.at(maxGrp).size() ); it++ ) { ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(C5Possibilities.at(maxGrp).at(it)) ); }
2879
2880 //============================================ Report progress
2881 ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of six C5 axes successfull." );
2882
2883 //============================================ Done
2884 return ;
2885 }
2886
2887 //================================================ Done
2888 return ;
2889
2890 }
2891
2899 std::pair< proshade_unsign, proshade_unsign > findBestIcosDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
2900 {
2901 //================================================ Initialise variables
2902 std::pair< proshade_unsign, proshade_unsign > ret;
2903 std::vector< proshade_unsign > C5List;
2904 proshade_double bestDihedralAngle = 999.9;
2905 proshade_double dotProduct;
2906
2907 //================================================ Find all C5 symmetries
2908 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { if ( CSymList->at(cSym)[0] == 5 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C5List, cSym ); } }
2909
2910 //================================================ For each unique pair of C5 and C3
2911 for ( proshade_unsign c5 = 0; c5 < static_cast<proshade_unsign> ( C5List.size() ); c5++ )
2912 {
2913 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
2914 {
2915 //======================================== Compare only C3s to the C5List and only with decent average peak height
2916 if ( CSymList->at(cSym)[0] != 3 ) { continue; }
2917 if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
2918
2919 //======================================== Check the angle between the C5 and C3 axes
2920 dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C5List.at(c5))[1],
2921 &CSymList->at(C5List.at(c5))[2],
2922 &CSymList->at(C5List.at(c5))[3],
2923 &CSymList->at(cSym)[1],
2924 &CSymList->at(cSym)[2],
2925 &CSymList->at(cSym)[3] );
2926
2927 //======================================== Is the angle approximately the dihedral angle?
2928 if ( std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) < axErr )
2929 {
2930 if ( bestDihedralAngle > std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) ) )
2931 {
2932 bestDihedralAngle = std::abs ( std::abs( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - std::abs( dotProduct ) );
2933 ret.first = C5List.at(c5);
2934 ret.second = cSym;
2935 }
2936 }
2937 }
2938 }
2939
2940 //================================================ Done
2941 return ( ret );
2942 }
2943
2964 void ProSHADE_internal_symmetry::predictIcosAxes ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, proshade_double minPeakHeight )
2965 {
2966 //================================================ Find the best axis combination with dihedral angle and correct folds
2967 std::pair< proshade_unsign, proshade_unsign > initAxes = findBestIcosDihedralPair ( CSymList, minPeakHeight, axErr );
2968
2969 //================================================ Find rotation between the detected C5 and the model C5 axes.
2970 proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( ProSHADE_internal_precomputedVals::icosahedronAxes.at(0).at(1),
2971 ProSHADE_internal_precomputedVals::icosahedronAxes.at(0).at(2),
2972 ProSHADE_internal_precomputedVals::icosahedronAxes.at(0).at(3),
2973 CSymList->at(initAxes.first)[1],
2974 CSymList->at(initAxes.first)[2],
2975 CSymList->at(initAxes.first)[3] );
2976
2977 //================================================ Rotate the model C3 to the correct orientation relative to the detected C5 axis.
2978 proshade_double* rotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
2979 ProSHADE_internal_precomputedVals::icosahedronAxes.at(6).at(1),
2980 ProSHADE_internal_precomputedVals::icosahedronAxes.at(6).at(2),
2981 ProSHADE_internal_precomputedVals::icosahedronAxes.at(6).at(3) );
2982
2983 //================================================ Find the angle betwen the rotated model C3 and the detected C3 axes along the detected C5 axis.
2984 proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
2985 for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.01 )
2986 {
2987 //============================================ Compute rotation matrix for this angle value
2988 proshade_double* rotMatHlp = new proshade_double[9];
2989 ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
2990 ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], ang );
2991
2992 //============================================ Rotate the rotated C2 by the matrix
2993 proshade_double* rotRotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
2994 rotModelC3[0],
2995 rotModelC3[1],
2996 rotModelC3[2] );
2997
2998 //============================================ Find distance
2999 curAngDist = std::sqrt ( std::pow ( rotRotModelC3[0] - CSymList->at(initAxes.second)[1], 2.0 ) +
3000 std::pow ( rotRotModelC3[1] - CSymList->at(initAxes.second)[2], 2.0 ) +
3001 std::pow ( rotRotModelC3[2] - CSymList->at(initAxes.second)[3], 2.0 ) );
3002
3003 //============================================ Save best angle
3004 if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
3005
3006 //============================================ Release memory
3007 delete[] rotMatHlp;
3008 delete[] rotRotModelC3;
3009 }
3010
3011 //================================================ For the rotation matrix along the detected C5 axis with the same anlge as is between the rotated model C3 and the detected C3 axes.
3012 proshade_double* rotMat2 = new proshade_double[9];
3013 ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
3014 ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], bestAng );
3015
3016 //================================================ Combine the two rotation matrices into a single rotation matrix
3017 proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
3018
3019 //================================================ For each model axis
3020 for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ProSHADE_internal_precomputedVals::icosahedronAxes.size() ); iter++ )
3021 {
3022 //============================================ Rotate the model axis to fit the detected orientation
3023 proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
3024 ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(1),
3025 ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(2),
3026 ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(3) );
3027
3028 //============================================ Create ProSHADE symmetry axis representation
3029 proshade_double* axis = new proshade_double[6];
3030 ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
3031
3032 axis[0] = ProSHADE_internal_precomputedVals::icosahedronAxes.at(iter).at(0);
3033 axis[1] = rotAxis[0];
3034 axis[2] = rotAxis[1];
3035 axis[3] = rotAxis[2];
3036 axis[4] = ( 2.0 * M_PI ) / axis[0];
3037 axis[5] = 0.0;
3038
3039 //============================================ Save axis to ret
3040 ProSHADE_internal_misc::addToDblPtrVector ( ret, axis );
3041
3042 //============================================ Release memory
3043 delete[] rotAxis;
3044 }
3045
3046 //================================================ Release memory
3047 delete[] rotMat;
3048 delete[] rotMat2;
3049 delete[] rotMatFin;
3050 delete[] rotModelC3;
3051
3052 //================================================ Done
3053 return ;
3054
3055 }
3056
3064 std::pair< proshade_unsign, proshade_unsign > findBestOctaDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
3065 {
3066 //================================================ Initialise variables
3067 std::pair< proshade_unsign, proshade_unsign > ret;
3068 std::vector< proshade_unsign > C4List;
3069 proshade_double bestDihedralAngle = 999.9;
3070 proshade_double dotProduct;
3071
3072 //================================================ Find all C5 symmetries
3073 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { if ( CSymList->at(cSym)[0] == 4 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C4List, cSym ); } }
3074
3075 //================================================ For each unique pair of C5 and C3
3076 for ( proshade_unsign c4 = 0; c4 < static_cast<proshade_unsign> ( C4List.size() ); c4++ )
3077 {
3078 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
3079 {
3080 //======================================== Compare only C3s to the C5List and only with decent average peak height
3081 if ( CSymList->at(cSym)[0] != 3 ) { continue; }
3082 if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
3083
3084 //======================================== Check the angle between the C5 and C3 axes
3085 dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C4List.at(c4))[1],
3086 &CSymList->at(C4List.at(c4))[2],
3087 &CSymList->at(C4List.at(c4))[3],
3088 &CSymList->at(cSym)[1],
3089 &CSymList->at(cSym)[2],
3090 &CSymList->at(cSym)[3] );
3091
3092 //======================================== Is the angle approximately the dihedral angle?
3093 if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
3094 {
3095 if ( bestDihedralAngle > std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) ) )
3096 {
3097 bestDihedralAngle = std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) );
3098 ret.first = C4List.at(c4);
3099 ret.second = cSym;
3100 }
3101 }
3102 }
3103 }
3104
3105 //================================================ Done
3106 return ( ret );
3107
3108 }
3109
3130 void ProSHADE_internal_symmetry::predictOctaAxes ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, proshade_double minPeakHeight )
3131 {
3132 //================================================ Find the best axis combination with dihedral angle and correct folds
3133 std::pair< proshade_unsign, proshade_unsign > initAxes = findBestOctaDihedralPair ( CSymList, minPeakHeight, axErr );
3134
3135 //================================================ Find rotation between the detected C4 and the model C4 axes.
3136 proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( ProSHADE_internal_precomputedVals::octahedronAxes.at(0).at(1),
3137 ProSHADE_internal_precomputedVals::octahedronAxes.at(0).at(2),
3138 ProSHADE_internal_precomputedVals::octahedronAxes.at(0).at(3),
3139 CSymList->at(initAxes.first)[1],
3140 CSymList->at(initAxes.first)[2],
3141 CSymList->at(initAxes.first)[3] );
3142
3143 //================================================ Rotate the model C3 to the correct orientation relative to the detected C4 axis.
3144 proshade_double* rotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
3145 ProSHADE_internal_precomputedVals::octahedronAxes.at(3).at(1),
3146 ProSHADE_internal_precomputedVals::octahedronAxes.at(3).at(2),
3147 ProSHADE_internal_precomputedVals::octahedronAxes.at(3).at(3) );
3148
3149 //================================================ Find the angle betwen the rotated model C3 and the detected C3 axes along the detected C4 axis.
3150 proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
3151 for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.01 )
3152 {
3153 //============================================ Compute rotation matrix for this angle value
3154 proshade_double* rotMatHlp = new proshade_double[9];
3155 ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
3156 ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], ang );
3157
3158 //============================================ Rotate the rotated C2 by the matrix
3159 proshade_double* rotRotModelC3 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
3160 rotModelC3[0],
3161 rotModelC3[1],
3162 rotModelC3[2] );
3163
3164 //============================================ Find distance
3165 curAngDist = std::sqrt ( std::pow ( rotRotModelC3[0] - CSymList->at(initAxes.second)[1], 2.0 ) +
3166 std::pow ( rotRotModelC3[1] - CSymList->at(initAxes.second)[2], 2.0 ) +
3167 std::pow ( rotRotModelC3[2] - CSymList->at(initAxes.second)[3], 2.0 ) );
3168
3169 //============================================ Save best angle
3170 if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
3171
3172 //============================================ Release memory
3173 delete[] rotMatHlp;
3174 delete[] rotRotModelC3;
3175 }
3176
3177 //================================================ For the rotation matrix along the detected C5 axis with the same anlge as is between the rotated model C3 and the detected C3 axes.
3178 proshade_double* rotMat2 = new proshade_double[9];
3179 ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
3180 ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], bestAng );
3181
3182 //================================================ Combine the two rotation matrices into a single rotation matrix
3183 proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
3184
3185 //================================================ For each model axis
3186 for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ProSHADE_internal_precomputedVals::octahedronAxes.size() ); iter++ )
3187 {
3188 //============================================ Rotate the model axis to fit the detected orientation
3189 proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
3190 ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(1),
3191 ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(2),
3192 ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(3) );
3193
3194 //============================================ Create ProSHADE symmetry axis representation
3195 proshade_double* axis = new proshade_double[6];
3196 ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
3197
3198 axis[0] = ProSHADE_internal_precomputedVals::octahedronAxes.at(iter).at(0);
3199 axis[1] = rotAxis[0];
3200 axis[2] = rotAxis[1];
3201 axis[3] = rotAxis[2];
3202 axis[4] = ( 2.0 * M_PI ) / axis[0];
3203 axis[5] = 0.0;
3204
3205 //============================================ Save axis to ret
3206 ProSHADE_internal_misc::addToDblPtrVector ( ret, axis );
3207
3208 //============================================ Release memory
3209 delete[] rotAxis;
3210 }
3211
3212 //================================================ Release memory
3213 delete[] rotMat;
3214 delete[] rotMat2;
3215 delete[] rotMatFin;
3216 delete[] rotModelC3;
3217
3218 //================================================ Done
3219 return ;
3220
3221 }
3222
3236 void ProSHADE_internal_symmetry::findIcos10C3s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_unsign verbose, proshade_double minPeakHeight )
3237 {
3238 //================================================ Initialise variables
3239 std::vector< proshade_unsign > prospectiveC3s, retGrp;
3240 proshade_double dotProd;
3241 proshade_unsign noClose, noAway;
3242
3243 //================================================ Report progress
3244 ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of ten C3 axes." );
3245
3246 //================================================ For each C3
3247 for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
3248 {
3249 //============================================ Use only C3s with hight enough average
3250 if ( CSymList->at(cIt)[0] != 3.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
3251
3252 //============================================ Check the C3 has acos ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) to 3 C5s and acos ( 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) ) to the other three C5s
3253 noClose = 0; noAway = 0;
3254 for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
3255 {
3256 dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
3257 &ret->at(rIt)[2],
3258 &ret->at(rIt)[3],
3259 &CSymList->at(cIt)[1],
3260 &CSymList->at(cIt)[2],
3261 &CSymList->at(cIt)[3] );
3262
3263 if ( ( std::abs ( dotProd ) > ( ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) + axErr ) ) ) { noClose += 1; continue; }
3264 if ( ( std::abs ( dotProd ) > ( 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) - axErr ) ) && ( std::abs ( dotProd ) < ( 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ) + axErr ) ) ) { noAway += 1; continue; }
3265 }
3266
3267 //============================================ If correct angles distribution is found, save the axis
3268 if ( ( noClose == 3 ) && ( noAway == 3 ) )
3269 {
3270 ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC3s, cIt );
3271 }
3272 }
3273
3274 //================================================ Search for missing axes
3275 for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
3276 if ( !ProSHADE_internal_symmetry::findMissingAxesDual ( &prospectiveC3s, CSymList, ret, &retGrp, 10, axErr, 3, std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ), 3, 1.0 - ( std::sqrt ( ( 1.0 + 2.0 / std::sqrt ( 5.0 ) ) / 3.0 ) ), 3, dataObj ) )
3277 {
3278 return ;
3279 }
3280
3281 //================================================ Found correct number of axes! Now save the
3282 for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC3s.size() ); iter++ )
3283 {
3284 ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC3s.at(iter)) );
3285 }
3286
3287 //================================================ Report progress
3288 ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of ten C3 axes successfull." );
3289
3290 //================================================ Done
3291 return ;
3292
3293 }
3294
3308 void ProSHADE_internal_symmetry::findIcos15C2s ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data* dataObj, proshade_unsign verbose, proshade_double minPeakHeight )
3309 {
3310 //================================================ Initialise variables
3311 std::vector< proshade_unsign > prospectiveC2s, retGrp;
3312 proshade_double dotProd;
3313 proshade_unsign noClose, noMidway, noAway;
3314
3315 //================================================ Report progress
3316 ProSHADE_internal_messages::printProgressMessage ( verbose, 2, "Starting detection of fifteen C2 axes." );
3317
3318 //================================================ For each C2
3319 for ( proshade_unsign cIt = 0; cIt < static_cast<proshade_unsign> ( CSymList->size() ); cIt++ )
3320 {
3321 //============================================ Use only C2s
3322 if ( CSymList->at(cIt)[0] != 2.0 || CSymList->at(cIt)[0] < minPeakHeight ) { continue; }
3323
3324 //============================================ Check the C2 has acos ( 0.0 ) to 2 C5s, acos ( 0.5 ) to another 2 C5s and acos ( sqrt ( 3.0 ) / 2.0 ) to the last two C5s
3325 noClose = 0; noMidway = 0; noAway = 0;
3326 for ( proshade_unsign rIt = 0; rIt < 6; rIt++ )
3327 {
3328 dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(rIt)[1],
3329 &ret->at(rIt)[2],
3330 &ret->at(rIt)[3],
3331 &CSymList->at(cIt)[1],
3332 &CSymList->at(cIt)[2],
3333 &CSymList->at(cIt)[3] );
3334
3335 if ( ( std::abs ( dotProd ) > ( ( sqrt ( 3.0 ) / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( sqrt ( 3.0 ) / 2.0 ) + axErr ) ) ) { noAway += 1; continue; }
3336 if ( ( std::abs ( dotProd ) > ( ( 1.0 / 2.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 1.0 / 2.0 ) + axErr ) ) ) { noMidway += 1; continue; }
3337 if ( ( std::abs ( dotProd ) > ( ( 0.0 ) - axErr ) ) && ( std::abs ( dotProd ) < ( ( 0.0 ) + axErr ) ) ) { noClose += 1; continue; }
3338 }
3339
3340 //============================================ If correct angles distribution is found, save the axis
3341 if ( ( noClose == 2 ) && ( noMidway == 2 ) && ( noAway == 2 ) )
3342 {
3343 ProSHADE_internal_misc::addToUnsignVector ( &prospectiveC2s, cIt );
3344 }
3345 }
3346
3347 //================================================ Search for missing axes
3348 for ( proshade_unsign iter = 0; iter < 6; iter++ ) { ProSHADE_internal_misc::addToUnsignVector ( &retGrp, iter ); }
3349 if ( !ProSHADE_internal_symmetry::findMissingAxesTriple ( &prospectiveC2s, CSymList, ret, &retGrp, 15, axErr, 2, 0.0, 2, 1.0/2.0, 2, sqrt ( 3.0 ) / 2.0, 2, dataObj ) )
3350 {
3351 return ;
3352 }
3353
3354 //================================================ Found correct number of axes! Now save the
3355 for ( proshade_unsign iter = 0; iter < static_cast<proshade_unsign> ( prospectiveC2s.size() ); iter++ )
3356 {
3357 ProSHADE_internal_misc::addToDblPtrVector ( ret, CSymList->at(prospectiveC2s.at(iter)) );
3358 }
3359
3360 //================================================ Report progress
3361 ProSHADE_internal_messages::printProgressMessage ( verbose, 3, "Detection of fifteen C2 axes successfull." );
3362
3363 //================================================ Done
3364 return ;
3365
3366 }
3367
3390 bool ProSHADE_internal_symmetry::findMissingAxesTriple ( std::vector< proshade_unsign >* possibilities, std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, std::vector< proshade_unsign >* retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data* dataObj )
3391 {
3392 //================================================ Initialise variables
3393 bool atLeastOne = false;
3394 std::vector< proshade_double* > prosp;
3395 std::vector< proshade_double > sol;
3396
3397 //================================================ Proceed only if need be
3398 if ( static_cast<proshade_unsign> ( possibilities->size() ) == requiredNoAxes ) { atLeastOne = true; return ( atLeastOne ); }
3399
3400 //================================================ Copy already found to prospective
3401 for ( proshade_unsign prIt = 0; prIt < static_cast<proshade_unsign> ( possibilities->size() ); prIt++ )
3402 {
3403 ProSHADE_internal_symmetry::addAxisUnlessSame ( CSymList->at(possibilities->at(prIt))[0],
3404 CSymList->at(possibilities->at(prIt))[1],
3405 CSymList->at(possibilities->at(prIt))[2],
3406 CSymList->at(possibilities->at(prIt))[3],
3407 CSymList->at(possibilities->at(prIt))[5], &prosp, axErr );
3408 }
3409
3410 //================================================ Start generating possible solutions
3411 for ( proshade_unsign rgIt1 = 0; rgIt1 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt1++ )
3412 {
3413 for ( proshade_unsign rgIt2 = 0; rgIt2 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt2++ )
3414 {
3415 //======================================== Use unique combinations (order matters here!)
3416 if ( rgIt1 == rgIt2 ) { continue; }
3417
3418 for ( proshade_unsign rgIt3 = 0; rgIt3 < static_cast<proshade_unsign> ( retGroup->size() ); rgIt3++ )
3419 {
3420 //==================================== Use unique combinations (order matters here!)
3421 if ( ( rgIt1 == rgIt3 ) || ( rgIt2 == rgIt3 ) ) { continue; }
3422
3423 //==================================== Generate possible solution (1)
3424 sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
3425 ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
3426 ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], angle1, angle2, angle3 );
3427
3428 //==================================== Check if solution fits the group completely
3429 ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, noMatchesG3, angle3, dataObj );
3430 if ( prosp.size() == requiredNoAxes ) { break; }
3431
3432 //==================================== Generate possible solution (2)
3433 sol = ProSHADE_internal_maths::findVectorFromThreeVAndThreeD ( ret->at(rgIt1)[1], ret->at(rgIt1)[2], ret->at(rgIt1)[3],
3434 ret->at(rgIt2)[1], ret->at(rgIt2)[2], ret->at(rgIt2)[3],
3435 ret->at(rgIt3)[1], ret->at(rgIt3)[2], ret->at(rgIt3)[3], -angle1, -angle2, -angle3 );
3436
3437 //==================================== Check if solution fits the group completely
3438 ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave ( retGroup, ret, fold, sol.at(0), sol.at(1), sol.at(2), &prosp, axErr, noMatchesG1, angle1, noMatchesG2, angle2, noMatchesG3, angle3, dataObj );
3439 if ( prosp.size() == requiredNoAxes ) { break; }
3440 }
3441
3442 if ( prosp.size() == requiredNoAxes ) { break; }
3443 }
3444
3445 if ( prosp.size() == requiredNoAxes ) { break; }
3446 }
3447
3448 //================================================ Found all required axes
3449 if ( prosp.size() == requiredNoAxes )
3450 {
3451 //============================================ For each found missing axis
3452 for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
3453 {
3454 if ( ProSHADE_internal_maths::isAxisUnique ( CSymList, prosp.at(axIt), axErr ) )
3455 {
3456 //======================================== Add
3457 ProSHADE_internal_misc::addToDblPtrVector ( CSymList, prosp.at(axIt) );
3458 ProSHADE_internal_misc::addToUnsignVector ( possibilities, static_cast<proshade_unsign> ( CSymList->size()-1 ) );
3459 }
3460 }
3461
3462 atLeastOne = true;
3463 return ( atLeastOne );
3464 }
3465 else
3466 {
3467 //============================================ Delete all found, but unnecessary axes
3468 for ( proshade_unsign axIt = static_cast<proshade_unsign> ( possibilities->size() ); axIt < static_cast<proshade_unsign> ( prosp.size() ); axIt++ )
3469 {
3470 delete[] prosp.at(axIt);
3471 }
3472 }
3473
3474 //================================================ Done
3475 return ( atLeastOne );
3476
3477 }
3478
3501 void ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave ( std::vector< proshade_unsign >* retGroup, std::vector< proshade_double* >* ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double* >* prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, ProSHADE_internal_data::ProSHADE_data* dataObj )
3502 {
3503 //================================================ Initialise variables
3504 proshade_unsign noG1 = 0;
3505 proshade_unsign noG2 = 0;
3506 proshade_unsign noG3 = 0;
3507 proshade_double dotProd = 0.0;
3508 proshade_double axHeight = 0.0;
3509
3510 //================================================ Find the angle and count dual matching frequencies
3511 for ( proshade_unsign rIt = 0; rIt < static_cast<proshade_unsign> ( retGroup->size() ); rIt++ )
3512 {
3513 dotProd = ProSHADE_internal_maths::computeDotProduct ( &ret->at(retGroup->at(rIt))[1],
3514 &ret->at(retGroup->at(rIt))[2],
3515 &ret->at(retGroup->at(rIt))[3],
3516 &axX, &axY, &axZ );
3517
3518 if ( ( std::abs ( dotProd ) > ( angle1 - axErr ) ) && ( std::abs ( dotProd ) < ( angle1 + axErr ) ) ) { noG1 += 1; continue; }
3519 if ( ( std::abs ( dotProd ) > ( angle2 - axErr ) ) && ( std::abs ( dotProd ) < ( angle2 + axErr ) ) ) { noG2 += 1; continue; }
3520 if ( ( std::abs ( dotProd ) > ( angle3 - axErr ) ) && ( std::abs ( dotProd ) < ( angle3 + axErr ) ) ) { noG3 += 1; continue; }
3521 }
3522
3523 //================================================ If correct frequencies are matched, check height.
3524 if ( ( noG1 == noMatchesG1 ) && ( noG2 == noMatchesG2 ) && ( noG3 == noMatchesG3 ) )
3525 {
3526 //============================================ Is the height good enough?
3527 axHeight = ProSHADE_internal_symmetry::missingAxisHeight ( axX, axY, axZ, dataObj, fold, axErr );
3528
3529 //============================================ If so, save
3530 if ( axHeight > 0.1 )
3531 {
3532 ProSHADE_internal_symmetry::addAxisUnlessSame ( fold, axX, axY, axZ, axHeight, prosp, axErr );
3533 }
3534 }
3535
3536 //================================================ Done
3537 return ;
3538
3539 }
3540
3553 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesListFromAngleAxis ( ProSHADE_settings* settings )
3554 {
3555 //================================================ Initialise variables
3556 std::vector< proshade_unsign > primes = ProSHADE_internal_maths::findAllPrimes ( settings->maxSymmetryFold );
3557 std::vector< proshade_double* > ret, tmpHolder;
3558 std::vector< proshade_unsign > testedFolds;
3559 proshade_double symThres;
3560 proshade_unsign foldToTest;
3561 bool foldDone, anyNewSyms = true;
3562
3563 //================================================ For each found prime number in the limit
3564 for ( proshade_unsign prIt = 0; prIt < static_cast< proshade_unsign > ( primes.size() ); prIt++ )
3565 {
3566 //============================================ Report progress
3567 std::stringstream hlpSS;
3568 hlpSS << "Searching for prime fold symmetry C" << primes.at(prIt) << ".";
3569 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS.str() );
3570
3571 //============================================ Get all symmetries for this prime fold
3572 std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, primes.at(prIt), &symThres );
3573
3574 //============================================ Save the detected C symmetries
3575 for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( prSyms.size() ); axIt++ )
3576 {
3577 //======================================== Is this symmetry passing the threshold?
3578 if ( prSyms.at(axIt)[5] >= symThres )
3579 {
3580 //==================================== Add this symmetry to final list
3581 if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(axIt), settings->axisErrTolerance, true ) )
3582 {
3583 ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, prSyms.at(axIt) );
3584 }
3585 }
3586
3587 //======================================== Release memory
3588 delete[] prSyms.at(axIt);
3589 }
3590 }
3591
3592 //================================================ Was anything found?
3593 if ( ret.size() < 1 ) { return ( ret ); }
3594
3595 //================================================ Check for prime symmetry fold multiples
3596 while ( anyNewSyms )
3597 {
3598 //============================================ Initialise new iteration
3599 anyNewSyms = false;
3600
3601 //============================================ For each passing symmetry, look if there are any combinations of symmetries that would contain it
3602 for ( proshade_unsign axIt1 = 0; axIt1 < static_cast< proshade_unsign > ( ret.size() ); axIt1++ )
3603 {
3604 for ( proshade_unsign axIt2 = 0; axIt2 < static_cast< proshade_unsign > ( ret.size() ); axIt2++ )
3605 {
3606 //==================================== Initialise iteration
3607 foldToTest = ret.at(axIt1)[0] * ret.at(axIt2)[0];
3608 if ( foldToTest > settings->maxSymmetryFold ) { continue; }
3609
3610 //==================================== Was this fold tested already?
3611 foldDone = false;
3612 for ( proshade_unsign fIt = 0; fIt < static_cast< proshade_unsign > ( testedFolds.size() ); fIt++ ) { if ( testedFolds.at(fIt) == foldToTest ) { foldDone = true; break; } }
3613 if ( foldDone ) { continue; }
3614 else { ProSHADE_internal_misc::addToUnsignVector ( &testedFolds, foldToTest ); }
3615
3616 //==================================== Report progress
3617 std::stringstream hlpSS2;
3618 hlpSS2 << "Searching for fold combination of detected folds " << ret.at(axIt1)[0] << " and " << ret.at(axIt2)[0] << ", i.e. C" << foldToTest << ".";
3619 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 3, hlpSS2.str() );
3620
3621 //==================================== Get all symmetries for this fold
3622 std::vector< proshade_double* > prSyms = this->findRequestedCSymmetryFromAngleAxis ( settings, foldToTest, &symThres );
3623
3624 //==================================== For each detected group with the required fold
3625 for ( proshade_unsign newAxIt = 0; newAxIt < static_cast< proshade_unsign > ( prSyms.size() ); newAxIt++ )
3626 {
3627 if ( prSyms.at(newAxIt)[5] >= symThres )
3628 {
3629 //================================ Add to detected axes
3630 if ( ProSHADE_internal_maths::isAxisUnique ( &ret, prSyms.at(newAxIt), settings->axisErrTolerance, true ) )
3631 {
3632 ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &tmpHolder, prSyms.at(newAxIt) );
3633 }
3634 }
3635
3636 //==================================== Release memory
3637 delete[] prSyms.at(newAxIt);
3638 }
3639 }
3640 }
3641
3642 //============================================ Add newly found groups and repeat if need be
3643 if ( tmpHolder.size() > 0 )
3644 {
3645 for ( proshade_unsign tmpIt = 0; tmpIt < static_cast< proshade_unsign > ( tmpHolder.size() ); tmpIt++ )
3646 {
3647 ProSHADE_internal_misc::deepCopyAxisToDblPtrVector ( &ret, tmpHolder.at(tmpIt) );
3648 delete[] tmpHolder.at(tmpIt);
3649 }
3650
3651 anyNewSyms = true;
3652 tmpHolder.clear ( );
3653 }
3654 }
3655
3656 //================================================ Sort the vector
3657 std::sort ( ret.begin(), ret.end(), ProSHADE_internal_misc::sortSymHlpInv );
3658
3659 //================================================ Done
3660 return ( ret );
3661 }
3662
3669 bool sortProSHADESymmetryByPeak ( proshade_double* a, proshade_double* b)
3670 {
3671 //================================================ Done
3672 return ( a[5] > b[5] );
3673
3674 }
3675
3692 std::vector < proshade_double* > ProSHADE_internal_data::ProSHADE_data::findRequestedCSymmetryFromAngleAxis ( ProSHADE_settings* settings, proshade_unsign fold, proshade_double* peakThres )
3693 {
3694 //================================================ Initialise variables
3695 proshade_double soughtAngle;
3696 std::vector< proshade_double > allPeakHeights;
3697 std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup* > peakGroups;
3698 std::vector< proshade_double* > ret;
3699 bool newPeak;
3700
3701 //================================================ Make sure we have a clean start
3702 this->sphereMappedRotFun.clear();
3703
3704 //================================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
3705 for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( fold ); angIt += 1.0 )
3706 {
3707 //============================================ Figure the angles to form the symmetry
3708 soughtAngle = angIt * ( 2.0 * M_PI / static_cast<proshade_double> ( fold ) );
3709
3710 //============================================ Create the angle-axis sphere with correct radius (angle)
3711 this->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( soughtAngle,
3712 M_PI / static_cast<proshade_double> ( fold ),
3713 this->maxShellBand * 2.0,
3714 soughtAngle,
3715 static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3716
3717 //=========================================== Interpolate rotation function onto the sphere
3718 this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->interpolateSphereValues ( this->getInvSO3Coeffs ( ) );
3719
3720 //============================================ Find all peaks for this sphere
3721 this->sphereMappedRotFun.at(static_cast<proshade_unsign> ( angIt - 1.0 ))->findAllPeaks ( settings->peakNeighbours, &allPeakHeights );
3722 }
3723
3724 //============================================ Report progress
3725 std::stringstream hlpSS;
3726 hlpSS << "Found a total of " << std::pow ( this->maxShellBand * 2.0 * (fold-1), 2.0 ) - allPeakHeights.size() << " non-peaks for thresholding.";
3727 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS.str() );
3728
3729 //================================================ Determine the threshold for significant peaks
3730 *peakThres = std::max ( settings->minSymPeak, determinePeakThreshold ( allPeakHeights, settings->noIQRsFromMedianNaivePeak ) );
3731
3732 //============================================ Report progress
3733 std::stringstream hlpSS2;
3734 hlpSS2 << "Determined peak threshold " << *peakThres << ".";
3735 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 4, hlpSS2.str() );
3736
3737 //================================================ Remove small peaks
3738 for ( proshade_unsign shIt = 0; shIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); shIt++ )
3739 {
3740 this->sphereMappedRotFun.at(shIt)->removeSmallPeaks ( *peakThres );
3741 }
3742
3743 //================================================ Group peaks
3744 for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.size() ); sphIt++ )
3745 {
3746 //============================================ For each peak
3747 for ( proshade_unsign pkIt = 0; pkIt < static_cast<proshade_unsign> ( this->sphereMappedRotFun.at(sphIt)->getPeaks().size() ); pkIt++ )
3748 {
3749 //======================================== Check if peak belongs to an already detected peak group
3750 newPeak = true;
3751 for ( proshade_unsign pkGrpIt = 0; pkGrpIt < static_cast<proshade_unsign> ( peakGroups.size() ); pkGrpIt++ )
3752 {
3753 if ( peakGroups.at(pkGrpIt)->checkIfPeakBelongs ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first, this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second, sphIt, settings->axisErrTolerance, settings->verbose ) ) { newPeak = false; break; }
3754 }
3755
3756 //======================================== If already added, go to next one
3757 if ( !newPeak ) { continue; }
3758
3759 //======================================== If not, create a new group with this peak
3760 peakGroups.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).first,
3761 this->sphereMappedRotFun.at(sphIt)->getPeaks().at(pkIt).second,
3762 sphIt,
3763 this->sphereMappedRotFun.at(sphIt)->getAngularDim() ) );
3764 }
3765 }
3766
3767 //================================================ For each peak group, look for the requested fold
3768 for ( proshade_unsign grIt = 0; grIt < static_cast<proshade_unsign> ( peakGroups.size() ); grIt++ )
3769 {
3770 //============================================ Report progress
3771 std::stringstream hlpSS3;
3772 hlpSS3 << "Now considering group with LAT " << peakGroups.at(grIt)->getLatFromIndices() << " - " << peakGroups.at(grIt)->getLatToIndices() << " and LON " << peakGroups.at(grIt)->getLonFromIndices() << " - " << peakGroups.at(grIt)->getLonToIndices() << " spanning spheres ";
3773 for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( peakGroups.at(grIt)->getSpherePositions().size() ); sphIt++ ) { hlpSS3 << peakGroups.at(grIt)->getSpherePositions().at(sphIt) << " ; "; }
3774 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 5, hlpSS3.str() );
3775
3776 //============================================ Find point groups in the peak group
3777 peakGroups.at(grIt)->findCyclicPointGroupsGivenFold ( this->sphereMappedRotFun, settings->axisErrTolerance, &ret, settings->useBiCubicInterpolationOnPeaks, fold, settings->verbose );
3778
3779 //============================================ Release the memory
3780 delete peakGroups.at(grIt);
3781 }
3782
3783 //================================================ Sort ret by peak height
3784 std::sort ( ret.begin(), ret.end(), sortProSHADESymmetryByPeak );
3785
3786 //================================================ Done
3787 return ( ret );
3788
3789 }
3790
3803 void ProSHADE_internal_symmetry::findPredictedAxesHeights ( std::vector< proshade_double* >* ret, ProSHADE_internal_data::ProSHADE_data* dataObj, ProSHADE_settings* settings )
3804 {
3805 //================================================ Initialise variables
3806 std::vector < proshade_unsign > folds;
3807 bool alreadyFound;
3808 proshade_double lat, lon;
3809 proshade_double latSamlUnit = ( 2.0 * M_PI ) / ( dataObj->maxShellBand * 2.0 );
3810 proshade_double lonSamlUnit = ( 1.0 * M_PI ) / ( dataObj->maxShellBand * 2.0 );
3811
3812 //================================================ Determine all the folds for which rotation function mapping will be required
3813 for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ret->size() ); iter++ )
3814 {
3815 alreadyFound = false;
3816 for ( proshade_unsign it = 0; it < static_cast < proshade_unsign > ( folds.size() ); it++ ) { if ( folds.at(it) == ret->at(iter)[0] ) { alreadyFound = true; break; } }
3817
3818 if ( !alreadyFound ) { ProSHADE_internal_misc::addToUnsignVector ( &folds, ret->at(iter)[0] ); }
3819 }
3820
3821 //================================================ For each fold which needs rotation function mapping
3822 for ( proshade_unsign foldIt = 0; foldIt < static_cast < proshade_unsign > ( folds.size() ); foldIt++ )
3823 {
3824 //============================================ Make sure we have a clean start
3825 dataObj->sphereMappedRotFun.clear();
3826
3827 //============================================ Convert rotation function to only the required angle-axis space spheres and find all peaks
3828 for ( proshade_double angIt = 1.0; angIt < static_cast<proshade_double> ( folds.at(foldIt) ); angIt += 1.0 )
3829 {
3830 //======================================== Create the angle-axis sphere with correct radius (angle)
3831 dataObj->sphereMappedRotFun.emplace_back ( new ProSHADE_internal_spheres::ProSHADE_rotFun_sphere ( ( 2.0 * M_PI ) / static_cast < proshade_double > ( folds.at(foldIt) ),
3832 M_PI / static_cast < proshade_double > ( folds.at(foldIt) ),
3833 dataObj->maxShellBand * 2.0,
3834 ( 2.0 * M_PI ) / static_cast < proshade_double > ( folds.at(foldIt) ),
3835 static_cast<proshade_unsign> ( angIt - 1.0 ) ) );
3836
3837 //=========================================== Interpolate rotation function onto the sphere
3838 dataObj->sphereMappedRotFun.at( static_cast < proshade_unsign > ( angIt - 1.0 ))->interpolateSphereValues ( dataObj->getInvSO3Coeffs ( ) );
3839 }
3840
3841 //============================================ For each ret axis with this fold
3842 for ( proshade_unsign axIt = 0; axIt < static_cast< proshade_unsign > ( ret->size() ); axIt++ )
3843 {
3844 //======================================== Ignore different folds
3845 if ( ret->at(axIt)[0] != folds.at(foldIt) ) { continue; }
3846
3847 //======================================== Convert XYZ to lat and lon INDICES
3848 lat = std::atan2( ret->at(axIt)[2], ret->at(axIt)[1] ) / latSamlUnit;
3849 lon = std::acos ( ret->at(axIt)[3] ) / lonSamlUnit;
3850
3851 if ( lat < 0.0 ) { lat += ( dataObj->maxShellBand * 2.0 ); }
3852 if ( lon < 0.0 ) { lon += ( dataObj->maxShellBand * 2.0 ); }
3853
3854 lat = std::round ( lat );
3855 lon = std::round ( lon );
3856
3857 //======================================== Initialise the peak group
3858 ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup* grp = nullptr;
3859
3860 //======================================== Construct a peak group with entry from each sphere with the axis as the peak
3861 for ( proshade_unsign sphIt = 0; sphIt < static_cast<proshade_unsign> ( dataObj->sphereMappedRotFun.size() ); sphIt++ )
3862 {
3863 if ( sphIt == 0 )
3864 {
3865 //================================ If first sphere, create the peak group
3866 grp = new ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup ( lat, lon, sphIt, dataObj->sphereMappedRotFun.at(sphIt)->getAngularDim() );
3867 }
3868 else
3869 {
3870 //================================ Add to the existing object
3871 grp->checkIfPeakBelongs ( lat, lon, sphIt, settings->axisErrTolerance, settings->verbose );
3872 }
3873 }
3874
3875 //======================================== Find the peak height
3876 std::vector < proshade_double* > detectedAxis;
3877 grp->findCyclicPointGroupsGivenFold ( dataObj->sphereMappedRotFun, settings->axisErrTolerance, &detectedAxis, settings->useBiCubicInterpolationOnPeaks, folds.at(foldIt), settings->verbose );
3878
3879 //======================================== Save it!
3880 ret->at(axIt)[5] = detectedAxis.at(0)[5];
3881
3882 //======================================== Release memory
3883 for ( proshade_unsign i = 0; i < static_cast < proshade_unsign > ( detectedAxis.size() ); i++ ) { delete detectedAxis.at(i); }
3884 delete grp;
3885 }
3886 }
3887
3888 //================================================ Done
3889 return ;
3890
3891 }
3892
3900 std::pair< proshade_unsign, proshade_unsign > findBestTetraDihedralPair ( std::vector< proshade_double* >* CSymList, proshade_double minPeakHeight, proshade_double axErr )
3901 {
3902 //================================================ Initialise variables
3903 std::pair< proshade_unsign, proshade_unsign > ret;
3904 std::vector< proshade_unsign > C3List;
3905 proshade_double bestDihedralAngle = 999.9;
3906 proshade_double dotProduct;
3907
3908 //================================================ Find all C5 symmetries
3909 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ ) { if ( CSymList->at(cSym)[0] == 3 && CSymList->at(cSym)[5] >= minPeakHeight ) { ProSHADE_internal_misc::addToUnsignVector ( &C3List, cSym ); } }
3910
3911 //================================================ For each unique pair of C3 and C2
3912 for ( proshade_unsign c3 = 0; c3 < static_cast<proshade_unsign> ( C3List.size() ); c3++ )
3913 {
3914 for ( proshade_unsign cSym = 0; cSym < static_cast<proshade_unsign> ( CSymList->size() ); cSym++ )
3915 {
3916 //======================================== Compare only C3s to the C5List and only with decent average peak height
3917 if ( CSymList->at(cSym)[0] != 2 ) { continue; }
3918 if ( CSymList->at(cSym)[5] < minPeakHeight ) { continue; }
3919
3920 //======================================== Check the angle between the C5 and C3 axes
3921 dotProduct = ProSHADE_internal_maths::computeDotProduct ( &CSymList->at(C3List.at(c3))[1],
3922 &CSymList->at(C3List.at(c3))[2],
3923 &CSymList->at(C3List.at(c3))[3],
3924 &CSymList->at(cSym)[1],
3925 &CSymList->at(cSym)[2],
3926 &CSymList->at(cSym)[3] );
3927
3928 //======================================== Is the angle approximately the dihedral angle?
3929 if ( ( ( 1.0 / sqrt ( 3.0 ) ) > ( std::abs( dotProduct ) - axErr ) ) && ( ( 1.0 / sqrt ( 3.0 ) ) < ( std::abs( dotProduct ) + axErr ) ) )
3930 {
3931 if ( bestDihedralAngle > std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) ) )
3932 {
3933 bestDihedralAngle = std::abs( ( 1.0 / sqrt ( 3.0 ) ) - std::abs( dotProduct ) );
3934 ret.first = C3List.at(c3);
3935 ret.second = cSym;
3936 }
3937 }
3938 }
3939 }
3940
3941 //================================================ Done
3942 return ( ret );
3943
3944 }
3945
3960 std::vector< proshade_double* > ProSHADE_internal_data::ProSHADE_data::getPredictedTetrahedralSymmetriesList ( ProSHADE_settings* settings, std::vector< proshade_double* >* CSymList )
3961 {
3962 //================================================ Initialise variables
3963 std::vector< proshade_double* > ret;
3964
3965 //================================================ Report progress
3966 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 1, "Starting T symmetry prediction." );
3967
3968 //================================================ Are the basic requirements for icosahedral symmetry met?
3969 if ( ProSHADE_internal_symmetry::detectTetrahedralSymmetry ( CSymList, settings->axisErrTolerance, settings->minSymPeak ) )
3970 {
3971 //============================================ Generate the rest of the axes
3972 ProSHADE_internal_symmetry::predictTetraAxes ( CSymList, &ret, settings->axisErrTolerance, settings->minSymPeak );
3973
3974 //============================================ Get heights for the predicted axes
3975 ProSHADE_internal_symmetry::findPredictedAxesHeights ( &ret, this, settings );
3976
3977 //============================================ Add predicted axes to detected C axes list and also to the settings Icosahedral symmetry list
3978 for ( proshade_unsign retIt = 0; retIt < static_cast < proshade_unsign > ( ret.size() ); retIt++ )
3979 {
3980 ProSHADE_internal_misc::addToDblPtrVector ( CSymList, ret.at(retIt) );
3981 ProSHADE_internal_misc::addToUnsignVector ( &settings->allDetectedIAxes, static_cast < proshade_unsign > ( CSymList->size() ) );
3982 }
3983 }
3984
3985 //================================================ Report progress
3986 ProSHADE_internal_messages::printProgressMessage ( settings->verbose, 2, "T symmetry prediction complete." );
3987
3988 //================================================ Done
3989 return ( ret );
3990
3991 }
3992
4013 void ProSHADE_internal_symmetry::predictTetraAxes ( std::vector< proshade_double* >* CSymList, std::vector< proshade_double* >* ret, proshade_double axErr, proshade_double minPeakHeight )
4014 {
4015 //================================================ Find the best axis combination with dihedral angle and correct folds
4016 std::pair< proshade_unsign, proshade_unsign > initAxes = findBestTetraDihedralPair ( CSymList, minPeakHeight, axErr );
4017
4018 //================================================ Find rotation between the detected C3 and the model C3 axes.
4019 proshade_double* rotMat = ProSHADE_internal_maths::findRotMatMatchingVectors ( ProSHADE_internal_precomputedVals::tetrahedronAxes.at(0).at(1),
4020 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(0).at(2),
4021 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(0).at(3),
4022 CSymList->at(initAxes.first)[1],
4023 CSymList->at(initAxes.first)[2],
4024 CSymList->at(initAxes.first)[3] );
4025
4026 //================================================ Rotate the model C2 to the correct orientation relative to the detected C3 axis.
4027 proshade_double* rotModelC2 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMat,
4028 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(4).at(1),
4029 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(4).at(2),
4030 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(4).at(3) );
4031
4032 //================================================ Find the angle betwen the rotated model C2 and the detected C2 axes along the detected C3 axis.
4033 proshade_double bestAng = 0.0, curAngDist, bestAngDist = 999.9;
4034 for ( proshade_double ang = 0.0; ang < ( M_PI * 2.0 ); ang += 0.01 )
4035 {
4036 //============================================ Compute rotation matrix for this angle value
4037 proshade_double* rotMatHlp = new proshade_double[9];
4038 ProSHADE_internal_misc::checkMemoryAllocation ( rotMatHlp, __FILE__, __LINE__, __func__ );
4039 ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMatHlp, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], ang );
4040
4041 //============================================ Rotate the rotated C2 by the matrix
4042 proshade_double* rotRotModelC2 = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatHlp,
4043 rotModelC2[0],
4044 rotModelC2[1],
4045 rotModelC2[2] );
4046
4047 //============================================ Find distance
4048 curAngDist = std::sqrt ( std::pow ( rotRotModelC2[0] - CSymList->at(initAxes.second)[1], 2.0 ) +
4049 std::pow ( rotRotModelC2[1] - CSymList->at(initAxes.second)[2], 2.0 ) +
4050 std::pow ( rotRotModelC2[2] - CSymList->at(initAxes.second)[3], 2.0 ) );
4051
4052 //============================================ Save best angle
4053 if ( curAngDist < bestAngDist ) { bestAngDist = curAngDist; bestAng = ang; }
4054
4055 //============================================ Release memory
4056 delete[] rotMatHlp;
4057 delete[] rotRotModelC2;
4058 }
4059
4060 //================================================ For the rotation matrix along the detected C5 axis with the same anlge as is between the rotated model C3 and the detected C3 axes.
4061 proshade_double* rotMat2 = new proshade_double[9];
4062 ProSHADE_internal_misc::checkMemoryAllocation ( rotMat2, __FILE__, __LINE__, __func__ );
4063 ProSHADE_internal_maths::getRotationMatrixFromAngleAxis ( rotMat2, CSymList->at(initAxes.first)[1], CSymList->at(initAxes.first)[2], CSymList->at(initAxes.first)[3], bestAng );
4064
4065 //================================================ Combine the two rotation matrices into a single rotation matrix
4066 proshade_double* rotMatFin = ProSHADE_internal_maths::compute3x3MatrixMultiplication ( rotMat2, rotMat );
4067
4068 //================================================ For each model axis
4069 for ( proshade_unsign iter = 0; iter < static_cast < proshade_unsign > ( ProSHADE_internal_precomputedVals::tetrahedronAxes.size() ); iter++ )
4070 {
4071 //============================================ Rotate the model axis to fit the detected orientation
4072 proshade_double* rotAxis = ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication ( rotMatFin,
4073 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(1),
4074 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(2),
4075 ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(3) );
4076
4077 //============================================ Create ProSHADE symmetry axis representation
4078 proshade_double* axis = new proshade_double[6];
4079 ProSHADE_internal_misc::checkMemoryAllocation ( axis, __FILE__, __LINE__, __func__ );
4080
4081 axis[0] = ProSHADE_internal_precomputedVals::tetrahedronAxes.at(iter).at(0);
4082 axis[1] = rotAxis[0];
4083 axis[2] = rotAxis[1];
4084 axis[3] = rotAxis[2];
4085 axis[4] = ( 2.0 * M_PI ) / axis[0];
4086 axis[5] = 0.0;
4087
4088 //============================================ Save axis to ret
4089 ProSHADE_internal_misc::addToDblPtrVector ( ret, axis );
4090
4091 //============================================ Release memory
4092 delete[] rotAxis;
4093 }
4094
4095 //================================================ Release memory
4096 delete[] rotMat;
4097 delete[] rotMat2;
4098 delete[] rotMatFin;
4099 delete[] rotModelC2;
4100
4101 //================================================ Done
4102 return ;
4103
4104 }
ProSHADE_settings::noIQRsFromMedianNaivePeak
proshade_double noIQRsFromMedianNaivePeak
When doing peak searching, how many IQRs from the median the threshold for peak height should be (in ...
Definition: ProSHADE_settings.hpp:165
ProSHADE_internal_distances::normaliseEMatrices
void normaliseEMatrices(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function normalises the E matrices.
Definition: ProSHADE_distances.cpp:577
sortProSHADESymmetryByPeak
bool sortProSHADESymmetryByPeak(proshade_double *a, proshade_double *b)
This function allows using std::sort to sort vectors of ProSHADE symmetry format..
Definition: ProSHADE_symmetry.cpp:3669
ProSHADE_internal_symmetry::detectOctahedralSymmetry
bool detectOctahedralSymmetry(std::vector< proshade_double * > *CSymList, proshade_double axErr, proshade_double minPeakHeight)
This function takes the list of C symmetries and decides whether basic requirements for octahhedral s...
Definition: ProSHADE_symmetry.cpp:2152
ProSHADE_internal_maths::findVectorFromThreeVAndThreeD
std::vector< proshade_double > findVectorFromThreeVAndThreeD(proshade_double x1, proshade_double y1, proshade_double z1, proshade_double x2, proshade_double y2, proshade_double z2, proshade_double x3, proshade_double y3, proshade_double z3, proshade_double dot1, proshade_double dot2, proshade_double dot3)
Function for finding a vector which would have a given three dot products to three other vectors.
Definition: ProSHADE_maths.cpp:2101
ProSHADE_internal_symmetry::findExpectedPeakRotations
void findExpectedPeakRotations(proshade_unsign fold, std::vector< proshade_double > *expAngs)
This function computes the expected peak rotations for given fold.
Definition: ProSHADE_symmetry.cpp:705
ProSHADE_internal_symmetry::findMissingAxes
bool findMissingAxes(std::vector< std::vector< proshade_unsign > > *possibilities, std::vector< proshade_double * > *CSymList, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_double angle, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_double minPeakHeight)
This function tries to find an axis which would complete a particular group of axes for polyhedral sy...
Definition: ProSHADE_symmetry.cpp:1582
ProSHADE_internal_misc::addToDblPtrVector
void addToDblPtrVector(std::vector< proshade_double * > *vecToAddTo, proshade_double *elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:143
ProSHADE_settings::maxSymmetryFold
proshade_unsign maxSymmetryFold
The highest symmetry fold to search for.
Definition: ProSHADE_settings.hpp:181
ProSHADE_internal_symmetry::groupSameAxes
std::vector< std::vector< proshade_unsign > > groupSameAxes(std::vector< proshade_double * > &peaks, proshade_double errTolerance)
This function groups the peaks by their axes of rotation.
Definition: ProSHADE_symmetry.cpp:419
ProSHADE_internal_data::ProSHADE_data::computeRotationFunction
void computeRotationFunction(ProSHADE_settings *settings)
This function computes the self-rotation function for this structure.
Definition: ProSHADE_symmetry.cpp:34
ProSHADE_internal_symmetry::sortArrVecHlp
bool sortArrVecHlp(const proshade_double *a, const proshade_double *b)
This function compares two arrays of two based on the first number.
Definition: ProSHADE_symmetry.cpp:1642
ProSHADE_internal_symmetry::findTetra3C2s
void findTetra3C2s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 3 C2 symmetries with correct angles requir...
Definition: ProSHADE_symmetry.cpp:1966
ProSHADE_internal_symmetry::testGroupAgainstGroup
bool testGroupAgainstGroup(std::vector< proshade_double * > *CSymList, std::vector< proshade_unsign > *grp1, std::vector< proshade_double * > *RetList, std::vector< proshade_unsign > *grp2, proshade_double angle, proshade_double axErr)
This function compares two groups of axes for a single pair having the required angle.
Definition: ProSHADE_symmetry.cpp:2050
ProSHADE_internal_symmetry::addAxisUnlessSame
void addAxisUnlessSame(proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double axHeight, std::vector< proshade_double * > *prosp, proshade_double axErr)
This function simply creates a new axis from information in aruments and tests if no such axis alread...
Definition: ProSHADE_symmetry.cpp:2527
ProSHADE_internal_symmetry::predictOctaAxes
void predictOctaAxes(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all octahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:3130
ProSHADE_internal_symmetry::smallestDistanceBetweenAngles
bool smallestDistanceBetweenAngles(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, std::vector< proshade_double > *tried, proshade_double *dist)
This function finds the smallest distance between the rotation angles within a group.
Definition: ProSHADE_symmetry.cpp:551
ProSHADE_internal_symmetry::predictTetraAxes
void predictTetraAxes(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all tetrahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:4013
ProSHADE_internal_spheres::ProSHADE_rotFun_sphere
This class contains all inputed data for the rotation function angle-axis converted spheres.
Definition: ProSHADE_maths.hpp:51
ProSHADE_internal_symmetry::saveAllCSymmetries
void saveAllCSymmetries(std::vector< std::vector< proshade_unsign > > detected, std::vector< proshade_double * > peaks, std::vector< proshade_double * > *ret, proshade_double axErr)
This function takes the detected symmetries indices and peaks and saves these in the main cyclic symm...
Definition: ProSHADE_symmetry.cpp:1115
ProSHADE_internal_maths::normalDistributionValue
proshade_double normalDistributionValue(proshade_double mean, proshade_double standardDev, proshade_double value)
Function to the heiht of normal distribution given by mean and standard deviation for a given value.
Definition: ProSHADE_maths.cpp:1688
ProSHADE_internal_symmetry::findPredictedAxesHeights
void findPredictedAxesHeights(std::vector< proshade_double * > *ret, ProSHADE_internal_data::ProSHADE_data *dataObj, ProSHADE_settings *settings)
This function finds the rotation function value for all axes supplied in the ret parameter.
Definition: ProSHADE_symmetry.cpp:3803
ProSHADE_settings::allDetectedDAxes
std::vector< std::vector< proshade_unsign > > allDetectedDAxes
The vector of all detected dihedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:194
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:174
ProSHADE_internal_symmetry::findMissingAxesDual
bool findMissingAxesDual(std::vector< proshade_unsign > *possibilities, std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, std::vector< proshade_unsign > *retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function tries to find a particular symmetry axes which would complete a group of symmetries wit...
Definition: ProSHADE_symmetry.cpp:2434
ProSHADE_internal_data::ProSHADE_data::maxShellBand
proshade_unsign maxShellBand
The maximum band for any shell of the object.
Definition: ProSHADE_data.hpp:123
ProSHADE_settings::symMissPeakThres
proshade_double symMissPeakThres
Percentage of peaks that could be missing that would warrant starting the missing peaks search proced...
Definition: ProSHADE_settings.hpp:171
ProSHADE_internal_maths::getRotationMatrixFromEulerZXZAngles
void getRotationMatrixFromEulerZXZAngles(proshade_double eulerAlpha, proshade_double eulerBeta, proshade_double eulerGamma, proshade_double *matrix)
Function to find the rotation matrix from Euler angles (ZXZ convention).
Definition: ProSHADE_maths.cpp:1005
ProSHADE_internal_data::ProSHADE_data::getDihedralSymmetriesList
std::vector< proshade_double * > getDihedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all D symmetries from already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:1222
ProSHADE_internal_symmetry::addZeroPeakToGroups
void addZeroPeakToGroups(std::vector< std::vector< proshade_unsign > > &grpsVec, std::vector< proshade_double * > &peaks)
This function takes the peak groups and adds zero peak to each of them.
Definition: ProSHADE_symmetry.cpp:621
ProSHADE_internal_symmetry::findOcta3C4s
void findOcta3C4s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 3 C4 symmetries with perpendicular angles ...
Definition: ProSHADE_symmetry.cpp:2206
ProSHADE_symmetry.hpp
This header file declares all the functions required for symmetry detection and construction.
ProSHADE_settings::allDetectedTAxes
std::vector< proshade_unsign > allDetectedTAxes
The vector of all detected tetrahedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:195
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:196
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:188
ProSHADE_internal_symmetry::printSymmetryGroup
void printSymmetryGroup(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, proshade_signed verbose)
This function simply prints the detected symmetry and all its supporting peaks.
Definition: ProSHADE_symmetry.cpp:1059
ProSHADE_internal_maths::vectorOrientationSimilarity
bool vectorOrientationSimilarity(proshade_double a1, proshade_double a2, proshade_double a3, proshade_double b1, proshade_double b2, proshade_double b3, proshade_double tolerance=0.1)
This function compares two vectors using cosine distance and decides if they are similar using tolera...
Definition: ProSHADE_maths.cpp:2232
ProSHADE_internal_data::ProSHADE_data::getPredictedIcosahedralSymmetriesList
std::vector< proshade_double * > getPredictedIcosahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function predicts a list of all I symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2691
ProSHADE_internal_misc::addToDoubleVector
void addToDoubleVector(std::vector< proshade_double > *vecToAddTo, proshade_double elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:77
ProSHADE_internal_symmetry::checkExpectedAgainstFound
proshade_unsign checkExpectedAgainstFound(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, std::vector< proshade_double > *expAngs, std::vector< proshade_unsign > *matchedAngs, std::vector< proshade_unsign > *missingAngs, proshade_double axisTol)
This function computes the expected peak rotations for given fold.
Definition: ProSHADE_symmetry.cpp:734
ProSHADE_settings::useBiCubicInterpolationOnPeaks
bool useBiCubicInterpolationOnPeaks
This variable switch decides whether best symmetry is detected from peak indices, or whether bicubic ...
Definition: ProSHADE_settings.hpp:180
determinePeakThreshold
proshade_double determinePeakThreshold(std::vector< proshade_double > inArr, proshade_double noIQRsFromMedian)
This function takes a vector of values and determines the threshold for removing noise from it.
Definition: ProSHADE_symmetry.cpp:64
ProSHADE_internal_symmetry::saveMissingAxisNewOnly
void saveMissingAxisNewOnly(std::vector< proshade_double * > *axVec, proshade_double axX, proshade_double axY, proshade_double axZ, proshade_double height, proshade_unsign fold, proshade_double axErr)
This function saves the recovered information about missing axis into a full symmetry,...
Definition: ProSHADE_symmetry.cpp:1799
ProSHADE_internal_maths::computeDotProduct
proshade_double computeDotProduct(proshade_double *x1, proshade_double *y1, proshade_double *z1, proshade_double *x2, proshade_double *y2, proshade_double *z2)
Simple 3D vector dot product computation.
Definition: ProSHADE_maths.cpp:1705
ProSHADE_internal_symmetry::findIcos6C5s
void findIcos6C5s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the six C5 symmetries with given angles requir...
Definition: ProSHADE_symmetry.cpp:2841
ProSHADE_internal_symmetry::findSymmetryUsingFold
void findSymmetryUsingFold(ProSHADE_internal_data::ProSHADE_data *dataObj, std::vector< proshade_unsign > *angsToTry, std::vector< proshade_unsign > *grp, std::vector< proshade_double * > *peaks, std::vector< std::vector< proshade_unsign > > *ret, std::vector< proshade_unsign > *testedAlready, proshade_double axErrTolerance, bool axErrToleranceDefault, proshade_double missPeakThres, proshade_unsign verbose)
This function tests all supplied folds for being supported by the peaks (i.e. and being complete pres...
Definition: ProSHADE_symmetry.cpp:989
ProSHADE_internal_misc::deepCopyAxisToDblPtrVector
void deepCopyAxisToDblPtrVector(std::vector< proshade_double * > *dblPtrVec, proshade_double *axis)
Does a deep copy of a double array to a vector of double arrays.
Definition: ProSHADE_misc.cpp:287
ProSHADE_internal_maths::getRotationMatrixFromAngleAxis
void getRotationMatrixFromAngleAxis(proshade_double *rotMat, proshade_double x, proshade_double y, proshade_double z, proshade_double ang)
This function converts the axis-angle representation to the rotation matrix representation.
Definition: ProSHADE_maths.cpp:1343
ProSHADE_internal_data::ProSHADE_data::getMaxBand
proshade_unsign getMaxBand(void)
This function returns the maximum band value for the object.
Definition: ProSHADE_data.cpp:3004
ProSHADE_internal_symmetry::detectIcosahedralSymmetry
bool detectIcosahedralSymmetry(std::vector< proshade_double * > *CSymList, proshade_double axErr, proshade_double minPeakHeight)
This function takes the list of C symmetries and decides whether basic requirements for isosahedral s...
Definition: ProSHADE_symmetry.cpp:2783
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup::findCyclicPointGroupsGivenFold
void findCyclicPointGroupsGivenFold(std::vector< ProSHADE_internal_spheres::ProSHADE_rotFun_sphere * > sphereVals, proshade_double axisTolerance, std::vector< proshade_double * > *detectedCs, bool bicubicInterp, proshade_unsign fold, proshade_unsign verbose)
Function detecting cyclic point groups with a particular fold in a peak group.
Definition: ProSHADE_spheres.cpp:1366
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup::checkIfPeakBelongs
bool checkIfPeakBelongs(proshade_double lat, proshade_double lon, proshade_unsign sphPos, proshade_double cosTol, proshade_signed verbose)
This function takes a new prospective peak and tests if it belongs to this peak group or not.
Definition: ProSHADE_spheres.cpp:1137
ProSHADE_internal_symmetry::findTetra4C3s
void findTetra4C3s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the 4 C3 symmetries with correct angles requir...
Definition: ProSHADE_symmetry.cpp:1445
ProSHADE_settings
This class stores all the settings and is passed to the executive classes instead of a multitude of p...
Definition: ProSHADE_settings.hpp:89
ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesList
std::vector< proshade_double * > getCyclicSymmetriesList(ProSHADE_settings *settings)
This function obtains a list of all C symmetries from already computed self-rotation map.
Definition: ProSHADE_symmetry.cpp:191
ProSHADE_internal_data::ProSHADE_data::getPredictedTetrahedralSymmetriesList
std::vector< proshade_double * > getPredictedTetrahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function predicts a list of all T symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:3960
ProSHADE_internal_symmetry::completeMissingCSymmetry
bool completeMissingCSymmetry(ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign fold, std::vector< proshade_unsign > *grp, std::vector< proshade_double * > *peaks, std::vector< proshade_unsign > *missingPeaks, std::vector< proshade_double > *expectedAngles, std::vector< proshade_unsign > *matchedPeaks, proshade_double axErrTolerance, proshade_unsign verbose)
This function does the complete missing peak searching and filling in the missing peaks.
Definition: ProSHADE_symmetry.cpp:924
ProSHADE_internal_distances::computeEMatrices
void computeEMatrices(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function computes the complete E matrices and their weights between any two objects.
Definition: ProSHADE_distances.cpp:515
ProSHADE_internal_symmetry::giveOppositeAxesSameDirection
void giveOppositeAxesSameDirection(std::vector< proshade_double * > peaks)
This function modifiest the axes so that the highest vector element is always positive.
Definition: ProSHADE_symmetry.cpp:491
ProSHADE_internal_data::ProSHADE_data::convertRotationFunction
void convertRotationFunction(ProSHADE_settings *settings)
This function converts the self-rotation function of this structure to angle-axis representation.
Definition: ProSHADE_symmetry.cpp:113
ProSHADE_internal_symmetry::printSymmetryPeaks
void printSymmetryPeaks(std::vector< proshade_unsign > grp, std::vector< proshade_double * > peaks, proshade_signed verbose, proshade_unsign groupNo)
This function simply prints the symmetry axis group supplied in the first parameter from the second p...
Definition: ProSHADE_symmetry.cpp:518
ProSHADE_internal_distances::computeInverseSOFTTransform
void computeInverseSOFTTransform(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function computes the inverse SO(3) transform.
Definition: ProSHADE_distances.cpp:859
ProSHADE_internal_spheres::ProSHADE_rotFun_spherePeakGroup
This class contains peak groups detected in the rotation function mapped spheres.
Definition: ProSHADE_spheres.hpp:130
ProSHADE_internal_maths::isAxisUnique
bool isAxisUnique(std::vector< proshade_double * > *CSymList, proshade_double *axis, proshade_double tolerance=0.1, bool improve=false)
This function checks if new axis is unique, or already detected.
Definition: ProSHADE_maths.cpp:2613
findBestIcosDihedralPair
std::pair< proshade_unsign, proshade_unsign > findBestIcosDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds the best pair of axes conforming to the icosahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:2899
ProSHADE_internal_symmetry::findMissingAxesTriple
bool findMissingAxesTriple(std::vector< proshade_unsign > *possibilities, std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, std::vector< proshade_unsign > *retGroup, proshade_unsign requiredNoAxes, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, proshade_unsign fold, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function tries to find a particular symmetry axis which would complete a group of symmetries wit...
Definition: ProSHADE_symmetry.cpp:3390
ProSHADE_internal_maths::getAxisAngleFromRotationMatrix
void getAxisAngleFromRotationMatrix(proshade_double *rotMat, proshade_double *x, proshade_double *y, proshade_double *z, proshade_double *ang)
This function converts rotation matrix to the axis-angle representation.
Definition: ProSHADE_maths.cpp:1039
ProSHADE_internal_maths::findRotMatMatchingVectors
proshade_double * findRotMatMatchingVectors(proshade_double x1, proshade_double y1, proshade_double z1, proshade_double x2, proshade_double y2, proshade_double z2)
Computation of rotation matrix rotating one vector onto the other.
Definition: ProSHADE_maths.cpp:1883
ProSHADE_settings::peakNeighbours
proshade_unsign peakNeighbours
Number of points in any direction that have to be lower than the considered index in order to conside...
Definition: ProSHADE_settings.hpp:164
ProSHADE_internal_symmetry::getPeaksAngleAxisPositions
std::vector< proshade_double * > getPeaksAngleAxisPositions(std::vector< proshade_double * > allPeaks, proshade_unsign verbose)
This function converts peaks ZXZ Euler anles to angle-axis representation for further processing.
Definition: ProSHADE_symmetry.cpp:256
ProSHADE_internal_symmetry::findIcos10C3s
void findIcos10C3s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the ten C3 symmetries with correct angles requ...
Definition: ProSHADE_symmetry.cpp:3236
ProSHADE_settings::smoothingFactor
proshade_double smoothingFactor
This factor decides how small the group sizes should be - larger factor means more smaller groups.
Definition: ProSHADE_settings.hpp:168
ProSHADE_internal_symmetry::predictIcosAxes
void predictIcosAxes(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, proshade_double minPeakHeight)
This function predicts all icosahedral point group symmetry axes from the cyclic point groups list.
Definition: ProSHADE_symmetry.cpp:2964
ProSHADE_internal_mapManip::myRound
proshade_signed myRound(proshade_double x)
Calls the appropriate version of round function depending on compiler version.
Definition: ProSHADE_mapManip.cpp:31
ProSHADE_internal_maths::compute3x3MatrixVectorMultiplication
proshade_double * compute3x3MatrixVectorMultiplication(proshade_double *mat, proshade_double x, proshade_double y, proshade_double z)
Function for computing a 3x3 matrix to 3x1 vector multiplication.
Definition: ProSHADE_maths.cpp:1789
findBestTetraDihedralPair
std::pair< proshade_unsign, proshade_unsign > findBestTetraDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds the best pair of axes conforming to the tetrahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:3900
ProSHADE_internal_symmetry::findOcta4C3s
void findOcta4C3s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the four C3 symmetries with correct angles req...
Definition: ProSHADE_symmetry.cpp:2272
ProSHADE_internal_symmetry::testGroupAgainstSymmetry
bool testGroupAgainstSymmetry(std::vector< proshade_double * > *CSymList, std::vector< proshade_unsign > *grp, proshade_double *sym, proshade_double axErr, proshade_double angle, bool improve, proshade_unsign pos=0)
This function tests whether a symmetry has particular angle to all members of a group.
Definition: ProSHADE_symmetry.cpp:1515
ProSHADE_internal_misc::checkMemoryAllocation
void checkMemoryAllocation(chVar checkVar, std::string fileP, unsigned int lineP, std::string funcP, std::string infoP="This error may occurs when ProSHADE requests memory to be\n : allocated to it and this operation fails. This could\n : happen when not enough memory is available, either due to\n : other processes using a lot of memory, or when the machine\n : does not have sufficient memory available. Re-run to see\n : if this problem persists.")
Checks if memory was allocated properly.
Definition: ProSHADE_misc.hpp:65
ProSHADE_internal_symmetry::saveDetectedCSymmetry
void saveDetectedCSymmetry(proshade_unsign fold, std::vector< proshade_unsign > *matchedPeaks, std::vector< std::vector< proshade_unsign > > *ret, proshade_signed verbose)
This function saves a detected symmetry for reporting to the user.
Definition: ProSHADE_symmetry.cpp:884
ProSHADE_internal_maths::getEulerZXZFromSOFTPosition
void getEulerZXZFromSOFTPosition(proshade_signed band, proshade_signed x, proshade_signed y, proshade_signed z, proshade_double *eulerAlpha, proshade_double *eulerBeta, proshade_double *eulerGamma)
Function to find Euler angles (ZXZ convention) from index position in the inverse SOFT map.
Definition: ProSHADE_maths.cpp:961
ProSHADE_internal_symmetry::saveDSymmetry
void saveDSymmetry(std::vector< proshade_double * > *ret, std::vector< proshade_double * > *CSymList, proshade_unsign axisOne, proshade_unsign axisTwo)
This function saves a detected dihedral symmetry to the dihedral symmetries list.
Definition: ProSHADE_symmetry.cpp:1301
ProSHADE_internal_maths::vectorMedianAndIQR
void vectorMedianAndIQR(std::vector< proshade_double > *vec, proshade_double *&ret)
Function to get vector median and inter-quartile range.
Definition: ProSHADE_maths.cpp:147
ProSHADE_internal_data::ProSHADE_data::getPredictedOctahedralSymmetriesList
std::vector< proshade_double * > getPredictedOctahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function predicts a list of all O symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2739
ProSHADE_internal_symmetry::checkForMissingPeak
proshade_double checkForMissingPeak(ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_double x, proshade_double y, proshade_double z, proshade_double angle, proshade_double heightThres, proshade_double axTol)
This function checks for the high of the correlation for particular rotation angle and axis.
Definition: ProSHADE_symmetry.cpp:816
ProSHADE_internal_data::ProSHADE_data::getInvSO3Coeffs
proshade_complex * getInvSO3Coeffs(void)
This function allows access to the inverse SO(3) coefficients array.
Definition: ProSHADE_data.cpp:3231
ProSHADE_settings::allDetectedIAxes
std::vector< proshade_unsign > allDetectedIAxes
The vector of all detected icosahedral symmetry axes indices in allDetectedCAxes.
Definition: ProSHADE_settings.hpp:197
ProSHADE_internal_misc::addToUnsignVector
void addToUnsignVector(std::vector< proshade_unsign > *vecToAddTo, proshade_unsign elementToAdd)
Adds the element to the vector.
Definition: ProSHADE_misc.cpp:99
ProSHADE_internal_data::ProSHADE_data::getIcosahedralSymmetriesList
std::vector< proshade_double * > getIcosahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all I symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2629
ProSHADE_internal_data::ProSHADE_data::getCyclicSymmetriesListFromAngleAxis
std::vector< proshade_double * > getCyclicSymmetriesListFromAngleAxis(ProSHADE_settings *settings)
This function obtains a list of all C symmetries from the angle-axis space mapped rotation function v...
Definition: ProSHADE_symmetry.cpp:3553
ProSHADE_internal_symmetry::checkFittingAxisTripleAndSave
void checkFittingAxisTripleAndSave(std::vector< proshade_unsign > *retGroup, std::vector< proshade_double * > *ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double * > *prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, proshade_unsign noMatchesG3, proshade_double angle3, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function takes a newly detected "missing" axis and tests it for belonging to the group,...
Definition: ProSHADE_symmetry.cpp:3501
findBestOctaDihedralPair
std::pair< proshade_unsign, proshade_unsign > findBestOctaDihedralPair(std::vector< proshade_double * > *CSymList, proshade_double minPeakHeight, proshade_double axErr)
This function finds the best pair of axes conforming to the octahedron dihedral angle.
Definition: ProSHADE_symmetry.cpp:3064
ProSHADE_internal_data::ProSHADE_data::getTetrahedralSymmetriesList
std::vector< proshade_double * > getTetrahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all T symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:1340
ProSHADE_internal_maths::findVectorFromTwoVAndTwoD
std::vector< proshade_double > findVectorFromTwoVAndTwoD(proshade_double x1, proshade_double y1, proshade_double z1, proshade_double x2, proshade_double y2, proshade_double z2, proshade_double dot1, proshade_double dot2)
Function for finding a vector which would have a given two dot products to two other vectors.
Definition: ProSHADE_maths.cpp:1959
ProSHADE_internal_symmetry::findOcta6C2s
void findOcta6C2s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the six C2 symmetries with correct angles requ...
Definition: ProSHADE_symmetry.cpp:2354
ProSHADE_settings::axisErrTolerance
proshade_double axisErrTolerance
Allowed error on vector axis in in dot product ( acos ( 1 - axErr ) is the allowed difference in radi...
Definition: ProSHADE_settings.hpp:172
ProSHADE_internal_symmetry::determineFoldToTry
bool determineFoldToTry(proshade_double dist, proshade_double *divBasis, proshade_double *divRem, proshade_double peakErr, proshade_double *symmErr, std::vector< proshade_unsign > *angsToTry)
This function determines the symmetry fold to be searched for.
Definition: ProSHADE_symmetry.cpp:658
ProSHADE_internal_distances::generateSO3CoeffsFromEMatrices
void generateSO3CoeffsFromEMatrices(ProSHADE_internal_data::ProSHADE_data *obj1, ProSHADE_internal_data::ProSHADE_data *obj2, ProSHADE_settings *settings)
This function converts the E matrices to SO(3) coefficients.
Definition: ProSHADE_distances.cpp:706
ProSHADE_internal_data::ProSHADE_data::findRequestedCSymmetryFromAngleAxis
std::vector< proshade_double * > findRequestedCSymmetryFromAngleAxis(ProSHADE_settings *settings, proshade_unsign fold, proshade_double *peakThres)
This function searches the angle-axis representation of the rotation function for a cyclic point grou...
Definition: ProSHADE_symmetry.cpp:3692
ProSHADE_internal_maths::findAllPrimes
std::vector< proshade_unsign > findAllPrimes(proshade_unsign upTo)
This function finds all prime numbers up to the supplied limit.
Definition: ProSHADE_maths.cpp:2693
ProSHADE_internal_data::ProSHADE_data::getOctahedralSymmetriesList
std::vector< proshade_double * > getOctahedralSymmetriesList(ProSHADE_settings *settings, std::vector< proshade_double * > *CSymList)
This function obtains a list of all O symmetry axes from the already computed C symmetries list.
Definition: ProSHADE_symmetry.cpp:2093
ProSHADE_internal_messages::printProgressMessage
void printProgressMessage(proshade_signed verbose, proshade_signed messageLevel, std::string message)
General stdout message printing.
Definition: ProSHADE_messages.cpp:70
ProSHADE_internal_symmetry::printSymmetryCompletion
void printSymmetryCompletion(proshade_unsign noSyms, proshade_unsign verbose)
This function simply prints the summary and warnings for cyclic symmetries detection completion.
Definition: ProSHADE_symmetry.cpp:1085
ProSHADE_internal_symmetry::detectTetrahedralSymmetry
bool detectTetrahedralSymmetry(std::vector< proshade_double * > *CSymList, proshade_double axErr, proshade_double minPeakHeight)
This function takes the list of C symmetries and decides whether basic requirements for tetrahedral s...
Definition: ProSHADE_symmetry.cpp:1396
ProSHADE_internal_misc::sortSymHlpInv
bool sortSymHlpInv(const proshade_double *a, const proshade_double *b)
This function compares two arrays of two based on the fifth number, sorting highest first.
Definition: ProSHADE_misc.cpp:243
ProSHADE_internal_symmetry::checkFittingAxisDualAndSave
bool checkFittingAxisDualAndSave(std::vector< proshade_unsign > *retGroup, std::vector< proshade_double * > *ret, proshade_unsign fold, proshade_double axX, proshade_double axY, proshade_double axZ, std::vector< proshade_double * > *prosp, proshade_double axErr, proshade_unsign noMatchesG1, proshade_double angle1, proshade_unsign noMatchesG2, proshade_double angle2, ProSHADE_internal_data::ProSHADE_data *dataObj)
This function takes a newly detected "missing" axis and tests it for belonging to the group,...
Definition: ProSHADE_symmetry.cpp:2577
ProSHADE_internal_symmetry::findMissingAxisPoints
std::vector< proshade_double * > findMissingAxisPoints(proshade_double xVal, proshade_double yVal, proshade_double zVal, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_double axErr)
This function searches for all the self-rotation map points conforming to the axis,...
Definition: ProSHADE_symmetry.cpp:1725
ProSHADE_internal_maths::compute3x3MatrixMultiplication
proshade_double * compute3x3MatrixMultiplication(proshade_double *mat1, proshade_double *mat2)
Function for computing a 3x3 matrix multiplication.
Definition: ProSHADE_maths.cpp:1759
ProSHADE_internal_symmetry::findIcos15C2s
void findIcos15C2s(std::vector< proshade_double * > *CSymList, std::vector< proshade_double * > *ret, proshade_double axErr, ProSHADE_internal_data::ProSHADE_data *dataObj, proshade_unsign verbose, proshade_double minPeakHeight)
This function takes the list of C symmetries and finds the fifteen C3 symmetries with correct angles ...
Definition: ProSHADE_symmetry.cpp:3308