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 \(q_{lm}\), 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 \(c_{ij}\) (or \(a_{ij}\) 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

◆ 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 \(c_{ij}\) (alternatively \(a_{ij}\) 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 }

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

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

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

◆ 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 \(q_6\) 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 \(q_6\) 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)

◆ 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

◆ 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 \(Q_3\)

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)

◆ lookupTableQ3Vec()

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

Lookup table for Q3.

Calculates \(Q_3\) 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

◆ 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 \(Q_6\).

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 }

◆ lookupTableQ6Vec()

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

Lookup table for Q6.

Calculates \(Q_6\) 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

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

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

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

◆ 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 \(q_6\) and \(q_3\) parameters. This function can be called after both averaged \(q_6\) and bond order correlation function \(c_{ij}\) have been calculated as described here.

Parameters
[in,out]yCloudThe output molSys::PointCloud
[in]q6Vector containing the previously calculated averaged \(q_6\) 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.

◆ 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

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.