All Data Structures Namespaces Files Functions Variables Enumerations Enumerator Modules Pages
Chill

Namespaces

namespace  chill
 CHILL and CHILL+ structure classification. This namespace contains functions that are used in the CHILL/CHILL+ classification scheme, as well as a yodaCloud struct to hold all the information.
 
namespace  sph
 Functions used for spherical harmonics.
 

Data Structures

struct  chill::YlmAtom
 This contains a complex vector of length 2l+1. More...
 
struct  chill::QlmAtom
 This is the local orientational bond order parameter qlm, of length 2l+1. More...
 

Functions

molSys::PointCloud< molSys::Point< double >, double > chill::getCorrel (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, bool isSlice=false)
 
molSys::PointCloud< molSys::Point< double >, double > chill::getIceTypeNoPrint (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, bool isSlice=false)
 Classifies each atom according to the CHILL algorithm without printing.
 
molSys::PointCloud< molSys::Point< double >, double > chill::getIceType (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, std::string path, int firstFrame, bool isSlice=false, std::string outputFileName="chill.txt")
 Classifies each atom according to the CHILL algorithm.
 
molSys::PointCloud< molSys::Point< double >, double > chill::getCorrelPlus (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, bool isSlice=false)
 Gets c_ij and then classifies bond types according to the CHILL+ algorithm.
 
molSys::PointCloud< molSys::Point< double >, double > chill::getIceTypePlus (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, std::string path, int firstFrame, bool isSlice=false, std::string outputFileName="chillPlus.txt")
 Classifies each atom according to the CHILL+ algorithm.
 
std::vector< double > chill::getq6 (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, bool isSlice=false)
 
molSys::PointCloud< molSys::Point< double >, double > chill::reclassifyWater (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< double > *q6)
 
int chill::printIceType (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::string path, int firstFrame, bool isSlice=false, std::string outputFileName="superChill.txt")
 Prints out the iceType for a particular frame onto the terminal.
 
bool chill::isInterfacial (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, int iatom, int num_staggrd, int num_eclipsd)
 
int chill::numStaggered (molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, int jatom)
 Finds the number of staggered bonds for a given atom of index jatom.
 
std::vector< std::complex< double > > sph::spheriHarmo (int orderL, std::array< double, 2 > radialCoord)
 
std::array< double, 2 > sph::radialCoord (std::array< double, 3 > cartCoord)
 
std::vector< std::complex< double > > sph::lookupTableQ3Vec (std::array< double, 2 > angles)
 Lookup table for Q3.
 
std::complex< double > sph::lookupTableQ3 (int m, std::array< double, 2 > angles)
 Lookup table for Q3 (m=0 to m=6)
 
std::vector< std::complex< double > > sph::lookupTableQ6Vec (std::array< double, 2 > angles)
 Lookup table for Q6.
 
std::complex< double > sph::lookupTableQ6 (int m, std::array< double, 2 > angles)
 Lookup table for Q6 (m=0 to m=12)
 

Variables

std::vector< std::complex< double > > chill::YlmAtom::ylm
 
std::vector< YlmAtomchill::QlmAtom::ptq
 

Detailed Description

Function Documentation

◆ getCorrel()

molSys::PointCloud< molSys::Point< double >, double > chill::getCorrel ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
bool  isSlice = false 
)

Function for getting the bond order correlations cij (or aij in some treatments) according to the CHILL algorithm.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]nListThe row-ordered neighbour list, by ID. The first element of each row is the particle ID, followed by the IDs of the neighbours
[in]isSliceThis decides whether there is a slice or not

Uses Boost for spherical harmonics, and gets c_ij according to the CHILL algorithm

Definition at line 299 of file bop.cpp.

