1 /* 2 * Copyright 2009-2021 The VOTCA Development Team (http://www.votca.org) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 // Standard includes 19 #include <cmath> 20 #include <cstdio> 21 #include <fstream> 22 #include <iostream> 23 #include <memory> 24 #include <sstream> 25 26 // VOTCA includes 27 #include <votca/tools/cubicspline.h> 28 #include <votca/tools/linalg.h> 29 #include <votca/tools/table.h> 30 31 // Local VOTCA includes 32 #include "votca/csg/beadlist.h" 33 #include "votca/csg/nblistgrid.h" 34 #include "votca/csg/nblistgrid_3body.h" 35 36 // Local private includes 37 #include "csg_fmatch.h" 38 39 int main(int argc, char **argv) { 40 CGForceMatching app; 41 return app.Exec(argc, argv); 42 } 43 44 void CGForceMatching::Initialize(void) { 45 CsgApplication::Initialize(); 46 AddProgramOptions()("options", boost::program_options::value<string>(), 47 " options file for coarse graining")( 48 "trj-force", boost::program_options::value<string>(), 49 " coarse-grained trajectory containing forces of already known " 50 "interactions"); 51 } 52 53 bool CGForceMatching::EvaluateOptions() { 54 CsgApplication::EvaluateOptions(); 55 CheckRequired("trj", "no trajectory file specified"); 56 CheckRequired("options", "need to specify options file"); 57 LoadOptions(OptionsMap()["options"].as<string>()); 58 59 has_existing_forces_ = false; 60 if (OptionsMap().count("trj-force")) { 61 has_existing_forces_ = true; 62 } 63 return true; 64 } 65 66 void CGForceMatching::BeginEvaluate(Topology *top, Topology *) { 67 // set counters to zero value: 68 nblocks_ = 0; 69 line_cntr_ = col_cntr_ = 0; 70 71 // Number of CG beads in topology 72 nbeads_ = top->BeadCount(); 73 // Set frame counter to zero 74 frame_counter_ = 0; 75 76 // accuracy for evaluating the difference in bead positions (default 1e-5) 77 dist_ = 1e-5; 78 if (options_.exists("cg.fmatch.dist")) { 79 dist_ = options_.get("cg.fmatch.dist").as<double>(); 80 } 81 82 // read nframes_ from input file 83 nframes_ = options_.get("cg.fmatch.frames_per_block").as<votca::Index>(); 84 // read constr_least_sq_ from input file 85 constr_least_sq_ = options_.get("cg.fmatch.constrainedLS").as<bool>(); 86 87 // initializing bonded interactions 88 for (votca::tools::Property *prop : bonded_) { 89 // add spline to container 90 splines_.emplace_back(splines_.size(), true, col_cntr_, prop); 91 // adjust initial Eigen::Matrix3d dimensions: 92 line_cntr_ += splines_.back().num_gridpoints; 93 col_cntr_ += 2 * splines_.back().num_gridpoints; 94 // if periodic potential, one additional constraint has to be taken into 95 // account -> 1 additional line in matrix 96 if (splines_.back().periodic != 0) { 97 line_cntr_ += 1; 98 } 99 } 100 101 // initializing non-bonded interactions 102 for (votca::tools::Property *prop : nonbonded_) { 103 // add spline to container 104 splines_.emplace_back(splines_.size(), false, col_cntr_, prop); 105 // adjust initial Eigen::Matrix3d dimensions: 106 // number of constraints/restraints 107 line_cntr_ += splines_.back().num_gridpoints; 108 // number of coefficients 109 col_cntr_ += 2 * splines_.back().num_gridpoints; 110 111 // preliminary: use also spline functions for the threebody interaction. So 112 // far only angular interaction implemented 113 } 114 115 cout << "\nYou are using VOTCA!\n"; 116 cout << "\nhey, somebody wants to forcematch!\n"; 117 118 // now initialize A_, b_, x_ and probably B_constr_ 119 // depending on least-squares algorithm used 120 if (constr_least_sq_) { // Constrained Least Squares 121 122 cout << "\nUsing constrained Least Squares!\n " << endl; 123 124 // assign least_sq_offset_ 125 least_sq_offset_ = 0; 126 127 // resize and clear B_constr_ 128 B_constr_ = Eigen::MatrixXd::Zero(line_cntr_, col_cntr_); 129 130 // resize Eigen::Matrix3d A_ 131 A_ = Eigen::MatrixXd::Zero(3 * nbeads_ * nframes_, col_cntr_); 132 // resize vector b_ 133 b_ = Eigen::VectorXd::Zero(3 * nbeads_ * nframes_); 134 135 // in case of constrained least squares smoothing conditions 136 // are assigned to Eigen::Matrix3d B_constr_ 137 FmatchAssignSmoothCondsToMatrix(B_constr_); 138 } else { // Simple Least Squares 139 140 cout << "\nUsing simple Least Squares! " << endl; 141 // assign least_sq_offset_ 142 least_sq_offset_ = line_cntr_; 143 144 // resize Eigen::Matrix3d A_ 145 A_ = Eigen::MatrixXd::Zero(line_cntr_ + 3 * nbeads_ * nframes_, col_cntr_); 146 // resize vector b_ 147 b_ = Eigen::VectorXd::Zero(line_cntr_ + 3 * nbeads_ * nframes_); 148 149 // in case of simple least squares smoothing conditions 150 // are assigned to Eigen::Matrix3d A_ 151 FmatchAssignSmoothCondsToMatrix(A_); 152 // clear b_ (only necessary in simple least squares) 153 b_.setZero(); 154 } 155 // resize and clear x_ 156 x_ = Eigen::VectorXd::Zero(col_cntr_); 157 158 if (has_existing_forces_) { 159 top_force_.CopyTopologyData(top); 160 trjreader_force_ = 161 TrjReaderFactory().Create(OptionsMap()["trj-force"].as<string>()); 162 if (trjreader_force_ == nullptr) { 163 throw runtime_error(string("input format not supported: ") + 164 OptionsMap()["trj-force"].as<string>()); 165 } 166 // open the trajectory 167 trjreader_force_->Open(OptionsMap()["trj-force"].as<string>()); 168 // read in first frame 169 trjreader_force_->FirstFrame(top_force_); 170 } 171 } 172 173 CGForceMatching::SplineInfo::SplineInfo(votca::Index index, 174 bool bonded_interaction, 175 votca::Index matr_pos_col, 176 votca::tools::Property *options) { 177 // initialize standard data 178 splineIndex = index; 179 options_ = options; 180 splineName = options->get("name").value(); 181 bonded = bonded_interaction; 182 // in general natural boundary conditions are used for splines (default is no) 183 periodic = 0; 184 // check if non-bonded 3-body interaction or not (default is no) 185 threebody = 0; 186 // initialize additional parameters for threebody interactions 187 //(values of Molinero water potential) 188 a = 0.37; //(0.37 nm) 189 sigma = 1; //(dimensionless) 190 gamma = 0.12; //(0.12 nm = 1.2 Ang) 191 192 // get non-bonded information 193 if (!bonded) { 194 // check if option threebody exists 195 if (options->exists("threebody")) { 196 threebody = options->get("threebody").as<bool>(); 197 } 198 // check if threebody interaction or not 199 if (threebody) { 200 type1 = options->get("type1").value(); 201 type2 = options->get("type2").value(); 202 type3 = options->get("type3").value(); 203 // read in additional parameters for threebody interactions 204 if (options->exists("fmatch.a")) { 205 a = options->get("fmatch.a").as<double>(); 206 } 207 if (options->exists("fmatch.sigma")) { 208 sigma = options->get("fmatch.sigma").as<double>(); 209 } 210 if (options->exists("fmatch.gamma")) { 211 gamma = options->get("fmatch.gamma").as<double>(); 212 } 213 } 214 // if not threebody only read in the two bead types 215 if (!threebody) { 216 type1 = options->get("type1").value(); 217 type2 = options->get("type2").value(); 218 } 219 } 220 if (bonded) { 221 // check if option periodic exists 222 if (options->exists("fmatch.periodic")) { 223 periodic = options->get("fmatch.periodic").as<bool>(); 224 // set cubic spline Spline boundary conditions to periodic 225 Spline.setBCInt(1); 226 } 227 } 228 std::cout << "a: " << a << " ,sigma: " << sigma << " ,gamma: " << gamma 229 << std::endl; 230 231 // initialize the grid 232 double grid_min = options->get("fmatch.min").as<double>(); 233 double grid_max = options->get("fmatch.max").as<double>(); 234 double grid_step = options->get("fmatch.step").as<double>(); 235 236 // GenerateGrid returns number of grid points. We subtract 1 to get 237 // the number of spline functions 238 num_gridpoints = Spline.GenerateGrid(grid_min, grid_max, grid_step); 239 num_splinefun = num_gridpoints - 1; 240 241 cout << "Number of spline functions for the interaction " << splineName << ":" 242 << num_splinefun << endl; 243 244 matr_pos = matr_pos_col; 245 246 // initialize grid for block averaging 247 dx_out = options->get("fmatch.out_step").as<double>(); 248 // number of output grid points 249 num_outgrid = 1 + (votca::Index)((grid_max - grid_min) / dx_out); 250 result = Eigen::VectorXd::Zero(num_outgrid); 251 error = Eigen::VectorXd::Zero(num_outgrid); 252 resSum = Eigen::VectorXd::Zero(num_outgrid); 253 resSum2 = Eigen::VectorXd::Zero(num_outgrid); 254 255 // Only if threebody interaction, the derivatives are explicitly calculated 256 if (threebody) { 257 resultDer = Eigen::VectorXd::Zero(num_outgrid); 258 errorDer = Eigen::VectorXd::Zero(num_outgrid); 259 resSumDer = Eigen::VectorXd::Zero(num_outgrid); 260 resSumDer2 = Eigen::VectorXd::Zero(num_outgrid); 261 } 262 263 block_res_f = Eigen::VectorXd::Zero(num_outgrid); 264 block_res_f2 = Eigen::VectorXd::Zero(num_outgrid); 265 } 266 267 void CGForceMatching::EndEvaluate() { 268 // sanity check 269 if (nblocks_ == 0) { 270 cerr << "\nERROR in CGForceMatching::EndEvaluate - No blocks have been " 271 "processed so far" 272 << endl; 273 cerr << "It might be that you are using trajectory, which is smaller than " 274 "needed for one block" 275 << endl; 276 cerr << "Check your input!" << endl; 277 exit(-1); 278 } 279 280 cout << "\nWe are done, thank you very much!" << endl; 281 if (has_existing_forces_) { 282 trjreader_force_->Close(); 283 } 284 } 285 286 void CGForceMatching::WriteOutFiles() { 287 string file_extension = ".force"; 288 string file_extension_pot = ".pot"; 289 string file_name; 290 string file_nameDer; 291 votca::tools::Table force_tab; 292 votca::tools::Table force_tabDer; 293 294 // table with error column 295 force_tab.SetHasYErr(true); 296 force_tabDer.SetHasYErr(true); 297 298 for (SplineInfo &spline : splines_) { 299 // construct meaningful outfile name 300 file_name = spline.splineName; 301 302 // resize table 303 force_tab.resize(spline.num_outgrid); 304 305 // If not threebody, the result represents the force 306 if (!(spline.threebody)) { 307 file_name = file_name + file_extension; 308 // print output file names on stdout 309 cout << "Updating file: " << file_name << endl; 310 } 311 312 // If threebody interaction, the result represents the potential and (-1) 313 // the derivative represents the force Only then, the derivatives are 314 // explicitly calculated 315 if (spline.threebody) { 316 file_name = file_name + file_extension_pot; 317 file_nameDer = spline.splineName; 318 file_nameDer = file_nameDer + file_extension; 319 320 force_tabDer.resize(spline.num_outgrid); 321 // print output file names on stdout 322 cout << "Updating files: " << file_name << " and: " << file_nameDer 323 << endl; 324 } 325 326 spline.result = (spline.resSum).array() / nblocks_; 327 spline.error = (((spline.resSum2).array() / nblocks_ - 328 (spline.result).array().abs2()) / 329 nblocks_) 330 .abs() 331 .sqrt(); 332 333 if (spline.threebody) { 334 spline.resultDer = (spline.resSumDer).array() / nblocks_; 335 spline.errorDer = (((spline.resSumDer2).array() / nblocks_ - 336 (spline.resultDer).array().abs2()) / 337 nblocks_) 338 .abs() 339 .sqrt(); 340 } 341 342 // first output point = first grid point 343 double out_x = spline.Spline.getGridPoint(0); 344 // loop over output grid 345 for (votca::Index i = 0; i < spline.num_outgrid; i++) { 346 347 // If not threebody the result is (-1) the force 348 if (!(spline.threebody)) { 349 // put point, result, flag and accuracy at point out_x into the table 350 force_tab.set(i, out_x, (-1.0) * spline.result[i], 'i', 351 spline.error[i]); 352 } 353 354 // If threebody interaction, force_tab represents the potential (-1) which 355 // is the Antiderivative of the force Only if threebody interaction, the 356 // derivatives are explicitly calculated 357 if (spline.threebody) { 358 // put point, result, flag and accuracy at point out_x into the table 359 force_tab.set(i, out_x, (+1.0) * spline.result[i], 'i', 360 spline.error[i]); 361 force_tabDer.set(i, out_x, (-1.0) * spline.resultDer[i], 'i', 362 spline.errorDer[i]); 363 } 364 365 // update out_x for the next iteration 366 out_x += spline.dx_out; 367 } 368 // save table in the file 369 force_tab.Save(file_name); 370 371 // clear the table for the next spline 372 force_tab.clear(); 373 374 // Only if threebody interaction, the derivatives are explicitly calculated 375 if (spline.threebody) { 376 force_tabDer.Save(file_nameDer); 377 // clear the table for the next spline 378 force_tabDer.clear(); 379 } 380 } 381 } 382 383 void CGForceMatching::EvalConfiguration(Topology *conf, Topology *) { 384 if (conf->BeadCount() == 0) { 385 throw std::runtime_error( 386 "CG Topology has 0 beads, check your mapping file!"); 387 } 388 if (has_existing_forces_) { 389 if (conf->BeadCount() != top_force_.BeadCount()) { 390 throw std::runtime_error( 391 "number of beads in topology and force topology does not match"); 392 } 393 for (votca::Index i = 0; i < conf->BeadCount(); ++i) { 394 conf->getBead(i)->F() -= top_force_.getBead(i)->getF(); 395 Eigen::Vector3d d = 396 conf->getBead(i)->getPos() - top_force_.getBead(i)->getPos(); 397 if (d.norm() > dist_) { // default is 1e-5, otherwise it can be a too 398 // strict criterion 399 throw std::runtime_error( 400 "One or more bead positions in mapped and reference force " 401 "trajectory differ by more than 1e-5"); 402 } 403 } 404 } 405 406 for (SplineInfo &sinfo : splines_) { 407 if (sinfo.bonded) { 408 EvalBonded(conf, &sinfo); 409 } else { 410 if (sinfo.threebody) { 411 EvalNonbonded_Threebody(conf, &sinfo); 412 } else { 413 EvalNonbonded(conf, &sinfo); 414 } 415 } 416 } 417 418 // loop for the forces vector: 419 // hack, change the Has functions.. 420 if (conf->getBead(0)->HasF()) { 421 for (votca::Index iatom = 0; iatom < nbeads_; ++iatom) { 422 const Eigen::Vector3d &Force = conf->getBead(iatom)->getF(); 423 b_(least_sq_offset_ + 3 * nbeads_ * frame_counter_ + iatom) = Force.x(); 424 b_(least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + iatom) = 425 Force.y(); 426 b_(least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + 427 iatom) = Force.z(); 428 } 429 } else { 430 throw std::runtime_error( 431 "\nERROR in csg_fmatch::EvalConfiguration - No forces in " 432 "configuration!"); 433 } 434 // update the frame counter 435 frame_counter_ += 1; 436 437 if (frame_counter_ % nframes_ == 0) { // at this point we processed nframes_ 438 // frames, which is enough for one 439 // block 440 // update block counter 441 nblocks_++; 442 // solve FM equations and accumulate the result 443 FmatchAccumulateData(); 444 // print status information 445 cout << "\nBlock No" << nblocks_ << " done!" << endl; 446 // write results to output files 447 WriteOutFiles(); 448 449 // we must count frames from zero again for the next block 450 frame_counter_ = 0; 451 if (constr_least_sq_) { // Constrained Least Squares 452 // Matrices should be cleaned after each block is evaluated 453 A_.setZero(); 454 b_.setZero(); 455 // clear and assign smoothing conditions to B_constr_ 456 FmatchAssignSmoothCondsToMatrix(B_constr_); 457 } else { // Simple Least Squares 458 // Matrices should be cleaned after each block is evaluated 459 // clear and assign smoothing conditions to A_ 460 FmatchAssignSmoothCondsToMatrix(A_); 461 b_.setZero(); 462 } 463 } 464 if (has_existing_forces_) { 465 trjreader_force_->NextFrame(top_force_); 466 } 467 } 468 469 void CGForceMatching::FmatchAccumulateData() { 470 if (constr_least_sq_) { // Constrained Least Squares 471 // Solving linear equations system 472 x_ = votca::tools::linalg_constrained_qrsolve(A_, b_, B_constr_); 473 } else { // Simple Least Squares 474 475 Eigen::HouseholderQR<Eigen::MatrixXd> dec(A_); 476 x_ = dec.solve(b_); 477 Eigen::VectorXd residual = b_ - A_ * x_; 478 // calculate FM residual - quality of FM 479 // FM residual is calculated in (kJ/(mol*nm))^2 480 double fm_resid = residual.cwiseAbs2().sum(); 481 482 fm_resid /= (double)(3 * nbeads_ * frame_counter_); 483 484 cout << endl; 485 cout << "#### Force matching residual ####" << endl; 486 cout << " Chi_2[(kJ/(mol*nm))^2] = " << fm_resid << endl; 487 cout << "#################################" << endl; 488 cout << endl; 489 } 490 491 for (SplineInfo &sinfo : splines_) { 492 votca::Index mp = sinfo.matr_pos; 493 votca::Index ngp = sinfo.num_gridpoints; 494 495 // x_ contains results for all splines. Here we cut the results for one 496 // spline 497 498 sinfo.block_res_f = x_.segment(mp, ngp); 499 sinfo.block_res_f2 = x_.segment(mp + ngp, ngp); 500 501 // result cut before is assigned to the corresponding spline 502 sinfo.Spline.setSplineData(sinfo.block_res_f, sinfo.block_res_f2); 503 504 // first output point = first grid point 505 double out_x = sinfo.Spline.getGridPoint(0); 506 507 // point in the middle of the output grid for printing debug information 508 votca::Index grid_point_debug = sinfo.num_outgrid / 2; 509 510 // loop over output grid 511 for (votca::Index i = 0; i < sinfo.num_outgrid; i++) { 512 // update resSum (add result of a particular block) 513 sinfo.resSum[i] += sinfo.Spline.Calculate(out_x); 514 // update resSum2 (add result of a particular block) 515 sinfo.resSum2[i] += 516 sinfo.Spline.Calculate(out_x) * sinfo.Spline.Calculate(out_x); 517 518 // Only if threebody interaction, the derivatives are explicitly 519 // calculated 520 if (sinfo.threebody) { 521 sinfo.resSumDer[i] += sinfo.Spline.CalculateDerivative(out_x); 522 // update resSumDer2 (add result of a particular block) 523 sinfo.resSumDer2[i] += sinfo.Spline.CalculateDerivative(out_x) * 524 sinfo.Spline.CalculateDerivative(out_x); 525 } 526 527 // print useful debug information 528 if (i == grid_point_debug) { 529 cout << "This should be a number: " << sinfo.Spline.Calculate(out_x) 530 << " " << endl; 531 } 532 533 // output point for the next iteration 534 out_x += sinfo.dx_out; 535 } 536 } 537 } 538 539 void CGForceMatching::FmatchAssignSmoothCondsToMatrix(Eigen::MatrixXd &Matrix) { 540 // This function assigns Spline smoothing conditions to the Matrix. 541 // For the simple least squares the function is used for Eigen::Matrix3d A_ 542 // For constrained least squares - for Eigen::Matrix3d B_constr_ 543 544 Matrix.setZero(); 545 votca::Index line_tmp = 0; 546 votca::Index col_tmp = 0; 547 548 for (SplineInfo &sinfo : splines_) { 549 550 sinfo.Spline.AddBCToFitMatrix(Matrix, line_tmp, col_tmp); 551 // if periodic potential, one additional constraint has to be taken into 552 // account! 553 if (sinfo.periodic != 0) { 554 sinfo.Spline.AddBCSumZeroToFitMatrix(Matrix, line_tmp, col_tmp); 555 // update counter 556 line_tmp += 1; 557 } 558 // update counters 559 votca::Index sfnum = sinfo.num_splinefun; 560 line_tmp += sfnum + 1; 561 col_tmp += 2 * (sfnum + 1); 562 } 563 } 564 565 void CGForceMatching::LoadOptions(const string &file) { 566 options_.LoadFromXML(file); 567 bonded_ = options_.Select("cg.bonded"); 568 nonbonded_ = options_.Select("cg.non-bonded"); 569 } 570 571 void CGForceMatching::EvalBonded(Topology *conf, SplineInfo *sinfo) { 572 573 std::vector<Interaction *> interList = 574 conf->InteractionsInGroup(sinfo->splineName); 575 576 for (Interaction *inter : interList) { 577 578 votca::Index beads_in_int = inter->BeadCount(); // 2 for bonds, 3 for 579 // angles, 4 for dihedrals 580 581 votca::tools::CubicSpline &SP = sinfo->Spline; 582 583 votca::Index mpos = sinfo->matr_pos; 584 585 double var = inter->EvaluateVar(*conf); // value of bond, angle, 586 // or dihedral 587 588 for (votca::Index loop = 0; loop < beads_in_int; loop++) { 589 votca::Index ii = inter->getBeadId(loop); 590 Eigen::Vector3d gradient = inter->Grad(*conf, loop); 591 592 SP.AddToFitMatrix(A_, var, 593 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + ii, 594 mpos, -gradient.x()); 595 SP.AddToFitMatrix( 596 A_, var, 597 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + ii, mpos, 598 -gradient.y()); 599 SP.AddToFitMatrix( 600 A_, var, 601 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + ii, 602 mpos, -gradient.z()); 603 } 604 } 605 } 606 607 void CGForceMatching::EvalNonbonded(Topology *conf, SplineInfo *sinfo) { 608 609 // generate the neighbour list 610 std::unique_ptr<NBList> nb; 611 612 bool gridsearch = false; 613 614 if (options_.exists("cg.nbsearch")) { 615 if (options_.get("cg.nbsearch").as<string>() == "grid") { 616 gridsearch = true; 617 } else if (options_.get("cg.nbsearch").as<string>() == "simple") { 618 gridsearch = false; 619 } else { 620 throw std::runtime_error("cg.nbsearch invalid, can be grid or simple"); 621 } 622 } 623 if (gridsearch) { 624 nb = std::make_unique<NBListGrid>(); 625 } else { 626 nb = std::make_unique<NBList>(); 627 } 628 629 nb->setCutoff( 630 sinfo->options_->get("fmatch.max").as<double>()); // implement different 631 // cutoffs for 632 // different 633 // interactions! 634 635 // generate the bead lists 636 BeadList beads1, beads2; 637 beads1.Generate(*conf, sinfo->type1); 638 beads2.Generate(*conf, sinfo->type2); 639 640 // is it same types or different types? 641 if (sinfo->type1 == sinfo->type2) { 642 nb->Generate(beads1, true); 643 } else { 644 nb->Generate(beads1, beads2, true); 645 } 646 647 for (BeadPair *pair : *nb) { 648 votca::Index iatom = pair->first()->getId(); 649 votca::Index jatom = pair->second()->getId(); 650 double var = pair->dist(); 651 Eigen::Vector3d gradient = pair->r(); 652 gradient.normalize(); 653 654 votca::tools::CubicSpline &SP = sinfo->Spline; 655 656 votca::Index mpos = sinfo->matr_pos; 657 658 // add iatom 659 SP.AddToFitMatrix(A_, var, 660 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + iatom, 661 mpos, gradient.x()); 662 SP.AddToFitMatrix( 663 A_, var, 664 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + iatom, mpos, 665 gradient.y()); 666 SP.AddToFitMatrix( 667 A_, var, 668 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + iatom, 669 mpos, gradient.z()); 670 671 // add jatom 672 SP.AddToFitMatrix(A_, var, 673 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + jatom, 674 mpos, -gradient.x()); 675 SP.AddToFitMatrix( 676 A_, var, 677 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + jatom, mpos, 678 -gradient.y()); 679 SP.AddToFitMatrix( 680 A_, var, 681 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + jatom, 682 mpos, -gradient.z()); 683 } 684 } 685 686 void CGForceMatching::EvalNonbonded_Threebody(Topology *conf, 687 SplineInfo *sinfo) { 688 // so far option gridsearch ignored. Only simple search 689 690 // generate the neighbour list 691 std::unique_ptr<NBList_3Body> nb; 692 693 bool gridsearch = false; 694 695 if (options_.exists("cg.nbsearch")) { 696 if (options_.get("cg.nbsearch").as<string>() == "grid") { 697 gridsearch = true; 698 } else if (options_.get("cg.nbsearch").as<string>() == "simple") { 699 gridsearch = false; 700 } else { 701 throw std::runtime_error("cg.nbsearch invalid, can be grid or simple"); 702 } 703 } 704 if (gridsearch) { 705 nb = std::make_unique<NBListGrid_3Body>(); 706 } else { 707 nb = std::make_unique<NBList_3Body>(); 708 } 709 710 nb->setCutoff(sinfo->a); // implement different cutoffs for different 711 // interactions! 712 // Here, a is the distance between two beads of a triple, where the 3-body 713 // interaction is zero 714 715 // generate the bead lists 716 BeadList beads1, beads2, beads3; 717 beads1.Generate(*conf, sinfo->type1); 718 beads2.Generate(*conf, sinfo->type2); 719 beads3.Generate(*conf, sinfo->type3); 720 721 // check if type1 and type2 are the same 722 if (sinfo->type1 == sinfo->type2) { 723 // if all three types are the same 724 if (sinfo->type2 == sinfo->type3) { 725 nb->Generate(beads1, true); 726 } 727 // if type2 and type3 are different, use the Generate function for 2 bead 728 // types 729 if (sinfo->type2 != sinfo->type3) { 730 nb->Generate(beads1, beads3, true); 731 } 732 } 733 // if type1 != type2 734 if (sinfo->type1 != sinfo->type2) { 735 // if the last two types are the same, use Generate function with them as 736 // the first two bead types Neighborlist_3body is constructed in a way that 737 // the two equal bead types have two be the first 2 types 738 if (sinfo->type2 == sinfo->type3) { 739 nb->Generate(beads1, beads2, true); 740 } 741 if (sinfo->type2 != sinfo->type3) { 742 // type1 = type3 !=type2 743 if (sinfo->type1 == sinfo->type3) { 744 nb->Generate(beads2, beads1, true); 745 } 746 // type1 != type2 != type3 747 if (sinfo->type1 != sinfo->type3) { 748 nb->Generate(beads1, beads2, beads3, true); 749 } 750 } 751 } 752 753 for (BeadTriple *triple : *nb) { 754 votca::Index iatom = triple->bead1()->getId(); 755 votca::Index jatom = triple->bead2()->getId(); 756 votca::Index katom = triple->bead3()->getId(); 757 double distij = triple->dist12(); 758 double distik = triple->dist13(); 759 Eigen::Vector3d rij = triple->r12(); 760 Eigen::Vector3d rik = triple->r13(); 761 762 double gamma_sigma = (sinfo->gamma) * (sinfo->sigma); 763 double denomij = (distij - (sinfo->a) * (sinfo->sigma)); 764 double denomik = (distik - (sinfo->a) * (sinfo->sigma)); 765 double expij = std::exp(gamma_sigma / denomij); 766 double expik = std::exp(gamma_sigma / denomik); 767 768 votca::tools::CubicSpline &SP = sinfo->Spline; 769 770 votca::Index mpos = sinfo->matr_pos; 771 772 double var = 773 std::acos(rij.dot(rik) / sqrt(rij.squaredNorm() * rik.squaredNorm())); 774 775 double acos_prime = 776 1.0 / (sqrt(1 - std::pow(rij.dot(rik), 2) / 777 (distij * distik * distij * distik))); 778 779 Eigen::Vector3d gradient1 = 780 acos_prime * 781 ((rij + rik) / (distij * distik) - 782 rij.dot(rik) * (rik.squaredNorm() * rij + rij.squaredNorm() * rik) / 783 (distij * distij * distij * distik * distik * distik)) * 784 expij * expik; 785 Eigen::Vector3d gradient2 = 786 ((rij / distij) * (gamma_sigma / (denomij * denomij)) + 787 (rik / distik) * (gamma_sigma / (denomik * denomik))) * 788 expij * expik; 789 790 // add iatom 791 SP.AddToFitMatrix(A_, var, 792 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + iatom, 793 mpos, -gradient1.x(), -gradient2.x()); 794 SP.AddToFitMatrix( 795 A_, var, 796 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + iatom, mpos, 797 -gradient1.y(), -gradient2.y()); 798 SP.AddToFitMatrix( 799 A_, var, 800 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + iatom, 801 mpos, -gradient1.z(), -gradient2.z()); 802 803 // evaluate gradient1 and gradient2 for jatom: 804 gradient1 = acos_prime * 805 (-rik / (distij * distik) + 806 rij.dot(rik) * rij / (distik * distij * distij * distij)) * 807 expij * expik; 808 // gradient2 809 gradient2 = ((rij / distij) * (-1.0 * gamma_sigma / (denomij * denomij))) * 810 expij * expik; 811 812 // add jatom 813 SP.AddToFitMatrix(A_, var, 814 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + jatom, 815 mpos, -gradient1.x(), -gradient2.x()); 816 SP.AddToFitMatrix( 817 A_, var, 818 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + jatom, mpos, 819 -gradient1.y(), -gradient2.y()); 820 SP.AddToFitMatrix( 821 A_, var, 822 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + jatom, 823 mpos, -gradient1.z(), -gradient2.z()); 824 825 // evaluate gradient1 and gradient2 for katom: 826 gradient1 = acos_prime * 827 (-rij / (distij * distik) + 828 rij.dot(rik) * rik / (distij * distik * distik * distik)) * 829 expij * expik; 830 // gradient2 831 gradient2 = ((rik / distik) * (-1.0 * gamma_sigma / (denomik * denomik))) * 832 expij * expik; 833 834 // add katom 835 SP.AddToFitMatrix(A_, var, 836 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + katom, 837 mpos, -gradient1.x(), -gradient2.x()); 838 SP.AddToFitMatrix( 839 A_, var, 840 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + nbeads_ + katom, mpos, 841 -gradient1.y(), -gradient2.y()); 842 SP.AddToFitMatrix( 843 A_, var, 844 least_sq_offset_ + 3 * nbeads_ * frame_counter_ + 2 * nbeads_ + katom, 845 mpos, -gradient1.z(), -gradient2.z()); 846 } 847 } 848