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

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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
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. More...
 
std::complex< double > sph::lookupTableQ3 (int m, std::array< double, 2 > angles)
 Lookup table for Q3 (m=0 to m=6) More...
 
std::vector< std::complex< double > > sph::lookupTableQ6Vec (std::array< double, 2 > angles)
 Lookup table for Q6. More...
 
std::complex< double > sph::lookupTableQ6 (int m, std::array< double, 2 > angles)
 Lookup table for Q6 (m=0 to m=12) More...
 

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.

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
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 }
T acos(T... args)
T atan2(T... args)
T end(T... args)
T find(T... args)
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.
T pow(T... args)
T push_back(T... args)
T real(T... args)
T reserve(T... args)
T size(T... args)
T sqrt(T... args)
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
Code

◆ 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.

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
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 }
Code

◆ 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.

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 }
T close(T... args)
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
bond_type
Definition: mol_sys.hpp:75
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)
T open(T... args)
Code

◆ 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.

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 }
Code

◆ 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.

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.
Code

◆ 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.

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
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
T resize(T... args)
Code

◆ 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.

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
Code

◆ 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.

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 }
T cos(T... args)
T exp(T... args)
const double pi
Definition: generic.hpp:54
T sin(T... args)
Code

◆ 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.

107  {
108  // For keeping track of the index of the output vector
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
Code

◆ 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.

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 }
Code

◆ 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.

189  {
190  // For keeping track of the index of the output vector
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
Code

◆ 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.

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 }
Code

◆ 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.

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 }
Code

◆ 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.

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 }
Code

◆ 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.

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.
Code

◆ 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.

57  {
58  // For keeping track of the index of the output vector
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 }
std::array< double, 2 > radialCoord(std::array< double, 3 > cartCoord)
Definition: bop.cpp:81
Code

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.