Code
300 {
301 //
302 int l = 3; // TODO: Don't hard-code this; change later
303 int iatomID; // Atom ID (key) of iatom
304 int iatomIndex; // Index (value) of iatom
305 int jatomID; // Atom ID (key) of jatom
306 int jatomIndex; // Index (value) of nearest neighbour
307 std::array<double, 3> delta;
308 std::array<double, 2> angles;
309 chill::QlmAtom QlmTotal; // Qlm for each iatom
310 std::vector<std::complex<double>>
311 yl; // temp q_lm for each pair of iatom and jatom
312 std::complex<double> dot_product = {0, 0};
313 std::complex<double> qI = {0, 0};
314 std::complex<double> qJ = {0, 0};
315 std::complex<double> Inorm = {0, 0};
316 std::complex<double> Jnorm = {0, 0};
317 std::complex<double> complexDenominator = {0, 0};
318 std::complex<double> complexCij = {0, 0};
319 molSys::Result temp_cij; // Holds the c_ij value
320 double cij_real;
321 int nnumNeighbours; // Number of nearest neighbours for iatom
322
323 QlmTotal.ptq.resize(yCloud->nop);
324
325 // Loop through the neighbour list
326 for (int iatom = 0; iatom < nList.size(); iatom++) {
327 // The atom index is iatom
328 iatomIndex = iatom;
329 iatomID =
330 nList[iatomIndex][0]; // The first element in nList is the ID of iatom
331 nnumNeighbours = nList[iatomIndex].size() - 1;
332 // Now loop over the first four neighbours
333 for (int j = 1; j <= nnumNeighbours; j++) {
334 // Get the ID of jatom saved in the neighbour list
335 jatomID = nList[iatomIndex][j];
336
337 // Get the index of jatom
338 auto it = yCloud->idIndexMap.find(jatomID);
339
340 if (it != yCloud->idIndexMap.end()) {
341 jatomIndex = it->second;
342 } // found jatom
343 else {
344 std::cerr << "Something is wrong with the ID and index map.\n";
345 return *yCloud;
346 } // error handling
347
348 // Get the relative distance now that the index values are known
349 delta = gen::relDist(yCloud, iatomIndex, jatomIndex);
350 double r = std::sqrt(std::pow(delta[0], 2.0) + std::pow(delta[1], 2.0) +
351 std::pow(delta[2], 2.0));
352 angles[1] = acos(delta[2] / r); // theta
353 angles[0] = atan2(delta[0], delta[1]); // phi
354
355 // Now add over all nearest neighbours
356 if (j == 1) {
357 QlmTotal.ptq[iatomIndex].ylm = sph::spheriHarmo(3, angles);
358 // QlmTotal.ptq[iatom].ylm = sph::lookupTableQ3Vec(angles);
359 continue;
360 }
361 // Not for the first jatom
362 yl = sph::spheriHarmo(3, angles);
363 for (int m = 0; m < 2 * l + 1; m++) {
364 QlmTotal.ptq[iatomIndex].ylm[m] += yl[m];
365 // QlmTotal.ptq[iatom].ylm[m] += sph::lookupTableQ3(m, angles);
366 }
367 } // End of loop over 4 nearest neighbours
368
369 // Divide by 4
370 QlmTotal.ptq[iatomIndex].ylm =
371 gen::avgVector(QlmTotal.ptq[iatom].ylm, l, nnumNeighbours);
372 } // End of looping over all iatom
373
374 // ------------------------------------------------
375 // Now that you have all qlm for the particles,
376 // find c_ij
377 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
378 // if(yCloud->pts[iatom].type!=typeO){continue;}
379 // if this is a slice and the particle is not in the slice
380 // then skip
381 if (isSlice) {
382 if (yCloud->pts[iatom].inSlice == false) {
383 continue;
384 }
385 }
386 // The index is what we are looping through
387 iatomIndex = iatom;
388 nnumNeighbours = nList[iatomIndex].size() - 1;
389 yCloud->pts[iatomIndex].c_ij.reserve(nnumNeighbours);
390 // loop over the 4 nearest neighbours
391 for (int j = 1; j <= nnumNeighbours; j++) {
392 // Init to zero
393 dot_product = {0, 0};
394 Inorm = {0, 0};
395 Jnorm = {0, 0};
396 // Get ID of the nearest neighbour
397 jatomID = nList[iatomIndex][j];
398 // Get the index (value) from the ID (key)
399 auto it = yCloud->idIndexMap.find(jatomID);
400
401 if (it != yCloud->idIndexMap.end()) {
402 jatomIndex = it->second;
403 } // found jatom
404 else {
405 std::cerr << "Something is wrong with the ID and index map.\n";
406 return *yCloud;
407 } // error handling
408 // Spherical harmonics
409 for (int m = 0; m < 2 * l + 1; m++) {
410 qI = QlmTotal.ptq[iatomIndex].ylm[m];
411 qJ = QlmTotal.ptq[jatomIndex].ylm[m];
412 dot_product = dot_product + (qI * std::conj(qJ)); // unnormalized
413 Inorm = Inorm + (qI * std::conj(qI));
414 Jnorm = Jnorm + (qJ * std::conj(qJ));
415 } // end loop over m components
416 // Get the denominator
417 complexDenominator = std::sqrt(Inorm * Jnorm);
418 complexCij = dot_product / complexDenominator;
419 // Update c_ij and type
420 cij_real = complexCij.real();
421 temp_cij.c_value = cij_real;
422 if (cij_real < -0.8) {
424 } else if (cij_real > -0.2 && cij_real < -0.05) {
426 } else {
428 }
429 yCloud->pts[iatomIndex].c_ij.push_back(temp_cij);
430 } // end loop over nearest neighbours
431 }
432
433 return *yCloud;
434}
std::vector< YlmAtom > ptq
Definition bop.hpp:170
std::vector< std::complex< double > > spheriHarmo(int orderL, std::array< double, 2 > radialCoord)
Definition bop.cpp:57
std::vector< std::complex< double > > avgVector(std::vector< std::complex< double > > v, int l, int neigh)
Definition generic.hpp:313
std::array< double, 3 > relDist(molSys::PointCloud< molSys::Point< double >, double > *yCloud, int iatom, int jatom)
Definition generic.hpp:200
int nop
Current frame number.
Definition mol_sys.hpp:173
std::vector< S > pts
Definition mol_sys.hpp:171
bond_type classifier
Definition mol_sys.hpp:133
std::unordered_map< int, int > idIndexMap
xlo, ylo, zlo
Definition mol_sys.hpp:176
double c_value
Classifier according to CHILL, CHILL+ etc.
Definition mol_sys.hpp:134
@ eclipsed
The bond is an eclipsed bond.
@ out_of_range
The bond cannot be classified as either staggered or eclipsed.
@ staggered
The bond is a staggered bond, according to the or value.
This is the local orientational bond order parameter , of length .
Definition bop.hpp:169
This contains the bond classifier of enum class type bond_type, and the bond correlation factor.
Definition mol_sys.hpp:132

◆ getCorrelPlus()

molSys::PointCloud< molSys::Point< double >, double > chill::getCorrelPlus ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
bool  isSlice = false 
)

Gets c_ij and then classifies bond types according to the CHILL+ algorithm.

Function for getting the bond order correlations cij (alternatively aij in certain texts) using the CHILL+ algorithm

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]nListRow-ordered neighbour list by atom ID
[in]isSliceThis decides whether there is a slice or not

Definition at line 602 of file bop.cpp.

