Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
PoissonBoltzmannPotential.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// Compute electrostatic.
11 ///
12 /// Options:
13 ///
14 /// vector1<Integer> pb_potential::charged_chain
15 /// The chain numbers (>=1) of which charge is non-zero.
16 /// The electrostatic will be computed for the atoms in these chains.
17 ///
18 /// @file core/scoring/PoissonBoltzmannPotential.cc
19 /// @brief PoissonBoltzmann potential class implementation
20 /// @author Yifan Song (yfsong@uw.edu)
21 
22 // basic
23 #include <basic/Tracer.hh>
24 #include <basic/options/option.hh>
25 #include <basic/options/keys/out.OptionKeys.gen.hh>
26 #include <basic/options/keys/pb_potential.OptionKeys.gen.hh>
27 
28 // Unit Headers
31 
32 // Package Headers
35 
36 // Project Headers
38 #include <core/pose/Pose.hh>
39 #include <core/pose/util.hh>
40 
41 // Numeric Headers
42 
43 // Utility Headers
44 #include <utility/pointer/ReferenceCount.hh>
45 #include <utility/io/izstream.hh>
46 
47 // utility
48 #include <ObjexxFCL/format.hh>
49 #include <ObjexxFCL/FArray3D.hh>
50 #include <numeric/xyz.functions.hh>
51 #include <numeric/xyzMatrix.hh>
52 #include <numeric/xyzVector.hh>
53 #include <algorithm>
54 
56 #include <utility/vector1.hh>
57 #include <vector>
58 #include <ctime>
59 
60 // option key includes
61 
62 
63 #include <fstream>
64 
65 static basic::Tracer TR("core.scoring.PoissonBoltzmannPotential");
66 
67 namespace core {
68 namespace scoring {
69 
71 
72 const std::string PB::APBS_CONFIG_EXT = ".in";
73 const std::string PB::APBS_PQR_EXT = ".pqr";
74 const std::string PB::APBS_DX_EXT = ".dx";
75 const std::string PB::DEFAULT_APBS_PATH = "apbs";
76 
77 // @brief Auto-generated virtual destructor
79 
81  :config_filename_("Unknown.in"),
82  pqr_filename_("Unknown.pqr"),
83  dx_filename_("Unknown.dx"),
84  apbs_path_(DEFAULT_APBS_PATH),
85  calcenergy_(false)
86 {
87 }
88 
90 PB::get_potential(ObjexxFCL::FArray3D< core::Real > const & potential,
91 numeric::xyzVector<core::Real> const & cartX) const {
92  if (out_of_bounds(cartX)) return 0.0;
93 
95  cart2idx(cartX, idxX);
96  return core::scoring::electron_density::interp_linear(potential, idxX);
97 }
98 void
100  core::conformation::Residue const & rsd,
101  Real & PB_energy_residue,
102  Real & PB_energy_backbone,
103  Real & PB_energy_sidechain,
104  Real const & PB_burial_weight
105  ) const {
106 
107  PB_energy_residue = 0.0;
108  PB_energy_backbone = 0.0;
109  PB_energy_sidechain = 0.0;
110 
111  // Compute potential. Linear sum.
112  for ( Size iatom=1; iatom<=rsd.natoms(); ++iatom ) {
113  if (rsd.atomic_charge(iatom) > -1e-6 && rsd.atomic_charge(iatom) < 1e-6) continue;
114  core::Real iatom_potential = get_potential(potential_, rsd.xyz(iatom));
115 
116  core::Real atom_energy = rsd.atomic_charge(iatom)*iatom_potential * PB_burial_weight;
117 
118  if (rsd.atom_is_backbone(iatom)) {
119  PB_energy_backbone += atom_energy;
120  }
121  else {
122  PB_energy_sidechain += atom_energy;
123  }
124  PB_energy_residue += atom_energy;
125  }
126 
127 }
128 
129 //============================================================================
130 #ifdef LINK_APBS_LIB // APBS libraries are linked
131 
132 void
133 PB::solve_pb( core::pose::Pose const & pose,
134  std::string const & tag,
135  std::map<std::string, bool> const &charged_residues )
136 {
137  using namespace std;
138  time_t begin;
139  time(&begin);
140 
142  pqr_filename_ = tag + APBS_PQR_EXT;
143  dx_filename_ = tag + APBS_DX_EXT;
144 
145  int apbs_dbg = basic::options::option[ basic::options::OptionKeys::pb_potential::apbs_debug ];
146  calcenergy_ = basic::options::option[ basic::options::OptionKeys::pb_potential::calcenergy ];
147  APBSWrapper apbs(pose, charged_residues, apbs_dbg, calcenergy_);
148 
149  APBSResultCOP result = 0;
150 
151  result = apbs.exec();
152 
153  if( result == 0 ) {
154  TR.Error << "APBS failed! Terminating the program..." << std::endl;
155  TR.flush();
156  runtime_assert(false);
157  }
158  TR.Debug << "Solved PB. Loading potential..." << std::endl;
159  const double * meta = result->grid_meta;
160  const double * data = &(result->grid_data[0][0]);
161  load_potential(meta, data);
162 
163  time_t end;
164  time(&end);
165  TR << "PB took " << end-begin << " seconds" << std::endl;
166 }
167 void
168 PB::load_potential(const double grid_meta[],
169  const double pot[]) {
170  int nx = grid_meta[1];
171  int ny = grid_meta[2];
172  int nz = grid_meta[3];
173  n_grid_[0] = nx;
174  n_grid_[1] = ny;
175  n_grid_[2] = nz;
176  grid_spacing_[0] = grid_meta[4];
177  grid_spacing_[1] = grid_meta[5];
178  grid_spacing_[2] = grid_meta[6];
179  //double centx = grid_meta[7];
180  //double centy = grid_meta[8];
181  //double centz = grid_meta[9];
182  lower_bound_[0] = grid_meta[10];
183  lower_bound_[1] = grid_meta[11];
184  lower_bound_[2] = grid_meta[12];
185 
187  grid_spacing_[0],0.,0.,
188  0.,grid_spacing_[1],0.,
189  0.,0.,grid_spacing_[2]);
191  1./grid_spacing_[0],0.,0.,
192  0.,1./grid_spacing_[1],0.,
193  0.,0.,1./grid_spacing_[2]);
194 
195  potential_.dimension(nx, ny, nz);
196 
197  double cap = basic::options::option[ basic::options::OptionKeys::pb_potential::potential_cap ];
198 
199  int icol=0;
200  int u;
201  int lines = 0;
202  //using namespace ObjexxFCL::fmt;
203  //std::ofstream ofs(dx_filename_.c_str());
204  //ofs << "object 1 class gridpositions counts " << nx << " " << ny << " " << nz << std::endl;
205  //ofs << "origin " << lower_bound_[0] << " " << lower_bound_[1] << " " << lower_bound_[2] << std::endl;
206  //ofs << "object 3 array type double rank 0 items " << nx*ny*nz << " data follows" << std::endl;
207  for (int i=1; i<=nx; i++) {
208  for (int j=1; j<=ny; j++) {
209  for (int k=1; k<=nz; k++) {
210  u = (k-1)*(nx)*(ny)+(j-1)*(nx)+(i-1);
211  //ofs << E(12,6,pot[u]) << " ";
212  icol++;
213  if (icol == 3) {
214  icol = 0;
215  lines++;
216  //ofs << std::endl;
217  }
218  if( pot[u] > cap ){
219  potential_(i,j,k) = cap;
220  }
221  else if( pot[u] < -cap ) {
222  potential_(i,j,k) = -cap;
223  }
224  else{
225  potential_(i,j,k) = pot[u];
226  }
227  }
228  }
229  }
230  //ofs.close();
231  TR.Debug << "PB potential is successfully loaded." << std::endl;
232 
234  TR << "Convertion of PB potential to Certesian coordinates is completed" << std::endl;
235 }
236 
237 #else // APBS libraries are not linked. Use system call.
238 
239 void
241  std::string const & tag,
242  std::map<std::string, bool> const &charged_residues )
243 {
244  using namespace std;
245  time_t begin;
246  time(&begin);
247 
248  if (basic::options::option[basic::options::OptionKeys::pb_potential::apbs_path].user()) {
249  apbs_path_ = basic::options::option[basic::options::OptionKeys::pb_potential::apbs_path];
250  }
251 
252  calcenergy_ = basic::options::option[ basic::options::OptionKeys::pb_potential::calcenergy ];
253 
254  // Generate filenames based on the given tag.
256  pqr_filename_ = tag + APBS_PQR_EXT;
257  dx_filename_ = tag + APBS_DX_EXT;
258 
259  write_pqr(pose, charged_residues );
260  write_config(pose);
261  std::string command_line(apbs_path_ + " " + config_filename_);
262  int ret = system(command_line.c_str());
263 
264  // Check if APBS succeeded. If not, get out.
265  std::ifstream dxstream(dx_filename_.c_str());
266  if( ! dxstream.good() ) {
267  TR << "APBS failed to generate the result file. Terminating the program." << std::endl;
268  TR.flush();
269  runtime_assert(false);
270  }
271  dxstream.close();
272 
273  // load the result
275 
276  time_t end;
277  time(&end);
278  TR << "PB took " << end-begin << " seconds" << std::endl;
279 }
280 void
282 {
283  utility::io::izstream p_stream( dx_filename_ );
284  runtime_assert(p_stream);
285 
286  std::string line;
287  while (p_stream) {
288  // # Comments
289  char first_char = p_stream.peek();
290  if ( first_char == '#' ) {
291  p_stream.getline( line );
292  continue;
293  }
294 
295  //object 1 class gridpositions counts nx ny nz
296  std::string buff;
297  for (Size i=0;i<5;++i) p_stream >> buff;
298  p_stream >> n_grid_[0] >> n_grid_[1] >> n_grid_[2];
299 
300  //origin xmin ymin zmin
301  p_stream >> buff;
302  p_stream >> lower_bound_[0];
303  p_stream >> lower_bound_[1];
304  p_stream >> lower_bound_[2];
305 
306  //delta hx 0.0 0.0found
307  //delta 0.0 hy 0.0
308  //delta 0.0 0.0 hz
309 
310  p_stream >> buff >> grid_spacing_[0] >> buff >> buff;
311  p_stream >> buff >> buff >> grid_spacing_[1] >> buff;
312  p_stream >> buff >> buff >> buff >> grid_spacing_[2];
314  grid_spacing_[0],0.,0.,
315  0.,grid_spacing_[1],0.,
316  0.,0.,grid_spacing_[2]);
318  1./grid_spacing_[0],0.,0.,
319  0.,1./grid_spacing_[1],0.,
320  0.,0.,1./grid_spacing_[2]);
321 
322  //object 2 class gridconnections counts nx ny nz
323  //object 3 class array type double rank 0 items n data follows
324  p_stream.getline( line );
325  p_stream.getline( line );
326  p_stream.getline( line );
327 
328  //u(0,0,0) u(0,0,1) u(0,0,2)
329  potential_.dimension(n_grid_[0],n_grid_[1],n_grid_[2]);
330 
331  for (int i=1; i<=potential_.u1(); i++)
332  for (int j=1; j<=potential_.u2(); j++)
333  for (int k=1; k<=potential_.u3(); k++) {
334  p_stream >> buff;
335  potential_(i,j,k) = atof(buff.c_str());
336  if (potential_(i,j,k) > basic::options::option[ basic::options::OptionKeys::pb_potential::potential_cap ]) {
337  potential_(i,j,k) = basic::options::option[ basic::options::OptionKeys::pb_potential::potential_cap ];
338  }
339  if (potential_(i,j,k) < -basic::options::option[ basic::options::OptionKeys::pb_potential::potential_cap ]) {
340  potential_(i,j,k) = -basic::options::option[ basic::options::OptionKeys::pb_potential::potential_cap ];
341  }
342  }
343  break;
344  }
345 
346 
347  TR << "PB potential is successfully loaded from: " << dx_filename_ << std::endl;
348 
350  TR << "Convertion of PB potential to Certesian coordinates is completed" << std::endl;
351 
352  //attribute "dep" string "positions"
353  //object "regular positions regular connections" class field
354  //component "positions" value 1
355  //component "connections" value 2
356  //component "data" value 3
357 
358 }
359 
360 #endif // LINK_APBS_LIB
361 //==============================================================================
362 void
364  std::map<std::string, bool> const & is_residue_charged_by_name_) const {
365  // Generate .pqr
366  std::ofstream pqr_ostr(pqr_filename_.c_str());
367 
368  using namespace basic::options::OptionKeys;
369  utility::vector1<Size> charged_chains;
370  charged_chains.push_back(1);
371 
372  Size const nres( pose.total_residue() );
373 
374  Size number(0);
375 
376  static std::string const chains( " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890" );
377 
378  for ( Size i=1; i<= nres; ++i ) {
379  conformation::Residue const & rsd( pose.residue(i) );
380  // TODO: Sachko 11/19/2012
381  // This search result should be cached.
382  bool residue_charged = const_cast<std::map<std::string,bool>&>(is_residue_charged_by_name_)[rsd.type().name()];
383  for ( Size j=1; j<= rsd.natoms(); ++j ) {
384  conformation::Atom const & atom( rsd.atom(j) );
385 
386  //skip outputing virtual atom unless specified.
387  //fixed so that the last atom in atom type set can be something other than a virtual atom --steven combs
388  //if ( !basic::options::option[ basic::options::OptionKeys::out::file::output_virtual ]() &&
389  // rsd.atom_type(j).is_virtual() ) continue;
390  if ( rsd.atom_type(j).is_virtual() ) continue;
391 
392  ++number;
393  runtime_assert( rsd.chain() < chains.size() ); // silly restriction
394 
395  char const chain( chains[ rsd.chain() ] );
396  if (residue_charged) {
397  using namespace ObjexxFCL::fmt;
398  pqr_ostr << "ATOM " << I(5,number) << ' ' << rsd.atom_name(j) << ' ' <<
399  rsd.name3() << ' ' << chain << I(4,rsd.seqpos() ) << " " <<
400  F(8,3,atom.xyz()(1)) <<
401  F(8,3,atom.xyz()(2)) <<
402  F(8,3,atom.xyz()(3)) <<
403  F(8,3,pose.residue_type(i).atom(j).charge()) <<
404  F(8,3,rsd.atom_type(j).lj_radius()) << '\n';
405  }
406  else {
407  using namespace ObjexxFCL::fmt;
408  pqr_ostr << "ATOM " << I(5,number) << ' ' << rsd.atom_name(j) << ' ' <<
409  rsd.name3() << ' ' << chain << I(4,rsd.seqpos() ) << " " <<
410  F(8,3,atom.xyz()(1)) <<
411  F(8,3,atom.xyz()(2)) <<
412  F(8,3,atom.xyz()(3)) <<
413  F(8,3,0.0) <<
414  F(8,3,rsd.atom_type(j).lj_radius()) << '\n';
415  }
416  }
417  }
418  pqr_ostr.close();
419 
420  TR << pqr_filename_ << " is successfully written." << std::endl;
421 }
422 ///
423 /// Write out the configurati
424 ///
425 void
426 PB::write_config (core::pose::Pose const & pose) const {
427 
428  // Generate .in
429  std::ofstream config_ostr(config_filename_.c_str());
430 
431  numeric::xyzVector <core::Real> min_r(9999,9999,9999);
432  numeric::xyzVector <core::Real> max_r(-9999,-9999,-9999);
433  // Find the min & max coords within the moleculer system to define the grid.
434  for (core::Size ires=1; ires<=pose.total_residue(); ++ires) {
435  for (core::Size iatom=1; iatom<=pose.residue(ires).natoms(); ++iatom) {
436  for (core::Size i=0; i<3; ++i) {
437  if (pose.residue(ires).xyz(iatom)[i] < min_r[i]) {
438  min_r[i] = pose.residue(ires).xyz(iatom)[i];
439  }
440  if (pose.residue(ires).xyz(iatom)[i] > max_r[i]) {
441  max_r[i] = pose.residue(ires).xyz(iatom)[i];
442  }
443  }
444  }
445  }
446 
447  //min_r -= numeric::xyzVector <core::Real> (20,20,20);
448  //max_r += numeric::xyzVector <core::Real> (20,20,20);
449  numeric::xyzVector <core::Real> length = max_r - min_r; // grid widths
450  numeric::xyzVector <core::Real> center = (min_r + max_r)/2.; // grid center coords
451  //APBS psize.py paramters:
452  core::Real cfac(1.7);
453  core::Real fadd(20);
454  core::Real space(0.5);
455  numeric::xyzVector <core::Real> dimension_fine = length + numeric::xyzVector <core::Real> (fadd,fadd,fadd);
456  numeric::xyzVector <core::Real> dimension_coarse = length * cfac;
457  numeric::xyzVector <core::Size> n_grid = static_cast< numeric::xyzVector <core::Size> > (dimension_fine / space + numeric::xyzVector <core::Real> (1.,1.,1.));
458 
459  using namespace ObjexxFCL::fmt;
460  config_ostr << "#" << std::endl;
461  config_ostr << "# Note that most of the comments here were taken from sample" << std::endl;
462  config_ostr << "# input files that came with APBS. You can find APBS at" << std::endl;
463  config_ostr << "# http://agave.wustl.edu/apbs/" << std::endl;
464  config_ostr << "# Note that APBS is BSD & MIT'd code." << std::endl;
465  config_ostr << "#" << std::endl;
466  config_ostr << "read" << std::endl;
467  config_ostr << "mol pqr "<< pqr_filename_ <<" # read molecule 1" << std::endl;
468  config_ostr << "end" << std::endl;
469  config_ostr << "elec" << std::endl;
470  config_ostr << "mg-auto" << std::endl;
471  config_ostr << "dime " << I(5,n_grid.x()) << " "<< I(5,n_grid.y()) << " "<< I(5,n_grid.z()) << " # number of find grid points" << std::endl;
472  config_ostr << "# calculated by psize.py" << std::endl;
473  config_ostr << "cglen " << F(11,6,dimension_coarse.x()) << " " << F(11,6,dimension_coarse.y()) << " " << F(11,6,dimension_coarse.z()) << " # coarse mesh lengths (A)" << std::endl;
474  config_ostr << "fglen " << F(11,6,dimension_fine.x()) << " " << F(11,6,dimension_fine.y()) << " " << F(11,6,dimension_fine.z()) << " # fine mesh lengths (A)" << std::endl;
475  config_ostr << "# calculated by psize.py" << std::endl;
476  config_ostr << "cgcent " << F(11,6,center.x()) << " " << F(11,6,center.y()) << " " << F(11,6,center.z()) << " # (could also give (x,y,z) form psize.py) #known center" << std::endl;
477  config_ostr << "fgcent " << F(11,6,center.x()) << " " << F(11,6,center.y()) << " " << F(11,6,center.z()) << " # (could also give (x,y,z) form psize.py) #known center" << std::endl;
478  config_ostr << "npbe # solve the full nonlinear PBE with npbe" << std::endl;
479  config_ostr << "#lpbe # solve the linear PBE with lpbe" << std::endl;
480  config_ostr << "bcfl sdh # Boundary condition flag" << std::endl;
481  config_ostr << "# 0 => Zero" << std::endl;
482  config_ostr << "# 1 => Single DH sphere" << std::endl;
483  config_ostr << "# 2 => Multiple DH spheres" << std::endl;
484  config_ostr << "# 4 => Focusing" << std::endl;
485  config_ostr << "#" << std::endl;
486  config_ostr << "#ion 1 0.000 2.0 # Counterion declaration:" << std::endl;
487  config_ostr << "ion 1 0.150000 2.000000 # Counterion declaration:" << std::endl;
488  config_ostr << "ion -1 0.150000 2.000000 # ion <charge> <conc (M)> <radius>" << std::endl;
489  config_ostr << "ion 2 0.000000 2.000000 # ion <charge> <conc (M)> <radius>" << std::endl;
490  config_ostr << "ion -2 0.000000 2.000000 # ion <charge> <conc (M)> <radius>" << std::endl;
491  config_ostr << "pdie 4.000000 # Solute dielectric" << std::endl;
492  config_ostr << "sdie 80.000000 # Solvent dielectric" << std::endl;
493  config_ostr << "chgm spl2 # Charge disc method" << std::endl;
494  config_ostr << "# 0 is linear splines" << std::endl;
495  config_ostr << "# 1 is cubic b-splines" << std::endl;
496  config_ostr << "mol 1 # which molecule to use" << std::endl;
497  config_ostr << "srfm smol # Surface calculation method" << std::endl;
498  config_ostr << "# 0 => Mol surface for epsilon;" << std::endl;
499  config_ostr << "# inflated VdW for kappa; no" << std::endl;
500  config_ostr << "# smoothing" << std::endl;
501  config_ostr << "# 1 => As 0 with harmoic average" << std::endl;
502  config_ostr << "# smoothing" << std::endl;
503  config_ostr << "# 2 => Cubic spline " << std::endl;
504  config_ostr << "srad 1.400000 # Solvent radius (1.4 for water)" << std::endl;
505  config_ostr << "swin 0.3 # Surface cubic spline window .. default 0.3" << std::endl;
506  config_ostr << "temp 310.000000 # System temperature (298.15 default)" << std::endl;
507  config_ostr << "sdens 10.000000 # Specify the number of grid points per square-angstrom to use in Vacc object. Ignored when srad is 0.0 (see srad) or srfm is spl2 (see srfm). There is a direct correlation between the value used for the Vacc sphere density, the accuracy of the Vacc object, and the APBS calculation time. APBS default value is 10.0." << std::endl;
508  config_ostr << "gamma 0.105 # Surface tension parameter for apolar forces (in kJ/mol/A^2)" << std::endl;
509  config_ostr << "# only used for force calculations, so we don't care, but" << std::endl;
510  config_ostr << "# it's always required, and 0.105 is the default" << std::endl;
511  config_ostr << "calcenergy " << (calcenergy_? "yes" : "no") << " # Energy I/O to stdout" << std::endl;
512  config_ostr << "# 0 => don't write out energy" << std::endl;
513  config_ostr << "# 1 => write out total energy" << std::endl;
514  config_ostr << "# 2 => write out total energy and all" << std::endl;
515  config_ostr << "# components" << std::endl;
516  config_ostr << "calcforce no # Atomic forces I/O (to stdout)" << std::endl;
517  config_ostr << "# 0 => don't write out forces" << std::endl;
518  config_ostr << "# 1 => write out net forces on molecule" << std::endl;
519  config_ostr << "# 2 => write out atom-level forces" << std::endl;
520  config_ostr << "write pot dx " << dx_filename_.substr(0,dx_filename_.size()-APBS_DX_EXT.size()) << " # What to write .. this says write the potential in dx" << std::endl;
521  config_ostr << "# format to a file." << std::endl;
522  config_ostr << "end" << std::endl;
523  config_ostr << "quit" << std::endl;
524 
525  config_ostr.close();
526 
527  TR << config_filename_ << " is successfully written." << std::endl;
528 }
529 
530 }
531 }