00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <cstdlib>
00011 #include <cstring>
00012
00013 #include "colvarmodule.h"
00014 #include "colvartypes.h"
00015 #include "colvarparse.h"
00016
00017 #ifdef COLVARS_LAMMPS
00018
00019 #include "math_eigen_impl.h"
00020 #else
00021
00022 #include "nr_jacobi.h"
00023 #endif
00024
00025
00026 bool colvarmodule::rotation::monitor_crossings = false;
00027 cvm::real colvarmodule::rotation::crossing_threshold = 1.0E-02;
00028
00029
00030 std::string cvm::rvector::to_simple_string() const
00031 {
00032 std::ostringstream os;
00033 os.setf(std::ios::scientific, std::ios::floatfield);
00034 os.precision(cvm::cv_prec);
00035 os << x << " " << y << " " << z;
00036 return os.str();
00037 }
00038
00039
00040 int cvm::rvector::from_simple_string(std::string const &s)
00041 {
00042 std::stringstream stream(s);
00043 if ( !(stream >> x) ||
00044 !(stream >> y) ||
00045 !(stream >> z) ) {
00046 return COLVARS_ERROR;
00047 }
00048 return COLVARS_OK;
00049 }
00050
00051
00052 std::ostream & operator << (std::ostream &os, colvarmodule::rvector const &v)
00053 {
00054 std::streamsize const w = os.width();
00055 std::streamsize const p = os.precision();
00056
00057 os.width(2);
00058 os << "( ";
00059 os.width(w); os.precision(p);
00060 os << v.x << " , ";
00061 os.width(w); os.precision(p);
00062 os << v.y << " , ";
00063 os.width(w); os.precision(p);
00064 os << v.z << " )";
00065 return os;
00066 }
00067
00068
00069 std::istream & operator >> (std::istream &is, colvarmodule::rvector &v)
00070 {
00071 std::streampos const start_pos = is.tellg();
00072 char sep;
00073 if ( !(is >> sep) || !(sep == '(') ||
00074 !(is >> v.x) || !(is >> sep) || !(sep == ',') ||
00075 !(is >> v.y) || !(is >> sep) || !(sep == ',') ||
00076 !(is >> v.z) || !(is >> sep) || !(sep == ')') ) {
00077 is.clear();
00078 is.seekg(start_pos, std::ios::beg);
00079 is.setstate(std::ios::failbit);
00080 return is;
00081 }
00082 return is;
00083 }
00084
00085 std::string cvm::quaternion::to_simple_string() const
00086 {
00087 std::ostringstream os;
00088 os.setf(std::ios::scientific, std::ios::floatfield);
00089 os.precision(cvm::cv_prec);
00090 os << q0 << " " << q1 << " " << q2 << " " << q3;
00091 return os.str();
00092 }
00093
00094 int cvm::quaternion::from_simple_string(std::string const &s)
00095 {
00096 std::stringstream stream(s);
00097 if ( !(stream >> q0) ||
00098 !(stream >> q1) ||
00099 !(stream >> q2) ||
00100 !(stream >> q3) ) {
00101 return COLVARS_ERROR;
00102 }
00103 return COLVARS_OK;
00104 }
00105
00106 std::ostream & operator << (std::ostream &os, colvarmodule::quaternion const &q)
00107 {
00108 std::streamsize const w = os.width();
00109 std::streamsize const p = os.precision();
00110
00111 os.width(2);
00112 os << "( ";
00113 os.width(w); os.precision(p);
00114 os << q.q0 << " , ";
00115 os.width(w); os.precision(p);
00116 os << q.q1 << " , ";
00117 os.width(w); os.precision(p);
00118 os << q.q2 << " , ";
00119 os.width(w); os.precision(p);
00120 os << q.q3 << " )";
00121 return os;
00122 }
00123
00124
00125 std::istream & operator >> (std::istream &is, colvarmodule::quaternion &q)
00126 {
00127 std::streampos const start_pos = is.tellg();
00128 char sep;
00129 if ( !(is >> sep) || !(sep == '(') ||
00130 !(is >> q.q0) || !(is >> sep) || !(sep == ',') ||
00131 !(is >> q.q1) || !(is >> sep) || !(sep == ',') ||
00132 !(is >> q.q2) || !(is >> sep) || !(sep == ',') ||
00133 !(is >> q.q3) || !(is >> sep) || !(sep == ')') ) {
00134 is.clear();
00135 is.seekg(start_pos, std::ios::beg);
00136 is.setstate(std::ios::failbit);
00137 }
00138 return is;
00139 }
00140
00141
00142 cvm::quaternion
00143 cvm::quaternion::position_derivative_inner(cvm::rvector const &pos,
00144 cvm::rvector const &vec) const
00145 {
00146 cvm::quaternion result(0.0, 0.0, 0.0, 0.0);
00147
00148
00149 result.q0 = 2.0 * pos.x * q0 * vec.x
00150 +2.0 * pos.y * q0 * vec.y
00151 +2.0 * pos.z * q0 * vec.z
00152
00153 -2.0 * pos.y * q3 * vec.x
00154 +2.0 * pos.z * q2 * vec.x
00155
00156 +2.0 * pos.x * q3 * vec.y
00157 -2.0 * pos.z * q1 * vec.y
00158
00159 -2.0 * pos.x * q2 * vec.z
00160 +2.0 * pos.y * q1 * vec.z;
00161
00162
00163 result.q1 = +2.0 * pos.x * q1 * vec.x
00164 -2.0 * pos.y * q1 * vec.y
00165 -2.0 * pos.z * q1 * vec.z
00166
00167 +2.0 * pos.y * q2 * vec.x
00168 +2.0 * pos.z * q3 * vec.x
00169
00170 +2.0 * pos.x * q2 * vec.y
00171 -2.0 * pos.z * q0 * vec.y
00172
00173 +2.0 * pos.x * q3 * vec.z
00174 +2.0 * pos.y * q0 * vec.z;
00175
00176
00177 result.q2 = -2.0 * pos.x * q2 * vec.x
00178 +2.0 * pos.y * q2 * vec.y
00179 -2.0 * pos.z * q2 * vec.z
00180
00181 +2.0 * pos.y * q1 * vec.x
00182 +2.0 * pos.z * q0 * vec.x
00183
00184 +2.0 * pos.x * q1 * vec.y
00185 +2.0 * pos.z * q3 * vec.y
00186
00187 -2.0 * pos.x * q0 * vec.z
00188 +2.0 * pos.y * q3 * vec.z;
00189
00190
00191 result.q3 = -2.0 * pos.x * q3 * vec.x
00192 -2.0 * pos.y * q3 * vec.y
00193 +2.0 * pos.z * q3 * vec.z
00194
00195 -2.0 * pos.y * q0 * vec.x
00196 +2.0 * pos.z * q1 * vec.x
00197
00198 +2.0 * pos.x * q0 * vec.y
00199 +2.0 * pos.z * q2 * vec.y
00200
00201 +2.0 * pos.x * q1 * vec.z
00202 +2.0 * pos.y * q2 * vec.z;
00203
00204 return result;
00205 }
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215 #ifdef COLVARS_LAMMPS
00216 namespace {
00217 inline void *new_Jacobi_solver(int size) {
00218 return reinterpret_cast<void *>(new MathEigen::Jacobi<cvm::real,
00219 cvm::vector1d<cvm::real> &,
00220 cvm::matrix2d<cvm::real> &>(4));
00221 }
00222 }
00223 #endif
00224
00225
00226 int colvarmodule::rotation::init()
00227 {
00228 b_debug_gradients = false;
00229 lambda = 0.0;
00230 cvm::main()->cite_feature("Optimal rotation via flexible fitting");
00231 return COLVARS_OK;
00232 }
00233
00234
00235 colvarmodule::rotation::rotation()
00236 {
00237 init();
00238 #ifdef COLVARS_LAMMPS
00239 jacobi = new_Jacobi_solver(4);
00240 #else
00241 jacobi = NULL;
00242 #endif
00243 }
00244
00245
00246 colvarmodule::rotation::rotation(cvm::quaternion const &qi)
00247 : q(qi)
00248 {
00249 init();
00250 #ifdef COLVARS_LAMMPS
00251 jacobi = new_Jacobi_solver(4);
00252 #else
00253 jacobi = NULL;
00254 #endif
00255 }
00256
00257
00258 colvarmodule::rotation::rotation(cvm::real angle, cvm::rvector const &axis)
00259 {
00260 init();
00261 cvm::rvector const axis_n = axis.unit();
00262 cvm::real const sina = cvm::sin(angle/2.0);
00263 q = cvm::quaternion(cvm::cos(angle/2.0),
00264 sina * axis_n.x, sina * axis_n.y, sina * axis_n.z);
00265 #ifdef COLVARS_LAMMPS
00266 jacobi = new_Jacobi_solver(4);
00267 #else
00268 jacobi = NULL;
00269 #endif
00270 }
00271
00272
00273 colvarmodule::rotation::~rotation()
00274 {
00275 #ifdef COLVARS_LAMMPS
00276 delete reinterpret_cast<
00277 MathEigen::Jacobi<cvm::real,
00278 cvm::vector1d<cvm::real> &,
00279 cvm::matrix2d<cvm::real> &> *>(jacobi);
00280 #endif
00281 }
00282
00283
00284 void colvarmodule::rotation::build_correlation_matrix(
00285 std::vector<cvm::atom_pos> const &pos1,
00286 std::vector<cvm::atom_pos> const &pos2)
00287 {
00288
00289 size_t i;
00290 for (i = 0; i < pos1.size(); i++) {
00291 C.xx += pos1[i].x * pos2[i].x;
00292 C.xy += pos1[i].x * pos2[i].y;
00293 C.xz += pos1[i].x * pos2[i].z;
00294 C.yx += pos1[i].y * pos2[i].x;
00295 C.yy += pos1[i].y * pos2[i].y;
00296 C.yz += pos1[i].y * pos2[i].z;
00297 C.zx += pos1[i].z * pos2[i].x;
00298 C.zy += pos1[i].z * pos2[i].y;
00299 C.zz += pos1[i].z * pos2[i].z;
00300 }
00301 }
00302
00303
00304 void colvarmodule::rotation::compute_overlap_matrix()
00305 {
00306
00307
00308 S[0][0] = C.xx + C.yy + C.zz;
00309 S[1][0] = C.yz - C.zy;
00310 S[0][1] = S[1][0];
00311 S[2][0] = - C.xz + C.zx ;
00312 S[0][2] = S[2][0];
00313 S[3][0] = C.xy - C.yx;
00314 S[0][3] = S[3][0];
00315 S[1][1] = C.xx - C.yy - C.zz;
00316 S[2][1] = C.xy + C.yx;
00317 S[1][2] = S[2][1];
00318 S[3][1] = C.xz + C.zx;
00319 S[1][3] = S[3][1];
00320 S[2][2] = - C.xx + C.yy - C.zz;
00321 S[3][2] = C.yz + C.zy;
00322 S[2][3] = S[3][2];
00323 S[3][3] = - C.xx - C.yy + C.zz;
00324 }
00325
00326
00327 #ifndef COLVARS_LAMMPS
00328 namespace {
00329
00330 void diagonalize_matrix(cvm::matrix2d<cvm::real> &m,
00331 cvm::vector1d<cvm::real> &eigval,
00332 cvm::matrix2d<cvm::real> &eigvec)
00333 {
00334 eigval.resize(4);
00335 eigval.reset();
00336 eigvec.resize(4, 4);
00337 eigvec.reset();
00338
00339
00340 int jac_nrot = 0;
00341 if (NR_Jacobi::jacobi(m.c_array(), eigval.c_array(), eigvec.c_array(), &jac_nrot) !=
00342 COLVARS_OK) {
00343 cvm::error("Too many iterations in jacobi diagonalization.\n"
00344 "This is usually the result of an ill-defined set of atoms for "
00345 "rotational alignment (RMSD, rotateReference, etc).\n");
00346 }
00347 NR_Jacobi::eigsrt(eigval.c_array(), eigvec.c_array());
00348
00349 NR_Jacobi::transpose(eigvec.c_array());
00350
00351
00352 for (size_t ie = 0; ie < 4; ie++) {
00353 cvm::real norm2 = 0.0;
00354 size_t i;
00355 for (i = 0; i < 4; i++) {
00356 norm2 += eigvec[ie][i] * eigvec[ie][i];
00357 }
00358 cvm::real const norm = cvm::sqrt(norm2);
00359 for (i = 0; i < 4; i++) {
00360 eigvec[ie][i] /= norm;
00361 }
00362 }
00363 }
00364
00365 }
00366 #endif
00367
00368
00369
00370
00371 void colvarmodule::rotation::calc_optimal_rotation(
00372 std::vector<cvm::atom_pos> const &pos1,
00373 std::vector<cvm::atom_pos> const &pos2)
00374 {
00375 C.reset();
00376 build_correlation_matrix(pos1, pos2);
00377
00378 S.resize(4, 4);
00379 S.reset();
00380 compute_overlap_matrix();
00381
00382 S_backup.resize(4, 4);
00383 S_backup = S;
00384
00385 if (b_debug_gradients) {
00386 cvm::log("S = "+cvm::to_str(S_backup, cvm::cv_width, cvm::cv_prec)+"\n");
00387 }
00388
00389 S_eigval.resize(4);
00390 S_eigvec.resize(4, 4);
00391
00392 #ifdef COLVARS_LAMMPS
00393 MathEigen::Jacobi<cvm::real,
00394 cvm::vector1d<cvm::real> &,
00395 cvm::matrix2d<cvm::real> &> *ecalc =
00396 reinterpret_cast<MathEigen::Jacobi<cvm::real,
00397 cvm::vector1d<cvm::real> &,
00398 cvm::matrix2d<cvm::real> &> *>(jacobi);
00399
00400 int ierror = ecalc->Diagonalize(S, S_eigval, S_eigvec);
00401 if (ierror) {
00402 cvm::error("Too many iterations in jacobi diagonalization.\n"
00403 "This is usually the result of an ill-defined set of atoms for "
00404 "rotational alignment (RMSD, rotateReference, etc).\n");
00405 }
00406 #else
00407 diagonalize_matrix(S, S_eigval, S_eigvec);
00408 #endif
00409
00410
00411
00412 cvm::real const L0 = S_eigval[0];
00413 cvm::real const L1 = S_eigval[1];
00414 cvm::real const L2 = S_eigval[2];
00415 cvm::real const L3 = S_eigval[3];
00416 cvm::quaternion const Q0(S_eigvec[0]);
00417 cvm::quaternion const Q1(S_eigvec[1]);
00418 cvm::quaternion const Q2(S_eigvec[2]);
00419 cvm::quaternion const Q3(S_eigvec[3]);
00420
00421 lambda = L0;
00422 q = Q0;
00423
00424 if (cvm::rotation::monitor_crossings) {
00425 if (q_old.norm2() > 0.0) {
00426 q.match(q_old);
00427 if (q_old.inner(q) < (1.0 - crossing_threshold)) {
00428 cvm::log("Warning: one molecular orientation has changed by more than "+
00429 cvm::to_str(crossing_threshold)+": discontinuous rotation ?\n");
00430 }
00431 }
00432 q_old = q;
00433 }
00434
00435 if (b_debug_gradients) {
00436 cvm::log("L0 = "+cvm::to_str(L0, cvm::cv_width, cvm::cv_prec)+
00437 ", Q0 = "+cvm::to_str(Q0, cvm::cv_width, cvm::cv_prec)+
00438 ", Q0*Q0 = "+cvm::to_str(Q0.inner(Q0), cvm::cv_width, cvm::cv_prec)+
00439 "\n");
00440 cvm::log("L1 = "+cvm::to_str(L1, cvm::cv_width, cvm::cv_prec)+
00441 ", Q1 = "+cvm::to_str(Q1, cvm::cv_width, cvm::cv_prec)+
00442 ", Q0*Q1 = "+cvm::to_str(Q0.inner(Q1), cvm::cv_width, cvm::cv_prec)+
00443 "\n");
00444 cvm::log("L2 = "+cvm::to_str(L2, cvm::cv_width, cvm::cv_prec)+
00445 ", Q2 = "+cvm::to_str(Q2, cvm::cv_width, cvm::cv_prec)+
00446 ", Q0*Q2 = "+cvm::to_str(Q0.inner(Q2), cvm::cv_width, cvm::cv_prec)+
00447 "\n");
00448 cvm::log("L3 = "+cvm::to_str(L3, cvm::cv_width, cvm::cv_prec)+
00449 ", Q3 = "+cvm::to_str(Q3, cvm::cv_width, cvm::cv_prec)+
00450 ", Q0*Q3 = "+cvm::to_str(Q0.inner(Q3), cvm::cv_width, cvm::cv_prec)+
00451 "\n");
00452 }
00453
00454
00455
00456
00457 size_t ia;
00458 for (ia = 0; ia < dS_1.size(); ia++) {
00459
00460 cvm::real const &a2x = pos2[ia].x;
00461 cvm::real const &a2y = pos2[ia].y;
00462 cvm::real const &a2z = pos2[ia].z;
00463
00464 cvm::matrix2d<cvm::rvector> &ds_1 = dS_1[ia];
00465
00466
00467 ds_1.reset();
00468 ds_1[0][0].set( a2x, a2y, a2z);
00469 ds_1[1][0].set( 0.0, a2z, -a2y);
00470 ds_1[0][1] = ds_1[1][0];
00471 ds_1[2][0].set(-a2z, 0.0, a2x);
00472 ds_1[0][2] = ds_1[2][0];
00473 ds_1[3][0].set( a2y, -a2x, 0.0);
00474 ds_1[0][3] = ds_1[3][0];
00475 ds_1[1][1].set( a2x, -a2y, -a2z);
00476 ds_1[2][1].set( a2y, a2x, 0.0);
00477 ds_1[1][2] = ds_1[2][1];
00478 ds_1[3][1].set( a2z, 0.0, a2x);
00479 ds_1[1][3] = ds_1[3][1];
00480 ds_1[2][2].set(-a2x, a2y, -a2z);
00481 ds_1[3][2].set( 0.0, a2z, a2y);
00482 ds_1[2][3] = ds_1[3][2];
00483 ds_1[3][3].set(-a2x, -a2y, a2z);
00484
00485 cvm::rvector &dl0_1 = dL0_1[ia];
00486 cvm::vector1d<cvm::rvector> &dq0_1 = dQ0_1[ia];
00487
00488
00489
00490
00491
00492 dl0_1.reset();
00493 for (size_t i = 0; i < 4; i++) {
00494 for (size_t j = 0; j < 4; j++) {
00495 dl0_1 += Q0[i] * ds_1[i][j] * Q0[j];
00496 }
00497 }
00498
00499 dq0_1.reset();
00500 for (size_t p = 0; p < 4; p++) {
00501 for (size_t i = 0; i < 4; i++) {
00502 for (size_t j = 0; j < 4; j++) {
00503 dq0_1[p] +=
00504 (Q1[i] * ds_1[i][j] * Q0[j]) / (L0-L1) * Q1[p] +
00505 (Q2[i] * ds_1[i][j] * Q0[j]) / (L0-L2) * Q2[p] +
00506 (Q3[i] * ds_1[i][j] * Q0[j]) / (L0-L3) * Q3[p];
00507 }
00508 }
00509 }
00510 }
00511
00512
00513 for (ia = 0; ia < dS_2.size(); ia++) {
00514
00515 cvm::real const &a1x = pos1[ia].x;
00516 cvm::real const &a1y = pos1[ia].y;
00517 cvm::real const &a1z = pos1[ia].z;
00518
00519 cvm::matrix2d<cvm::rvector> &ds_2 = dS_2[ia];
00520
00521 ds_2.reset();
00522 ds_2[0][0].set( a1x, a1y, a1z);
00523 ds_2[1][0].set( 0.0, -a1z, a1y);
00524 ds_2[0][1] = ds_2[1][0];
00525 ds_2[2][0].set( a1z, 0.0, -a1x);
00526 ds_2[0][2] = ds_2[2][0];
00527 ds_2[3][0].set(-a1y, a1x, 0.0);
00528 ds_2[0][3] = ds_2[3][0];
00529 ds_2[1][1].set( a1x, -a1y, -a1z);
00530 ds_2[2][1].set( a1y, a1x, 0.0);
00531 ds_2[1][2] = ds_2[2][1];
00532 ds_2[3][1].set( a1z, 0.0, a1x);
00533 ds_2[1][3] = ds_2[3][1];
00534 ds_2[2][2].set(-a1x, a1y, -a1z);
00535 ds_2[3][2].set( 0.0, a1z, a1y);
00536 ds_2[2][3] = ds_2[3][2];
00537 ds_2[3][3].set(-a1x, -a1y, a1z);
00538
00539 cvm::rvector &dl0_2 = dL0_2[ia];
00540 cvm::vector1d<cvm::rvector> &dq0_2 = dQ0_2[ia];
00541
00542 dl0_2.reset();
00543 for (size_t i = 0; i < 4; i++) {
00544 for (size_t j = 0; j < 4; j++) {
00545 dl0_2 += Q0[i] * ds_2[i][j] * Q0[j];
00546 }
00547 }
00548
00549 dq0_2.reset();
00550 for (size_t p = 0; p < 4; p++) {
00551 for (size_t i = 0; i < 4; i++) {
00552 for (size_t j = 0; j < 4; j++) {
00553 dq0_2[p] +=
00554 (Q1[i] * ds_2[i][j] * Q0[j]) / (L0-L1) * Q1[p] +
00555 (Q2[i] * ds_2[i][j] * Q0[j]) / (L0-L2) * Q2[p] +
00556 (Q3[i] * ds_2[i][j] * Q0[j]) / (L0-L3) * Q3[p];
00557 }
00558 }
00559 }
00560
00561 if (b_debug_gradients) {
00562
00563 cvm::matrix2d<cvm::real> S_new(4, 4);
00564 cvm::vector1d<cvm::real> S_new_eigval(4);
00565 cvm::matrix2d<cvm::real> S_new_eigvec(4, 4);
00566
00567
00568
00569 for (size_t comp = 0; comp < 3; comp++) {
00570
00571 S_new = S_backup;
00572
00573 for (size_t i = 0; i < 4; i++) {
00574 for (size_t j = 0; j < 4; j++) {
00575 S_new[i][j] +=
00576 colvarmodule::debug_gradients_step_size * ds_2[i][j][comp];
00577 }
00578 }
00579
00580
00581
00582 #ifdef COLVARS_LAMMPS
00583 ecalc->Diagonalize(S_new, S_new_eigval, S_new_eigvec);
00584 #else
00585 diagonalize_matrix(S_new, S_new_eigval, S_new_eigvec);
00586 #endif
00587
00588 cvm::real const &L0_new = S_new_eigval[0];
00589 cvm::quaternion const Q0_new(S_new_eigvec[0]);
00590
00591 cvm::real const DL0 = (dl0_2[comp]) * colvarmodule::debug_gradients_step_size;
00592 cvm::quaternion const DQ0(dq0_2[0][comp] * colvarmodule::debug_gradients_step_size,
00593 dq0_2[1][comp] * colvarmodule::debug_gradients_step_size,
00594 dq0_2[2][comp] * colvarmodule::debug_gradients_step_size,
00595 dq0_2[3][comp] * colvarmodule::debug_gradients_step_size);
00596
00597 cvm::log( "|(l_0+dl_0) - l_0^new|/l_0 = "+
00598 cvm::to_str(cvm::fabs(L0+DL0 - L0_new)/L0, cvm::cv_width, cvm::cv_prec)+
00599 ", |(q_0+dq_0) - q_0^new| = "+
00600 cvm::to_str((Q0+DQ0 - Q0_new).norm(), cvm::cv_width, cvm::cv_prec)+
00601 "\n");
00602 }
00603 }
00604 }
00605 }
00606
00607
00608