Code
603 {
604 //
605 int l = 3; // TODO: Don't hard-code this; change later
606 int iatomID; // Atom ID (key) of iatom
607 int iatomIndex; // Index (value) of iatom
608 int jatomID; // Atom ID (key) of jatom
609 int jatomIndex; // Index (value) of nearest neighbour
610 std::array<double, 3> delta;
611 std::array<double, 2> angles;
612 chill::QlmAtom QlmTotal; // Qlm for each iatom
613 std::vector<std::complex<double>>
614 yl; // temp q_lm for each pair of iatom and jatom
615 std::complex<double> dot_product = {0, 0};
616 std::complex<double> qI = {0, 0};
617 std::complex<double> qJ = {0, 0};
618 std::complex<double> Inorm = {0, 0};
619 std::complex<double> Jnorm = {0, 0};
620 std::complex<double> complexDenominator = {0, 0};
621 std::complex<double> complexCij = {0, 0};
622 molSys::Result temp_cij; // Holds the c_ij value
623 double cij_real;
624 int nnumNeighbours; // Number of nearest neighbours for iatom
625
626 QlmTotal.ptq.resize(yCloud->nop);
627
628 // Loop through the neighbour list
629 for (int iatom = 0; iatom < nList.size(); iatom++) {
630 // The atom index is iatom
631 iatomIndex = iatom;
632 iatomID =
633 nList[iatomIndex][0]; // The first element in nList is the ID of iatom
634 nnumNeighbours = nList[iatomIndex].size() - 1;
635 // Now loop over the first four neighbours
636 for (int j = 1; j <= nnumNeighbours; j++) {
637 // Get the ID of jatom saved in the neighbour list
638 jatomID = nList[iatomIndex][j];
639
640 // Get the index of jatom
641 auto it = yCloud->idIndexMap.find(jatomID);
642
643 if (it != yCloud->idIndexMap.end()) {
644 jatomIndex = it->second;
645 } // found jatom
646 else {
647 std::cerr << "Something is wrong with the ID and index map.\n";
648 return *yCloud;
649 } // error handling
650
651 // Get the relative distance now that the index values are known
652 delta = gen::relDist(yCloud, iatomIndex, jatomIndex);
653 double r = std::sqrt(std::pow(delta[0], 2.0) + std::pow(delta[1], 2.0) +
654 std::pow(delta[2], 2.0));
655 angles[1] = acos(delta[2] / r); // theta
656 angles[0] = atan2(delta[0], delta[1]); // phi
657
658 // Now add over all nearest neighbours
659 if (j == 1) {
660 QlmTotal.ptq[iatomIndex].ylm = sph::spheriHarmo(3, angles);
661 // QlmTotal.ptq[iatom].ylm = sph::lookupTableQ3Vec(angles);
662 continue;
663 }
664 // Not for the first jatom
665 yl = sph::spheriHarmo(3, angles);
666 for (int m = 0; m < 2 * l + 1; m++) {
667 QlmTotal.ptq[iatomIndex].ylm[m] += yl[m];
668 // QlmTotal.ptq[iatom].ylm[m] += sph::lookupTableQ3(m, angles);
669 }
670 } // End of loop over 4 nearest neighbours
671
672 // Divide by 4
673 QlmTotal.ptq[iatomIndex].ylm =
674 gen::avgVector(QlmTotal.ptq[iatom].ylm, l, nnumNeighbours);
675 } // End of looping over all iatom
676
677 // ------------------------------------------------
678 // Now that you have all qlm for the particles,
679 // find c_ij
680 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
681 // if(yCloud->pts[iatom].type!=typeO){continue;}
682 // if this is a slice and the particle is not in the slice
683 // then skip
684 if (isSlice) {
685 if (yCloud->pts[iatom].inSlice == false) {
686 continue;
687 }
688 }
689 // The index is what we are looping through
690 iatomIndex = iatom;
691 nnumNeighbours = nList[iatomIndex].size() - 1;
692 yCloud->pts[iatomIndex].c_ij.reserve(nnumNeighbours);
693 // loop over the 4 nearest neighbours
694 for (int j = 1; j <= nnumNeighbours; j++) {
695 // Init to zero
696 dot_product = {0, 0};
697 Inorm = {0, 0};
698 Jnorm = {0, 0};
699 // Get ID of the nearest neighbour
700 jatomID = nList[iatomIndex][j];
701 // Get the index (value) from the ID (key)
702 auto it = yCloud->idIndexMap.find(jatomID);
703
704 if (it != yCloud->idIndexMap.end()) {
705 jatomIndex = it->second;
706 } // found jatom
707 else {
708 std::cerr << "Something is wrong with the ID and index map.\n";
709 return *yCloud;
710 } // error handling
711 // Spherical harmonics
712 for (int m = 0; m < 2 * l + 1; m++) {
713 qI = QlmTotal.ptq[iatomIndex].ylm[m];
714 qJ = QlmTotal.ptq[jatomIndex].ylm[m];
715 dot_product = dot_product + (qI * std::conj(qJ)); // unnormalized
716 Inorm = Inorm + (qI * std::conj(qI));
717 Jnorm = Jnorm + (qJ * std::conj(qJ));
718 } // end loop over m components
719 // Get the denominator
720 complexDenominator = std::sqrt(Inorm * Jnorm);
721 complexCij = dot_product / complexDenominator;
722 // Update c_ij and type
723 cij_real = complexCij.real();
724 temp_cij.c_value = cij_real;
725 if (cij_real <= -0.8) {
727 } else if (cij_real >= -0.35 && cij_real <= 0.25) {
729 } else {
731 }
732 yCloud->pts[iatomIndex].c_ij.push_back(temp_cij);
733 } // end loop over nearest neighbours
734 }
735
736 // ------------------------------------------------
737
738 return *yCloud;
739}

◆ getIceType()

molSys::PointCloud< molSys::Point< double >, double > chill::getIceType ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
std::string  path,
int  firstFrame,
bool  isSlice = false,
std::string  outputFileName = "chill.txt" 
)

Classifies each atom according to the CHILL algorithm.

Function that classifies every particle's molSys::atom_state_type ice type, according to the CHILL algorithm.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]nListRow-ordered neighbour list by atom ID
[in]pathPath to the output directory to which ice types are written out to
[in]firstFrameFirst frame to be analyzed
[in]isSliceThis decides whether there is a slice or not
[in]outputFileNameName of the output file, to which the ice types will be written out.

Definition at line 505 of file bop.cpp.

Code
507 {
508 int ih, ic, water, interIce, unknown, total; // No. of particles of each type
509 ih = ic = water = unknown = interIce = total = 0;
510 int num_staggrd, num_eclipsd, na;
511 molSys::bond_type bondType;
512 int nnumNeighbours; // Number of nearest neighbours
513
514 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
515 // if(yCloud->pts[iatom].type!=typeO){continue;}
516 // if this is a slice and the particle is not in the slice
517 // then skip
518 if (isSlice) {
519 if (yCloud->pts[iatom].inSlice == false) {
520 continue;
521 }
522 }
523 total++; // Update the total number of atoms considered. Change this to
524 // check for slices
525 num_staggrd = num_eclipsd = na =
526 0; // init to zero before loop through neighbours
527
528 nnumNeighbours = nList[iatom].size() - 1;
529 // Loop through the bond cij and get the number of staggered, eclipsed bonds
530 for (int j = 0; j < nnumNeighbours; j++) {
531 bondType = yCloud->pts[iatom].c_ij[j].classifier;
532 if (bondType == molSys::bond_type::eclipsed) {
533 num_eclipsd++;
534 } else if (bondType == molSys::bond_type::staggered) {
535 num_staggrd++;
536 } else {
537 na++;
538 }
539 } // End of loop through neighbours
540
541 // Add more tests later
542 yCloud->pts[iatom].iceType = molSys::atom_state_type::unclassified; // default
543 // Cubic ice
544 // if (num_eclipsd==0 && num_staggrd==4){
545 // yCloud->pts[iatom].iceType = molSys::cubic;
546 // ic++;
547 // }
548 if (num_staggrd >= 4) {
549 yCloud->pts[iatom].iceType = molSys::atom_state_type::cubic;
550 ic++;
551 }
552 // Hexagonal
553 else if (num_eclipsd == 1 && num_staggrd == 3) {
554 yCloud->pts[iatom].iceType = molSys::atom_state_type::hexagonal;
555 ih++;
556 }
557 // Interfacial
558 else if (isInterfacial(yCloud, nList, iatom, num_staggrd, num_eclipsd)) {
559 yCloud->pts[iatom].iceType = molSys::atom_state_type::interfacial;
560 interIce++;
561 } else {
562 yCloud->pts[iatom].iceType = molSys::atom_state_type::water;
563 water++;
564 }
565
566 } // End of loop through every iatom
567
568 // water = total - ic -ih;
569
570 // --------------------
571 // Create the directories if needed
572 sout::makePath(path);
573 std::string outputDirName = path + "bop";
574 sout::makePath(outputDirName);
575 // --------------------
576
577 // Print to file
578 std::ofstream outputFile;
579 outputFile.open(path + "bop/" + outputFileName, std::ios_base::app);
580 // --------------------
581 // Write out the comment line for the first frame
582 if (yCloud->currentFrame == firstFrame) {
583 outputFile << "Frame Ic Ih Interfacial Water Total \n";
584 }
585 // --------------------
586 outputFile << yCloud->currentFrame << " " << ic << " " << ih << " "
587 << interIce << " " << water << " " << total << "\n";
588 outputFile.close();
589
590 return *yCloud;
591}
bool isInterfacial(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, int iatom, int num_staggrd, int num_eclipsd)
Definition bop.cpp:1129
int currentFrame
Collection of points.
Definition mol_sys.hpp:172
@ hexagonal
Ih, or particle type signifying Hexagonal Ice.
@ interfacial
Interfacial ice: ice-like molecules which do not fulfill the strict criteria of the Ic or Ih phases.
@ cubic
Ic, or particle type signifying Cubic Ice.
@ water
Liquid/amorphous phase.
@ unclassified
Not classified into any other category.
int makePath(const std::string &path)

