shapeMatch.cpp
Go to the documentation of this file.
Code
1//-----------------------------------------------------------------------------------
2// d-SEAMS - Deferred Structural Elucidation Analysis for Molecular Simulations
3//
4// Copyright (c) 2018--present d-SEAMS core team
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the MIT License as published by
8// the Open Source Initiative.
9//
10// A copy of the MIT License is included in the LICENSE file of this repository.
11// You should have received a copy of the MIT License along with this program.
12// If not, see <https://opensource.org/licenses/MIT>.
13//-----------------------------------------------------------------------------------
14
15#include <shapeMatch.hpp>
16
23 std::vector<std::vector<int>> nList, const Eigen::MatrixXd &refPoints,
24 std::vector<int> *basal1, std::vector<int> *basal2,
25 std::vector<double> *rmsdPerAtom, bool isPerfect) {
26 //
27 int ringSize = (*basal1).size(); // Number of nodes in each basal ring
28 std::vector<int> matchedBasal1,
29 matchedBasal2; // Re-ordered basal rings 1 and 2
30 int dim = 3; // Number of dimensions
31 // Matrices for the point sets of the target basal rings
32 Eigen::MatrixXd basal1Set(ringSize,
33 dim); // Point set for the first basal ring
34 Eigen::MatrixXd basal2Set(ringSize,
35 dim); // Point set for the second basal ring
36 int startingIndex; // Index in the basal rings from which the reference point
37 // set is matched
38 double cutoffAngle = 15; // Tolerance for the angular distance between the
39 // quaternions of the basal rings
40 double angDist; // Calculated angular distance between the two basal rings
41 // Variables for the absolute orientation
42 std::vector<double> quat1, quat2; // quaternion rotation
43 double rmsd1, rmsd2; // least total RMSD
44 std::vector<double> rmsdList1,
45 rmsdList2; // List of RMSD per atom in the order fed in
46 double scale1, scale2; // Scale factor
47 // Deformed block classification
48 bool doAngleCriterion = false; // For deformed blocks
49 // -----------------------
50 // Getting the target Eigen vectors
51 // Get the re-ordered matched basal rings, ordered with respect to each other
52 pntToPnt::relOrderPrismBlock(yCloud, *basal1, *basal2, nList, &matchedBasal1,
53 &matchedBasal2);
54 // -----------------------
55 // Match the basal rings with a complete prism block, given the relatively
56 // ordered basal rings This actually only needs to be done for deformed prism
57 // blocks
58 bool blockMatch = match::matchPrismBlock(
59 yCloud, nList, refPoints, &matchedBasal1, &matchedBasal2, &startingIndex);
60 // -----------------------
61 // Check for deformed prisms
62 if (!isPerfect) {
63 if (!blockMatch) {
64 return blockMatch;
65 }
66 }
67 // If the deformed prism does not fulfil the shape matching criterion, then do
68 // not calculate the RMSD per atom
69 // -----------------------
70 // Section for RMSD per atom, based on basal ring matching
71
72 basal1Set =
73 pntToPnt::fillPointSetPrismRing(yCloud, matchedBasal1, startingIndex);
74 // Fill up the point set for basal2
75 basal2Set =
76 pntToPnt::fillPointSetPrismRing(yCloud, matchedBasal2, startingIndex);
77 // Use Horn's algorithm to calculate the absolute orientation and RMSD etc.
78 absor::hornAbsOrientation(refPoints, basal1Set, &quat1, &rmsd1, &rmsdList1,
79 &scale1); // basal1
80 absor::hornAbsOrientation(refPoints, basal2Set, &quat2, &rmsd2, &rmsdList2,
81 &scale2); // basal2
82 // // ------------
83 // // Update the per-atom RMSD for each particle
84 // // Basal1
85 // match::updatePerAtomRMSDRing(matchedBasal1, startingIndex, rmsdList1,
86 // rmsdPerAtom);
87 // // Basal2
88 // match::updatePerAtomRMSDRing(matchedBasal2, startingIndex, rmsdList2,
89 // rmsdPerAtom);
90 // // ------------
91 // ------------
92 // Update the RMSD (obtained for each ring) for each particle
93 // Basal1
94 match::updateRMSDRing(matchedBasal1, startingIndex, rmsd1, rmsdPerAtom);
95 // Basal2
96 match::updateRMSDRing(matchedBasal2, startingIndex, rmsd2, rmsdPerAtom);
97 // ------------
98
99 return true;
100} // end of function
101
112 std::vector<std::vector<int>> nList, const Eigen::MatrixXd &refPoints,
113 std::vector<int> *basal1, std::vector<int> *basal2,
114 std::vector<double> *rmsdPerAtom) {
115 //
116 int ringSize = (*basal1).size(); // Number of nodes in each basal ring
117 std::vector<int> matchedBasal1,
118 matchedBasal2; // Re-ordered basal rings 1 and 2
119 int dim = 3; // Number of dimensions
120 // Matrices for the point sets of the target basal rings
121 Eigen::MatrixXd basal1Set(ringSize,
122 dim); // Point set for the first basal ring
123 Eigen::MatrixXd basal2Set(ringSize,
124 dim); // Point set for the second basal ring
125 int startingIndex; // Index in the basal rings from which the reference point
126 // set is matched
127 double angDist; // Calculated angular distance between the two basal rings
128 // Variables for the absolute orientation
129 std::vector<double> quat1, quat2; // quaternion rotation
130 double rmsd1, rmsd2; // least total RMSD
131 std::vector<double> rmsdList1,
132 rmsdList2; // List of RMSD per atom in the order fed in
133 double scale1, scale2; // Scale factor
134 // Deformed block classification
135 bool doAngleCriterion = false; // For deformed blocks
136
137 // Getting the target Eigen vectors
138 // Get the re-ordered matched basal rings, ordered with respect to each other
139 pntToPnt::relOrderPrismBlock(yCloud, *basal1, *basal2, &matchedBasal1,
140 &matchedBasal2);
141 // -----------------------
142 // Match the basal rings with a complete prism block, given the relatively
143 // ordered basal rings This actually only needs to be done for deformed prism
144 // blocks
145 bool blockMatch = match::matchPrismBlock(
146 yCloud, nList, refPoints, &matchedBasal1, &matchedBasal2, &startingIndex);
147 // -----------------------
148 // Check to see if the prism matches the reference prism block
149 if (!blockMatch) {
150 return blockMatch;
151 }
152
153 // If the deformed prism does not fulfil the shape matching criterion, then do
154 // not calculate the RMSD per atom
155 // -----------------------
156 // Section for RMSD per atom, based on basal ring matching
157
158 basal1Set =
159 pntToPnt::fillPointSetPrismRing(yCloud, matchedBasal1, startingIndex);
160 // Fill up the point set for basal2
161 basal2Set =
162 pntToPnt::fillPointSetPrismRing(yCloud, matchedBasal2, startingIndex);
163 // Use Horn's algorithm to calculate the absolute orientation and RMSD etc.
164 absor::hornAbsOrientation(refPoints, basal1Set, &quat1, &rmsd1, &rmsdList1,
165 &scale1); // basal1
166 absor::hornAbsOrientation(refPoints, basal2Set, &quat2, &rmsd2, &rmsdList2,
167 &scale2); // basal2
168 // // Calculate the angular distance between basal1 and basal2
169 // angDist = gen::angDistDegQuaternions(quat1, quat2);
170 // // Check if the shapes are aligned
171 // if (angDist > cutoffAngle) {
172 // return false;
173 // } // If not aligned, it is not a prism block
174 // ------------
175 // Update the RMSD (obtained for each ring) for each particle
176 // Basal1
177 match::updateRMSDRing(matchedBasal1, startingIndex, rmsd1, rmsdPerAtom);
178 // Basal2
179 match::updateRMSDRing(matchedBasal2, startingIndex, rmsd2, rmsdPerAtom);
180 // ------------
181
182 return true;
183} // end of function
184
186int match::updatePerAtomRMSDRing(std::vector<int> basalRing, int startingIndex,
187 std::vector<double> rmsdFromMatch,
188 std::vector<double> *rmsdPerAtom) {
189 //
190 int atomIndex; // Atom particle index, in rmsdPerAtom
191 int ringSize = basalRing.size(); // Size of the basal ring
192 int index; // Corresponding index in the basal ring
193 double iRMSD; // Per-particle RMSD
194
195 // -----------------
196 // Update the RMSD per particle
197 for (int i = 0; i < ringSize; i++) {
198 index = i + startingIndex; // Corresponding index in the basal ring
199 // Wrap-around
200 if (index >= ringSize) {
201 index -= ringSize;
202 } // end of wrap-around
203 //
204 // Get the atom index
205 atomIndex = basalRing[index];
206 // Get and update the RMSD per particle
207 iRMSD = rmsdFromMatch[i];
208 if ((*rmsdPerAtom)[atomIndex] == -1) {
209 (*rmsdPerAtom)[atomIndex] = iRMSD;
210 } // end of update of RMSD
211
212 } // end of updating the RMSD per particle
213 // -----------------
214 // finito
215 return 0;
216} // end of function
217
220int match::updateRMSDRing(std::vector<int> basalRing, int startingIndex,
221 double rmsdVal, std::vector<double> *rmsdPerAtom) {
222 //
223 int atomIndex; // Atom particle index, in rmsdPerAtom
224 int ringSize = basalRing.size(); // Size of the basal ring
225 int index; // Corresponding index in the basal ring
226
227 // -----------------
228 // Update the RMSD per particle
229 for (int i = 0; i < ringSize; i++) {
230 index = i + startingIndex; // Corresponding index in the basal ring
231 // Wrap-around
232 if (index >= ringSize) {
233 index -= ringSize;
234 } // end of wrap-around
235 //
236 // Get the atom index
237 atomIndex = basalRing[index];
238 // The RMSD value to update with is rmsdVal
239 if ((*rmsdPerAtom)[atomIndex] == -1) {
240 (*rmsdPerAtom)[atomIndex] = rmsdVal;
241 } // end of update of RMSD
242
243 } // end of updating the RMSD for each particle
244 // -----------------
245 // finito
246 return 0;
247} // end of function
248
253 std::vector<std::vector<int>> nList, const Eigen::MatrixXd &refPoints,
254 std::vector<int> *basal1, std::vector<int> *basal2, int *beginIndex) {
255 //
256 int ringSize = (*basal1).size(); // Number of nodes in the basal rings
257 bool isMatch; // Qualifier for whether the prism block matches or not
258 int dim = 3; // Number of dimensions
259 int nop = 2 * ringSize; // Number of particles in the prism block
260 Eigen::MatrixXd refPrismBlock(
261 nop, dim); // eigen matrix for the reference prism block
262 Eigen::MatrixXd targetPrismBlock(
263 nop, dim); // eigen matrix for the target prism block
264 int startingIndex; // Index from which the basal rings will be ordered
265 // (permutations)
266 // variables for shape-matching
267 std::vector<double> quat; // Quaternion for the rotation
268 double rmsd; // RMSD value for the entire shape
269 std::vector<double> rmsdList; // RMSD value for each point
270 double scale; // Scale factor
271 // ----------------------------------------------------
272 // Get the reference prism block
273 refPrismBlock =
274 pntToPnt::createPrismBlock(yCloud, refPoints, ringSize, *basal1, *basal2);
275 // ----------------------------------------------------
276
277 // Loop through possible point-to-point correspondences
278 if (ringSize % 2 == 0 || ringSize == 3) {
279 startingIndex = 0;
280 // Fill up the point set for the target prism block
281 targetPrismBlock =
282 pntToPnt::fillPointSetPrismBlock(yCloud, *basal1, *basal2, 0);
283 // Shape-matching
284 absor::hornAbsOrientation(refPrismBlock, targetPrismBlock, &quat, &rmsd,
285 &rmsdList,
286 &scale); // basal2
287 } // even or if there are 3 nodes
288 else {
289 // Define the vector, RMSD etc:
290 std::vector<double> currentQuat; // quaternion rotation
291 double currentRmsd; // least total RMSD
292 std::vector<double>
293 currentRmsdList; // List of RMSD per atom in the order fed in
294 double currentScale;
295 // Loop through all possible startingIndex
296 for (int i = 0; i < ringSize; i++) {
297 //
298 // Fill up the point set for basal1
299 targetPrismBlock =
300 pntToPnt::fillPointSetPrismBlock(yCloud, *basal1, *basal2, i);
301 // Use Horn's algorithm to calculate the absolute orientation and RMSD
302 // etc. for basal1
303 absor::hornAbsOrientation(refPrismBlock, targetPrismBlock, &currentQuat,
304 &currentRmsd, &currentRmsdList, &currentScale);
305 // Comparison to get the least RMSD for the correct mapping
306 if (i == 0) {
307 // Init
308 quat = currentQuat;
309 rmsd = currentRmsd;
310 rmsdList = currentRmsdList;
311 scale = currentScale;
312 startingIndex = i;
313 } // init
314 else {
315 // Check to see if the calculated RMSD is less than the RMSD already
316 // saved
317 if (currentRmsd < rmsd) {
318 quat = currentQuat;
319 rmsd = currentRmsd;
320 rmsdList = currentRmsdList;
321 scale = currentScale;
322 startingIndex = i;
323 } // end of check to see if the current RMSD is smaller
324 } // end of comparison and filling
325 } // end of loop through startingIndex
326 } // ringSize is odd, so every point must be tried
327
328 // Update the index from which matching is started
329 *beginIndex = startingIndex;
330
331 // // TEST DELETE BLOCK LATER
332 // if (ringSize == 6) {
333 // std::fstream rmsdFile;
334 // rmsdFile.open("../runOne/topoINT/rmsd6.dat",
335 // std::fstream::in | std::fstream::out | std::fstream::app);
336 // rmsdFile << rmsd << "\n";
337 // rmsdFile.close();
338 // } else if (ringSize == 4) {
339 // std::fstream rmsdFile;
340 // rmsdFile.open("../runOne/topoINT/rmsd4.dat",
341 // std::fstream::in | std::fstream::out | std::fstream::app);
342 // rmsdFile << rmsd << "\n";
343 // rmsdFile.close();
344 // }
345 // // END OF BLOCK TO BE DELETED
346
347 // Condition for shape-matching
348 if (rmsd <= 6) {
349 isMatch = true; // Is a prism block
350 } // within RMSD range
351 else {
352 isMatch = false;
353 }
354
355 // Return
356 return isMatch;
357}
int hornAbsOrientation(const Eigen::MatrixXd &refPoints, const Eigen::MatrixXd &targetPoints, std::vector< double > *quat, double *rmsd, std::vector< double > *rmsdList, double *scale)
Get the absolute orientation using Horn's algorithm (with quaternions)
int updatePerAtomRMSDRing(std::vector< int > basalRing, int startingIndex, std::vector< double > rmsdFromMatch, std::vector< double > *rmsdPerAtom)
Update the per-particle RMSD for a prism block basal ring.
bool matchUntetheredPrism(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, const Eigen::MatrixXd &refPoints, std::vector< int > *basal1, std::vector< int > *basal2, std::vector< double > *rmsdPerAtom)
bool matchPrism(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, const Eigen::MatrixXd &refPoints, std::vector< int > *basal1, std::vector< int > *basal2, std::vector< double > *rmsdPerAtom, bool isPerfect=true)
int updateRMSDRing(std::vector< int > basalRing, int startingIndex, double rmsdVal, std::vector< double > *rmsdPerAtom)
bool matchPrismBlock(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< std::vector< int > > nList, const Eigen::MatrixXd &refPoints, std::vector< int > *basal1, std::vector< int > *basal2, int *beginIndex)
Eigen::MatrixXd fillPointSetPrismRing(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< int > basalRing, int startingIndex)
int relOrderPrismBlock(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< int > basal1, std::vector< int > basal2, std::vector< std::vector< int > > nList, std::vector< int > *outBasal1, std::vector< int > *outBasal2)
Eigen::MatrixXd createPrismBlock(molSys::PointCloud< molSys::Point< double >, double > *yCloud, const Eigen::MatrixXd &refPoints, int ringSize, std::vector< int > basal1, std::vector< int > basal2)
Eigen::MatrixXd fillPointSetPrismBlock(molSys::PointCloud< molSys::Point< double >, double > *yCloud, std::vector< int > basal1, std::vector< int > basal2, int startingIndex)
This contains a collection of points; contains information for a particular frame.
Definition mol_sys.hpp:170
This contains per-particle information.
Definition mol_sys.hpp:149