SSAGES  0.1
A MetaDynamics Package
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Groups Pages
GyrationTensorCV.h
1 
21 #pragma once
22 
23 #include "CVs/CollectiveVariable.h"
24 #include "Validator/ObjectRequirement.h"
25 #include "Drivers/DriverException.h"
26 #include "Snapshot.h"
27 #include "schema.h"
28 #include <Eigen/Eigenvalues>
29 
30 namespace SSAGES
31 {
32  enum GyrationTensor
33  {
34  Rg = 0, // Radius of gyration (squared)
35  principal1 = 1, // First (largest) principal moment
36  principal2 = 2, // Second (middle) principal moment
37  principal3 = 3, // Third (smallest) principal moment
38  asphericity = 4, // Asphericity
39  acylindricity = 5, // Acylindricity
40  shapeaniso = 6 // Relative shape anisotropy
41  };
42 
44 
52  {
53  private:
55  GyrationTensor component_;
56 
57  public:
59 
66  GyrationTensorCV(const Label& atomids, GyrationTensor component) :
67  atomids_(atomids), component_(component)
68  {
69  }
70 
72 
75  void Initialize(const Snapshot& snapshot) override
76  {
77  using std::to_string;
78 
79  auto n = atomids_.size();
80 
81  // Make sure atom ID's are on at least one processor.
82  std::vector<int> found(n, 0);
83  for(size_t i = 0; i < n; ++i)
84  {
85  if(snapshot.GetLocalIndex(atomids_[i]) != -1)
86  found[i] = 1;
87  }
88 
89  MPI_Allreduce(MPI_IN_PLACE, found.data(), n, MPI_INT, MPI_SUM, snapshot.GetCommunicator());
90  unsigned ntot = std::accumulate(found.begin(), found.end(), 0, std::plus<int>());
91  if(ntot != n)
92  throw BuildException({
93  "GyrationTensorCV: Expected to find " +
94  to_string(n) +
95  " atoms, but only found " +
96  to_string(ntot) + "."
97  });
98  }
99 
101 
104  void Evaluate(const Snapshot& snapshot) override
105  {
106  using namespace Eigen;
107 
108  // Get local atom indices and compute COM.
109  std::vector<int> idx;
110  snapshot.GetLocalIndices(atomids_, &idx);
111 
112  // Get data from snapshot.
113  auto n = snapshot.GetNumAtoms();
114  const auto& masses = snapshot.GetMasses();
115  const auto& pos = snapshot.GetPositions();
116 
117  // Initialize gradient.
118  std::fill(grad_.begin(), grad_.end(), Vector3{0,0,0});
119  grad_.resize(n, Vector3{0,0,0});
120 
121  // Compute total and center of mass.
122  auto masstot = snapshot.TotalMass(idx);
123  Vector3 com = snapshot.CenterOfMass(idx, masstot);
124 
125  // Gyration tensor and temporary vector to store positions in inertial frame.
126  Matrix3 S = Matrix3::Zero();
127  std::vector<Vector3> ris;
128  ris.reserve(idx.size());
129  for(auto& i : idx)
130  {
131  ris.emplace_back(snapshot.ApplyMinimumImage(pos[i] - com));
132  S.noalias() += masses[i]*ris.back()*ris.back().transpose();
133  }
134 
135  // Reduce gyration tensor across processors and normalize.
136  MPI_Allreduce(MPI_IN_PLACE, S.data(), S.size(), MPI_DOUBLE, MPI_SUM, snapshot.GetCommunicator());
137  S /= masstot;
138 
139  // Perform EVD. The columns are the eigenvectors.
140  // SelfAdjoint solver sorts in ascending order.
141  SelfAdjointEigenSolver<Matrix3> solver;
142  solver.computeDirect(S);
143  const auto& eigvals = solver.eigenvalues();
144  const auto& eigvecs = solver.eigenvectors();
145 
146  // Assign variables for clarity. l1 is largest.
147  auto l1 = eigvals[2],
148  l2 = eigvals[1],
149  l3 = eigvals[0];
150  const auto& n1 = eigvecs.col(2),
151  n2 = eigvecs.col(1),
152  n3 = eigvecs.col(0);
153 
154  // Compute gradient.
155  size_t j = 0;
156  val_ = 0;
157  for(auto& i : idx)
158  {
159  // Compute derivative of each eigenvalue and use combos in components.
160  auto dl1 = 2.*masses[i]/masstot*(1.-masses[i]/masstot)*ris[j].dot(n1)*n1;
161  auto dl2 = 2.*masses[i]/masstot*(1.-masses[i]/masstot)*ris[j].dot(n2)*n2;
162  auto dl3 = 2.*masses[i]/masstot*(1.-masses[i]/masstot)*ris[j].dot(n3)*n3;
163 
164  // It is inefficient to keep reassigning val, but it's better than having two
165  // switches, and the compiler will probably optimize it out anyways.
166  switch(component_)
167  {
168  case Rg:
169  grad_[i] = dl1 + dl2 + dl3;
170  val_ = l1 + l2 + l3;
171  break;
172  case principal1:
173  grad_[i] = dl1;
174  val_ = l1;
175  break;
176  case principal2:
177  grad_[i] = dl2;
178  val_ = l2;
179  break;
180  case principal3:
181  grad_[i] = dl3;
182  val_ = l3;
183  break;
184  case asphericity:
185  grad_[i] = dl1 - 0.5*(dl2 + dl3);
186  val_ = l1 - 0.5*(l2 + l3);
187  break;
188  case acylindricity:
189  grad_[i] = dl2 - dl3;
190  val_ = l2 - l3;
191  break;
192  case shapeaniso:
193  auto l1_2 = l1*l1, l2_2 = l2*l2, l3_2 = l3*l3;
194  auto sum = l1 + l2 + l3;
195  auto sqsum = l1_2 + l2_2 + l3_2;
196  grad_[i] = 3.*(l1*dl1+l2*dl2+l3*dl3)/(sum*sum) -
197  3.*sqsum*(dl1+dl2+dl3)/(sum*sum*sum);
198  val_ = 1.5*sqsum/(sum*sum) - 0.5;
199  break;
200  }
201 
202  ++j;
203  }
204  }
205 
206  static GyrationTensorCV* Build(const Json::Value& json, const std::string& path)
207  {
208  Json::ObjectRequirement validator;
209  Json::Value schema;
210  Json::Reader reader;
211 
212  reader.parse(JsonSchema::GyrationTensorCV, schema);
213  validator.Parse(schema, path);
214 
215  // Validate inputs.
216  validator.Validate(json, path);
217  if(validator.HasErrors())
218  throw BuildException(validator.GetErrors());
219 
220  std::vector<int> atomids;
221  for(auto& s : json["atom_ids"])
222  atomids.push_back(s.asInt());
223 
224  GyrationTensor component = Rg;
225  auto comp = json["component"].asString();
226 
227  if(comp == "Rg")
228  component = Rg;
229  else if(comp == "principal1")
230  component = principal1;
231  else if(comp == "principal2")
232  component = principal2;
233  else if(comp == "principal3")
234  component = principal3;
235  else if(comp == "asphericity")
236  component = asphericity;
237  else if(comp == "acylindricity")
238  component = acylindricity;
239  else if(comp == "shapeaniso")
240  component = shapeaniso;
241 
242  return new GyrationTensorCV(atomids, component);
243  }
244  };
245 }
std::vector< int > Label
List of integers.
Definition: types.h:48
Eigen::Matrix3d Matrix3
3x3 matrix.
Definition: types.h:42
bool HasErrors()
Check if errors have occured.
Definition: Requirement.h:86
unsigned GetNumAtoms() const
Get number of atoms in this snapshot.
Definition: Snapshot.h:200
GyrationTensor component_
Component of gyration tensor to compute.
GyrationTensorCV(const Label &atomids, GyrationTensor component)
Constructor.
double TotalMass(const Label &indices) const
Compute the total mass of a group of particles based on index.
Definition: Snapshot.h:451
Class containing a snapshot of the current simulation in time.
Definition: Snapshot.h:43
Collective variable on components of the gyration tensor.
virtual void Parse(Value json, const std::string &path) override
Parse JSON value to generate Requirement(s).
int GetLocalIndex(int id) const
Gets the local atom index corresponding to an atom ID.
Definition: Snapshot.h:555
std::vector< Vector3 > grad_
Gradient vector dCv/dxi.
const mxx::comm & GetCommunicator() const
Get communicator for walker.
Definition: Snapshot.h:184
void Initialize(const Snapshot &snapshot) override
Initialize necessary variables.
void GetLocalIndices(const Label &ids, Label *indices) const
Definition: Snapshot.h:573
Exception to be thrown when building the Driver fails.
std::vector< std::string > GetErrors()
Get list of error messages.
Definition: Requirement.h:92
Vector3 CenterOfMass(const Label &indices) const
Definition: Snapshot.h:466
const std::vector< double > & GetMasses() const
Const access to the particle masses.
Definition: Snapshot.h:378
Label atomids_
IDs of the atoms used for calculation.
Requirements on an object.
Eigen::Vector3d Vector3
Three-dimensional vector.
Definition: types.h:33
void ApplyMinimumImage(Vector3 *v) const
Apply minimum image to a vector.
Definition: Snapshot.h:418
double val_
Current value of CV.
Abstract class for a collective variable.
void Evaluate(const Snapshot &snapshot) override
Evaluate the CV.
const std::vector< Vector3 > & GetPositions() const
Access the particle positions.
Definition: Snapshot.h:323
virtual void Validate(const Value &json, const std::string &path) override
Validate JSON value.