◆ getIceTypeNoPrint()

molSys::PointCloud< molSys::Point< double >, double > chill::getIceTypeNoPrint ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
bool  isSlice = false 
)

Classifies each atom according to the CHILL algorithm without printing.

Function that classifies every particle's molSys::atom_state_type ice type, according to the CHILL algorithm. Does not print out the information.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]isSliceThis decides whether there is a slice or not
[in]nListRow-ordered neighbour list by atom ID

Definition at line 437 of file bop.cpp.

Code
439 {
440 int ih, ic, water, interIce, unknown, total; // No. of particles of each type
441 ih = ic = water = unknown = interIce = total = 0;
442 int num_staggrd, num_eclipsd, na;
443 molSys::bond_type bondType;
444 int nnumNeighbours; // Number of nearest neighbours
445
446 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
447 // if(yCloud->pts[iatom].type!=typeO){continue;}
448 // if this is a slice and the particle is not in the slice
449 // then skip
450 if (isSlice) {
451 if (yCloud->pts[iatom].inSlice == false) {
452 continue;
453 }
454 }
455 total++; // Update the total number of atoms considered. Change this to
456 // check for slices
457 num_staggrd = num_eclipsd = na =
458 0; // init to zero before loop through neighbours
459
460 nnumNeighbours = nList[iatom].size() - 1;
461 // Loop through the bond cij and get the number of staggered, eclipsed bonds
462 for (int j = 0; j < nnumNeighbours; j++) {
463 bondType = yCloud->pts[iatom].c_ij[j].classifier;
464 if (bondType == molSys::bond_type::eclipsed) {
465 num_eclipsd++;
466 } else if (bondType == molSys::bond_type::staggered) {
467 num_staggrd++;
468 } else {
469 na++;
470 }
471 } // End of loop through neighbours
472
473 // Add more tests later
474 yCloud->pts[iatom].iceType = molSys::atom_state_type::unclassified; // default
475 // Cubic ice
476 // if (num_eclipsd==0 && num_staggrd==4){
477 // yCloud->pts[iatom].iceType = molSys::cubic;
478 // ic++;
479 // }
480 if (num_staggrd >= 4) {
481 yCloud->pts[iatom].iceType = molSys::atom_state_type::cubic;
482 ic++;
483 }
484 // Hexagonal
485 else if (num_eclipsd == 1 && num_staggrd == 3) {
486 yCloud->pts[iatom].iceType = molSys::atom_state_type::hexagonal;
487 ih++;
488 }
489 // Interfacial
490 else if (isInterfacial(yCloud, nList, iatom, num_staggrd, num_eclipsd)) {
491 yCloud->pts[iatom].iceType = molSys::atom_state_type::interfacial;
492 interIce++;
493 } else {
494 yCloud->pts[iatom].iceType = molSys::atom_state_type::water;
495 water++;
496 }
497
498 } // End of loop through every iatom
499
500 return *yCloud;
501}

◆ getIceTypePlus()

molSys::PointCloud< molSys::Point< double >, double > chill::getIceTypePlus ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
std::string  path,
int  firstFrame,
bool  isSlice = false,
std::string  outputFileName = "chillPlus.txt" 
)

Classifies each atom according to the CHILL+ algorithm.

Function that classifies the molSys::atom_state_type ice type of each particle, according to the CHILL+ algorithm.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]nListRow-ordered neighbour list by atom ID
[in]pathPath to the output directory to which ice types are written out to
[in]firstFrameThe first frame to be analyzed
[in]isSliceThis decides whether there is a slice or not
[in]outputFileNameName of the output file, to which the ice types will be written out. The default file name is "chillPlus.txt"

Definition at line 755 of file bop.cpp.

Code
758 {
759 int ih, ic, interIce, water, unknown, clath, interClath,
760 total; // No. of particles of each type
761 ih = ic = water = unknown = interIce = total = 0;
762 clath = interClath = 0;
763 int num_staggrd, num_eclipsd, na;
764 molSys::bond_type bondType;
765 int nnumNeighbours; // number of nearest neighbours
766
767 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
768 // if(yCloud->pts[iatom].type!=typeO){continue;}
769 // if this is a slice and the particle is not in the slice
770 // then skip
771 if (isSlice) {
772 if (yCloud->pts[iatom].inSlice == false) {
773 continue;
774 }
775 }
776 total++; // Update the total number of atoms considered. Change this to a
777 // check for slices
778 nnumNeighbours =
779 nList[iatom].size() - 1; // number of nearest neighbours (total -1)
780 num_staggrd = num_eclipsd = na =
781 0; // init to zero before loop through neighbours
782 // Loop through the bond cij and get the number of staggered, eclipsed bonds
783 for (int j = 0; j < nnumNeighbours; j++) {
784 bondType = yCloud->pts[iatom].c_ij[j].classifier;
785 if (bondType == molSys::bond_type::eclipsed) {
786 num_eclipsd++;
787 } else if (bondType == molSys::bond_type::staggered) {
788 num_staggrd++;
789 } else {
790 na++;
791 }
792 } // End of loop through neighbours
793
794 // Add more tests later
795 yCloud->pts[iatom].iceType = molSys::atom_state_type::unclassified; // default
796 if (nnumNeighbours == 4) {
797 // Cubic ice
798 if (num_eclipsd == 0 && num_staggrd == 4) {
799 yCloud->pts[iatom].iceType = molSys::atom_state_type::cubic;
800 ic++;
801 }
802 // Hexagonal
803 else if (num_eclipsd == 1 && num_staggrd == 3) {
804 yCloud->pts[iatom].iceType = molSys::atom_state_type::hexagonal;
805 ih++;
806 }
807 // Interfacial
808 else if (isInterfacial(yCloud, nList, iatom, num_staggrd, num_eclipsd)) {
809 yCloud->pts[iatom].iceType = molSys::atom_state_type::interfacial;
810 interIce++;
811 }
812 // Clathrate
813 else if (num_eclipsd == 4 && num_staggrd == 0) {
814 yCloud->pts[iatom].iceType = molSys::atom_state_type::clathrate;
815 clath++;
816 }
817 // Interfacial clathrate
818 else if (num_eclipsd == 3) {
819 yCloud->pts[iatom].iceType = molSys::atom_state_type::interClathrate;
820 interClath++;
821 }
822 // Water
823 else {
824 yCloud->pts[iatom].iceType = molSys::atom_state_type::water;
825 water++;
826 }
827 } else {
828 yCloud->pts[iatom].iceType = molSys::atom_state_type::water;
829 water++;
830 }
831
832 } // End of loop through every iatom
833
834 // water = total - ic -ih;
835
836 // --------------------
837 // Create the directories if needed
838 sout::makePath(path);
839 std::string outputDirName = path + "bop";
840 sout::makePath(outputDirName);
841 // --------------------
842
843 std::ofstream outputFile;
844 outputFile.open(path + "bop/" + outputFileName, std::ios_base::app);
845 // --------------------
846 // Comment line for the first line
847 if (yCloud->currentFrame == firstFrame) {
848 outputFile << "Frame Ic Ih Interfacial Clath InterClath Water Total\n";
849 }
850 // --------------------
851 outputFile << yCloud->currentFrame << " " << ic << " " << ih << " "
852 << interIce << " " << clath << " " << interClath << " " << water
853 << " " << total << "\n";
854 outputFile.close();
855
856 return *yCloud;
857}
@ interClathrate
Interfacial clathrate ice phase.
@ clathrate
Clathrate ice phase.

