Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members

colvarcomp_distances.C

Go to the documentation of this file.
00001 #include <cmath>
00002 
00003 #include "colvarmodule.h"
00004 #include "colvarvalue.h"
00005 #include "colvarparse.h"
00006 #include "colvar.h"
00007 #include "colvarcomp.h"
00008 
00009 
00010 
00013 
00014 // "twogroup" flag defaults to true; set to false by selfCoordNum
00015 // (only distance-derived component based on only one group)
00016 
00017 colvar::distance::distance (std::string const &conf, bool twogroups)
00018   : cvc (conf)
00019 {
00020   function_type = "distance";
00021   b_inverse_gradients = true;
00022   b_Jacobian_derivative = true;
00023   if (get_keyval (conf, "forceNoPBC", b_no_PBC, false)) {
00024     cvm::log ("Computing distance using absolute positions (not minimal-image)");
00025   }
00026   if (twogroups && get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) {
00027     cvm::log ("Computing system force on group 1 only");
00028   }
00029   parse_group (conf, "group1", group1);
00030   atom_groups.push_back (&group1);
00031   if (twogroups) {
00032     parse_group (conf, "group2", group2);
00033     atom_groups.push_back (&group2);
00034   }
00035   x.type (colvarvalue::type_scalar);
00036 }
00037 
00038 
00039 colvar::distance::distance()
00040   : cvc ()
00041 {
00042   function_type = "distance";
00043   b_inverse_gradients = true;
00044   b_Jacobian_derivative = true;
00045   b_1site_force = false;
00046   x.type (colvarvalue::type_scalar);
00047 }
00048 
00049 void colvar::distance::calc_value()
00050 {
00051   group1.reset_atoms_data();
00052   group2.reset_atoms_data();
00053 
00054   group1.read_positions();
00055   group2.read_positions();
00056 
00057   if (b_no_PBC) {
00058     dist_v = group2.center_of_mass() - group1.center_of_mass();
00059   } else {
00060     dist_v = cvm::position_distance (group1.center_of_mass(),
00061                                      group2.center_of_mass());
00062   }
00063   x.real_value = dist_v.norm();
00064 }
00065 
00066 void colvar::distance::calc_gradients()
00067 {
00068   cvm::rvector const u = dist_v.unit();
00069   group1.set_weighted_gradient (-1.0 * u);
00070   group2.set_weighted_gradient (       u);
00071 }
00072 
00073 void colvar::distance::calc_force_invgrads()
00074 {
00075   group1.read_system_forces();
00076   if ( b_1site_force ) {
00077     ft.real_value = -1.0 * (group1.system_force() * dist_v.unit());
00078   } else {
00079     group2.read_system_forces();
00080     ft.real_value = 0.5 * ((group2.system_force() - group1.system_force()) * dist_v.unit());
00081   }
00082 }
00083 
00084 void colvar::distance::calc_Jacobian_derivative()
00085 {
00086   jd.real_value = x.real_value ? (2.0 / x.real_value) : 0.0;
00087 }
00088 
00089 void colvar::distance::apply_force (colvarvalue const &force)
00090 {
00091   if (!group1.noforce)
00092     group1.apply_colvar_force (force);
00093 
00094   if (!group2.noforce)
00095     group2.apply_colvar_force (force);
00096 }
00097 
00098 
00099 
00100 colvar::distance_vec::distance_vec (std::string const &conf)
00101   : distance (conf)
00102 {
00103   function_type = "distance_vec";
00104   x.type (colvarvalue::type_vector);
00105 }
00106 
00107 colvar::distance_vec::distance_vec()
00108   : distance()
00109 {
00110   function_type = "distance_vec";
00111   x.type (colvarvalue::type_vector);
00112 }
00113 
00114 void colvar::distance_vec::calc_value()
00115 {
00116   group1.reset_atoms_data();
00117   group2.reset_atoms_data();
00118 
00119   group1.read_positions();
00120   group2.read_positions();
00121 
00122   if (b_no_PBC) {
00123     x.rvector_value = group2.center_of_mass() - group1.center_of_mass();
00124   } else {
00125     x.rvector_value = cvm::position_distance (group1.center_of_mass(),
00126                                               group2.center_of_mass());
00127   }
00128 }
00129 
00130 void colvar::distance_vec::calc_gradients()
00131 { 
00132   // gradients are not stored: a 3x3 matrix for each atom would be
00133   // needed to store just the identity matrix
00134 }
00135 
00136 void colvar::distance_vec::apply_force (colvarvalue const &force)
00137 {
00138   if (!group1.noforce)
00139     group1.apply_force (-1.0 * force.rvector_value);
00140 
00141   if (!group2.noforce)
00142     group2.apply_force (       force.rvector_value);
00143 }
00144 
00145 
00146 
00147 colvar::distance_z::distance_z (std::string const &conf)
00148   : cvc (conf)
00149 {
00150   function_type = "distance_z";
00151   b_inverse_gradients = true;
00152   b_Jacobian_derivative = true;
00153   x.type (colvarvalue::type_scalar);
00154 
00155   // TODO detect PBC from MD engine (in simple cases)
00156   // and then update period in real time
00157   if (period != 0.0)
00158     b_periodic = true;    
00159 
00160   if ((wrap_center != 0.0) && (period == 0.0)) {
00161     cvm::fatal_error ("Error: wrapAround was defined in a distanceZ component,"
00162                       " but its period has not been set.\n");
00163   }
00164 
00165   parse_group (conf, "main", main);
00166   parse_group (conf, "ref", ref1);
00167   atom_groups.push_back (&main);
00168   atom_groups.push_back (&ref1);
00169   // this group is optional
00170   parse_group (conf, "ref2", ref2, true);
00171  
00172   if (ref2.size()) {
00173     atom_groups.push_back (&ref2);
00174     cvm::log ("Using axis joining the centers of mass of groups \"ref\" and \"ref2\"");
00175     fixed_axis = false;
00176     if (key_lookup (conf, "axis"))
00177       cvm::log ("Warning: explicit axis definition will be ignored!");
00178   } else {
00179     if (get_keyval (conf, "axis", axis, cvm::rvector (0.0, 0.0, 1.0))) {
00180       if (axis.norm2() == 0.0)
00181         cvm::fatal_error ("Axis vector is zero!");
00182       axis = axis.unit();
00183     }
00184     fixed_axis = true;
00185   }
00186 
00187   if (get_keyval (conf, "forceNoPBC", b_no_PBC, false)) {
00188     cvm::log ("Computing distance using absolute positions (not minimal-image)");
00189   }
00190   if (get_keyval (conf, "oneSiteSystemForce", b_1site_force, false)) {
00191     cvm::log ("Computing system force on group \"main\" only");
00192   }
00193 }
00194 
00195 colvar::distance_z::distance_z()
00196 {
00197   function_type = "distance_z";
00198   b_inverse_gradients = true;
00199   b_Jacobian_derivative = true;
00200   x.type (colvarvalue::type_scalar);
00201 }
00202 
00203 void colvar::distance_z::calc_value()
00204 {
00205   main.reset_atoms_data();
00206   ref1.reset_atoms_data();
00207 
00208   main.read_positions();
00209   ref1.read_positions();
00210 
00211   if (fixed_axis) {
00212     if (b_no_PBC) {
00213       dist_v = main.center_of_mass() - ref1.center_of_mass();
00214     } else {
00215       dist_v = cvm::position_distance (ref1.center_of_mass(),
00216                                        main.center_of_mass());
00217     }
00218   } else {
00219     ref2.reset_atoms_data();
00220     ref2.read_positions();
00221 
00222     if (b_no_PBC) {
00223       dist_v = main.center_of_mass() -
00224                (0.5 * (ref1.center_of_mass() + ref2.center_of_mass()));
00225       axis = ref2.center_of_mass() - ref1.center_of_mass();
00226     } else {
00227       dist_v = cvm::position_distance (0.5 * (ref1.center_of_mass() +
00228                ref2.center_of_mass()), main.center_of_mass());
00229       axis = cvm::position_distance (ref1.center_of_mass(), ref2.center_of_mass());
00230     }
00231     axis_norm = axis.norm();
00232     axis = axis.unit();
00233   }
00234   x.real_value = axis * dist_v;
00235   this->wrap (x);
00236 }
00237 
00238 void colvar::distance_z::calc_gradients()
00239 {
00240   main.set_weighted_gradient ( axis );
00241 
00242   if (fixed_axis) {
00243     ref1.set_weighted_gradient (-1.0 * axis);
00244   } else {
00245     if (b_no_PBC) {
00246       ref1.set_weighted_gradient ( 1.0 / axis_norm * (main.center_of_mass() - ref2.center_of_mass() -
00247                                    x.real_value * axis ));
00248       ref2.set_weighted_gradient ( 1.0 / axis_norm * (ref1.center_of_mass() - main.center_of_mass() +
00249                                    x.real_value * axis ));
00250     } else {
00251       ref1.set_weighted_gradient ( 1.0 / axis_norm * (
00252         cvm::position_distance (ref2.center_of_mass(), main.center_of_mass()) - x.real_value * axis ));
00253       ref2.set_weighted_gradient ( 1.0 / axis_norm * (
00254         cvm::position_distance (main.center_of_mass(), ref1.center_of_mass()) + x.real_value * axis ));
00255     }
00256   }
00257 }
00258 
00259 void colvar::distance_z::calc_force_invgrads()
00260 {
00261   main.read_system_forces();
00262 
00263   if (fixed_axis && !b_1site_force) {
00264     ref1.read_system_forces();
00265     ft.real_value = 0.5 * ((main.system_force() - ref1.system_force()) * axis);
00266   } else {
00267     ft.real_value = main.system_force() * axis;
00268   }
00269 }
00270 
00271 void colvar::distance_z::calc_Jacobian_derivative()
00272 {
00273   jd.real_value = 0.0;
00274 }
00275 
00276 void colvar::distance_z::apply_force (colvarvalue const &force)
00277 {
00278   if (!ref1.noforce)
00279     ref1.apply_colvar_force (force.real_value);
00280 
00281   if (ref2.size() && !ref2.noforce)
00282     ref2.apply_colvar_force (force.real_value);
00283 
00284   if (!main.noforce)
00285     main.apply_colvar_force (force.real_value);
00286 }
00287 
00288 
00289 
00290 colvar::distance_xy::distance_xy (std::string const &conf)
00291   : distance_z (conf)
00292 {
00293   function_type = "distance_xy";
00294   b_inverse_gradients = true;
00295   b_Jacobian_derivative = true;
00296   x.type (colvarvalue::type_scalar);
00297 }
00298 
00299 colvar::distance_xy::distance_xy()
00300   : distance_z()
00301 {
00302   function_type = "distance_xy";
00303   b_inverse_gradients = true;
00304   b_Jacobian_derivative = true;
00305   x.type (colvarvalue::type_scalar);
00306 }
00307 
00308 void colvar::distance_xy::calc_value()
00309 {
00310   ref1.reset_atoms_data();
00311   main.reset_atoms_data();
00312 
00313   ref1.read_positions();
00314   main.read_positions();
00315 
00316   if (b_no_PBC) {
00317     dist_v = main.center_of_mass() - ref1.center_of_mass();
00318   } else {
00319     dist_v = cvm::position_distance (ref1.center_of_mass(),
00320                                      main.center_of_mass());
00321   }
00322   if (!fixed_axis) {
00323     ref2.reset_atoms_data();
00324     ref2.read_positions();
00325 
00326     if (b_no_PBC) {
00327       v12 = ref2.center_of_mass() - ref1.center_of_mass();
00328     } else {
00329       v12 = cvm::position_distance (ref1.center_of_mass(), ref2.center_of_mass());
00330     }
00331     axis_norm = v12.norm();
00332     axis = v12.unit();
00333   }
00334 
00335   dist_v_ortho = dist_v - (dist_v * axis) * axis;
00336   x.real_value = dist_v_ortho.norm();
00337 }
00338 
00339 void colvar::distance_xy::calc_gradients()
00340 {
00341   // Intermediate quantity (r_P3 / r_12 where P is the projection
00342   // of 3 (main) on the plane orthogonal to 12, containing 1 (ref1))
00343   cvm::real A;
00344   cvm::real x_inv;
00345 
00346   if (x.real_value == 0.0) return;
00347   x_inv = 1.0 / x.real_value;
00348 
00349   if (fixed_axis) {
00350     ref1.set_weighted_gradient (-1.0 * x_inv * dist_v_ortho);
00351     main.set_weighted_gradient (       x_inv * dist_v_ortho);
00352   } else {
00353     if (b_no_PBC) {
00354       v13 = main.center_of_mass() - ref1.center_of_mass();
00355     } else {
00356       v13 = cvm::position_distance (ref1.center_of_mass(), main.center_of_mass());
00357     }
00358     A = (dist_v * axis) / axis_norm;
00359 
00360     ref1.set_weighted_gradient ( (A - 1.0) * x_inv * dist_v_ortho);
00361     ref2.set_weighted_gradient ( -A        * x_inv * dist_v_ortho);
00362     main.set_weighted_gradient (      1.0  * x_inv * dist_v_ortho);
00363   }
00364 }
00365 
00366 void colvar::distance_xy::calc_force_invgrads()
00367 {
00368   main.read_system_forces();
00369 
00370   if (fixed_axis && !b_1site_force) {
00371     ref1.read_system_forces();
00372     ft.real_value = 0.5 / x.real_value * ((main.system_force() - ref1.system_force()) * dist_v_ortho);
00373   } else {
00374     ft.real_value = 1.0 / x.real_value * main.system_force() * dist_v_ortho;
00375   }
00376 }
00377 
00378 void colvar::distance_xy::calc_Jacobian_derivative()
00379 {
00380   jd.real_value = x.real_value ? (1.0 / x.real_value) : 0.0;
00381 }
00382 
00383 void colvar::distance_xy::apply_force (colvarvalue const &force)
00384 {
00385   if (!ref1.noforce)
00386     ref1.apply_colvar_force (force.real_value);
00387 
00388   if (ref2.size() && !ref2.noforce)
00389     ref2.apply_colvar_force (force.real_value);
00390 
00391   if (!main.noforce)
00392     main.apply_colvar_force (force.real_value);
00393 }
00394 
00395 
00396 
00397 colvar::min_distance::min_distance (std::string const &conf)
00398   : distance (conf)
00399 {
00400   function_type = "min_distance";
00401   x.type (colvarvalue::type_scalar);
00402 
00403   get_keyval (conf, "smoothing", smoothing, (1.0 * cvm::unit_angstrom()));
00404 }
00405 
00406 colvar::min_distance::min_distance()
00407   : distance()
00408 {
00409   function_type = "min_distance";
00410   x.type (colvarvalue::type_scalar);
00411 }
00412 
00413 void colvar::min_distance::calc_value()
00414 {
00415   group1.reset_atoms_data();
00416   group2.reset_atoms_data();
00417 
00418   group1.read_positions();
00419   group2.read_positions();
00420 
00421   x.real_value = 0.0;
00422 
00423   bool zero_dist = false;
00424 
00425   for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) {
00426     for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) {
00427       cvm::rvector const dv = cvm::position_distance (ai1->pos, ai2->pos);
00428       cvm::real const d = dv.norm();
00429       if (d > 0.0)
00430         x.real_value += std::exp (smoothing / d);
00431       else
00432         zero_dist = true;
00433     }
00434   }
00435 
00436   x.real_value = zero_dist ? 0.0 : smoothing/(std::log (x.real_value));
00437 }
00438 
00439 void colvar::min_distance::calc_gradients()
00440 {
00441   if (x.real_value > 0.0) {
00442     cvm::real const sum = std::exp (smoothing/x.real_value);
00443     cvm::real const dxdsum = -1.0 *
00444       (x.real_value/smoothing) * (x.real_value/smoothing) *
00445       (1.0 / sum);
00446 
00447     for (cvm::atom_iter ai1 = group1.begin(); ai1 != group1.end(); ai1++) {
00448       for (cvm::atom_iter ai2 = group2.begin(); ai2 != group2.end(); ai2++) {
00449         cvm::rvector const dv = cvm::position_distance (ai1->pos, ai2->pos);
00450         cvm::real const d = dv.norm();
00451         if (d > 0.0) {
00452           cvm::rvector const dvu = dv / dv.norm();
00453           ai1->grad += dxdsum * std::exp (smoothing / d) *
00454             smoothing * (-1.0/(d*d)) * (-1.0) * dvu;
00455           ai2->grad += dxdsum * std::exp (smoothing / d) *
00456             smoothing * (-1.0/(d*d)) * dvu;
00457         }
00458       }
00459     }
00460   }
00461 }
00462 
00463 void colvar::min_distance::apply_force (colvarvalue const &force)
00464 {
00465   if (!group1.noforce)
00466     group1.apply_colvar_force (force.real_value);
00467 
00468   if (!group2.noforce)
00469     group2.apply_colvar_force (force.real_value);
00470 }
00471 
00472 
00473 
00474 colvar::distance_dir::distance_dir (std::string const &conf)
00475   : distance (conf)
00476 {
00477   function_type = "distance_dir";
00478   x.type (colvarvalue::type_unitvector);
00479 }
00480 
00481 
00482 colvar::distance_dir::distance_dir()
00483   : distance()
00484 {
00485   function_type = "distance_dir";
00486   x.type (colvarvalue::type_unitvector);
00487 }
00488 
00489 
00490 void colvar::distance_dir::calc_value()
00491 {
00492   group1.reset_atoms_data();
00493   group2.reset_atoms_data();
00494 
00495   group1.read_positions();
00496   group2.read_positions();
00497 
00498   if (b_no_PBC) {
00499     dist_v = group2.center_of_mass() - group1.center_of_mass();
00500   } else {
00501     dist_v = cvm::position_distance (group1.center_of_mass(),
00502                                      group2.center_of_mass());
00503   }
00504   x.rvector_value = dist_v.unit();
00505 }
00506 
00507 
00508 void colvar::distance_dir::calc_gradients()
00509 {
00510   // gradients are computed on the fly within apply_force()
00511   // Note: could be a problem if a future bias relies on gradient
00512   // calculations...
00513 }
00514 
00515 
00516 void colvar::distance_dir::apply_force (colvarvalue const &force)
00517 {
00518   // remove the radial force component
00519   cvm::real const iprod = force.rvector_value * x.rvector_value;
00520   cvm::rvector const force_tang = force.rvector_value - iprod * x.rvector_value;
00521 
00522   if (!group1.noforce)
00523     group1.apply_force (-1.0 * force_tang);
00524 
00525   if (!group2.noforce)
00526     group2.apply_force (       force_tang);
00527 }
00528 
00529 
00530 
00531 
00532 colvar::gyration::gyration (std::string const &conf)
00533   : cvc (conf)
00534 {
00535   function_type = "gyration";
00536   b_inverse_gradients = true;
00537   b_Jacobian_derivative = true;
00538   parse_group (conf, "atoms", atoms);
00539   atom_groups.push_back (&atoms);
00540   x.type (colvarvalue::type_scalar);
00541 }
00542 
00543 
00544 colvar::gyration::gyration()
00545 {
00546   function_type = "gyration";
00547   b_inverse_gradients = true;
00548   b_Jacobian_derivative = true;
00549   x.type (colvarvalue::type_scalar);
00550 }
00551 
00552 
00553 void colvar::gyration::calc_value()
00554 {
00555   atoms.reset_atoms_data();
00556   atoms.read_positions();
00557   atoms.apply_translation ((-1.0) * atoms.center_of_geometry());
00558 
00559   x.real_value = 0.0;
00560   for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) {
00561     x.real_value += (ai->mass/atoms.total_mass) * (ai->pos).norm2();
00562   }
00563   x.real_value = std::sqrt (x.real_value);
00564 }
00565 
00566 
00567 void colvar::gyration::calc_gradients()
00568 {
00569   cvm::real const drdx = 1.0/(cvm::real (atoms.size()) * x.real_value);
00570   for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) {
00571     ai->grad = drdx * ai->pos;
00572   }
00573 }
00574 
00575 
00576 void colvar::gyration::calc_force_invgrads()
00577 {
00578   atoms.read_system_forces();
00579 
00580   cvm::real const dxdr = 1.0/x.real_value;
00581   ft.real_value = 0.0;
00582 
00583   for (cvm::atom_iter ai = atoms.begin(); ai != atoms.end(); ai++) {
00584     ft.real_value += dxdr * ai->pos * ai->system_force;
00585   }
00586 }
00587 
00588 
00589 void colvar::gyration::calc_Jacobian_derivative()
00590 {
00591   jd = x.real_value ? (3.0 * cvm::real (atoms.size()) - 4.0) / x.real_value : 0.0;
00592 }
00593 
00594 
00595 void colvar::gyration::apply_force (colvarvalue const &force)
00596 {
00597   if (!atoms.noforce)
00598     atoms.apply_colvar_force (force.real_value);
00599 }
00600 
00601 
00602 
00603 colvar::rmsd::rmsd (std::string const &conf)
00604   : orientation (conf)
00605 {
00606   b_inverse_gradients = true;
00607   b_Jacobian_derivative = true;
00608   function_type = "rmsd";
00609   x.type (colvarvalue::type_scalar);
00610 
00611   ref_pos_sum2 = 0.0;
00612   for (size_t i = 0; i < ref_pos.size(); i++) {
00613     ref_pos_sum2 += ref_pos[i].norm2();
00614   }
00615 }
00616 
00617   
00618 void colvar::rmsd::calc_value()
00619 {
00620   atoms.reset_atoms_data();
00621   atoms.read_positions();
00622 
00623   atoms_cog = atoms.center_of_geometry();
00624   rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog));
00625 
00626   cvm::real group_pos_sum2 = 0.0;
00627   for (size_t i = 0; i < atoms.size(); i++) {
00628     group_pos_sum2 += (atoms[i].pos - atoms_cog).norm2();
00629   }
00630 
00631   // value of the RMSD (Coutsias et al)
00632   cvm::real const MSD = 1.0/(cvm::real (atoms.size())) *
00633     ( group_pos_sum2 + ref_pos_sum2 - 2.0 * rot.lambda );
00634 
00635   x.real_value = (MSD > 0.0) ? std::sqrt (MSD) : 0.0;
00636 }
00637 
00638 
00639 void colvar::rmsd::calc_gradients()
00640 {
00641   cvm::real const drmsddx2 = (x.real_value > 0.0) ?
00642     0.5 / (x.real_value * cvm::real (atoms.size())) :
00643     0.0;
00644 
00645   for (size_t ia = 0; ia < atoms.size(); ia++) {
00646     atoms[ia].grad  = (drmsddx2 * 2.0 * (atoms[ia].pos - atoms_cog -
00647                                          rot.q.rotate (ref_pos[ia])));
00648   }
00649 }
00650 
00651 
00652 void colvar::rmsd::apply_force (colvarvalue const &force)
00653 {
00654   if (!atoms.noforce)
00655     atoms.apply_colvar_force (force.real_value);
00656 }
00657 
00658 
00659 void colvar::rmsd::calc_force_invgrads()
00660 {
00661   atoms.read_system_forces();
00662   ft.real_value = 0.0;
00663     
00664   // Note: gradient square norm is 1/N_atoms
00665           
00666   for (size_t ia = 0; ia < atoms.size(); ia++) {
00667     ft.real_value += atoms[ia].grad * atoms[ia].system_force;
00668   }
00669   ft.real_value *= atoms.size();
00670 }
00671 
00672 
00673 void colvar::rmsd::calc_Jacobian_derivative()
00674 {
00675   // divergence of the back-rotated target coordinates
00676   cvm::real divergence = 0.0;
00677  
00678   // gradient of the rotation matrix
00679   cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat;
00680 
00681   // gradients of products of 2 quaternion components 
00682   cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23;
00683 
00684   for (size_t ia = 0; ia < atoms.size(); ia++) {
00685 
00686     // Gradient of optimal quaternion wrt current Cartesian position
00687     cvm::vector1d< cvm::rvector, 4 >      &dq = rot.dQ0_2[ia];
00688 
00689     g11 = 2.0 * (rot.q)[1]*dq[1];
00690     g22 = 2.0 * (rot.q)[2]*dq[2];
00691     g33 = 2.0 * (rot.q)[3]*dq[3];
00692     g01 = (rot.q)[0]*dq[1] + (rot.q)[1]*dq[0];
00693     g02 = (rot.q)[0]*dq[2] + (rot.q)[2]*dq[0];
00694     g03 = (rot.q)[0]*dq[3] + (rot.q)[3]*dq[0];
00695     g12 = (rot.q)[1]*dq[2] + (rot.q)[2]*dq[1];
00696     g13 = (rot.q)[1]*dq[3] + (rot.q)[3]*dq[1];
00697     g23 = (rot.q)[2]*dq[3] + (rot.q)[3]*dq[2];
00698 
00699     // Gradient of the rotation matrix wrt current Cartesian position
00700     grad_rot_mat[0][0] = -2.0 * (g22 + g33); 
00701     grad_rot_mat[1][0] =  2.0 * (g12 + g03); 
00702     grad_rot_mat[2][0] =  2.0 * (g13 - g02); 
00703     grad_rot_mat[0][1] =  2.0 * (g12 - g03); 
00704     grad_rot_mat[1][1] = -2.0 * (g11 + g33); 
00705     grad_rot_mat[2][1] =  2.0 * (g01 + g23); 
00706     grad_rot_mat[0][2] =  2.0 * (g02 + g13); 
00707     grad_rot_mat[1][2] =  2.0 * (g23 - g01); 
00708     grad_rot_mat[2][2] = -2.0 * (g11 + g22); 
00709 
00710     cvm::atom_pos &y = ref_pos[ia]; 
00711 
00712     for (size_t i = 0; i < 3; i++) {
00713       for (size_t j = 0; j < 3; j++) {
00714         divergence += grad_rot_mat[i][j][i] * y[j];
00715       }
00716     }
00717   }
00718 
00719   jd.real_value = x.real_value > 0.0 ? (3.0 * atoms.size() - 4.0 - divergence) / x.real_value : 0.0;
00720 }
00721 
00722 
00723 
00724 colvar::logmsd::logmsd (std::string const &conf)
00725   : orientation (conf)
00726 {
00727   b_inverse_gradients = true;
00728   b_Jacobian_derivative = true;
00729   function_type = "logmsd";
00730   x.type (colvarvalue::type_scalar);
00731 
00732   ref_pos_sum2 = 0.0;
00733   for (size_t i = 0; i < ref_pos.size(); i++) {
00734     ref_pos_sum2 += ref_pos[i].norm2();
00735   }
00736 }
00737 
00738   
00739 void colvar::logmsd::calc_value()
00740 {
00741   atoms.reset_atoms_data();
00742   atoms.read_positions();
00743 
00744   if (cvm::debug())
00745     cvm::log ("colvar::logmsd: current com: "+
00746               cvm::to_str (atoms.center_of_mass())+"\n");
00747 
00748   atoms_cog = atoms.center_of_geometry();
00749   rot.calc_optimal_rotation (ref_pos, atoms.positions_shifted (-1.0 * atoms_cog));
00750 
00751   cvm::real group_pos_sum2 = 0.0;
00752   for (size_t i = 0; i < atoms.size(); i++) {
00753     group_pos_sum2 += (atoms[i].pos-atoms_cog).norm2();
00754   }
00755 
00756   // value of the MSD (Coutsias et al)
00757   MSD = 1.0/(cvm::real (atoms.size())) *
00758     ( group_pos_sum2 + ref_pos_sum2 - 2.0 * rot.lambda );
00759 
00760   x.real_value = (MSD > 0.0) ? std::log(MSD) : 0.0;
00761 }
00762 
00763 
00764 void colvar::logmsd::calc_gradients()
00765 {
00766   cvm::real fact = (MSD > 0.0) ? 2.0/(cvm::real (atoms.size()) * MSD) : 0.0;
00767 
00768   for (size_t ia = 0; ia < atoms.size(); ia++) {
00769     atoms[ia].grad = fact * (atoms[ia].pos - atoms_cog - rot.dL0_2[ia]);
00770   }
00771 }
00772 
00773 
00774 void colvar::logmsd::apply_force (colvarvalue const &force)
00775 {
00776   if (!atoms.noforce)
00777     atoms.apply_colvar_force (force.real_value);
00778 }
00779 
00780 
00781 void colvar::logmsd::calc_force_invgrads()
00782 {
00783   atoms.read_system_forces();
00784   ft.real_value = 0.0;
00785     
00786   // Note: gradient square norm is 4.0 / (N_atoms * E)
00787           
00788   for (size_t ia = 0; ia < atoms.size(); ia++) {
00789     ft.real_value += atoms[ia].grad * atoms[ia].system_force;
00790   }
00791   ft.real_value *= atoms.size() * MSD / 4.0;
00792 }
00793 
00794 
00795 void colvar::logmsd::calc_Jacobian_derivative()
00796 {
00797   // divergence of the back-rotated target coordinates
00798   cvm::real divergence = 0.0;
00799  
00800   // gradient of the rotation matrix
00801   cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat;
00802 
00803   // gradients of products of 2 quaternion components 
00804   cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23;
00805  
00806   for (size_t ia = 0; ia < atoms.size(); ia++) {
00807 
00808     // Gradient of optimal quaternion wrt current Cartesian position
00809     cvm::vector1d< cvm::rvector, 4 >      &dq = rot.dQ0_2[ia];
00810 
00811     g11 = 2.0 * (rot.q)[1]*dq[1];
00812     g22 = 2.0 * (rot.q)[2]*dq[2];
00813     g33 = 2.0 * (rot.q)[3]*dq[3];
00814     g01 = (rot.q)[0]*dq[1] + (rot.q)[1]*dq[0];
00815     g02 = (rot.q)[0]*dq[2] + (rot.q)[2]*dq[0];
00816     g03 = (rot.q)[0]*dq[3] + (rot.q)[3]*dq[0];
00817     g12 = (rot.q)[1]*dq[2] + (rot.q)[2]*dq[1];
00818     g13 = (rot.q)[1]*dq[3] + (rot.q)[3]*dq[1];
00819     g23 = (rot.q)[2]*dq[3] + (rot.q)[3]*dq[2];
00820 
00821     // Gradient of the rotation matrix wrt current Cartesian position
00822     // Note: we are only going to use "diagonal" terms: grad_rot_mat[i][j][i]
00823     grad_rot_mat[0][0] = -2.0 * (g22 + g33); 
00824     grad_rot_mat[1][0] =  2.0 * (g12 + g03); 
00825     grad_rot_mat[2][0] =  2.0 * (g13 - g02); 
00826     grad_rot_mat[0][1] =  2.0 * (g12 - g03); 
00827     grad_rot_mat[1][1] = -2.0 * (g11 + g33); 
00828     grad_rot_mat[2][1] =  2.0 * (g01 + g23); 
00829     grad_rot_mat[0][2] =  2.0 * (g02 + g13); 
00830     grad_rot_mat[1][2] =  2.0 * (g23 - g01); 
00831     grad_rot_mat[2][2] = -2.0 * (g11 + g22); 
00832 
00833     cvm::atom_pos &y = ref_pos[ia]; 
00834 
00835     for (size_t i = 0; i < 3; i++) {
00836       for (size_t j = 0; j < 3; j++) {
00837         divergence += grad_rot_mat[i][j][i] * y[j];
00838       }
00839     }
00840   }
00841 
00842   jd.real_value = (3.0 * atoms.size() - 3.0 - divergence) / 2.0;
00843 }
00844 
00845 
00846 
00847 colvar::eigenvector::eigenvector (std::string const &conf)
00848   : cvc (conf)
00849 {
00850   b_inverse_gradients = true;
00851   b_Jacobian_derivative = true;
00852   function_type = "eigenvector";
00853   x.type (colvarvalue::type_scalar);
00854 
00855   parse_group (conf, "atoms", atoms);
00856   atom_groups.push_back (&atoms);
00857 
00858   if (atoms.b_rotate) {
00859     cvm::fatal_error ("Error: rotateReference should be disabled:"
00860         "eigenvector component will set it internally.");
00861   }
00862 
00863   if (get_keyval (conf, "refPositions", ref_pos, ref_pos)) {
00864     cvm::log ("Using reference positions from input file.\n");
00865     if (ref_pos.size() != atoms.size()) {
00866       cvm::fatal_error ("Error: reference positions do not "
00867                         "match the number of requested atoms.\n");
00868     }
00869   }
00870 
00871   {
00872     std::string file_name;
00873     if (get_keyval (conf, "refPositionsFile", file_name)) {
00874 
00875       std::string file_col;
00876       double file_col_value;
00877       if (get_keyval (conf, "refPositionsCol", file_col, std::string (""))) {
00878         // use PDB flags if column is provided
00879         bool found = get_keyval (conf, "refPositionsColValue", file_col_value, 0.0);
00880         if (found && !file_col_value)
00881           cvm::fatal_error ("Error: refPositionsColValue, "
00882                             "if provided, must be non-zero.\n");
00883       } else {
00884         // if not, use atom indices
00885         atoms.create_sorted_ids();
00886       }
00887 
00888       ref_pos.resize (atoms.size());
00889       cvm::load_coords (file_name.c_str(), ref_pos, atoms.sorted_ids, file_col, file_col_value);
00890     }
00891   }
00892 
00893   // Set mobile frame of reference for atom group
00894   atoms.b_center = true;
00895   atoms.b_rotate = true;
00896   atoms.ref_pos = ref_pos;
00897 
00898   // now load the eigenvector
00899   if (get_keyval (conf, "vector", eigenvec, eigenvec)) {
00900     cvm::log ("Using vector components from input file.\n");
00901     if (eigenvec.size() != atoms.size()) {
00902       cvm::fatal_error ("Error: vector components do not "
00903                         "match the number of requested atoms.\n");
00904     }
00905   }
00906 
00907   {
00908     std::string file_name;
00909     if (get_keyval (conf, "vectorFile", file_name)) {
00910 
00911       std::string file_col;
00912       if (!get_keyval (conf, "vectorCol", file_col, std::string (""))) {
00913         cvm::fatal_error ("Error: parameter vectorCol is required if vectorFile is set.\n");
00914       }
00915 
00916       double file_col_value;
00917       bool found = get_keyval (conf, "vectorColValue", file_col_value, 0.0);
00918       if (found && !file_col_value)
00919         cvm::fatal_error ("Error: eigenvectorColValue, "
00920                           "if provided, must be non-zero.\n");
00921 
00922       eigenvec.resize (atoms.size());
00923       cvm::load_coords (file_name.c_str(), eigenvec, atoms.sorted_ids, file_col, file_col_value);
00924     }
00925   }
00926 
00927   if (!ref_pos.size() || !eigenvec.size()) {
00928     cvm::fatal_error ("Error: both reference coordinates"
00929                       "and eigenvector must be defined.\n");
00930   }
00931 
00932   cvm::rvector center (0.0, 0.0, 0.0);
00933   eigenvec_invnorm2 = 0.0;
00934 
00935   for (size_t i = 0; i < atoms.size(); i++) {
00936     center += eigenvec[i];
00937   }
00938 
00939   cvm::log ("Subtracting sum of eigenvector components: " + cvm::to_str (center) + "\n");
00940 
00941   for (size_t i = 0; i < atoms.size(); i++) {
00942     eigenvec[i] = eigenvec[i] - center;
00943     eigenvec_invnorm2 += eigenvec[i].norm2();
00944   }
00945   eigenvec_invnorm2 = 1.0 / eigenvec_invnorm2;
00946 
00947   // request derivatives of optimal rotation wrt 2nd group
00948   // for Jacobian
00949   atoms.rot.request_group1_gradients(atoms.size());
00950   atoms.rot.request_group2_gradients(atoms.size());
00951 }
00952 
00953   
00954 void colvar::eigenvector::calc_value()
00955 {
00956   atoms.reset_atoms_data();
00957   atoms.read_positions(); // this will also update atoms.rot
00958 
00959   x.real_value = 0.0;
00960   for (size_t i = 0; i < atoms.size(); i++) {
00961     x.real_value += (atoms[i].pos - ref_pos[i]) * eigenvec[i];
00962   }
00963 }
00964 
00965 
00966 void colvar::eigenvector::calc_gradients()
00967 {
00968   // There are two versions of this code
00969   // The simple version is not formally exact, but its
00970   // results are numerically indistinguishable from the
00971   // exact one. The exact one is more expensive and possibly
00972   // less stable in cases where the optimal rotation
00973   // becomes ill-defined.
00974 
00975   // Version A: simple, intuitive, cheap, robust. Wrong.
00976   // Works just fine in practice.
00977   for (size_t ia = 0; ia < atoms.size(); ia++) {
00978     atoms[ia].grad = eigenvec[ia];
00979   }
00980 
00981 /*
00982   // Version B: complex, expensive, fragile. Right.
00983  
00984   // gradient of the rotation matrix
00985   cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat;
00986 
00987   cvm::quaternion &quat0 = atoms.rot.q;
00988 
00989   // gradients of products of 2 quaternion components
00990   cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23;
00991 
00992   // a term that involves the rotation gradients
00993   cvm::rvector rot_grad_term;
00994 
00995   cvm::atom_pos x_relative;
00996   cvm::atom_pos atoms_cog = atoms.center_of_geometry();
00997 
00998   for (size_t ia = 0; ia < atoms.size(); ia++) {
00999 
01000     // Gradient of optimal quaternion wrt current Cartesian position
01001     // WARNING: we want derivatives wrt the FIRST group here (unlike RMSD)
01002     cvm::vector1d< cvm::rvector, 4 >      &dq = atoms.rot.dQ0_1[ia];
01003 
01004     g11 = 2.0 * quat0[1]*dq[1];
01005     g22 = 2.0 * quat0[2]*dq[2];
01006     g33 = 2.0 * quat0[3]*dq[3];
01007     g01 = quat0[0]*dq[1] + quat0[1]*dq[0];
01008     g02 = quat0[0]*dq[2] + quat0[2]*dq[0];
01009     g03 = quat0[0]*dq[3] + quat0[3]*dq[0];
01010     g12 = quat0[1]*dq[2] + quat0[2]*dq[1];
01011     g13 = quat0[1]*dq[3] + quat0[3]*dq[1];
01012     g23 = quat0[2]*dq[3] + quat0[3]*dq[2];
01013 
01014     // Gradient of the rotation matrix wrt current Cartesian position
01015     grad_rot_mat[0][0] = -2.0 * (g22 + g33);
01016     grad_rot_mat[1][0] =  2.0 * (g12 + g03);
01017     grad_rot_mat[2][0] =  2.0 * (g13 - g02);
01018     grad_rot_mat[0][1] =  2.0 * (g12 - g03);
01019     grad_rot_mat[1][1] = -2.0 * (g11 + g33);
01020     grad_rot_mat[2][1] =  2.0 * (g01 + g23);
01021     grad_rot_mat[0][2] =  2.0 * (g02 + g13);
01022     grad_rot_mat[1][2] =  2.0 * (g23 - g01);
01023     grad_rot_mat[2][2] = -2.0 * (g11 + g22);
01024 
01025     // this term needs to be rotated back, so we sum it separately
01026     rot_grad_term.reset();
01027 
01028     for (size_t ja = 0; ja < atoms.size(); ja++) {
01029       x_relative = atoms[ja].pos - atoms_cog;
01030 
01031       for (size_t i = 0; i < 3; i++) {
01032         for (size_t j = 0; j < 3; j++) {
01033           rot_grad_term += eigenvec[ja][i] * grad_rot_mat[i][j] * x_relative[j];
01034         }
01035       }
01036     }
01037 
01038     // Rotate correction term back to reference frame
01039     atoms[ia].grad = eigenvec[ia] + quat0.rotate (rot_grad_term);
01040   }
01041 */
01042 }
01043 
01044 
01045 void colvar::eigenvector::apply_force (colvarvalue const &force)
01046 {
01047   if (!atoms.noforce)
01048     atoms.apply_colvar_force (force.real_value);
01049 }
01050 
01051 
01052 void colvar::eigenvector::calc_force_invgrads()
01053 {
01054   atoms.read_system_forces();
01055   ft.real_value = 0.0;
01056     
01057   for (size_t ia = 0; ia < atoms.size(); ia++) {
01058     ft.real_value += eigenvec_invnorm2 * atoms[ia].grad *
01059       atoms[ia].system_force;
01060   }
01061 }
01062 
01063 
01064 void colvar::eigenvector::calc_Jacobian_derivative()
01065 {
01066   // gradient of the rotation matrix
01067   cvm::matrix2d <cvm::rvector, 3, 3> grad_rot_mat;
01068   cvm::quaternion &quat0 = atoms.rot.q;
01069 
01070   // gradients of products of 2 quaternion components 
01071   cvm::rvector g11, g22, g33, g01, g02, g03, g12, g13, g23;
01072 
01073   cvm::atom_pos x_relative; 
01074   cvm::real sum = 0.0;
01075 
01076   for (size_t ia = 0; ia < atoms.size(); ia++) {
01077 
01078     // Gradient of optimal quaternion wrt current Cartesian position
01079     // trick: d(R^-1)/dx = d(R^t)/dx = (dR/dx)^t
01080     // we can just transpose the derivatives of the direct matrix
01081     cvm::vector1d< cvm::rvector, 4 >      &dq_1 = atoms.rot.dQ0_1[ia];
01082 
01083     g11 = 2.0 * quat0[1]*dq_1[1];
01084     g22 = 2.0 * quat0[2]*dq_1[2];
01085     g33 = 2.0 * quat0[3]*dq_1[3];
01086     g01 = quat0[0]*dq_1[1] + quat0[1]*dq_1[0];
01087     g02 = quat0[0]*dq_1[2] + quat0[2]*dq_1[0];
01088     g03 = quat0[0]*dq_1[3] + quat0[3]*dq_1[0];
01089     g12 = quat0[1]*dq_1[2] + quat0[2]*dq_1[1];
01090     g13 = quat0[1]*dq_1[3] + quat0[3]*dq_1[1];
01091     g23 = quat0[2]*dq_1[3] + quat0[3]*dq_1[2];
01092 
01093     // Gradient of the inverse rotation matrix wrt current Cartesian position
01094     // (transpose of the gradient of the direct rotation)
01095     grad_rot_mat[0][0] = -2.0 * (g22 + g33); 
01096     grad_rot_mat[0][1] =  2.0 * (g12 + g03); 
01097     grad_rot_mat[0][2] =  2.0 * (g13 - g02); 
01098     grad_rot_mat[1][0] =  2.0 * (g12 - g03); 
01099     grad_rot_mat[1][1] = -2.0 * (g11 + g33); 
01100     grad_rot_mat[1][2] =  2.0 * (g01 + g23); 
01101     grad_rot_mat[2][0] =  2.0 * (g02 + g13); 
01102     grad_rot_mat[2][1] =  2.0 * (g23 - g01); 
01103     grad_rot_mat[2][2] = -2.0 * (g11 + g22); 
01104 
01105     for (size_t i = 0; i < 3; i++) {
01106       for (size_t j = 0; j < 3; j++) {
01107         sum += grad_rot_mat[i][j][i] * eigenvec[ia][j];
01108       }
01109     }
01110   }
01111 
01112   jd.real_value = sum * std::sqrt (eigenvec_invnorm2); 
01113 }

Generated on Fri May 25 04:07:13 2012 for NAMD by  doxygen 1.3.9.1