Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2023 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 :
23 : #ifdef __PLUMED_HAS_LIBTORCH
24 : #include "colvar/Colvar.h"
25 : #include "core/ActionRegister.h"
26 : #include "core/PlumedMain.h"
27 : #include "tools/Communicator.h"
28 : #include "tools/Matrix.h"
29 : #include "core/GenericMolInfo.h"
30 : #include "core/ActionSet.h"
31 : #include "tools/File.h"
32 : #include "tools/OpenMP.h"
33 : #include <string>
34 : #include <cmath>
35 : #include <map>
36 : #include <numeric>
37 : #include <ctime>
38 : #include "tools/Random.h"
39 :
40 : #include <torch/torch.h>
41 : #include <torch/script.h>
42 :
43 : #ifndef M_PI
44 : #define M_PI 3.14159265358979323846
45 : #endif
46 :
47 : namespace PLMD {
48 : namespace isdb {
49 :
50 : //+PLUMEDOC ISDB_COLVAR EMMIVOX
51 : /*
52 : Bayesian single-structure and ensemble refinement with cryo-EM maps.
53 :
54 : This action implements the Bayesian approach for single-structure and ensemble refinement from cryo-EM maps introduced <a href="https://www.biorxiv.org/content/10.1101/2023.10.18.562710v1">here</a>.
55 : EMMIVox does not require fitting the cryo-EM map with a Gaussian Mixture Model, as done in \ref EMMI, but uses directly the voxels in the deposited map.
56 :
57 : When run in single-replica mode, this action allows atomistic, flexible refinement (and B-factors inference) of an individual structure into a density map.
58 : A coarse-grained forward model can also be used in combination with the MARTINI force field.
59 : Combined with a multi-replica framework (such as the -multi option in GROMACS), the user can model an ensemble of structures using
60 : the Metainference approach \cite Bonomi:2016ip . The approach can be used to model continous dynamics of flexible regions as well as semi-ordered waters, lipids, and ions.
61 :
62 : \warning
63 : To use EMMIVOX, PLUMED must be linked against the LibTorch library as described \ref ISDB "here"
64 :
65 : \par Examples
66 :
67 : Complete tutorials for single-structure and ensemble refinement can be found <a href="https://github.com/COSBlab/EMMIVox">here</a>.
68 :
69 : */
70 : //+ENDPLUMEDOC
71 :
72 : class EMMIVOX : public Colvar {
73 :
74 : private:
75 :
76 : // temperature in kbt
77 : double kbt_;
78 : // model - atom types
79 : std::vector<unsigned> Model_type_;
80 : // model - list of atom sigmas - one per atom type
81 : std::vector<Vector5d> Model_s_;
82 : // model - list of atom weights - one per atom type
83 : std::vector<Vector5d> Model_w_;
84 : // model - map between residue/chain IDs and list of atoms
85 : std::map< std::pair<unsigned,std::string>, std::vector<unsigned> > Model_resmap_;
86 : // model - list of residue/chain IDs per atom
87 : std::vector< std::pair<unsigned,std::string> > Model_res_;
88 : // model - list of neighboring voxels per atom
89 : std::vector< std::vector<unsigned> > Model_nb_;
90 : // model - map between residue/chain ID and bfactor
91 : std::map< std::pair<unsigned, std::string>, double> Model_b_;
92 : // model - global list of residue/chain IDs
93 : std::vector< std::pair<unsigned,std::string> > Model_rlist_;
94 : // model density
95 : std::vector<double> ovmd_;
96 :
97 : // data map - voxel position
98 : std::vector<Vector> Map_m_;
99 : // data map - density
100 : std::vector<double> ovdd_;
101 : // data map - error
102 : std::vector<double> exp_err_;
103 :
104 : // derivatives
105 : std::vector<Vector> ovmd_der_;
106 : std::vector<Vector> atom_der_;
107 : std::vector<double> score_der_;
108 : // constants
109 : double inv_sqrt2_, sqrt2_pi_, inv_pi2_;
110 : std::vector<Vector5d> pref_;
111 : std::vector<Vector5d> invs2_;
112 : std::vector<Vector5d> cfact_;
113 : std::vector<double> cut_;
114 : // metainference
115 : unsigned nrep_;
116 : unsigned replica_;
117 : std::vector<double> ismin_;
118 : // neighbor list
119 : double nl_dist_cutoff_;
120 : double nl_gauss_cutoff_;
121 : unsigned nl_stride_;
122 : bool first_time_;
123 : std::vector< std::pair<unsigned,unsigned> > nl_;
124 : std::vector< std::pair<unsigned,unsigned> > ns_;
125 : std::vector<Vector> refpos_;
126 : // averaging
127 : bool no_aver_;
128 : // correlation;
129 : bool do_corr_;
130 : // Monte Carlo stuff
131 : Random random_;
132 : // Scale and Offset
133 : double scale_;
134 : double offset_;
135 : // Bfact Monte Carlo
136 : double dbfact_;
137 : double bfactmin_;
138 : double bfactmax_;
139 : double bfactsig_;
140 : bool bfactnoc_;
141 : bool bfactread_;
142 : int MCBstride_;
143 : double MCBaccept_;
144 : double MCBtrials_;
145 : // residue neighbor list
146 : std::vector< std::vector<unsigned> > nl_res_;
147 : bool bfactemin_;
148 : // Martini scattering factors
149 : bool martini_;
150 : // status stuff
151 : unsigned int statusstride_;
152 : std::string statusfilename_;
153 : OFile statusfile_;
154 : bool first_status_;
155 : // total energy and virial
156 : double ene_;
157 : Tensor virial_;
158 : double eps_;
159 : // model density file
160 : unsigned int mapstride_;
161 : std::string mapfilename_;
162 : // Libtorch stuff
163 : bool gpu_;
164 : torch::Tensor ovmd_gpu_;
165 : torch::Tensor ovmd_der_gpu_;
166 : torch::Tensor ismin_gpu_;
167 : torch::Tensor ovdd_gpu_;
168 : torch::Tensor Map_m_gpu_;
169 : torch::Tensor pref_gpu_;
170 : torch::Tensor invs2_gpu_;
171 : torch::Tensor nl_id_gpu_;
172 : torch::Tensor nl_im_gpu_;
173 : torch::Tensor pref_nl_gpu_;
174 : torch::Tensor invs2_nl_gpu_;
175 : torch::Tensor Map_m_nl_gpu_;
176 : torch::DeviceType device_t_;
177 : //
178 : // write file with model density
179 : void write_model_density(long int step);
180 : // get median of vector
181 : double get_median(std::vector<double> v);
182 : // read and write status
183 : void read_status();
184 : void print_status(long int step);
185 : // accept or reject
186 : bool doAccept(double oldE, double newE, double kbt);
187 : // vector of close residues
188 : void get_close_residues();
189 : // do MonteCarlo for Bfactor
190 : void doMonteCarloBfact();
191 : // calculate model parameters
192 : std::vector<double> get_Model_param(std::vector<AtomNumber> &atoms);
193 : // read data file
194 : void get_exp_data(const std::string &datafile);
195 : // auxiliary methods
196 : void prepare_gpu();
197 : void initialize_Bfactor(double reso);
198 : void get_auxiliary_vectors();
199 : void push_auxiliary_gpu();
200 : // calculate overlap between two Gaussians
201 : double get_overlap(const Vector &d_m, const Vector &m_m,
202 : const Vector5d &cfact, const Vector5d &m_s, double bfact);
203 : // update the neighbor list
204 : void update_neighbor_list();
205 : // update data on device
206 : void update_gpu();
207 : // update the neighbor sphere
208 : void update_neighbor_sphere();
209 : bool do_neighbor_sphere();
210 : // calculate forward model and score on device
211 : void calculate_fmod();
212 : void calculate_score();
213 : // calculate correlation
214 : void calculate_corr();
215 :
216 : public:
217 : static void registerKeywords( Keywords& keys );
218 : explicit EMMIVOX(const ActionOptions&);
219 : // active methods:
220 : void prepare() override;
221 : void calculate() override;
222 : };
223 :
224 : PLUMED_REGISTER_ACTION(EMMIVOX,"EMMIVOX")
225 :
226 2 : void EMMIVOX::registerKeywords( Keywords& keys ) {
227 2 : Colvar::registerKeywords( keys );
228 4 : keys.add("atoms","ATOMS","atoms used in the calculation of the density map, typically all heavy atoms");
229 4 : keys.add("compulsory","DATA_FILE","file with cryo-EM map");
230 4 : keys.add("compulsory","RESOLUTION", "cryo-EM map resolution");
231 4 : keys.add("compulsory","NORM_DENSITY","integral of experimental density");
232 4 : keys.add("compulsory","WRITE_STRIDE","stride for writing status file");
233 4 : keys.add("optional","NL_DIST_CUTOFF","neighbor list distance cutoff");
234 4 : keys.add("optional","NL_GAUSS_CUTOFF","neighbor list Gaussian sigma cutoff");
235 4 : keys.add("optional","NL_STRIDE","neighbor list update frequency");
236 4 : keys.add("optional","SIGMA_MIN","minimum density error");
237 4 : keys.add("optional","DBFACT","Bfactor MC step");
238 4 : keys.add("optional","BFACT_MAX","Bfactor maximum value");
239 4 : keys.add("optional","MCBFACT_STRIDE", "Bfactor MC stride");
240 4 : keys.add("optional","BFACT_SIGMA","Bfactor sigma prior");
241 4 : keys.add("optional","STATUS_FILE","write a file with all the data useful for restart");
242 4 : keys.add("optional","SCALE","scale factor");
243 4 : keys.add("optional","OFFSET","offset");
244 4 : keys.add("optional","TEMP","temperature");
245 4 : keys.add("optional","WRITE_MAP","file with model density");
246 4 : keys.add("optional","WRITE_MAP_STRIDE","stride for writing model density to file");
247 4 : keys.addFlag("NO_AVER",false,"no ensemble averaging in multi-replica mode");
248 4 : keys.addFlag("CORRELATION",false,"calculate correlation coefficient");
249 4 : keys.addFlag("GPU",false,"calculate EMMIVOX on GPU with Libtorch");
250 4 : keys.addFlag("BFACT_NOCHAIN",false,"Do not use chain ID for Bfactor MC");
251 4 : keys.addFlag("BFACT_READ",false,"Read Bfactor on RESTART (automatic with DBFACT>0)");
252 4 : keys.addFlag("BFACT_MINIMIZE",false,"Accept only moves that decrease energy");
253 4 : keys.addFlag("MARTINI",false,"Use Martini scattering factors");
254 4 : keys.addOutputComponent("scoreb","default","scalar","Bayesian score");
255 4 : keys.addOutputComponent("scale", "default","scalar","scale factor");
256 4 : keys.addOutputComponent("offset","default","scalar","offset");
257 4 : keys.addOutputComponent("accB", "default","scalar", "Bfactor MC acceptance");
258 4 : keys.addOutputComponent("kbt", "default","scalar", "temperature in energy unit");
259 4 : keys.addOutputComponent("corr", "CORRELATION","scalar", "correlation coefficient");
260 2 : }
261 :
262 0 : EMMIVOX::EMMIVOX(const ActionOptions&ao):
263 : PLUMED_COLVAR_INIT(ao),
264 0 : nl_dist_cutoff_(1.0), nl_gauss_cutoff_(3.0), nl_stride_(50),
265 0 : first_time_(true), no_aver_(false), do_corr_(false),
266 0 : scale_(1.), offset_(0.),
267 0 : dbfact_(0.0), bfactmin_(0.05), bfactmax_(5.0),
268 0 : bfactsig_(0.1), bfactnoc_(false), bfactread_(false),
269 0 : MCBstride_(1), MCBaccept_(0.), MCBtrials_(0.), bfactemin_(false),
270 0 : martini_(false), statusstride_(0), first_status_(true),
271 0 : eps_(0.0001), mapstride_(0), gpu_(false)
272 : {
273 : // set constants
274 0 : inv_sqrt2_ = 1.0/sqrt(2.0);
275 0 : sqrt2_pi_ = sqrt(2.0 / M_PI);
276 0 : inv_pi2_ = 0.5 / M_PI / M_PI;
277 :
278 : // list of atoms
279 : std::vector<AtomNumber> atoms;
280 0 : parseAtomList("ATOMS", atoms);
281 :
282 : // file with experimental cryo-EM map
283 : std::string datafile;
284 0 : parse("DATA_FILE", datafile);
285 :
286 : // neighbor list cutoffs
287 0 : parse("NL_DIST_CUTOFF",nl_dist_cutoff_);
288 0 : parse("NL_GAUSS_CUTOFF",nl_gauss_cutoff_);
289 : // checks
290 0 : if(nl_dist_cutoff_<=0. && nl_gauss_cutoff_<=0.) error("You must specify either NL_DIST_CUTOFF or NL_GAUSS_CUTOFF or both");
291 0 : if(nl_gauss_cutoff_<=0.) nl_gauss_cutoff_ = 1.0e+10;
292 0 : if(nl_dist_cutoff_<=0.) nl_dist_cutoff_ = 1.0e+10;
293 : // neighbor list update stride
294 0 : parse("NL_STRIDE",nl_stride_);
295 0 : if(nl_stride_<=0) error("NL_STRIDE must be explicitly specified and positive");
296 :
297 : // minimum value for error
298 0 : double sigma_min = 0.2;
299 0 : parse("SIGMA_MIN", sigma_min);
300 0 : if(sigma_min<0.) error("SIGMA_MIN must be greater or equal to zero");
301 :
302 : // status file parameters
303 0 : parse("WRITE_STRIDE", statusstride_);
304 0 : if(statusstride_<=0) error("you must specify a positive WRITE_STRIDE");
305 0 : parse("STATUS_FILE", statusfilename_);
306 0 : if(statusfilename_=="") statusfilename_ = "EMMIStatus"+getLabel();
307 :
308 : // integral of the experimetal density
309 : double norm_d;
310 0 : parse("NORM_DENSITY", norm_d);
311 :
312 : // temperature
313 0 : kbt_ = getkBT();
314 :
315 : // scale and offset
316 0 : parse("SCALE", scale_);
317 0 : parse("OFFSET",offset_);
318 :
319 : // B-factors MC
320 0 : parse("DBFACT",dbfact_);
321 : // read Bfactors
322 0 : parseFlag("BFACT_READ",bfactread_);
323 : // do not use chains
324 0 : parseFlag("BFACT_NOCHAIN",bfactnoc_);
325 : // other parameters
326 0 : if(dbfact_>0.) {
327 0 : parse("MCBFACT_STRIDE",MCBstride_);
328 0 : parse("BFACT_MAX",bfactmax_);
329 0 : parse("BFACT_SIGMA",bfactsig_);
330 0 : parseFlag("BFACT_MINIMIZE",bfactemin_);
331 : // checks
332 0 : if(MCBstride_<=0) error("you must specify a positive MCBFACT_STRIDE");
333 0 : if(bfactmax_<=bfactmin_) error("you must specify a positive BFACT_MAX");
334 0 : if(MCBstride_%nl_stride_!=0) error("MCBFACT_STRIDE must be multiple of NL_STRIDE");
335 0 : if(bfactsig_<=0.) error("you must specify a positive BFACT_SIGMA");
336 : }
337 :
338 : // read map resolution
339 : double reso;
340 0 : parse("RESOLUTION", reso);
341 0 : if(reso<=0.) error("RESOLUTION must be strictly positive");
342 :
343 : // averaging or not
344 0 : parseFlag("NO_AVER",no_aver_);
345 :
346 : // calculate correlation coefficient
347 0 : parseFlag("CORRELATION",do_corr_);
348 :
349 : // write density file
350 0 : parse("WRITE_MAP_STRIDE", mapstride_);
351 0 : parse("WRITE_MAP", mapfilename_);
352 0 : if(mapstride_>0 && mapfilename_=="") error("With WRITE_MAP_STRIDE you must specify WRITE_MAP");
353 :
354 : // use GPU?
355 0 : parseFlag("GPU",gpu_);
356 : // set device
357 0 : if (gpu_ && torch::cuda::is_available()) {
358 0 : device_t_ = torch::kCUDA;
359 : } else {
360 0 : device_t_ = torch::kCPU;
361 0 : gpu_ = false;
362 : }
363 :
364 : // Martini model
365 0 : parseFlag("MARTINI",martini_);
366 :
367 : // check read
368 0 : checkRead();
369 :
370 : // set parallel stuff
371 0 : unsigned mpisize=comm.Get_size();
372 0 : if(mpisize>1) error("EMMIVOX supports only OpenMP parallelization");
373 :
374 : // get number of replicas
375 0 : if(no_aver_) {
376 0 : nrep_ = 1;
377 : } else {
378 0 : nrep_ = multi_sim_comm.Get_size();
379 : }
380 0 : replica_ = multi_sim_comm.Get_rank();
381 :
382 0 : if(nrep_>1 && dbfact_>0) error("Bfactor sampling not supported with ensemble averaging");
383 :
384 0 : log.printf(" number of atoms involved : %u\n", atoms.size());
385 0 : log.printf(" experimental density map : %s\n", datafile.c_str());
386 0 : if(no_aver_) log.printf(" without ensemble averaging\n");
387 0 : if(gpu_) {log.printf(" running on GPU \n");}
388 0 : else {log.printf(" running on CPU \n");}
389 0 : if(nl_dist_cutoff_ <1.0e+10) log.printf(" neighbor list distance cutoff : %lf\n", nl_dist_cutoff_);
390 0 : if(nl_gauss_cutoff_<1.0e+10) log.printf(" neighbor list Gaussian sigma cutoff : %lf\n", nl_gauss_cutoff_);
391 0 : log.printf(" neighbor list update stride : %u\n", nl_stride_);
392 0 : log.printf(" minimum density error : %f\n", sigma_min);
393 0 : log.printf(" scale factor : %lf\n", scale_);
394 0 : log.printf(" offset : %lf\n", offset_);
395 0 : log.printf(" reading/writing to status file : %s\n", statusfilename_.c_str());
396 0 : log.printf(" with stride : %u\n", statusstride_);
397 0 : if(dbfact_>0) {
398 0 : log.printf(" maximum Bfactor MC move : %f\n", dbfact_);
399 0 : log.printf(" stride MC move : %u\n", MCBstride_);
400 0 : log.printf(" using prior with sigma : %f\n", bfactsig_);
401 : }
402 0 : if(bfactread_) log.printf(" reading Bfactors from file : %s\n", statusfilename_.c_str());
403 0 : log.printf(" temperature of the system in energy unit : %f\n", kbt_);
404 0 : if(nrep_>1) {
405 0 : log.printf(" number of replicas for averaging: %u\n", nrep_);
406 0 : log.printf(" replica ID : %u\n", replica_);
407 : }
408 0 : if(mapstride_>0) {
409 0 : log.printf(" writing model density to file : %s\n", mapfilename_.c_str());
410 0 : log.printf(" with stride : %u\n", mapstride_);
411 : }
412 0 : if(martini_) log.printf(" using Martini scattering factors\n");
413 :
414 : // calculate model constant parameters
415 0 : std::vector<double> Model_w = get_Model_param(atoms);
416 :
417 : // read experimental map and errors
418 0 : get_exp_data(datafile);
419 0 : log.printf(" number of voxels : %u\n", static_cast<unsigned>(ovdd_.size()));
420 :
421 : // normalize atom weight map
422 : double norm_m = accumulate(Model_w.begin(), Model_w.end(), 0.0);
423 : // renormalization and constant factor on atom types
424 0 : for(unsigned i=0; i<Model_w_.size(); ++i) {
425 0 : Vector5d cf;
426 0 : for(unsigned j=0; j<5; ++j) {
427 0 : Model_w_[i][j] *= norm_d / norm_m;
428 0 : cf[j] = Model_w_[i][j]/pow( 2.0*pi, 1.5 );
429 : }
430 0 : cfact_.push_back(cf);
431 : }
432 :
433 : // median density
434 0 : double ovdd_m = get_median(ovdd_);
435 : // median experimental error
436 0 : double err_m = get_median(exp_err_);
437 : // minimum error
438 0 : double minerr = sigma_min*ovdd_m;
439 : // print out statistics
440 0 : log.printf(" median density : %lf\n", ovdd_m);
441 0 : log.printf(" minimum error : %lf\n", minerr);
442 0 : log.printf(" median error : %lf\n", err_m);
443 : // populate ismin: cycle on all voxels
444 0 : for(unsigned id=0; id<ovdd_.size(); ++id) {
445 : // define smin
446 0 : double smin = std::max(minerr, exp_err_[id]);
447 : // and to ismin_
448 0 : ismin_.push_back(1.0/smin);
449 : }
450 :
451 : // prepare gpu stuff: map centers, data, and error
452 0 : prepare_gpu();
453 :
454 : // initialize Bfactors
455 0 : initialize_Bfactor(reso);
456 :
457 : // read status file if restarting
458 0 : if(getRestart() || bfactread_) read_status();
459 :
460 : // prepare auxiliary vectors
461 0 : get_auxiliary_vectors();
462 :
463 : // prepare other vectors: data and derivatives
464 0 : ovmd_.resize(ovdd_.size());
465 0 : atom_der_.resize(Model_type_.size());
466 0 : score_der_.resize(ovdd_.size());
467 :
468 : // add components
469 0 : addComponentWithDerivatives("scoreb"); componentIsNotPeriodic("scoreb");
470 0 : addComponent("scale"); componentIsNotPeriodic("scale");
471 0 : addComponent("offset"); componentIsNotPeriodic("offset");
472 0 : addComponent("kbt"); componentIsNotPeriodic("kbt");
473 0 : if(dbfact_>0) {addComponent("accB"); componentIsNotPeriodic("accB");}
474 0 : if(do_corr_) {addComponent("corr"); componentIsNotPeriodic("corr");}
475 :
476 : // initialize random seed
477 0 : unsigned iseed = time(NULL)+replica_;
478 0 : random_.setSeed(-iseed);
479 :
480 : // request atoms
481 0 : requestAtoms(atoms);
482 :
483 : // print bibliography
484 0 : log<<" Bibliography "<<plumed.cite("Bonomi, Camilloni, Bioinformatics, 33, 3999 (2017)");
485 0 : log<<plumed.cite("Hoff, Thomasen, Lindorff-Larsen, Bonomi, bioRxiv (2023) doi: 10.1101/2023.10.18.562710");
486 0 : if(!no_aver_ && nrep_>1)log<<plumed.cite("Bonomi, Camilloni, Cavalli, Vendruscolo, Sci. Adv. 2, e150117 (2016)");
487 0 : log<<"\n";
488 0 : }
489 :
490 0 : void EMMIVOX::prepare_gpu()
491 : {
492 : // number of data points
493 0 : int nd = ovdd_.size();
494 : // 1) put ismin_ on device_t_
495 0 : ismin_gpu_ = torch::from_blob(ismin_.data(), {nd}, torch::kFloat64).to(torch::kFloat32).to(device_t_);
496 : // 2) put ovdd_ on device_t_
497 0 : ovdd_gpu_ = torch::from_blob(ovdd_.data(), {nd}, torch::kFloat64).to(torch::kFloat32).to(device_t_);
498 : // 3) put Map_m_ on device_t_
499 0 : std::vector<double> Map_m_gpu(3*nd);
500 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
501 : for(int i=0; i<nd; ++i) {
502 : Map_m_gpu[i] = Map_m_[i][0];
503 : Map_m_gpu[i+nd] = Map_m_[i][1];
504 : Map_m_gpu[i+2*nd] = Map_m_[i][2];
505 : }
506 : // libtorch tensor
507 0 : Map_m_gpu_ = torch::from_blob(Map_m_gpu.data(), {3,nd}, torch::kFloat64).clone().to(torch::kFloat32).to(device_t_);
508 0 : }
509 :
510 0 : void EMMIVOX::write_model_density(long int step)
511 : {
512 0 : OFile ovfile;
513 0 : ovfile.link(*this);
514 0 : std::string num; Tools::convert(step,num);
515 0 : std::string name = mapfilename_+"-"+num;
516 0 : ovfile.open(name);
517 : ovfile.setHeavyFlush();
518 0 : ovfile.fmtField("%10.7e ");
519 : // write density
520 0 : for(unsigned i=0; i<ovmd_.size(); ++i) {
521 0 : ovfile.printField("Model", ovmd_[i]);
522 0 : ovfile.printField("ModelScaled", scale_ * ovmd_[i] + offset_);
523 0 : ovfile.printField("Data", ovdd_[i]);
524 0 : ovfile.printField();
525 : }
526 0 : ovfile.close();
527 0 : }
528 :
529 0 : double EMMIVOX::get_median(std::vector<double> v)
530 : {
531 : // dimension of vector
532 0 : unsigned size = v.size();
533 : // in case of only one entry
534 0 : if (size==1) {
535 0 : return v[0];
536 : } else {
537 : // reorder vector
538 0 : sort(v.begin(), v.end());
539 : // odd or even?
540 0 : if (size%2==0) {
541 0 : return (v[size/2-1]+v[size/2])/2.0;
542 : } else {
543 0 : return v[size/2];
544 : }
545 : }
546 : }
547 :
548 0 : void EMMIVOX::read_status()
549 : {
550 : double MDtime;
551 : // open file
552 0 : IFile *ifile = new IFile();
553 0 : ifile->link(*this);
554 0 : if(ifile->FileExist(statusfilename_)) {
555 0 : ifile->open(statusfilename_);
556 0 : while(ifile->scanField("MD_time", MDtime)) {
557 : // read scale and offset
558 0 : ifile->scanField("scale", scale_);
559 0 : ifile->scanField("offset", offset_);
560 : // read bfactors if doing fitting of reading it at restart
561 0 : if(dbfact_>0 || bfactread_) {
562 : // cycle on residues
563 0 : for(unsigned ir=0; ir<Model_rlist_.size(); ++ir) {
564 : // key: pair of residue/chain IDs
565 : std::pair<unsigned,std::string> key = Model_rlist_[ir];
566 : // convert ires to std::string
567 0 : std::string num; Tools::convert(key.first,num);
568 : // read entry
569 0 : std::string ch = key.second;
570 0 : if(ch==" ") ch="";
571 0 : ifile->scanField("bf-"+num+":"+ch, Model_b_[key]);
572 : }
573 : }
574 : // new line
575 0 : ifile->scanField();
576 : }
577 0 : ifile->close();
578 : } else {
579 0 : error("Cannot find status file "+statusfilename_+"\n");
580 : }
581 0 : delete ifile;
582 0 : }
583 :
584 0 : void EMMIVOX::print_status(long int step)
585 : {
586 : // if first time open the file
587 0 : if(first_status_) {
588 0 : first_status_ = false;
589 0 : statusfile_.link(*this);
590 0 : statusfile_.open(statusfilename_);
591 : statusfile_.setHeavyFlush();
592 0 : statusfile_.fmtField("%10.7e ");
593 : }
594 : // write fields
595 0 : double MDtime = static_cast<double>(step)*getTimeStep();
596 0 : statusfile_.printField("MD_time", MDtime);
597 : // write scale and offset
598 0 : statusfile_.printField("scale", scale_);
599 0 : statusfile_.printField("offset", offset_);
600 : // write bfactors only if doing fitting or reading bfactors
601 0 : if(dbfact_>0 || bfactread_) {
602 : // cycle on residues
603 0 : for(unsigned ir=0; ir<Model_rlist_.size(); ++ir) {
604 : // key: pair of residue/chain IDs
605 : std::pair<unsigned,std::string> key = Model_rlist_[ir];
606 : // bfactor from map
607 0 : double bf = Model_b_[key];
608 : // convert ires to std::string
609 0 : std::string num; Tools::convert(key.first,num);
610 : // print entry
611 0 : statusfile_.printField("bf-"+num+":"+key.second, bf);
612 : }
613 : }
614 0 : statusfile_.printField();
615 0 : }
616 :
617 0 : bool EMMIVOX::doAccept(double oldE, double newE, double kbt)
618 : {
619 : bool accept = false;
620 : // calculate delta energy
621 0 : double delta = ( newE - oldE ) / kbt;
622 : // if delta is negative always accept move
623 0 : if( delta < 0.0 ) {
624 : accept = true;
625 : } else {
626 : // otherwise extract random number
627 0 : double s = random_.RandU01();
628 0 : if( s < exp(-delta) ) { accept = true; }
629 : }
630 0 : return accept;
631 : }
632 :
633 0 : std::vector<double> EMMIVOX::get_Model_param(std::vector<AtomNumber> &atoms)
634 : {
635 : // check if MOLINFO line is present
636 0 : auto* moldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(this);
637 0 : if(!moldat) error("MOLINFO DATA not found\n");
638 0 : log<<" MOLINFO DATA found with label " <<moldat->getLabel()<<", using proper atom names\n";
639 :
640 : // list of weights - one per atom
641 : std::vector<double> Model_w;
642 : // 5-Gaussians parameters
643 : // map of atom types to A and B coefficients of scattering factor
644 : // f(s) = A * exp(-B*s**2)
645 : // B is in Angstrom squared
646 : // Elastic atomic scattering factors of electrons for neutral atoms
647 : // and s up to 6.0 A^-1: as implemented in PLUMED
648 : // map between an atom type and an index
649 : std::map<std::string, unsigned> type_map;
650 : // atomistic types
651 0 : type_map["C"]=0; type_map["O"]=1; type_map["N"]=2;
652 0 : type_map["S"]=3; type_map["P"]=4; type_map["F"]=5;
653 0 : type_map["NA"]=6; type_map["MG"]=7; type_map["CL"]=8;
654 0 : type_map["CA"]=9; type_map["K"]=10; type_map["ZN"]=11;
655 : // Martini types
656 0 : type_map["ALA_BB"]=12; type_map["ALA_SC1"]=13; type_map["CYS_BB"]=14;
657 0 : type_map["CYS_SC1"]=15; type_map["ASP_BB"]=16; type_map["ASP_SC1"]=17;
658 0 : type_map["GLU_BB"]=18; type_map["GLU_SC1"]=19; type_map["PHE_BB"]=20;
659 0 : type_map["PHE_SC1"]=21; type_map["PHE_SC2"]=22; type_map["PHE_SC3"]=23;
660 0 : type_map["GLY_BB"]=24; type_map["HIS_BB"]=25; type_map["HIS_SC1"]=26;
661 0 : type_map["HIS_SC2"]=27; type_map["HIS_SC3"]=28; type_map["ILE_BB"]=29;
662 0 : type_map["ILE_SC1"]=30; type_map["LYS_BB"]=31; type_map["LYS_SC1"]=32;
663 0 : type_map["LYS_SC2"]=33; type_map["LEU_BB"]=34; type_map["LEU_SC1"]=35;
664 0 : type_map["MET_BB"]=36; type_map["MET_SC1"]=37; type_map["ASN_BB"]=38;
665 0 : type_map["ASN_SC1"]=39; type_map["PRO_BB"]=40; type_map["PRO_SC1"]=41;
666 0 : type_map["GLN_BB"]=42; type_map["GLN_SC1"]=43; type_map["ARG_BB"]=44;
667 0 : type_map["ARG_SC1"]=45; type_map["ARG_SC2"]=46; type_map["SER_BB"]=47;
668 0 : type_map["SER_SC1"]=48; type_map["THR_BB"]=49; type_map["THR_SC1"]=50;
669 0 : type_map["VAL_BB"]=51; type_map["VAL_SC1"]=52; type_map["TRP_BB"]=53;
670 0 : type_map["TRP_SC1"]=54; type_map["TRP_SC2"]=55; type_map["TRP_SC3"]=56;
671 0 : type_map["TRP_SC4"]=57; type_map["TRP_SC5"]=58; type_map["TYR_BB"]=59;
672 0 : type_map["TYR_SC1"]=60; type_map["TYR_SC2"]=61; type_map["TYR_SC3"]=62;
673 0 : type_map["TYR_SC4"]=63;
674 : // fill in sigma vector for atoms
675 0 : Model_s_.push_back(0.01*Vector5d(0.114,1.0825,5.4281,17.8811,51.1341)); // C
676 0 : Model_s_.push_back(0.01*Vector5d(0.0652,0.6184,2.9449,9.6298,28.2194)); // O
677 0 : Model_s_.push_back(0.01*Vector5d(0.0541,0.5165,2.8207,10.6297,34.3764)); // N
678 0 : Model_s_.push_back(0.01*Vector5d(0.0838,0.7788,4.3462,15.5846,44.63655)); // S
679 0 : Model_s_.push_back(0.01*Vector5d(0.0977,0.9084,4.9654,18.5471,54.3648)); // P
680 0 : Model_s_.push_back(0.01*Vector5d(0.0613,0.5753,2.6858,8.8214,25.6668)); // F
681 0 : Model_s_.push_back(0.01*Vector5d(0.1684,1.7150,8.8386,50.8265,147.2073)); // NA
682 0 : Model_s_.push_back(0.01*Vector5d(0.1356,1.3579,6.9255,32.3165,92.1138)); // MG
683 0 : Model_s_.push_back(0.01*Vector5d(0.0694,0.6443,3.5351,12.5058,35.8633)); // CL
684 0 : Model_s_.push_back(0.01*Vector5d(0.1742,1.8329,8.8407,47.4583,134.9613)); // CA
685 0 : Model_s_.push_back(0.01*Vector5d(0.1660,1.6906,8.7447,46.7825,165.6923)); // K
686 0 : Model_s_.push_back(0.01*Vector5d(0.0876,0.8650,3.8612,18.8726,64.7016)); // ZN
687 : // fill in sigma vector for Martini beads
688 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // ALA_BB
689 0 : Model_s_.push_back(0.01*Vector5d(0.500000,0.500000,0.500000,0.500000,0.500000)); // ALA_SC1
690 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // CYS_BB
691 0 : Model_s_.push_back(0.01*Vector5d(8.500000,8.500000,8.500000,8.500000,8.500000)); // CYS_SC1
692 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // ASP_BB
693 0 : Model_s_.push_back(0.01*Vector5d(17.000000,17.000000,17.000000,17.000000,17.000000)); // ASP_SC1
694 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // GLU_BB
695 0 : Model_s_.push_back(0.01*Vector5d(24.000000,24.000000,24.000000,24.000000,24.000000)); // GLU_SC1
696 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // PHE_BB
697 0 : Model_s_.push_back(0.01*Vector5d(17.500000,17.500000,17.500000,17.500000,17.500000)); // PHE_SC1
698 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // PHE_SC2
699 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // PHE_SC3
700 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // GLY_BB
701 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // HIS_BB
702 0 : Model_s_.push_back(0.01*Vector5d(11.500000,11.500000,11.500000,11.500000,11.500000)); // HIS_SC1
703 0 : Model_s_.push_back(0.01*Vector5d(9.000000,9.000000,9.000000,9.000000,9.000000)); // HIS_SC2
704 0 : Model_s_.push_back(0.01*Vector5d(8.500000,8.500000,8.500000,8.500000,8.500000)); // HIS_SC3
705 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // ILE_BB
706 0 : Model_s_.push_back(0.01*Vector5d(25.500000,25.500000,25.500000,25.500000,25.500000)); // ILE_SC1
707 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // LYS_BB
708 0 : Model_s_.push_back(0.01*Vector5d(18.000000,18.000000,18.000000,18.000000,18.000000)); // LYS_SC1
709 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // LYS_SC2
710 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // LEU_BB
711 0 : Model_s_.push_back(0.01*Vector5d(21.500000,21.500000,21.500000,21.500000,21.500000)); // LEU_SC1
712 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // MET_BB
713 0 : Model_s_.push_back(0.01*Vector5d(22.500000,22.500000,22.500000,22.500000,22.500000)); // MET_SC1
714 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // ASN_BB
715 0 : Model_s_.push_back(0.01*Vector5d(18.500000,18.500000,18.500000,18.500000,18.500000)); // ASN_SC1
716 0 : Model_s_.push_back(0.01*Vector5d(23.500000,23.500000,23.500000,23.500000,23.500000)); // PRO_BB
717 0 : Model_s_.push_back(0.01*Vector5d(17.500000,17.500000,17.500000,17.500000,17.500000)); // PRO_SC1
718 0 : Model_s_.push_back(0.01*Vector5d(22.000000,22.000000,22.000000,22.000000,22.000000)); // GLN_BB
719 0 : Model_s_.push_back(0.01*Vector5d(24.500000,24.500000,24.500000,24.500000,24.500000)); // GLN_SC1
720 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // ARG_BB
721 0 : Model_s_.push_back(0.01*Vector5d(18.000000,18.000000,18.000000,18.000000,18.000000)); // ARG_SC1
722 0 : Model_s_.push_back(0.01*Vector5d(18.000000,18.000000,18.000000,18.000000,18.000000)); // ARG_SC2
723 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // SER_BB
724 0 : Model_s_.push_back(0.01*Vector5d(9.000000,9.000000,9.000000,9.000000,9.000000)); // SER_SC1
725 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // THR_BB
726 0 : Model_s_.push_back(0.01*Vector5d(17.000000,17.000000,17.000000,17.000000,17.000000)); // THR_SC1
727 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // VAL_BB
728 0 : Model_s_.push_back(0.01*Vector5d(18.000000,18.000000,18.000000,18.000000,18.000000)); // VAL_SC1
729 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // TRP_BB
730 0 : Model_s_.push_back(0.01*Vector5d(11.500000,11.500000,11.500000,11.500000,11.500000)); // TRP_SC1
731 0 : Model_s_.push_back(0.01*Vector5d(9.000000,9.000000,9.000000,9.000000,9.000000)); // TRP_SC2
732 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // TRP_SC3
733 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // TRP_SC4
734 0 : Model_s_.push_back(0.01*Vector5d(9.500000,9.500000,9.500000,9.500000,9.500000)); // TRP_SC5
735 0 : Model_s_.push_back(0.01*Vector5d(23.000000,23.000000,23.000000,23.000000,23.000000)); // TYR_BB
736 0 : Model_s_.push_back(0.01*Vector5d(12.000000,12.000000,12.000000,12.000000,12.000000)); // TYR_SC1
737 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // TYR_SC2
738 0 : Model_s_.push_back(0.01*Vector5d(11.000000,11.000000,11.000000,11.000000,11.000000)); // TYR_SC3
739 0 : Model_s_.push_back(0.01*Vector5d(8.500000,8.500000,8.500000,8.500000,8.500000)); // TYR_SC4
740 : // fill in weight vector for atoms
741 0 : Model_w_.push_back(Vector5d(0.0489,0.2091,0.7537,1.1420,0.3555)); // C
742 0 : Model_w_.push_back(Vector5d(0.0365,0.1729,0.5805,0.8814,0.3121)); // O
743 0 : Model_w_.push_back(Vector5d(0.0267,0.1328,0.5301,1.1020,0.4215)); // N
744 0 : Model_w_.push_back(Vector5d(0.0915,0.4312,1.0847,2.4671,1.0852)); // S
745 0 : Model_w_.push_back(Vector5d(0.1005,0.4615,1.0663,2.5854,1.2725)); // P
746 0 : Model_w_.push_back(Vector5d(0.0382,0.1822,0.5972,0.7707,0.2130)); // F
747 0 : Model_w_.push_back(Vector5d(0.1260,0.6442,0.8893,1.8197,1.2988)); // NA
748 0 : Model_w_.push_back(Vector5d(0.1130,0.5575,0.9046,2.1580,1.4735)); // MG
749 0 : Model_w_.push_back(Vector5d(0.0799,0.3891,1.0037,2.3332,1.0507)); // CL
750 0 : Model_w_.push_back(Vector5d(0.2355,0.9916,2.3959,3.7252,2.5647)); // CA
751 0 : Model_w_.push_back(Vector5d(0.2149,0.8703,2.4999,2.3591,3.0318)); // K
752 0 : Model_w_.push_back(Vector5d(0.1780,0.8096,1.6744,1.9499,1.4495)); // ZN
753 : // fill in weight vector for Martini beads
754 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // ALA_BB
755 0 : Model_w_.push_back(Vector5d(0.100000,0.100000,0.100000,0.100000,0.100000)); // ALA_SC1
756 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // CYS_BB
757 0 : Model_w_.push_back(Vector5d(1.100000,1.100000,1.100000,1.100000,1.100000)); // CYS_SC1
758 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // ASP_BB
759 0 : Model_w_.push_back(Vector5d(1.700000,1.700000,1.700000,1.700000,1.700000)); // ASP_SC1
760 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // GLU_BB
761 0 : Model_w_.push_back(Vector5d(2.300000,2.300000,2.300000,2.300000,2.300000)); // GLU_SC1
762 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // PHE_BB
763 0 : Model_w_.push_back(Vector5d(1.400000,1.400000,1.400000,1.400000,1.400000)); // PHE_SC1
764 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // PHE_SC2
765 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // PHE_SC3
766 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // GLY_BB
767 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // HIS_BB
768 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // HIS_SC1
769 0 : Model_w_.push_back(Vector5d(0.800000,0.800000,0.800000,0.800000,0.800000)); // HIS_SC2
770 0 : Model_w_.push_back(Vector5d(0.800000,0.800000,0.800000,0.800000,0.800000)); // HIS_SC3
771 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // ILE_BB
772 0 : Model_w_.push_back(Vector5d(2.000000,2.000000,2.000000,2.000000,2.000000)); // ILE_SC1
773 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // LYS_BB
774 0 : Model_w_.push_back(Vector5d(1.400000,1.400000,1.400000,1.400000,1.400000)); // LYS_SC1
775 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // LYS_SC2
776 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // LEU_BB
777 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // LEU_SC1
778 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // MET_BB
779 0 : Model_w_.push_back(Vector5d(2.300000,2.300000,2.300000,2.300000,2.300000)); // MET_SC1
780 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // ASN_BB
781 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // ASN_SC1
782 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // PRO_BB
783 0 : Model_w_.push_back(Vector5d(1.400000,1.400000,1.400000,1.400000,1.400000)); // PRO_SC1
784 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // GLN_BB
785 0 : Model_w_.push_back(Vector5d(2.300000,2.300000,2.300000,2.300000,2.300000)); // GLN_SC1
786 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // ARG_BB
787 0 : Model_w_.push_back(Vector5d(1.400000,1.400000,1.400000,1.400000,1.400000)); // ARG_SC1
788 0 : Model_w_.push_back(Vector5d(1.800000,1.800000,1.800000,1.800000,1.800000)); // ARG_SC2
789 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // SER_BB
790 0 : Model_w_.push_back(Vector5d(0.800000,0.800000,0.800000,0.800000,0.800000)); // SER_SC1
791 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // THR_BB
792 0 : Model_w_.push_back(Vector5d(1.400000,1.400000,1.400000,1.400000,1.400000)); // THR_SC1
793 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // VAL_BB
794 0 : Model_w_.push_back(Vector5d(1.400000,1.400000,1.400000,1.400000,1.400000)); // VAL_SC1
795 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // TRP_BB
796 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // TRP_SC1
797 0 : Model_w_.push_back(Vector5d(0.800000,0.800000,0.800000,0.800000,0.800000)); // TRP_SC2
798 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // TRP_SC3
799 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // TRP_SC4
800 0 : Model_w_.push_back(Vector5d(0.800000,0.800000,0.800000,0.800000,0.800000)); // TRP_SC5
801 0 : Model_w_.push_back(Vector5d(1.900000,1.900000,1.900000,1.900000,1.900000)); // TYR_BB
802 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // TYR_SC1
803 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // TYR_SC2
804 0 : Model_w_.push_back(Vector5d(0.900000,0.900000,0.900000,0.900000,0.900000)); // TYR_SC3
805 0 : Model_w_.push_back(Vector5d(0.800000,0.800000,0.800000,0.800000,0.800000)); // TYR_SC4
806 : // cycle on atoms
807 0 : for(unsigned i=0; i<atoms.size(); ++i) {
808 : // get atom name
809 0 : std::string name = moldat->getAtomName(atoms[i]);
810 : // get residue name
811 0 : std::string resname = moldat->getResidueName(atoms[i]);
812 : // type of atoms/bead
813 : std::string type_s;
814 : // Martini model
815 0 : if(martini_) {
816 0 : type_s = resname+"_"+name;
817 : // Atomistic model
818 : } else {
819 : char type;
820 : // get atom type
821 0 : char first = name.at(0);
822 : // GOLDEN RULE: type is first letter, if not a number
823 0 : if (!isdigit(first)) {
824 : type = first;
825 : // otherwise is the second
826 : } else {
827 0 : type = name.at(1);
828 : }
829 : // convert to std::string
830 0 : type_s = std::string(1,type);
831 : // special cases
832 0 : if(name=="SOD" || name=="NA" || name =="Na") type_s = "NA";
833 0 : if(name=="MG" || name=="Mg") type_s = "MG";
834 0 : if(name=="CLA" || name=="CL" || name =="Cl") type_s = "CL";
835 0 : if((resname=="CAL" || resname=="CA") && (name=="CAL" || name=="CA" || name =="C0")) type_s = "CA";
836 0 : if(name=="POT" || name=="K") type_s = "K";
837 0 : if(name=="ZN" || name=="Zn") type_s = "ZN";
838 : }
839 : // check if key in map
840 0 : if(type_map.find(type_s) != type_map.end()) {
841 : // save atom type
842 0 : Model_type_.push_back(type_map[type_s]);
843 : // this will be normalized in the final density
844 0 : Vector5d w = Model_w_[type_map[type_s]];
845 0 : Model_w.push_back(w[0]+w[1]+w[2]+w[3]+w[4]);
846 : // get residue id
847 0 : unsigned ires = moldat->getResidueNumber(atoms[i]);
848 : // and chain
849 0 : std::string c ("*");
850 0 : if(!bfactnoc_) c = moldat->getChainID(atoms[i]);
851 : // define pair residue/chain IDs
852 : std::pair<unsigned,std::string> key = std::make_pair(ires,c);
853 : // add to map between residue/chain and list of atoms
854 0 : Model_resmap_[key].push_back(i);
855 : // and global list of residue/chain per atom
856 0 : Model_res_.push_back(key);
857 : // initialize Bfactor map
858 0 : Model_b_[key] = 0.0;
859 : } else {
860 0 : error("Wrong atom type "+type_s+" from atom name "+name+"\n");
861 : }
862 : }
863 : // create ordered vector of residue-chain IDs
864 0 : for(unsigned i=0; i<Model_res_.size(); ++i) {
865 : std::pair<unsigned,std::string> key = Model_res_[i];
866 : // search in Model_rlist_
867 0 : if(find(Model_rlist_.begin(), Model_rlist_.end(), key) == Model_rlist_.end())
868 0 : Model_rlist_.push_back(key);
869 : }
870 : // return weights
871 0 : return Model_w;
872 : }
873 :
874 : // read experimental data file in PLUMED format:
875 0 : void EMMIVOX::get_exp_data(const std::string &datafile)
876 : {
877 0 : Vector pos;
878 : double dens, err;
879 : int idcomp;
880 :
881 : // open file
882 0 : IFile *ifile = new IFile();
883 0 : if(ifile->FileExist(datafile)) {
884 0 : ifile->open(datafile);
885 0 : while(ifile->scanField("Id",idcomp)) {
886 0 : ifile->scanField("Pos_0",pos[0]);
887 0 : ifile->scanField("Pos_1",pos[1]);
888 0 : ifile->scanField("Pos_2",pos[2]);
889 0 : ifile->scanField("Density",dens);
890 0 : ifile->scanField("Error",err);
891 : // voxel center
892 0 : Map_m_.push_back(pos);
893 : // experimental density
894 0 : ovdd_.push_back(dens);
895 : // error
896 0 : exp_err_.push_back(err);
897 : // new line
898 0 : ifile->scanField();
899 : }
900 0 : ifile->close();
901 : } else {
902 0 : error("Cannot find DATA_FILE "+datafile+"\n");
903 : }
904 0 : delete ifile;
905 0 : }
906 :
907 0 : void EMMIVOX::initialize_Bfactor(double reso)
908 : {
909 0 : double bfactini = 0.0;
910 : // if doing Bfactor Monte Carlo
911 0 : if(dbfact_>0) {
912 : // initialize B factor based on empirical relation between resolution and average bfactor
913 : // calculated on ~8000 cryo-EM data with resolution < 5 Ang
914 : // Bfact = A*reso**2+B; with A=6.95408 B=-2.45697/100.0 nm^2
915 0 : bfactini = 6.95408*reso*reso - 0.01*2.45697;
916 : // check for min and max
917 0 : bfactini = std::min(bfactmax_, std::max(bfactmin_, bfactini));
918 : }
919 : // set initial Bfactor
920 0 : for(std::map< std::pair<unsigned,std::string>, double>::iterator it=Model_b_.begin(); it!=Model_b_.end(); ++it) {
921 0 : it->second = bfactini;
922 : }
923 0 : log.printf(" experimental map resolution : %3.2f\n", reso);
924 : // if doing Bfactor Monte Carlo
925 0 : if(dbfact_>0) {
926 0 : log.printf(" minimum Bfactor value : %3.2f\n", bfactmin_);
927 0 : log.printf(" maximum Bfactor value : %3.2f\n", bfactmax_);
928 0 : log.printf(" initial Bfactor value : %3.2f\n", bfactini);
929 : }
930 0 : }
931 :
932 : // prepare auxiliary vectors
933 0 : void EMMIVOX::get_auxiliary_vectors()
934 : {
935 : // number of atoms
936 0 : unsigned natoms = Model_res_.size();
937 : // clear lists
938 0 : pref_.clear(); invs2_.clear(); cut_.clear();
939 : // resize
940 0 : pref_.resize(natoms); invs2_.resize(natoms); cut_.resize(natoms);
941 : // cycle on all atoms
942 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
943 : for(unsigned im=0; im<natoms; ++im) {
944 : // get atom type
945 : unsigned atype = Model_type_[im];
946 : // get residue/chain IDs
947 : std::pair<unsigned,std::string> key = Model_res_[im];
948 : // get bfactor
949 : double bfact = Model_b_[key];
950 : // sigma for 5 gaussians
951 : Vector5d m_s = Model_s_[atype];
952 : // calculate constant quantities
953 : Vector5d pref, invs2;
954 : // calculate cutoff
955 : double n = 0.0;
956 : double d = 0.0;
957 : for(unsigned j=0; j<5; ++j) {
958 : // total value of b
959 : double m_b = m_s[j] + bfact/4.0;
960 : // calculate invs2
961 : invs2[j] = 1.0/(inv_pi2_*m_b);
962 : // prefactor
963 : pref[j] = cfact_[atype][j] * pow(invs2[j],1.5);
964 : // cutoff
965 : n += pref[j] / invs2[j];
966 : d += pref[j];
967 : }
968 : // put into global lists
969 : pref_[im] = pref;
970 : invs2_[im] = invs2;
971 : cut_[im] = std::min(nl_dist_cutoff_, sqrt(n/d)*nl_gauss_cutoff_);
972 : }
973 : // push to GPU
974 0 : push_auxiliary_gpu();
975 0 : }
976 :
977 0 : void EMMIVOX::push_auxiliary_gpu()
978 : {
979 : // 1) create vector of pref_ and invs2_
980 0 : int natoms = Model_type_.size();
981 0 : std::vector<double> pref(5*natoms), invs2(5*natoms);
982 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
983 : for(int i=0; i<natoms; ++i) {
984 : for(int j=0; j<5; ++j) {
985 : pref[i+j*natoms] = pref_[i][j];
986 : invs2[i+j*natoms] = invs2_[i][j];
987 : }
988 : }
989 : // 2) initialize gpu tensors
990 0 : pref_gpu_ = torch::from_blob(pref.data(), {5,natoms}, torch::kFloat64).clone().to(torch::kFloat32).to(device_t_);
991 0 : invs2_gpu_ = torch::from_blob(invs2.data(), {5,natoms}, torch::kFloat64).clone().to(torch::kFloat32).to(device_t_);
992 0 : }
993 :
994 0 : void EMMIVOX::get_close_residues()
995 : {
996 : // clear neighbor list
997 0 : nl_res_.clear(); nl_res_.resize(Model_rlist_.size());
998 :
999 : // loop in parallel
1000 0 : #pragma omp parallel num_threads(OpenMP::getNumThreads())
1001 : {
1002 : // private variable
1003 : std::vector< std::vector<unsigned> > nl_res_l(Model_rlist_.size());
1004 : // cycle on residues/chains #1
1005 : #pragma omp for
1006 : for(unsigned i=0; i<Model_rlist_.size()-1; ++i) {
1007 :
1008 : // key1: pair of residue/chain IDs
1009 : std::pair<unsigned,std::string> key1 = Model_rlist_[i];
1010 :
1011 : // cycle over residues/chains #2
1012 : for(unsigned j=i+1; j<Model_rlist_.size(); ++j) {
1013 :
1014 : // key2: pair of residue/chain IDs
1015 : std::pair<unsigned,std::string> key2 = Model_rlist_[j];
1016 :
1017 : // set flag neighbor
1018 : bool neigh = false;
1019 :
1020 : // cycle over all the atoms belonging to key1
1021 : for(unsigned im1=0; im1<Model_resmap_[key1].size(); ++im1) {
1022 : // get atom position #1
1023 : Vector pos1 = getPosition(Model_resmap_[key1][im1]);
1024 : // cycle over all the atoms belonging to key2
1025 : for(unsigned im2=0; im2<Model_resmap_[key2].size(); ++im2) {
1026 : // get atom position #2
1027 : Vector pos2 = getPosition(Model_resmap_[key2][im2]);
1028 : // if closer than 0.5 nm, then residues key1 and key2 are neighbors
1029 : if(delta(pos1,pos2).modulo()<0.5) {
1030 : // set neighbors
1031 : neigh = true;
1032 : // and exit
1033 : break;
1034 : }
1035 : }
1036 : // check if neighbor already found
1037 : if(neigh) break;
1038 : }
1039 :
1040 : // if neighbors, add to local list
1041 : if(neigh) {
1042 : nl_res_l[i].push_back(j);
1043 : nl_res_l[j].push_back(i);
1044 : }
1045 : }
1046 : }
1047 : // add to global list
1048 : #pragma omp critical
1049 : {
1050 : for(unsigned i=0; i<nl_res_.size(); ++i)
1051 : nl_res_[i].insert(nl_res_[i].end(), nl_res_l[i].begin(), nl_res_l[i].end());
1052 : }
1053 : }
1054 0 : }
1055 :
1056 0 : void EMMIVOX::doMonteCarloBfact()
1057 : {
1058 : // update residue neighbor list
1059 0 : get_close_residues();
1060 :
1061 : // cycle over residues/chains
1062 0 : for(unsigned ir=0; ir<Model_rlist_.size(); ++ir) {
1063 :
1064 : // key: pair of residue/chain IDs
1065 : std::pair<unsigned,std::string> key = Model_rlist_[ir];
1066 : // old bfactor
1067 0 : double bfactold = Model_b_[key];
1068 :
1069 : // propose move in bfactor
1070 0 : double bfactnew = bfactold + dbfact_ * ( 2.0 * random_.RandU01() - 1.0 );
1071 : // check boundaries
1072 0 : if(bfactnew > bfactmax_) {bfactnew = 2.0*bfactmax_ - bfactnew;}
1073 0 : if(bfactnew < bfactmin_) {bfactnew = 2.0*bfactmin_ - bfactnew;}
1074 :
1075 : // useful quantities
1076 : std::map<unsigned, double> deltaov;
1077 :
1078 0 : #pragma omp parallel num_threads(OpenMP::getNumThreads())
1079 : {
1080 : // private variables
1081 : std::map<unsigned, double> deltaov_l;
1082 : #pragma omp for
1083 : // cycle over all the atoms belonging to key (residue/chain)
1084 : for(unsigned ia=0; ia<Model_resmap_[key].size(); ++ia) {
1085 :
1086 : // get atom id
1087 : unsigned im = Model_resmap_[key][ia];
1088 : // get atom type
1089 : unsigned atype = Model_type_[im];
1090 : // sigma for 5 Gaussians
1091 : Vector5d m_s = Model_s_[atype];
1092 : // prefactors
1093 : Vector5d cfact = cfact_[atype];
1094 : // and position
1095 : Vector pos = getPosition(im);
1096 :
1097 : // cycle on all the neighboring voxels affected by a change in Bfactor
1098 : for(unsigned i=0; i<Model_nb_[im].size(); ++i) {
1099 : // voxel id
1100 : unsigned id = Model_nb_[im][i];
1101 : // get contribution to density in id before change
1102 : double dold = get_overlap(Map_m_[id], pos, cfact, m_s, bfactold);
1103 : // get contribution after change
1104 : double dnew = get_overlap(Map_m_[id], pos, cfact, m_s, bfactnew);
1105 : // update delta density
1106 : deltaov_l[id] += dnew-dold;
1107 : }
1108 : }
1109 : // add to global list
1110 : #pragma omp critical
1111 : {
1112 : for(std::map<unsigned,double>::iterator itov=deltaov_l.begin(); itov!=deltaov_l.end(); ++itov)
1113 : deltaov[itov->first] += itov->second;
1114 : }
1115 : }
1116 :
1117 : // now calculate new and old score
1118 : double old_ene = 0.0;
1119 : double new_ene = 0.0;
1120 :
1121 : // cycle on all affected voxels
1122 0 : for(std::map<unsigned,double>::iterator itov=deltaov.begin(); itov!=deltaov.end(); ++itov) {
1123 : // id of the component
1124 0 : unsigned id = itov->first;
1125 : // new value
1126 0 : double ovmdnew = ovmd_[id]+itov->second;
1127 : // deviations
1128 0 : double devold = scale_ * ovmd_[id] + offset_ - ovdd_[id];
1129 0 : double devnew = scale_ * ovmdnew + offset_ - ovdd_[id];
1130 : // inverse of sigma_min
1131 0 : double ismin = ismin_[id];
1132 : // scores
1133 0 : if(devold==0.0) {
1134 0 : old_ene += -kbt_ * std::log( 0.5 * sqrt2_pi_ * ismin );
1135 : } else {
1136 0 : old_ene += -kbt_ * std::log( 0.5 / devold * erf ( devold * inv_sqrt2_ * ismin ));
1137 : }
1138 0 : if(devnew==0.0) {
1139 0 : new_ene += -kbt_ * std::log( 0.5 * sqrt2_pi_ * ismin );
1140 : } else {
1141 0 : new_ene += -kbt_ * std::log( 0.5 / devnew * erf ( devnew * inv_sqrt2_ * ismin ));
1142 : }
1143 : }
1144 :
1145 : // list of neighboring residues
1146 0 : std::vector<unsigned> close = nl_res_[ir];
1147 : // add restraint to keep Bfactors of close residues close
1148 0 : for(unsigned i=0; i<close.size(); ++i) {
1149 : // residue/chain IDs of neighbor
1150 0 : std::pair<unsigned,std::string> keyn = Model_rlist_[close[i]];
1151 : // deviations
1152 0 : double devold = bfactold - Model_b_[keyn];
1153 0 : double devnew = bfactnew - Model_b_[keyn];
1154 : // inverse of sigma_min
1155 0 : double ismin = 1.0 / bfactsig_;
1156 : // scores
1157 0 : if(devold==0.0) {
1158 0 : old_ene += -kbt_ * std::log( 0.5 * sqrt2_pi_ * ismin );
1159 : } else {
1160 0 : old_ene += -kbt_ * std::log( 0.5 / devold * erf ( devold * inv_sqrt2_ * ismin ));
1161 : }
1162 0 : if(devnew==0.0) {
1163 0 : new_ene += -kbt_ * std::log( 0.5 * sqrt2_pi_ * ismin );
1164 : } else {
1165 0 : new_ene += -kbt_ * std::log( 0.5 / devnew * erf ( devnew * inv_sqrt2_ * ismin ));
1166 : }
1167 : }
1168 :
1169 : // increment number of trials
1170 0 : MCBtrials_ += 1.0;
1171 :
1172 : // accept or reject
1173 : bool accept = false;
1174 0 : if(bfactemin_) {
1175 0 : if(new_ene < old_ene) accept = true;
1176 : } else {
1177 0 : accept = doAccept(old_ene, new_ene, kbt_);
1178 : }
1179 :
1180 : // in case of acceptance
1181 0 : if(accept) {
1182 : // update acceptance rate
1183 0 : MCBaccept_ += 1.0;
1184 : // update bfactor
1185 0 : Model_b_[key] = bfactnew;
1186 : // change all the ovmd_ affected
1187 0 : for(std::map<unsigned,double>::iterator itov=deltaov.begin(); itov!=deltaov.end(); ++itov)
1188 0 : ovmd_[itov->first] += itov->second;
1189 : }
1190 :
1191 : } // end cycle on bfactors
1192 :
1193 : // update auxiliary lists (to update pref_gpu_, invs2_gpu_, and cut_ on CPU/GPU)
1194 0 : get_auxiliary_vectors();
1195 : // update neighbor list (new cut_ + update pref_nl_gpu_ and invs2_nl_gpu_ on GPU)
1196 0 : update_neighbor_list();
1197 : // recalculate fmod (to update derivatives)
1198 0 : calculate_fmod();
1199 0 : }
1200 :
1201 : // get overlap
1202 0 : double EMMIVOX::get_overlap(const Vector &d_m, const Vector &m_m,
1203 : const Vector5d &cfact, const Vector5d &m_s, double bfact)
1204 : {
1205 : // calculate vector difference
1206 0 : Vector md = delta(m_m, d_m);
1207 : // norm squared
1208 0 : double md2 = md[0]*md[0]+md[1]*md[1]+md[2]*md[2];
1209 : // cycle on 5 Gaussians
1210 : double ov_tot = 0.0;
1211 0 : for(unsigned j=0; j<5; ++j) {
1212 : // total value of b
1213 0 : double m_b = m_s[j]+bfact/4.0;
1214 : // calculate invs2
1215 0 : double invs2 = 1.0/(inv_pi2_*m_b);
1216 : // final calculation
1217 0 : ov_tot += cfact[j] * pow(invs2, 1.5) * std::exp(-0.5 * md2 * invs2);
1218 : }
1219 0 : return ov_tot;
1220 : }
1221 :
1222 0 : void EMMIVOX::update_neighbor_sphere()
1223 : {
1224 : // number of atoms
1225 0 : unsigned natoms = Model_type_.size();
1226 : // clear neighbor sphere
1227 : ns_.clear();
1228 : // store reference positions
1229 0 : refpos_ = getPositions();
1230 :
1231 : // cycle on voxels - in parallel
1232 0 : #pragma omp parallel num_threads(OpenMP::getNumThreads())
1233 : {
1234 : // private variables
1235 : std::vector< std::pair<unsigned,unsigned> > ns_l;
1236 : #pragma omp for
1237 : for(unsigned id=0; id<ovdd_.size(); ++id) {
1238 : // grid point
1239 : Vector d_m = Map_m_[id];
1240 : // cycle on atoms
1241 : for(unsigned im=0; im<natoms; ++im) {
1242 : // calculate distance
1243 : double dist = delta(getPosition(im), d_m).modulo();
1244 : // add to local list
1245 : if(dist<=2.0*cut_[im]) ns_l.push_back(std::make_pair(id,im));
1246 : }
1247 : }
1248 : // add to global list
1249 : #pragma omp critical
1250 : ns_.insert(ns_.end(), ns_l.begin(), ns_l.end());
1251 : }
1252 0 : }
1253 :
1254 0 : bool EMMIVOX::do_neighbor_sphere()
1255 : {
1256 0 : std::vector<double> dist(getPositions().size());
1257 : bool update = false;
1258 :
1259 : // calculate displacement
1260 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
1261 : for(unsigned im=0; im<dist.size(); ++im) {
1262 : dist[im] = delta(getPosition(im),refpos_[im]).modulo()/cut_[im];
1263 : }
1264 :
1265 : // check if update or not
1266 0 : double maxdist = *max_element(dist.begin(), dist.end());
1267 0 : if(maxdist>=1.0) update=true;
1268 :
1269 : // return if update or not
1270 0 : return update;
1271 : }
1272 :
1273 0 : void EMMIVOX::update_neighbor_list()
1274 : {
1275 : // number of atoms
1276 0 : unsigned natoms = Model_type_.size();
1277 : // clear neighbor list
1278 : nl_.clear();
1279 :
1280 : // cycle on neighbour sphere - in parallel
1281 0 : #pragma omp parallel num_threads(OpenMP::getNumThreads())
1282 : {
1283 : // private variables
1284 : std::vector< std::pair<unsigned,unsigned> > nl_l;
1285 : #pragma omp for
1286 : for(unsigned long long i=0; i<ns_.size(); ++i) {
1287 : // calculate distance
1288 : double dist = delta(Map_m_[ns_[i].first], getPosition(ns_[i].second)).modulo();
1289 : // add to local neighbour list
1290 : if(dist<=cut_[ns_[i].second]) nl_l.push_back(ns_[i]);
1291 : }
1292 : // add to global list
1293 : #pragma omp critical
1294 : nl_.insert(nl_.end(), nl_l.begin(), nl_l.end());
1295 : }
1296 :
1297 : // new dimension of neighbor list
1298 : unsigned long long nl_size = nl_.size();
1299 : // now resize derivatives
1300 0 : ovmd_der_.resize(nl_size);
1301 :
1302 : // in case of B-factors sampling - at the right step
1303 0 : if(dbfact_>0 && getStep()%MCBstride_==0) {
1304 : // clear vectors
1305 0 : Model_nb_.clear(); Model_nb_.resize(natoms);
1306 : // cycle over the neighbor list to creat a list of voxels per atom
1307 0 : #pragma omp parallel num_threads(OpenMP::getNumThreads())
1308 : {
1309 : // private variables
1310 : std::vector< std::vector<unsigned> > Model_nb_l(natoms);
1311 : #pragma omp for
1312 : for(unsigned long long i=0; i<nl_size; ++i) {
1313 : Model_nb_l[nl_[i].second].push_back(nl_[i].first);
1314 : }
1315 : // add to global list
1316 : #pragma omp critical
1317 : {
1318 : for(unsigned i=0; i<natoms; ++i)
1319 : Model_nb_[i].insert(Model_nb_[i].end(), Model_nb_l[i].begin(), Model_nb_l[i].end());
1320 : }
1321 : }
1322 : }
1323 :
1324 : // transfer data to gpu
1325 0 : update_gpu();
1326 0 : }
1327 :
1328 0 : void EMMIVOX::update_gpu()
1329 : {
1330 : // dimension of neighbor list
1331 : long long nl_size = nl_.size();
1332 : // create useful vectors
1333 0 : std::vector<int> nl_id(nl_size), nl_im(nl_size);
1334 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
1335 : for(unsigned long long i=0; i<nl_size; ++i) {
1336 : nl_id[i] = static_cast<int>(nl_[i].first);
1337 : nl_im[i] = static_cast<int>(nl_[i].second);
1338 : }
1339 : // create tensors on device
1340 0 : nl_id_gpu_ = torch::from_blob(nl_id.data(), {nl_size}, torch::kInt32).clone().to(device_t_);
1341 0 : nl_im_gpu_ = torch::from_blob(nl_im.data(), {nl_size}, torch::kInt32).clone().to(device_t_);
1342 : // now we need to create pref_nl_gpu_ [5,nl_size]
1343 0 : pref_nl_gpu_ = torch::index_select(pref_gpu_,1,nl_im_gpu_);
1344 : // and invs2_nl_gpu_ [5,nl_size]
1345 0 : invs2_nl_gpu_ = torch::index_select(invs2_gpu_,1,nl_im_gpu_);
1346 : // and Map_m_nl_gpu_ [3,nl_size]
1347 0 : Map_m_nl_gpu_ = torch::index_select(Map_m_gpu_,1,nl_id_gpu_);
1348 0 : }
1349 :
1350 0 : void EMMIVOX::prepare()
1351 : {
1352 0 : if(getExchangeStep()) first_time_=true;
1353 0 : }
1354 :
1355 : // calculate forward model on gpu
1356 0 : void EMMIVOX::calculate_fmod()
1357 : {
1358 : // number of atoms
1359 0 : int natoms = Model_type_.size();
1360 : // number of data points
1361 0 : int nd = ovdd_.size();
1362 :
1363 : // fill positions in in parallel
1364 0 : std::vector<double> posg(3*natoms);
1365 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
1366 : for (int i=0; i<natoms; ++i) {
1367 : // fill vectors
1368 : posg[i] = getPosition(i)[0];
1369 : posg[i+natoms] = getPosition(i)[1];
1370 : posg[i+2*natoms] = getPosition(i)[2];
1371 : }
1372 : // transfer positions to pos_gpu [3,natoms]
1373 0 : torch::Tensor pos_gpu = torch::from_blob(posg.data(), {3,natoms}, torch::kFloat64).to(torch::kFloat32).to(device_t_);
1374 : // create pos_nl_gpu_ [3,nl_size]
1375 0 : torch::Tensor pos_nl_gpu = torch::index_select(pos_gpu,1,nl_im_gpu_);
1376 : // calculate vector difference [3,nl_size]
1377 0 : torch::Tensor md = Map_m_nl_gpu_ - pos_nl_gpu;
1378 : // calculate norm squared by column [1,nl_size]
1379 0 : torch::Tensor md2 = torch::sum(md*md,0);
1380 : // calculate density [5,nl_size]
1381 0 : torch::Tensor ov = pref_nl_gpu_ * torch::exp(-0.5 * md2 * invs2_nl_gpu_);
1382 : // and derivatives [5,nl_size]
1383 0 : ovmd_der_gpu_ = invs2_nl_gpu_ * ov;
1384 : // sum density over 5 columns [1,nl_size]
1385 0 : ov = torch::sum(ov,0);
1386 : // sum contributions from the same atom
1387 0 : auto options = torch::TensorOptions().device(device_t_).dtype(torch::kFloat32);
1388 0 : ovmd_gpu_ = torch::zeros({nd}, options);
1389 0 : ovmd_gpu_.index_add_(0, nl_id_gpu_, ov);
1390 : // sum derivatives over 5 rows [1,nl_size] and multiply by md [3,nl_size]
1391 0 : ovmd_der_gpu_ = md * torch::sum(ovmd_der_gpu_,0);
1392 :
1393 : // in case of metainference: average them across replicas
1394 0 : if(!no_aver_ && nrep_>1) {
1395 : // communicate ovmd_gpu_ to CPU [1, nd]
1396 0 : torch::Tensor ovmd_cpu = ovmd_gpu_.detach().to(torch::kCPU).to(torch::kFloat64);
1397 : // and put them in ovmd_
1398 0 : ovmd_ = std::vector<double>(ovmd_cpu.data_ptr<double>(), ovmd_cpu.data_ptr<double>() + ovmd_cpu.numel());
1399 : // sum across replicas
1400 0 : multi_sim_comm.Sum(&ovmd_[0], nd);
1401 : // and divide by number of replicas
1402 0 : double escale = 1.0 / static_cast<double>(nrep_);
1403 0 : for(int i=0; i<nd; ++i) ovmd_[i] *= escale;
1404 : // put back on device
1405 0 : ovmd_gpu_ = torch::from_blob(ovmd_.data(), {nd}, torch::kFloat64).to(torch::kFloat32).to(device_t_);
1406 : }
1407 :
1408 : // communicate back model density
1409 : // this is needed only in certain situations
1410 0 : long int step = getStep();
1411 : bool do_comm = false;
1412 0 : if(mapstride_>0 && step%mapstride_==0) do_comm = true;
1413 0 : if(dbfact_>0 && step%MCBstride_==0) do_comm = true;
1414 0 : if(do_corr_) do_comm = true;
1415 : // in case of metainference: already communicated
1416 0 : if(!no_aver_ && nrep_>1) do_comm = false;
1417 0 : if(do_comm) {
1418 : // communicate ovmd_gpu_ to CPU [1, nd]
1419 0 : torch::Tensor ovmd_cpu = ovmd_gpu_.detach().to(torch::kCPU).to(torch::kFloat64);
1420 : // and put them in ovmd_
1421 0 : ovmd_ = std::vector<double>(ovmd_cpu.data_ptr<double>(), ovmd_cpu.data_ptr<double>() + ovmd_cpu.numel());
1422 : }
1423 0 : }
1424 :
1425 : // calculate score
1426 0 : void EMMIVOX::calculate_score()
1427 : {
1428 : // number of atoms
1429 0 : int natoms = Model_type_.size();
1430 :
1431 : // calculate deviation model/data [1, nd]
1432 0 : torch::Tensor dev = scale_ * ovmd_gpu_ + offset_ - ovdd_gpu_;
1433 : // error function [1, nd]
1434 0 : torch::Tensor errf = torch::erf( dev * inv_sqrt2_ * ismin_gpu_ );
1435 : // take care of dev = zero
1436 0 : torch::Tensor zeros_d = torch::ne(dev, 0.0);
1437 : // redefine dev
1438 0 : dev = dev * zeros_d + eps_ * torch::logical_not(zeros_d);
1439 : // take care of errf = zero
1440 0 : torch::Tensor zeros_e = torch::ne(errf, 0.0);
1441 : // redefine errf
1442 0 : errf = errf * zeros_e + eps_ * torch::logical_not(zeros_e);
1443 : // logical AND: both dev and errf different from zero
1444 : torch::Tensor zeros = torch::logical_and(zeros_d, zeros_e);
1445 : // energy - with limit dev going to zero
1446 0 : torch::Tensor ene = 0.5 * ( errf / dev * zeros + torch::logical_not(zeros) * sqrt2_pi_ * ismin_gpu_);
1447 : // logarithm and sum
1448 0 : ene = -kbt_ * torch::sum(torch::log(ene));
1449 : // and derivatives [1, nd]
1450 0 : torch::Tensor d_der = -kbt_ * zeros * ( sqrt2_pi_ * torch::exp( -0.5 * dev * dev * ismin_gpu_ * ismin_gpu_ ) * ismin_gpu_ / errf - 1.0 / dev );
1451 : // tensor for derivatives wrt atoms [1, nl_size]
1452 0 : torch::Tensor der_gpu = torch::index_select(d_der,0,nl_id_gpu_);
1453 : // multiply by ovmd_der_gpu_ and scale [3, nl_size]
1454 0 : der_gpu = ovmd_der_gpu_ * scale_ * der_gpu;
1455 : // sum contributions for each atom
1456 0 : auto options = torch::TensorOptions().device(device_t_).dtype(torch::kFloat32);
1457 0 : torch::Tensor atoms_der_gpu = torch::zeros({3,natoms}, options);
1458 0 : atoms_der_gpu.index_add_(1, nl_im_gpu_, der_gpu);
1459 :
1460 : // FINAL STUFF
1461 : //
1462 : // 1) communicate total energy to CPU
1463 0 : torch::Tensor ene_cpu = ene.detach().to(torch::kCPU).to(torch::kFloat64);
1464 0 : ene_ = *ene_cpu.data_ptr<double>();
1465 : // with marginal, simply multiply by number of replicas!
1466 0 : if(!no_aver_ && nrep_>1) ene_ *= static_cast<double>(nrep_);
1467 : //
1468 : // 2) communicate derivatives to CPU
1469 0 : torch::Tensor atom_der_cpu = atoms_der_gpu.detach().to(torch::kCPU).to(torch::kFloat64);
1470 : // convert to std::vector<double>
1471 0 : std::vector<double> atom_der = std::vector<double>(atom_der_cpu.data_ptr<double>(), atom_der_cpu.data_ptr<double>() + atom_der_cpu.numel());
1472 : // and put in atom_der_
1473 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads())
1474 : for(int i=0; i<natoms; ++i) {
1475 : atom_der_[i] = Vector(atom_der[i],atom_der[i+natoms],atom_der[i+2*natoms]);
1476 : }
1477 : //
1478 : // 3) calculate virial on CPU
1479 0 : Tensor virial;
1480 : // declare omp reduction for Tensors
1481 : #pragma omp declare reduction( sumTensor : Tensor : omp_out += omp_in )
1482 :
1483 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads()) reduction (sumTensor : virial)
1484 : for(int i=0; i<natoms; ++i) {
1485 : virial += Tensor(getPosition(i), -atom_der_[i]);
1486 : }
1487 : // store virial
1488 0 : virial_ = virial;
1489 0 : }
1490 :
1491 0 : void EMMIVOX::calculate()
1492 : {
1493 : // get time step
1494 0 : long int step = getStep();
1495 :
1496 : // set temperature value
1497 0 : getPntrToComponent("kbt")->set(kbt_);
1498 :
1499 : // neighbor list update
1500 0 : if(first_time_ || getExchangeStep() || step%nl_stride_==0) {
1501 : // check if time to update neighbor sphere
1502 : bool update = false;
1503 0 : if(first_time_ || getExchangeStep()) update = true;
1504 0 : else update = do_neighbor_sphere();
1505 : // update neighbor sphere
1506 0 : if(update) update_neighbor_sphere();
1507 : // update neighbor list
1508 0 : update_neighbor_list();
1509 : // set flag
1510 0 : first_time_=false;
1511 : }
1512 :
1513 : // calculate forward model
1514 0 : calculate_fmod();
1515 :
1516 : // Monte Carlo on bfactors
1517 0 : if(dbfact_>0) {
1518 : double acc = 0.0;
1519 : // do Monte Carlo
1520 0 : if(step%MCBstride_==0 && !getExchangeStep() && step>0) doMonteCarloBfact();
1521 : // calculate acceptance ratio
1522 0 : if(MCBtrials_>0) acc = MCBaccept_ / MCBtrials_;
1523 : // set value
1524 0 : getPntrToComponent("accB")->set(acc);
1525 : }
1526 :
1527 : // calculate score
1528 0 : calculate_score();
1529 :
1530 : // set score, virial, and derivatives
1531 0 : Value* score = getPntrToComponent("scoreb");
1532 0 : score->set(ene_);
1533 0 : setBoxDerivatives(score, virial_);
1534 0 : #pragma omp parallel for
1535 : for(unsigned i=0; i<atom_der_.size(); ++i) setAtomsDerivatives(score, i, atom_der_[i]);
1536 : // set scale and offset value
1537 0 : getPntrToComponent("scale")->set(scale_);
1538 0 : getPntrToComponent("offset")->set(offset_);
1539 : // calculate correlation coefficient
1540 0 : if(do_corr_) calculate_corr();
1541 : // PRINT other quantities to files
1542 : // - status file
1543 0 : if(step%statusstride_==0) print_status(step);
1544 : // - density file
1545 0 : if(mapstride_>0 && step%mapstride_==0) write_model_density(step);
1546 0 : }
1547 :
1548 0 : void EMMIVOX::calculate_corr()
1549 : {
1550 : // number of data points
1551 0 : double nd = static_cast<double>(ovdd_.size());
1552 : // average ovmd_ and ovdd_
1553 0 : double ave_md = std::accumulate(ovmd_.begin(), ovmd_.end(), 0.) / nd;
1554 0 : double ave_dd = std::accumulate(ovdd_.begin(), ovdd_.end(), 0.) / nd;
1555 : // calculate correlation
1556 : double num = 0.;
1557 : double den1 = 0.;
1558 : double den2 = 0.;
1559 0 : #pragma omp parallel for num_threads(OpenMP::getNumThreads()) reduction( + : num, den1, den2)
1560 : for(unsigned i=0; i<ovdd_.size(); ++i) {
1561 : double md = ovmd_[i]-ave_md;
1562 : double dd = ovdd_[i]-ave_dd;
1563 : num += md*dd;
1564 : den1 += md*md;
1565 : den2 += dd*dd;
1566 : }
1567 : // correlation coefficient
1568 0 : double cc = num / sqrt(den1*den2);
1569 : // set plumed
1570 0 : getPntrToComponent("corr")->set(cc);
1571 0 : }
1572 :
1573 :
1574 : }
1575 : }
1576 :
1577 : #endif
|