◆ getq6()

std::vector< double > chill::getq6 ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
bool  isSlice = false 
)

q6 can distinguish between water and ice. Use this for the largest ice cluster

Function for getting the averaged q6 parameter.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]nListRow-ordered neighbour list by atom ID
[in]isSliceThis decides whether there is a slice or not
Returns
a double vector of the averaged q6 values.

Definition at line 868 of file bop.cpp.

Code
869 {
870 //
871 int l = 6; // We're using q6 here
872 int jatomID; // Atom ID of the nearest neighbour
873 int jatomIndex; // Index of nearest neighbour
874 std::array<double, 3> delta;
875 std::array<double, 2> angles;
876 chill::QlmAtom QlmTotal; // Qlm for each iatom
877 std::vector<std::complex<double>>
878 yl; // temp q_lm for each pair of iatom and jatom
879 std::complex<double> dot_product = {0, 0};
880 std::complex<double> qI = {0, 0};
881 std::complex<double> qJ = {0, 0};
882 std::complex<double> Inorm = {0, 0};
883 std::complex<double> Jnorm = {0, 0};
884 std::complex<double> complexDenominator = {0, 0};
885 std::complex<double> complexCij = {0, 0};
886 double cij_real;
887 int nnumNeighbours;
888 std::vector<double> resultQ; // Vector with averaged q values
889 double q_value = 0.0; // Averaged q value per neighbour pair
890
891 QlmTotal.ptq.resize(yCloud->nop);
892 resultQ.resize(yCloud->nop);
893
894 // Loop through every index in yCloud
895 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
896 // if(yCloud->pts[iatom].type!=typeO){continue;}
897
898 nnumNeighbours = nList[iatom].size() - 1; // One less than the actual length
899 // Now loop over the first four neighbours
900 for (int j = 1; j <= nnumNeighbours; j++) {
901 // Get the atom ID
902 jatomID = nList[iatom][j]; // Atom ID (key)
903
904 // Get the atom index (value) from the atom ID (key)
905 auto it = yCloud->idIndexMap.find(jatomID);
906
907 if (it != yCloud->idIndexMap.end()) {
908 jatomIndex = it->second;
909 } else {
910 std::cerr << "Your map must be wrong.\n";
911 return resultQ; // return with error
912 }
913
914 // Get the relative distances
915 delta = gen::relDist(yCloud, iatom, jatomIndex);
916
917 // angles = sph::radialCoord(delta);
918 double r = std::sqrt(std::pow(delta[0], 2.0) + std::pow(delta[1], 2.0) +
919 std::pow(delta[2], 2.0));
920 angles[1] = acos(delta[2] / r); // theta
921 angles[0] = atan2(delta[0], delta[1]); // phi
922
923 // Now add over all nearest neighbours
924 if (j == 1) {
925 // QlmTotal.ptq[iatom].ylm = sph::spheriHarmo(l, angles);
926 QlmTotal.ptq[iatom].ylm = sph::lookupTableQ6Vec(angles);
927 continue;
928 }
929 // Not for the first jatom
930 yl = sph::spheriHarmo(l, angles);
931 for (int m = 0; m < 2 * l + 1; m++) {
932 QlmTotal.ptq[iatom].ylm[m] += yl[m];
933 // QlmTotal.ptq[iatom].ylm[m] += sph::lookupTableQ6(m, angles);
934 }
935 } // End of loop over 4 nearest neighbours
936
937 // Divide by 4
938 QlmTotal.ptq[iatom].ylm =
939 gen::avgVector(QlmTotal.ptq[iatom].ylm, l, nnumNeighbours);
940 } // End of looping over all iatom
941
942 // ------------------------------------------------
943 // Now that you have all qlm for the particles,
944 // find c_ij
945 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
946 // if(yCloud->pts[iatom].type!=typeO){continue;}
947 // if this is a slice and the particle is not in the slice
948 // then skip TODO: UNCOMMENT
949 // if(isSlice){
950 // if(yCloud->pts[iatom].inSlice==false){continue;}
951 // }
952
953 nnumNeighbours = nList[iatom].size() - 1; // Number of nearest neighbours
954 q_value = 0.0; // initialize to zero
955 // yCloud->pts[iatom].c_ij.reserve(nnumNeighbours);
956 // loop over the 4 nearest neighbours
957 for (int j = 1; j <= nnumNeighbours; j++) {
958 // Init to zero
959 dot_product = {0, 0};
960 Inorm = {0, 0};
961 Jnorm = {0, 0};
962 // Get index of the nearest neighbour!
963 jatomID = nList[iatom][j]; // Atom ID (key)
964
965 // Get the index jatomIndex
966 auto it = yCloud->idIndexMap.find(jatomID);
967
968 if (it != yCloud->idIndexMap.end()) {
969 jatomIndex = it->second;
970 } // end of getting the index of jatom
971
972 for (int m = 0; m < 2 * l + 1; m++) {
973 qI = QlmTotal.ptq[iatom].ylm[m];
974 qJ = QlmTotal.ptq[jatomIndex].ylm[m];
975 dot_product = dot_product + (qI * std::conj(qJ)); // unnormalized
976 Inorm = Inorm + (qI * std::conj(qI));
977 Jnorm = Jnorm + (qJ * std::conj(qJ));
978 } // end loop over m components
979 // Get the denominator
980 complexDenominator = std::sqrt(Inorm * Jnorm);
981 complexCij = dot_product / complexDenominator;
982 // Update c_ij and type
983 cij_real = complexCij.real();
984
985 q_value += cij_real;
986
987 } // end loop over nearest neighbours
988
989 // Average q_value over all nearest neighbours
990 q_value /= (double)nnumNeighbours;
991
992 resultQ[iatom] = q_value; // Update the vector of averaged q6
993 }
994
995 // ------------------------------------------------
996
997 return resultQ;
998}
std::vector< std::complex< double > > lookupTableQ6Vec(std::array< double, 2 > angles)
Lookup table for Q6.
Definition bop.cpp:189

◆ isInterfacial()

bool chill::isInterfacial ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
int  iatom,
int  num_staggrd,
int  num_eclipsd 
)

Checks if a given iatom is interfacial ice or not, according to the CHILL algorithm

Function that checks if the particle with the given atom index is interfacial or not.

Parameters
[in]yCloudThe input molSys::PointCloud
[in]nListRow-ordered neighbour list by atom ID
[in]iatomThe vector index of the current particle
[in]num_staggrdThe number of staggered bonds that the current particle participates in
[in]num_eclipsdThe number of eclipsed bonds that the current particle participates in
Returns
a bool; true if the particle is interfacial and otherwise false

Definition at line 1129 of file bop.cpp.

Code
1132 {
1133 int nnumNeighbours =
1134 nList[iatom].size() - 1; // number of nearest neighbours of iatom
1135 int neighStaggered =
1136 0; // number of staggered bonds in the neighbours of iatom
1137 int jatomID; // ID of the nearest neighbour
1138 int jatomIndex; // Index (value) of nearest neighbour
1139
1140 // INTERFACIAL
1141 // Condition 1 : only two staggered bonds and at least
1142 // one neighbor with more than two staggered bonds
1143 if (num_staggrd == 2) {
1144 // Loop over the nearest neighbours
1145 for (int j = 1; j <= nnumNeighbours; j++) {
1146 // Get index of the nearest neighbour
1147 jatomID = nList[iatom][j];
1148 // Get the index (value) from the ID (key)
1149 auto it = yCloud->idIndexMap.find(jatomID);
1150
1151 if (it != yCloud->idIndexMap.end()) {
1152 jatomIndex = it->second;
1153 } else {
1154 std::cerr << "Something is gravely wrong with your map.\n";
1155 return false;
1156 }
1157 //
1158 neighStaggered = chill::numStaggered(yCloud, nList, jatomIndex);
1159 if (neighStaggered > 2) {
1160 return true;
1161 }
1162 } // End loop over nearest neighbours
1163 } // end condition 1
1164 // Condition 2 : three staggered bonds, no eclipsed bond,
1165 // and at least one neighbor with two staggered bonds
1166 if (num_staggrd == 3 && num_eclipsd == 0) {
1167 // Loop over the nearest neighbours
1168 for (int j = 1; j <= nnumNeighbours; j++) {
1169 // Get index of the nearest neighbour
1170 // ID of the nearest neighbour
1171 jatomID = nList[iatom][j];
1172 // Get the index (value) from the ID (key)
1173 auto it = yCloud->idIndexMap.find(jatomID);
1174
1175 if (it != yCloud->idIndexMap.end()) {
1176 jatomIndex = it->second;
1177 } else {
1178 std::cerr << "Something is gravely wrong with your map.\n";
1179 return false;
1180 }
1181 //
1182 neighStaggered = chill::numStaggered(yCloud, nList, jatomIndex);
1183 if (neighStaggered == 2) {
1184 return true;
1185 }
1186 }
1187 }
1188 // not interfacial
1189 return false;
1190}
int numStaggered(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, int jatom)
Finds the number of staggered bonds for a given atom of index jatom.
Definition bop.cpp:1201

◆ lookupTableQ3()

std::complex< double > sph::lookupTableQ3 ( int  m,
std::array< double, 2 >  angles 
)

Lookup table for Q3 (m=0 to m=6)

Look-up hard-coded values for Q3

It is recommended to use the Boost version of this function, sph::spheriHarmo, instead.

Parameters
[in]mAn int such that 3<=m<=3
[in]anglesThe azimuth and polar angles for a particular particle
Returns
a complex vector, of length 7, calculated using hard-coded values

Definition at line 133 of file bop.cpp.

Code
133 {
134 std::complex<double> result(0.0, 0.0);
135 const double pi = std::acos(-1);
136 const std::complex<double> i(0.0, 1.0);
137 double constant;
138 double theta = angles[1];
139 double phi = angles[0];
140
141 if (m == 0) {
142 constant = 0.125 * std::sqrt(35 / pi);
143 result = constant * std::exp(-3.0 * i * phi) * std::pow(sin(theta), 3.0);
144 return result;
145 } else if (m == 1) {
146 constant = 0.25 * std::sqrt(105 / (2 * pi));
147 result = constant * std::exp(-2.0 * i * phi) * std::pow(sin(theta), 2.0) *
148 cos(theta);
149 return result;
150 } else if (m == 2) {
151 constant = 0.125 * std::sqrt(21 / pi);
152 result = constant * std::exp(-1.0 * i * phi) * sin(theta) *
153 (5.0 * std::pow(cos(theta), 2.0) - 1);
154 return result;
155 } else if (m == 3) {
156 constant = 0.25 * std::sqrt(7 / pi);
157 result = constant * (5.0 * std::pow(cos(theta), 3.0) - 3.0 * cos(theta));
158 return result;
159 } else if (m == 4) {
160 constant = -0.125 * std::sqrt(21 / pi);
161 result = constant * std::exp(i * phi) * sin(theta) *
162 (5.0 * std::pow(cos(theta), 2.0) - 1);
163 return result;
164 } else if (m == 5) {
165 constant = 0.25 * std::sqrt(105 / (2 * pi));
166 result = constant * std::exp(2.0 * i * phi) * std::pow(sin(theta), 2.0) *
167 cos(theta);
168 return result;
169 } else if (m == 6) {
170 constant = -0.125 * std::sqrt(35 / pi);
171 result = constant * std::exp(3.0 * i * phi) * std::pow(sin(theta), 3.0);
172 return result;
173 }
174
175 return result;
176}

◆ lookupTableQ3Vec()

std::vector< std::complex< double > > sph::lookupTableQ3Vec ( std::array< double, 2 >  angles)

Lookup table for Q3.

Calculates Q3 using hard-coded look-up values.

Deprecated:
It is recommended to use the Boost version of this function, sph::spheriHarmo, instead.
Parameters
[in]anglesThe azimuth and polar angles of a particular point
Returns
a complex vector, of length 7, calculated using spherical harmonics

Definition at line 107 of file bop.cpp.

Code
107 {
108 // For keeping track of the index of the output vector
109 std::vector<std::complex<double>> result;
110 double theta = angles[1];
111 double phi = angles[0];
112
113 result.resize(7);
114
115 for (int m = 0; m < 7; m++) {
116 result[m] = sph::lookupTableQ3(m, angles);
117 }
118
119 return result;
120}
std::complex< double > lookupTableQ3(int m, std::array< double, 2 > angles)
Lookup table for Q3 (m=0 to m=6)
Definition bop.cpp:133

◆ lookupTableQ6()

std::complex< double > sph::lookupTableQ6 ( int  m,
std::array< double, 2 >  angles 
)

Lookup table for Q6 (m=0 to m=12)

Hard-coded calculations for determining Q6.

It is recommended to use the general Boost version of this function, sph::spheriHarmo, instead.

Parameters
[in]mAn int such that 6<=m<=6
[in]anglesThe azimuth and polar angles for a particular particle
Returns
a complex vector, of length 13, calculated using hard-coded values

Definition at line 215 of file bop.cpp.

Code
215 {
216 std::complex<double> result(0.0, 0.0);
217 const double pi = std::acos(-1);
218 const std::complex<double> i(0.0, 1.0);
219 double constant;
220 double theta = angles[1];
221 double phi = angles[0];
222
223 if (m == 0) {
224 constant = 0.015625 * std::sqrt(3003 / pi);
225 result = constant * std::exp(-6.0 * i * phi) * std::pow(sin(theta), 6.0);
226 return result;
227 } else if (m == 1) {
228 constant = 0.09375 * std::sqrt(1001 / pi);
229 result = constant * std::exp(-5.0 * i * phi) * std::pow(sin(theta), 5.0) *
230 cos(theta);
231 return result;
232 } else if (m == 2) {
233 constant = 0.09375 * std::sqrt(91 / (2 * pi));
234 result = constant * std::exp(-4.0 * i * phi) * std::pow(sin(theta), 4.0) *
235 (11.0 * std::pow(cos(theta), 2.0) - 1);
236 return result;
237 } else if (m == 3) {
238 constant = 0.03125 * std::sqrt(1365 / pi);
239 result = constant * std::exp(-3.0 * i * phi) * std::pow(sin(theta), 3.0) *
240 (11.0 * std::pow(cos(theta), 3.0) - 3.0 * cos(theta));
241 return result;
242 } else if (m == 4) {
243 constant = 0.015625 * std::sqrt(1365 / pi);
244 result = constant * std::exp(-2.0 * i * phi) * std::pow(sin(theta), 2.0) *
245 (33.0 * std::pow(cos(theta), 4.0) -
246 18.0 * std::pow(cos(theta), 2.0) + 1.0);
247 return result;
248 } else if (m == 5) {
249 constant = 0.0625 * std::sqrt(273 / (2 * pi));
250 result = constant * std::exp(-1.0 * i * phi) * sin(theta) *
251 (33.0 * std::pow(cos(theta), 5.0) -
252 30.0 * std::pow(cos(theta), 3.0) + 5.0 * cos(theta));
253 return result;
254 } else if (m == 6) {
255 constant = 0.03125 * std::sqrt(13 / pi);
256 result = constant * (231.0 * std::pow(cos(theta), 6.0) -
257 315.0 * std::pow(cos(theta), 4.0) +
258 105.0 * std::pow(cos(theta), 2.0) - 5.0);
259 return result;
260 } else if (m == 7) {
261 constant = -0.0625 * std::sqrt(273 / (2 * pi));
262 result = constant * std::exp(i * phi) * sin(theta) *
263 (33.0 * std::pow(cos(theta), 5.0) -
264 30.0 * std::pow(cos(theta), 3.0) + 5.0 * cos(theta));
265 return result;
266 } else if (m == 8) {
267 constant = 0.015625 * std::sqrt(1365 / pi);
268 result = constant * std::exp(2.0 * i * phi) * std::pow(sin(theta), 2.0) *
269 (33.0 * std::pow(cos(theta), 4.0) -
270 18.0 * std::pow(cos(theta), 2.0) + 1.0);
271 return result;
272 } else if (m == 9) {
273 constant = -0.03125 * std::sqrt(1365 / pi);
274 result = constant * std::exp(3.0 * i * phi) * std::pow(sin(theta), 3.0) *
275 (11.0 * std::pow(cos(theta), 3.0) - 3.0 * cos(theta));
276 return result;
277 } else if (m == 10) {
278 constant = 0.09375 * std::sqrt(91 / (2 * pi));
279 result = constant * std::exp(4.0 * i * phi) * std::pow(sin(theta), 4.0) *
280 (11.0 * std::pow(cos(theta), 2.0) - 1);
281 return result;
282 } else if (m == 11) {
283 constant = -0.09375 * std::sqrt(1001 / pi);
284 result = constant * std::exp(5.0 * i * phi) * std::pow(sin(theta), 5.0) *
285 cos(theta);
286 return result;
287 } else if (m == 12) {
288 constant = 0.015625 * std::sqrt(3003 / pi);
289 result = constant * std::exp(6.0 * i * phi) * std::pow(sin(theta), 6.0);
290 return result;
291 }
292
293 return result;
294}

◆ lookupTableQ6Vec()

std::vector< std::complex< double > > sph::lookupTableQ6Vec ( std::array< double, 2 >  angles)

Lookup table for Q6.

Calculates Q6 using hard-coded values.

It is recommended to use the Boost version of this function, sph::spheriHarmo, instead.

Parameters
[in]anglesThe azimuth and polar angles for a particular particle
Returns
a complex vector, of length 13, calculated using hard-coded values

Definition at line 189 of file bop.cpp.

Code
189 {
190 // For keeping track of the index of the output vector
191 std::vector<std::complex<double>> result;
192 double theta = angles[1];
193 double phi = angles[0];
194
195 result.resize(13);
196
197 for (int m = 0; m < 13; m++) {
198 result[m] = sph::lookupTableQ6(m, angles);
199 }
200
201 return result;
202}
std::complex< double > lookupTableQ6(int m, std::array< double, 2 > angles)
Lookup table for Q6 (m=0 to m=12)
Definition bop.cpp:215

◆ numStaggered()

int chill::numStaggered ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< std::vector< int > >  nList,
int  jatom 
)

Finds the number of staggered bonds for a given atom of index jatom.

Calculates the number of staggered bonds of an atom with the given index.

Parameters
[in]yCloudThe input molSys::PointCloud
[in]nListRow-ordered neighbour list by atom ID
[in]jatomThe vector index of the current particle
Returns
an int value, holding the number of staggered bonds of the given particle

Definition at line 1201 of file bop.cpp.

Code
1203 {
1204 int num_staggrd = 0; // Number of staggered bonds
1205 molSys::bond_type bondType; // Bond type
1206 int num_bonds; // No. of bonds of the jatom
1207 int nnumNeighbours =
1208 nList[jatom].size() - 1; // No. of nearest neighbours of index jatom
1209
1210 // Loop over all bonds
1211 for (int i = 0; i < nnumNeighbours; i++) {
1212 bondType = yCloud->pts[jatom].c_ij[i].classifier;
1213 // If the bond is staggered increment the number of staggered bonds
1214 if (bondType == molSys::bond_type::staggered) {
1215 num_staggrd++;
1216 }
1217 } // end of loop over c_ij
1218
1219 return num_staggrd;
1220}

◆ printIceType()

int chill::printIceType ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::string  path,
int  firstFrame,
bool  isSlice = false,
std::string  outputFileName = "superChill.txt" 
)

Prints out the iceType for a particular frame onto the terminal.

Prints out the molSys::atom_state_type per-particle ice type, for a particular frame, to a file.

Parameters
[in]yCloudThe input molSys::PointCloud for the current frame
[in]pathPath to the output directory to which ice types are written out to
[in]firstFrameFirst frame to be analyzed
[in]isSliceDetermines whether there is a slice or not
[in]outputFileNameFile name of the output file, to which the per-particle ice types will be written out. The default file name is "superChill.txt"

Definition at line 1060 of file bop.cpp.

Code
1062 {
1063 int ih, ic, interIce, water, unknown, clath, interClath,
1064 total; // No. of particles of each type
1065 ih = ic = water = unknown = interIce = total = 0;
1066 clath = interClath = 0;
1067
1068 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
1069 // if(yCloud->pts[iatom].type != typeO){continue;}
1070 // check for slice
1071 if (isSlice) {
1072 if (yCloud->pts[iatom].inSlice == false) {
1073 continue;
1074 }
1075 }
1076 total++;
1077 if (yCloud->pts[iatom].iceType == molSys::atom_state_type::cubic) {
1078 ic++;
1079 } else if (yCloud->pts[iatom].iceType == molSys::atom_state_type::hexagonal) {
1080 ih++;
1081 } else if (yCloud->pts[iatom].iceType == molSys::atom_state_type::water) {
1082 water++;
1083 } else if (yCloud->pts[iatom].iceType == molSys::atom_state_type::interfacial) {
1084 interIce++;
1085 } else if (yCloud->pts[iatom].iceType == molSys::atom_state_type::clathrate) {
1086 clath++;
1087 } else if (yCloud->pts[iatom].iceType == molSys::atom_state_type::interClathrate) {
1088 interClath++;
1089 } else {
1090 unknown++;
1091 }
1092 }
1093
1094 // --------------------
1095 // Create the directories if needed
1096 sout::makePath(path);
1097 std::string outputDirName = path + "bop";
1098 sout::makePath(outputDirName);
1099 // --------------------
1100 // Print to file
1101 std::ofstream outputFile;
1102 outputFile.open(path + "bop/" + outputFileName, std::ios_base::app);
1103 // --------------------
1104 // Write out the comment line
1105 if (yCloud->currentFrame == firstFrame) {
1106 outputFile << "Frame Ic Ih Interfacial Clath InterClath Water Total\n";
1107 }
1108 // --------------------
1109 outputFile << yCloud->currentFrame << " " << ic << " " << ih << " "
1110 << interIce << " " << clath << " " << interClath << " " << water
1111 << " " << total << "\n";
1112 outputFile.close();
1113
1114 return 0;
1115}

◆ radialCoord()

std::array< double, 2 > sph::radialCoord ( std::array< double, 3 >  cartCoord)

Function for the azimuth and polar angles, given the Cartesian coordinates This function uses the Boost libraries.

Parameters
[in]cartCoordThe Cartesian coordinates of a particular point
Returns
a double array, holding the azimuth and polar angles

Definition at line 81 of file bop.cpp.

Code
81 {
82 // The output
83 std::array<double, 2> result;
84 // Point Definitions
85 bg::model::point<long double, 3, bg::cs::cartesian> cartesianPoint;
86 bg::model::point<long double, 3, bg::cs::spherical<bg::radian>> azuPoint;
87 // Set Value (TODO: Recurse this)
88 bg::set<0>(cartesianPoint, cartCoord[0]);
89 bg::set<1>(cartesianPoint, cartCoord[1]);
90 bg::set<2>(cartesianPoint, cartCoord[2]);
91 // Transform
92 bg::transform(cartesianPoint, azuPoint);
93 result[0] = bg::get<0>(azuPoint);
94 result[1] = bg::get<1>(azuPoint);
95 return result;
96}

◆ reclassifyWater()

molSys::PointCloud< molSys::Point< double >, double > chill::reclassifyWater ( molSys::PointCloud< molSys::Point< double >, double > *  yCloud,
std::vector< double > *  q6 
)

'Test' condition for classifying hexagonal ice using averaged q6 and q3 Checks water According to https://!pubs.rsc.org/en/content/articlehtml/2011/cp/c1cp22167a Gets c_ij and then classifies bond types according to the CHILL+ algorithm

Reclassifies atoms which may have been mis-classified as water using the averaged q6 and q3 parameters. This function can be called after both averaged q6 and bond order correlation function cij have been calculated as described here.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]q6Vector containing the previously calculated averaged q6 values (using chill::getq6)

Definition at line 1011 of file bop.cpp.

Code
1013 {
1014 // If averaged q6 > 0.5, then consider it to be ice
1015 // If averaged q3 < -0.75 then it is ih or ic. If q3 < -0.85 then it is cubic,
1016 // otherwise it is hexagonal
1017 double avgQ3 = 0.0;
1018 int nnumNeighbours;
1019
1020 for (int iatom = 0; iatom < yCloud->nop; iatom++) {
1021 // Check if it has been classified as water
1022 if (yCloud->pts[iatom].iceType == molSys::atom_state_type::water) {
1023 if ((*q6)[iatom] > 0.5) {
1024 avgQ3 = 0.0; // init to zero
1025 // Loop through all c_ij
1026 nnumNeighbours = yCloud->pts[iatom].c_ij.size();
1027 for (int j = 0; j < nnumNeighbours; j++) {
1028 avgQ3 += yCloud->pts[iatom].c_ij[j].c_value;
1029 }
1030 avgQ3 /= (double)nnumNeighbours;
1031
1032 // If averaged q3 < -0.75, then reclassify
1033 if (avgQ3 <= -0.75) {
1034 if (avgQ3 < -0.85) {
1035 yCloud->pts[iatom].iceType = molSys::atom_state_type::reCubic;
1036 } // molSys::cubic
1037 else {
1038 yCloud->pts[iatom].iceType = molSys::atom_state_type::reHex;
1039 } // molSys::hexagonal
1040 } // end of reclassification
1041 } // check for solid atom!
1042 } // end of check for water
1043 } // End loop through every iatom
1044
1045 return *yCloud;
1046}
@ reCubic
Reclassified as cubic ice, according to the order parameter.
@ reHex
Reclassified as hexagonal ice, according to the order parameter.

◆ spheriHarmo()

std::vector< std::complex< double > > sph::spheriHarmo ( int  orderL,
std::array< double, 2 >  radialCoord 
)

Function for calculating spherical harmonics, that works for a general l.

This function uses the Boost libraries.

Parameters
[in]orderLThe int value of l
[in]radialCoordArray containing the polar and azimuth angles
Returns
a complex vector, holding the complex spherical harmonics values, of length 2l+1

Definition at line 57 of file bop.cpp.

Code
57 {
58 // For keeping track of the index of the output vector
59 std::vector<std::complex<double>> result;
60 std::complex<double> b; // Boost temp value
61 int m;
62
63 result.resize(2 * orderL + 1);
64
65 for (int k = 0; k < 2 * orderL + 1; k++) {
66 double theta = radialCoord[1];
67 double phi = radialCoord[0];
68 m = k - orderL;
69 result[k] = boost::math::spherical_harmonic(orderL, m, theta, phi);
70 }
71
72 return result;
73}

Variable Documentation

◆ ptq

std::vector<YlmAtom> chill::QlmAtom::ptq

Definition at line 170 of file bop.hpp.

◆ ylm

std::vector<std::complex<double> > chill::YlmAtom::ylm

Definition at line 149 of file bop.hpp.