Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MolecularSurfaceCalculator.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 /// @file core/scoring/sc/ShapeComplementarityCalculator.cc
11 /// @brief Implementation of molecular surface calculation for shape complementarity.
12 /// @detailed Lawrence & Coleman shape complementarity calculator (based on CCP4's sc)
13 /// @author Luki Goldschmidt <luki@mbi.ucla.edu>, refactored by Alex Ford (fordas@uw.edu)
14 
15 /// This code was ported from the original Fortran code found in CCP4:
16 /// Sc (Version 2.0): A program for determining Shape Complementarity
17 /// Copyright Michael Lawrence, Biomolecular Research Institute
18 /// 343 Royal Parade Parkville Victoria Australia
19 ///
20 #ifndef INCLUDED_core_scoring_sc_MolecularSurfaceCalculator_cc
21 #define INCLUDED_core_scoring_sc_MolecularSurfaceCalculator_cc
22 
23 // Project Headers
24 #include <core/types.hh>
25 #include <core/pose/Pose.hh>
27 #include <core/kinematics/Jump.hh>
32 #include <numeric/xyzVector.hh>
33 #include <numeric/NumericTraits.hh>
34 
35 // Utility headers
36 #include <utility/vector1.hh>
37 #include <utility/exit.hh>
38 #include <utility/io/izstream.hh>
39 #include <basic/Tracer.hh>
40 #include <basic/database/open.hh>
41 
42 // C headers
43 #include <stdio.h>
44 
45 // C++ headers
46 #include <iostream>
47 #include <ostream>
48 #include <fstream>
49 #include <vector>
50 #include <map>
51 #include <string>
52 
53 static basic::Tracer TR("core.scoring.sc.MolecularSurfaceCalculator");
54 
55 using namespace core;
56 
57 namespace core {
58 namespace scoring {
59 namespace sc {
60 
61 std::vector<ATOM_RADIUS> MolecularSurfaceCalculator::radii_; // static const
62 
63 ////////////////////////////////////////////////////////////////////////////
64 // Public class functions
65 ////////////////////////////////////////////////////////////////////////////
66 
67 /// @begin MolecularSurfaceCalculator::MolecularSurfaceCalculator()
68 /// @brief
69 /// MolecularSurfaceCalculator constructor, initializes default settings
70 
72 {
73  memset(&settings, 0, sizeof(settings));
74 
75  // Defaults from sc source
76  settings.rp = 1.7;
77  settings.density = 15.0;
78  settings.band = 1.5;
79  settings.sep = 8.0;
80  settings.weight = 0.5;
81  settings.binwidth_dist = 0.02;
82  settings.binwidth_norm = 0.02;
83 
84  run_.results.valid = 0;
85 }
86 
87 /// @begin MolecularSurfaceCalculator::Init()
88 /// @brief
89 /// Initializes calculation and GPU (if used)
90 /// Init() is also called implicitly by the static CalcSc() function.
91 
93 {
94  if(radii_.empty()) {
95  Reset();
96  ReadScRadii();
97  }
98  if(radii_.empty())
99  return 0;
100 
101  return 1;
102 }
103 
105 {
106  Reset();
107 }
108 
109 /// @begin MolecularSurfaceCalculator::Reset()
110 /// @brief
111 /// Reset calculator for another calculation.
112 /// Must be used when the MolecularSurfaceCalculator instance is re-used.
113 /// @detailed
114 /// Atom, probe and surface dot vectors are reset here. We don't clear them
115 /// after the calculation is finished in case the caller would like to use those
116 /// data elsewhere.
117 
119 {
120  // Free data
121  for(std::vector<Atom>::iterator a = run_.atoms.begin(); a < run_.atoms.end(); ++a) {
122  a->neighbors.clear();
123  a->buried.clear();
124  }
125 
126  // Clear structures
127  run_.atoms.clear();
128  run_.probes.clear();
129  run_.dots[0].clear();
130  run_.dots[1].clear();
131 
132  memset(&run_.results, 0, sizeof(run_.results));
133  memset(&run_.prevp, 0, sizeof(run_.prevp));
134  run_.prevburied = 0;
135 }
136 
137 /// @begin MolecularSurfaceCalculator::Calc(core::pose::Pose const & pose, core::Size jump_id = 0)
138 /// @brief Generate molecular surfaces for the given pose.
139 ///// @detailed
140 /// This function initializes the calculator, adds all residues in the given pose, and generates molecular surfaces.
141 ///
142 /// The pose is partitioned into separate molecules across the given jump. If the given jump is 0, the entire pose is
143 /// loaded as molecule 1.
144 /// To control what residues make up either surface, use the AddResidue() or even add_atom() function instead.
145 /// Returns true on success. Results are retrieved with GetResults().
146 ///
147 /// Example:
148 /// core::scoring::sc::MolecularSurfaceCalculator calc;
149 /// if(calc.Calc( pose ))
150 /// ... = calc.GetResults();
152 {
153  if(!Init())
154  return 0;
155 
156  if( jump_id > pose.num_jump() )
157  {
158  TR.Error << "Jump ID out of bounds (pose has " << pose.num_jump() << " jumps)" << std::endl;
159  return 0;
160  }
161 
162  // Partition pose by jump_id
163  ObjexxFCL::FArray1D_bool is_upstream ( pose.total_residue(), true );
164 
165  if( jump_id > 0 )
166  {
167  pose.fold_tree().partition_by_jump( jump_id, is_upstream );
168  }
169 
170  for(Size i = 1; i <= pose.n_residue(); ++i) {
171  core::conformation::Residue const & residue = pose.residue(i);
172  if(residue.type().name() == "VRT")
173  continue;
174  AddResidue(is_upstream(i) ? 0 : 1, residue);
175  }
176 
177  return Calc();
178 }
179 
180 /// @begin MolecularSurfaceCalculator::Calc()
181 /// @brief Generate molecular surfaces for loaded atoms.
182 ///// @detailed
183 /// This function generates molecular surfaces for atoms added via add_atom and AddResidue.
184 ///
185 /// Init() must be called before this function.
186 /// Returns true on success.
188 {
189  try
190  {
191  basic::gpu::Timer timer(TR.Debug);
192 
193  run_.results.valid = 0;
194 
195  if(run_.atoms.empty())
196  throw ShapeComplementarityCalculatorException("No atoms defined");
197 
198  // Determine assign the attention numbers for each atom
200 
202  return 1;
203  }
205  {
206  TR.Error << "Failed: " << e.error << std::endl;
207  }
208 
209  return 0;
210 }
211 
212 
213 /// @begin MolecularSurfaceCalculator::GenerateMolecularSurfaces
214 /// @brief Generate untrimmed surfaces for the defined molecules.
215 ///// @detailed
216 /// This function should be called within a try/catch block for ShapeComplementarityCalculatorException.
217 /// Raises exception on error.
219 {
220  if(run_.atoms.empty())
221  {
222  throw ShapeComplementarityCalculatorException("No atoms defined");
223  }
224 
225  // Now compute the surface for the atoms in the interface and its neighbours
226  TR.Debug << "Generating molecular surface, " << settings.density << " dots/A^2" << std::endl;
227  CalcDotsForAllAtoms(run_.atoms);
228 
229  if(TR.Debug.visible())
230  {
231  TR.Debug << " Buried atoms (1): " << run_.results.surface[0].nBuriedAtoms << std::endl;
232  TR.Debug << " Buried atoms (2): " << run_.results.surface[1].nBuriedAtoms << std::endl;
233  TR.Debug << " Blocked atoms (1): " << run_.results.surface[0].nBuriedAtoms << std::endl;
234  TR.Debug << " Blocked atoms (2): " << run_.results.surface[1].nBuriedAtoms << std::endl;
235  TR.Debug << " Convex dots: " << run_.results.dots.convex << std::endl;
236  TR.Debug << " Toroidal dots: " << run_.results.dots.toroidal << std::endl;
237  TR.Debug << " Concave dots: " << run_.results.dots.concave << std::endl;
238  TR.Debug << "Total surface dots (1): " << run_.dots[0].size() << std::endl;
239  TR.Debug << "Total surface dots (2): " << run_.dots[1].size() << std::endl;
240  TR.Debug << " Total surface dots: " << (run_.dots[0].size()+run_.dots[1].size()) << std::endl;
241  }
242 }
243 
244 /// @begin MolecularSurfaceCalculator::ReadScRadii()
245 /// @brief Read atom radius definitions from file
246 /// @defailed
247 /// This function is implicitly called, but can be overloaded or
248 /// called explicitly for custom handling of the atom radii library.
249 /// Returns true on success
250 
252 {
253  char const *fn = "scoring/score_functions/sc/sc_radii.lib";
254  ATOM_RADIUS radius;
255  utility::io::izstream in;
256 
257  if(!basic::database::open(in, fn)) {
258  TR.Error << "Failed to read " << fn << std::endl;
259  return 0;
260  }
261 
262  radii_.clear();
263 
264  while( in.good() ) {
265  memset(&radius, 0, sizeof(radius));
266  in >> radius.residue >> radius.atom >> radius.radius;
267  TR.Trace << "Atom Radius: " << radius.residue << ", " << radius.atom << ", " << radius.radius << std::endl;
268  if(*radius.residue && *radius.atom && radius.radius > 0)
269  radii_.push_back(radius);
270  }
271 
272  TR.Trace << "Atom radii read: " << radii_.size() << std::endl;
273 
274  return !radii_.empty();
275 }
276 
277 /// @begin MolecularSurfaceCalculator::AddResidue()
278 /// @brief Add a rosetta residue to a specific molecule
279 /// @detailed
280 /// Call this function when explicitly defining which residues belong to
281 /// which the molecular surface. If partitioning by jump_id is sufficient
282 /// for your application, you may use the Calc() or CalcSc() functions
283 /// instead.
284 /// Returns number of atoms added for the specified residue.
285 ///
286 /// Example:
287 /// core::scoring::sc::MolecularSurfaceCalculator calc;
288 /// core::Real sc;
289 /// calc.Init();
290 /// calc.Reset(); // Only needed when re-using the calculator
291 /// for(core::Size i = 1; i <= pose.n_residue(); i++)
292 /// calc.AddResidue((i < 100), pose.residue(i));
293 /// if(calc.Calc())
294 /// sc = calc.GetResults().sc;
295 
297  int molecule,
298  core::conformation::Residue const & residue)
299 {
300  Atom scatom;
301  int n =0;
302 
303  if(!Init())
304  return 0;
305 
306  // Only use heavy atoms for SC calculation
307  for(Size i = 1; i <= residue.nheavyatoms(); ++i) {
308  // Skip virtual atoms
309  if(residue.is_virtual(i))
310  continue;
311  numeric::xyzVector<Real> xyz = residue.xyz(i);
312  scatom.x(xyz.x());
313  scatom.y(xyz.y());
314  scatom.z(xyz.z());
315  scatom.nresidue = residue.seqpos();
316  strncpy(scatom.residue, residue.name3().c_str(), sizeof(scatom.residue)-1);
317  strncpy(scatom.atom, residue.atom_name(i).c_str()+1, sizeof(scatom.atom)-1);
318  if(add_atom(molecule, scatom))
319  ++n;
320  }
321 
322  return n;
323 }
324 
325 /// @begin MolecularSurfaceCalculator::add_atom()
326 /// @brief Add an atom to a molecule for computation.
327 /// @detailed
328 /// Add an core::scoring::sc::Atom to the molecule.
329 /// Normally this is called by AddResidue(). Explicit addition
330 /// of atoms via this function is rarely needed.
331 /// This function also looks-up the atom radius and density.
332 /// Returns true on success.
333 
335  int molecule,
336  Atom &atom)
337 {
338  if(AssignAtomRadius(atom)) {
339  molecule = (molecule == 1);
340  atom.density = settings.density;
341  atom.molecule = molecule;
342  atom.natom = ++run_.results.nAtoms;
343  atom.access = 0;
344  run_.atoms.push_back(atom);
345  ++run_.results.surface[molecule].nAtoms;
346  /*
347  printf("add_atom[%d] %d: %s:%s (%10.4f, %10.4f, %10.4f) = %.4f\n", molecule, run_.results.surface[molecule].nAtoms, atom.residue, atom.atom, atom.x(), atom.y(), atom.z(), atom.radius);
348  */
349  return 1;
350 
351  } else {
352  TR.Warning << "Failed to assign atom radius for residue "
353  << atom.residue << ":" << atom.atom
354  << ". Skipping atom!" << std::endl;
355  }
356  return 0;
357 }
358 
359 ////////////////////////////////////////////////////////////////////////////
360 // Protected class functions
361 ////////////////////////////////////////////////////////////////////////////
362 
363 // Look up the atom radius for an atom
365 {
366  std::vector<ATOM_RADIUS>::const_iterator radius;
367 
368  // Assign radius with wildcard matching
369  for(radius = radii_.begin(); radius != radii_.end(); ++radius) {
370  if(WildcardMatch(atom.residue, radius->residue, sizeof(atom.residue)) &&
371  WildcardMatch(atom.atom, radius->atom, sizeof(atom.atom))) {
372  atom.radius = radius->radius;
373  return 1;
374  }
375  }
376 
377  return 0;
378 }
379 
380 // Inline residue and atom name matching function
382  char const *r,
383  char const *pattern,
384  int l)
385 {
386  while(--l > 0) {
387  if((*pattern != '*') && (*r != *pattern) && !(*r == ' ' && !*pattern))
388  return 0;
389  ++r;
390  ++pattern;
391  }
392  return 1;
393 }
394 
395 // Assign attention numbers for each atom. By default attention numbers are assigned
396 // to compute surface dots using all defined atoms.
398 {
399  std::vector<Atom>::iterator pAtom;
400 
401  for(pAtom = atoms.begin(); pAtom < atoms.end(); ++pAtom)
402  {
403  pAtom->atten = ATTEN_BURIED_FLAGGED;
404  ++run_.results.surface[pAtom->molecule].nBuriedAtoms;
405  }
406 
407  return 1;
408 }
409 
410 ////////////////////////////////////////////////////////////////////////////
411 // Molecular surface calculation
412 ////////////////////////////////////////////////////////////////////////////
413 // M. L. Connolly J. Appl. Crystallogr., 16, p548 - p558 (1983)
414 // Compute the surface for the atoms in the interface and its neighbours
415 ////////////////////////////////////////////////////////////////////////////
416 
418 {
419  // Calc maximum radius for atoms list
420  run_.radmax = 0.0;
421  for(std::vector<Atom>::const_iterator pAtom1 = run_.atoms.begin(); pAtom1 < run_.atoms.end(); ++pAtom1) {
422  if(pAtom1->radius > run_.radmax)
423  run_.radmax = pAtom1->radius;
424  }
425 
426  // Add dots for each atom in the list
427  for(std::vector<Atom>::iterator pAtom1 = run_.atoms.begin(); pAtom1 < run_.atoms.end(); ++pAtom1) {
428  Atom &atom1 = *pAtom1;
429 
430  if(atom1.atten <= 0)
431  {
432  continue;
433  }
434 
435  // Find neighbor
436  if(!FindNeighbordsAndBuriedAtoms(atom1))
437  {
438  continue;
439  }
440 
441  if(!atom1.access)
442  {
443  continue;
444  }
445 
446  if(atom1.atten <= ATTEN_BLOCKER)
447  {
448  continue;
449  }
450 
451  if(atom1.atten == ATTEN_6 && atom1.buried.empty())
452  {
453  continue;
454  }
455 
456  // Generate convex surface
457  GenerateConvexSurface(atom1);
458  }
459 
460  // Concave surface generation
461  if(settings.rp > 0)
463 
464  return 1;
465 }
466 
467 // Private atom distance sort callback used below
470 {
471  ScValue d1 = _atom_distance_ref->distance(*((Atom*)a1));
472  ScValue d2 = _atom_distance_ref->distance(*((Atom*)a2));
473  return d1 < d2 ? -1: (d2 > d1 ? 1 : 0);
474 }
475 
476 // Calculate surface dots around a single atom (main loop in original code)
478 {
479  if(!FindNeighborsForAtom(atom1))
480  return 0;
481 
482  // sort neighbors by distance from atom1
483  _atom_distance_ref = &atom1;
484  std::sort(atom1.neighbors.begin(), atom1.neighbors.end(), MolecularSurfaceCalculator::_atom_distance_cb);
485  _atom_distance_ref = NULL;
486 
487  SecondLoop(atom1);
488 
489  return atom1.neighbors.size();
490 }
491 
492 // Make a list of neighboring atoms from atom1
493 // (loop to label 100 in original code)
495 {
496  std::vector<Atom>::iterator iAtom2;
497  std::vector<Atom*> &neighbors = atom1.neighbors;
498  ScValue d2;
499  ScValue bridge;
500  ScValue bb2 = pow(4 * run_.radmax + 4 * settings.rp, 2);
501  int nbb = 0;
502 
503  for(iAtom2 = run_.atoms.begin(); iAtom2 < run_.atoms.end(); ++iAtom2) {
504  Atom &atom2 = *iAtom2;
505  if(atom1 == atom2 || atom2.atten <= 0)
506  continue;
507 
508  if(atom1.molecule == atom2.molecule) {
509 
510  d2 = atom1.distance_squared(atom2);
511 
512  if(d2 <= 0.0001)
513  throw ShapeComplementarityCalculatorException("Coincident atoms: %d:%s:%s @ (%.3f, %.3f, %.3f) == %d:%s:%s @ (%.3f, %.3f, %.3f)",
514  atom1.natom, atom1.residue, atom1.atom, atom1.x(), atom1.y(), atom1.z(),
515  atom2.natom, atom2.residue, atom2.atom, atom2.x(), atom2.y(), atom2.z());
516 
517  bridge = atom1.radius + atom2.radius + 2 * settings.rp;
518  if (d2 >= bridge * bridge)
519  continue;
520 
521  neighbors.push_back(&atom2);
522 
523  } else {
524 
525  if(atom2.atten < ATTEN_BURIED_FLAGGED)
526  continue;
527 
528  d2 = atom1.distance_squared(atom2);
529  if (d2 < bb2)
530  ++nbb;
531 
532  bridge = atom1.radius + atom2.radius + 2 * settings.rp;
533  if (d2 >= bridge * bridge)
534  continue;
535 
536  atom1.buried.push_back(&atom2);
537  }
538  }
539 
540  if(atom1.atten == ATTEN_6 && !nbb)
541  return 0;
542 
543  if(neighbors.empty()) {
544  // no neighbors
545  atom1.access = 1;
546  // no convex surface generation
547  return 0;
548  }
549 
550  return neighbors.size();
551 }
552 
553 // second loop (per original code)
555 {
556  Vec3 uij, tij;
557  ScValue erj, eri, rij, /*density,*/ dij, asymm, _far_, contain;
558  int between;
559  std::vector<Atom*> &neighbors = atom1.neighbors;
560 
561  eri = atom1.radius + settings.rp;
562 
563  for(std::vector<Atom*>::iterator iAtom2 = neighbors.begin(); iAtom2 < neighbors.end(); ++iAtom2) {
564  Atom &atom2 = **iAtom2;
565 
566  if(atom2 <= atom1)
567  continue;
568 
569  erj = atom2.radius + settings.rp;
570  //density = (atom1.density + atom2.density) / 2; // set but never used ~Labonte
571  dij = atom1.distance(atom2);
572 
573  uij = (atom2 - atom1) / dij;
574  asymm = (eri * eri - erj * erj) / dij;
575  between = (ABS(asymm) < dij);
576 
577  tij = ((atom1 + atom2) * 0.5f) + (uij * (asymm * 0.5f));
578 
579  _far_ = (eri + erj) * (eri + erj) - dij * dij;
580  if (_far_ <= 0.0)
581  continue;
582 
583  _far_ = sqrt(_far_);
584 
585  contain = dij * dij - ((atom1.radius - atom2.radius) * (atom1.radius - atom2.radius));
586  if (contain <= 0.0)
587  continue;
588 
589  contain = sqrt(contain);
590  rij = 0.5 * _far_ * contain / dij;
591 
592  if (neighbors.size() <= 1) {
593  atom1.access = 1;
594  atom2.access = 1;
595  break;
596  }
597 
598  ThirdLoop(atom1, atom2, uij, tij, rij);
599 
600  if(atom1.atten > ATTEN_BLOCKER || (atom2.atten > ATTEN_BLOCKER && settings.rp > 0.0))
601  GenerateToroidalSurface(atom1, atom2, uij, tij, rij, between);
602  }
603 
604  return 1;
605 }
606 
607 // third loop (per original code)
609  Atom& atom1,
610  Atom& atom2,
611  Vec3 const &uij,
612  Vec3 const &tij,
613  ScValue rij)
614 {
615  std::vector<Atom*> &neighbors = atom1.neighbors;
616  ScValue eri, erj, erk, djk, dik;
617  ScValue asymm, dt, dtijk2, hijk, isign, wijk, swijk, rkp2;
618  int is0;
619  Vec3 uik, dijk, pijk, utb, iujk, tv, tik, bijk, uijk;
620 
621  eri = atom1.radius + settings.rp;
622  erj = atom2.radius + settings.rp;
623 
624  for(std::vector<Atom*>::iterator iAtom3 = neighbors.begin();
625  iAtom3 < neighbors.end(); ++iAtom3) {
626  Atom &atom3 = **iAtom3;
627  if(atom3 <= atom2)
628  continue;
629 
630  erk = atom3.radius + settings.rp;
631  djk = atom2.distance(atom3);
632  if(djk >= erj+erk)
633  continue;
634 
635  dik = atom1.distance(atom3);
636  if(dik >= eri+erk)
637  continue;
638 
639  if(atom1.atten <= ATTEN_BLOCKER && atom2.atten <= ATTEN_BLOCKER && atom3.atten <= ATTEN_BLOCKER)
640  continue;
641 
642  uik = (atom3 - atom1) / dik;
643  dt = uij.dot(uik);
644  wijk = acosf(dt);
645  swijk = sinf(wijk);
646 
647  if(dt >= 1.0 || dt <= -1.0 || wijk <= 0.0 || swijk <= 0.0) {
648  // collinear and other
649  dtijk2 = tij.distance(atom3);
650  rkp2 = erk * erk - rij * rij;
651  if(dtijk2 < rkp2)
652  // 600
653  return 0;
654  continue;
655  }
656 
657  uijk = uij.cross(uik) / swijk;
658  utb = uijk.cross(uij);
659  asymm = (eri*eri - erk*erk) / dik;
660  tik = (atom1 + atom3)*0.5f + uik*asymm*0.5f;
661 
662  // Was: tv = uik * (tik - tij);
663  tv = (tik - tij);
664  tv.x(uik.x() * tv.x());
665  tv.y(uik.y() * tv.y());
666  tv.z(uik.z() * tv.z());
667 
668  dt = tv.x() + tv.y() + tv.z();
669  bijk = tij + utb * dt / swijk;
670  hijk = eri*eri - bijk.distance_squared(atom1);
671  if(hijk <= 0.0)
672  // no height, skip
673  continue;
674 
675  hijk = sqrt(hijk);
676  for(is0 = 1; is0 <= 2; ++is0) {
677  isign = 3 - 2 * is0;
678  pijk = bijk + uijk * hijk * isign;
679 
680  // check for collision
681  if(CheckAtomCollision2(pijk, atom2, atom3, neighbors))
682  continue;
683 
684  // new probe position
685  PROBE probe;
686  if(isign > 0) {
687  probe.pAtoms[0] = &atom1;
688  probe.pAtoms[1] = &atom2;
689  probe.pAtoms[2] = &atom3;
690  } else {
691  probe.pAtoms[0] = &atom2;
692  probe.pAtoms[1] = &atom1;
693  probe.pAtoms[2] = &atom3;
694  }
695  probe.height = hijk;
696  probe.point = pijk;
697  probe.alt = uijk * isign;
698  run_.probes.push_back(probe);
699 
700  atom1.access = 1;
701  atom2.access = 1;
702  atom3.access = 1;
703  }
704  }
705 
706  return 1;
707 }
708 
709 // Check two atoms against a list of neighbors for collision
711  Vec3 const &pijk,
712  Atom const &atom1,
713  Atom const &atom2,
714  std::vector<Atom*> const &atoms)
715 {
716  for(std::vector<Atom*>::const_iterator ineighbor = atoms.begin();
717  ineighbor < atoms.end(); ++ineighbor) {
718  Atom const &neighbor = **ineighbor;
719  if(&atom1 == &neighbor || &atom2 == &neighbor)
720  continue;
721  if(pijk.distance_squared(neighbor) <= pow(neighbor.radius + settings.rp, 2))
722  // collision detected
723  return 1;
724  }
725  return 0;
726 }
727 
728 // Generate convex surface for a specific atom
730 {
731  std::vector<Atom*> const &neighbors = atom1.neighbors;
732  Atom const * neighbor;
733  Vec3 north(0, 0, 1);
734  Vec3 south(0, 0, -1);
735  Vec3 eqvec(1, 0, 0);
736  Vec3 vtemp, vql, uij, tij, pij, cen, pcen;
737  ScValue dt, erj, dij, eri, _far_, contain;
738  ScValue area, asymm, cs, ps, rad, ri, rij, rj;
739 
740  ri = atom1.radius;
741  eri = (atom1.radius + settings.rp);
742 
743  if(!neighbors.empty()) {
744  // use first neighbor
745  neighbor = neighbors[0];
746 
747  north = atom1 - *neighbor;
748  north.normalize();
749 
750  vtemp.x(north.y()*north.y() + north.z()*north.z());
751  vtemp.y(north.x()*north.x() + north.z()*north.z());
752  vtemp.z(north.x()*north.x() + north.y()*north.y());
753  vtemp.normalize();
754 
755  dt = vtemp.dot(north);
756  if(ABS(dt) > 0.99)
757  vtemp = Vec3(1, 0, 0);
758 
759  eqvec = north.cross(vtemp);
760  eqvec.normalize();
761  vql = eqvec.cross(north);
762 
763  rj = neighbor->radius;
764  erj = neighbor->radius + settings.rp;
765  dij = atom1.distance(*neighbor);
766  uij = (*neighbor - atom1) / dij;
767 
768  asymm = (eri*eri - erj*erj) / dij;
769  tij = ((atom1 + *neighbor) * 0.5f) + (uij * (asymm * 0.5f));
770  _far_ = pow(eri + erj, 2) - dij*dij;
771  if(_far_ <= 0.0)
772  throw ShapeComplementarityCalculatorException("Imaginary _far_ for atom %d, neighbor atom %d", atom1.natom, neighbor->natom);
773  _far_ = sqrt(_far_);
774 
775  contain = pow(dij, 2) - pow(ri - rj, 2);
776  if(contain <= 0.0)
777  throw ShapeComplementarityCalculatorException("Imaginary contain for atom %d, neighbor atom %d", atom1.natom, neighbor->natom);
778  contain = sqrt(contain);
779  rij = 0.5 * _far_ * contain / dij;
780  pij = tij + (vql * rij);
781  south = (pij - atom1) / eri;
782 
783  if(north.cross(south).dot(eqvec) <= 0.0)
784  throw ShapeComplementarityCalculatorException("Non-positive frame for atom %d, neighbor atom %d", atom1.natom, neighbor->natom);
785  }
786 
787  // Generate subdivided arc
788  std::vector<Vec3> lats;
789  Vec3 o(0);
790  cs = SubArc(o, ri, eqvec, atom1.density, north, south, lats);
791 
792  if(lats.empty())
793  return 0;
794 
795  // Project onto north vector
796  std::vector<Vec3> points;
797  for(std::vector<Vec3>::iterator ilat = lats.begin(); ilat < lats.end(); ++ilat) {
798  dt = ilat->dot(north);
799  cen = atom1 + (north*dt);
800  rad = ri*ri - dt*dt;
801  if(rad <= 0.0)
802  continue;
803  rad = sqrt(rad);
804 
805  points.clear();
806  ps = SubCir(cen, rad, north, atom1.density, points);
807  if(points.empty())
808  continue;
809  area = ps * cs;
810 
811  for(std::vector<Vec3>::iterator point = points.begin();
812  point < points.end(); ++point) {
813 
814  pcen = atom1 + ((*point - atom1) * (eri/ri));
815 
816  // Check for collision
817  if(CheckPointCollision(pcen, neighbors))
818  continue;
819 
820  // No collision, put point
821  ++run_.results.dots.convex;
822  AddDot(atom1.molecule, 1, *point, area, pcen, atom1);
823  }
824  }
825  return 1;
826 }
827 
828 // Check a point for collision against a list of atoms
830  Vec3 const &pcen,
831  std::vector<Atom*> const &atoms)
832 {
833  for(std::vector<Atom*>::const_iterator ineighbor = atoms.begin()+1;
834  ineighbor < atoms.end(); ++ineighbor) {
835  if(pcen.distance(**ineighbor) <= ((*ineighbor)->radius + settings.rp))
836  // collision detected
837  return 1;
838  }
839  return 0;
840 }
841 
842 // Generate toroidal surface between two atoms
844  Atom &atom1,
845  Atom &atom2,
846  Vec3 const uij,
847  Vec3 const tij,
848  ScValue rij,
849  int between)
850 {
851  std::vector<Atom*> &neighbors = atom1.neighbors;
852  ScValue density, /*ri,*/ /*rj,*/ rb, rci, rcj, rs, e, edens, eri, erj, erl, dtq, pcusp, /*anglei,*/ /*anglej,*/ dt, ts, ps, area;
853  Vec3 pi, pj, axis, dij, pqi, pqj, qij, qjk, qj;
854 
855  std::vector<Vec3> subs;
856 
857  // following Fortran original
858  // will be optimized by compiler
859  density = (atom1.density + atom2.density) / 2;
860  //ri = atom1.radius; // set but never used ~Labonte
861  //rj = atom2.radius; // set but never used ~Labonte
862  eri = (atom1.radius + settings.rp);
863  erj = (atom2.radius + settings.rp);
864  rci = rij * atom1.radius / eri;
865  rcj = rij * atom2.radius / erj;
866  rb = rij - settings.rp;
867 
868  if(rb <= 0.0)
869  rb = 0.0;
870 
871  rs = (rci + 2 * rb + rcj) / 4;
872  e = rs / rij;
873  edens = e * e * density;
874 
875  ts = SubCir(tij, rij, uij, edens, subs);
876  if(subs.empty())
877  return 0;
878 
879  for(std::vector<Vec3>::iterator isub = subs.begin(); isub < subs.end(); ++isub) {
880  Vec3 &sub = *isub;
881 
882  // check for collision
883  int tooclose = 0;
884  ScValue d2 = 0;
885  for(std::vector<Atom*>::iterator ineighbor = neighbors.begin();
886  !tooclose && ineighbor < neighbors.end() && !tooclose;
887  ++ineighbor) {
888  Atom const &neighbor = **ineighbor; // for readability
889  if(atom2 == neighbor)
890  continue;
891  erl = neighbor.radius + settings.rp;
892  d2 = sub.distance_squared(neighbor);
893  tooclose = d2 < (erl * erl);
894  }
895  if(tooclose)
896  continue;
897 
898  // no collision, toroidal arc generation
899  Vec3 &pij = sub;
900  atom1.access = 1;
901  atom2.access = 1;
902 
903  if(atom1.atten == ATTEN_6 && atom2.atten == ATTEN_6&& atom1.buried.empty())
904  continue;
905 
906  pi = (atom1 - pij) / eri;
907  pj = (atom2 - pij) / erj;
908  axis = pi.cross(pj);
909  axis.normalize();
910 
911  dtq = pow(settings.rp, 2) - pow(rij, 2);
912  pcusp = dtq > 0 && between;
913  if(pcusp) {
914  // point cusp -- two shortened arcs
915  dtq = sqrt(dtq);
916  qij = tij - uij * dtq;
917  qjk = tij + uij * dtq;
918  pqi = (qij - pij) / (ScValue)settings.rp;
919  pqj = Vec3(0.0);
920 
921  } else {
922  // no cusp
923  pqi = pi + pj;
924  pqi.normalize();
925  pqj = pqi;
926  }
927 
928  dt = pqi.dot(pi);
929  if(dt >= 1.0 || dt <= -1.0)
930  return 0;
931  //anglei = acosf(dt); // set but never used ~Labonte
932 
933  dt = pqj.dot(pj);
934  if(dt >= 1.0 || dt <= -1.0)
935  return 0;
936  //anglej = acosf(dt); // set but never used ~Labonte
937 
938  // convert two arcs to points
939  if(atom1.atten >= ATTEN_2) {
940  std::vector<Vec3> points;
941  ps = SubArc(pij, settings.rp, axis, density, pi, pqi, points);
942  for(std::vector<Vec3>::iterator point = points.begin(); point < points.end(); ++point) {
943  area = ps * ts * DistancePointToLine(tij, uij, *point) / rij;
944  ++run_.results.dots.toroidal;
945  AddDot(atom1.molecule, 2, *point, area, pij, atom1);
946  }
947  }
948 
949  if(atom2.atten >= ATTEN_2) {
950  std::vector<Vec3> points;
951  ps = SubArc(pij, settings.rp, axis, density, pqj, pj, points);
952  for(std::vector<Vec3>::iterator point = points.begin(); point < points.end(); ++point) {
953  area = ps * ts * DistancePointToLine(tij, uij, *point) / rij;
954  ++run_.results.dots.toroidal;
955  AddDot(atom1.molecule, 2, *point, area, pij, atom2);
956  }
957  }
958  }
959  return 1;
960 }
961 
962 // Generate concave surface for all probes
964 {
965  std::vector<PROBE const *> lowprobs, nears;
966 
967  // collect low probes
968  for(std::vector<PROBE>::iterator probe = run_.probes.begin();
969  probe < run_.probes.end(); ++probe) {
970  if(probe->height < settings.rp)
971  lowprobs.push_back(&(*probe));
972  }
973 
974  for(std::vector<PROBE>::iterator probe = run_.probes.begin();
975  probe < run_.probes.end(); ++probe) {
976 
977  if( probe->pAtoms[0]->atten == ATTEN_6 &&
978  probe->pAtoms[1]->atten == ATTEN_6 &&
979  probe->pAtoms[2]->atten == ATTEN_6) {
980  continue;
981  }
982 
983  Vec3 &pijk = probe->point, &uijk = probe->alt;
984  ScValue hijk = probe->height;
985  ScValue density = (
986  probe->pAtoms[0]->density +
987  probe->pAtoms[1]->density +
988  probe->pAtoms[2]->density ) / 3;
989 
990  // gather nearby low probes
991  nears.clear();
992  for(std::vector<PROBE const *>::const_iterator lprobe = lowprobs.begin();
993  lprobe < lowprobs.end(); ++lprobe) {
994  if(&(*probe) == *lprobe)
995  continue;
996 
997  ScValue d2 = pijk.distance_squared((*lprobe)->point);
998  if(d2 > 4 * pow(settings.rp, 2))
999  continue;
1000 
1001  nears.push_back(*lprobe);
1002  }
1003 
1004  // set up vectors from probe center to three atoms
1005  Vec3 vp[3], vectors[3];
1006  for(int i = 0; i < 3; ++i) {
1007  vp[i] = *(probe->pAtoms[i]) - pijk;
1008  vp[i].normalize();
1009  }
1010 
1011  // set up vectors to three cutting planes
1012  vectors[0] = vp[0].cross(vp[1]);
1013  vectors[1] = vp[1].cross(vp[2]);
1014  vectors[2] = vp[2].cross(vp[0]);
1015  vectors[0].normalize();
1016  vectors[1].normalize();
1017  vectors[2].normalize();
1018 
1019  // find latitude of highest vertex of triangle
1020  ScValue dm = -1.0;
1021  int mm = 0;
1022  for(int i = 0; i < 3; ++i) {
1023  ScValue dt = uijk.dot(vp[i]);
1024  if(dt > dm) {
1025  dm = dt;
1026  mm = i;
1027  }
1028  }
1029 
1030  // create arc for selecting latitudes
1031  Vec3 south = -uijk;
1032  Vec3 axis = vp[mm].cross(south);
1033  axis.normalize();
1034 
1035  std::vector<Vec3> lats;
1036  Vec3 o(0);
1037  ScValue cs;
1038 
1039  cs = SubArc(o, settings.rp, axis, density, vp[mm], south, lats);
1040  if(lats.empty())
1041  continue;
1042 
1043  std::vector<Vec3> points;
1044  for(std::vector<Vec3>::iterator ilat = lats.begin();
1045  ilat < lats.end(); ++ilat) {
1046  ScValue dt, area, rad, ps;
1047  Vec3 cen;
1048 
1049  dt = ilat->dot(south);
1050  cen = south * dt;
1051  rad = pow(settings.rp, 2) - pow(dt, 2);
1052  if(rad <= 0.0)
1053  continue;
1054  rad = sqrtf(rad);
1055 
1056  points.clear();
1057  ps = SubCir(cen, rad, south, density, points);
1058  if(points.empty())
1059  continue;
1060 
1061  area = ps * cs;
1062 
1063  for(std::vector<Vec3>::iterator point = points.begin();
1064  point < points.end(); ++point) {
1065  // check against 3 planes
1066  int bail = 0;
1067  for(int i = 0; i < 3; ++i) {
1068  ScValue dt = point->dot(vectors[i]);
1069  if(dt >= 0.0) {
1070  bail = 1;
1071  break;
1072  }
1073  }
1074  if(bail)
1075  continue;
1076 
1077  *point += pijk;
1078 
1079  if((hijk < settings.rp && !nears.empty()) &&
1080  CheckProbeCollision(*point, nears, pow(settings.rp, 2)))
1081  continue;
1082 
1083  // determine which atom the surface point is closest to
1084  int mc = 0;
1085  ScValue dmin = 2 * settings.rp;
1086  for(int i = 0; i < 3; ++i) {
1087  ScValue d = point->distance(*(probe->pAtoms[i])) -
1088  probe->pAtoms[i]->radius;
1089  if(d < dmin) {
1090  dmin = d;
1091  mc = i;
1092  }
1093  }
1094 
1095  // No collision, put point
1096  ++run_.results.dots.concave;
1097  AddDot(probe->pAtoms[mc]->molecule, 3, *point, area, pijk, *probe->pAtoms[mc]);
1098  }
1099  }
1100  }
1101  return 1;
1102 }
1103 
1104 // Check a point against a set of probes for collision within radius^2
1106  Vec3 const &point,
1107  std::vector<PROBE const *> const nears,
1108  ScValue const r2)
1109 {
1110  for(std::vector<const PROBE*>::const_iterator _near_ = nears.begin();
1111  _near_ < nears.end(); ++_near_) {
1112  if(point.distance_squared((*_near_)->point) < r2)
1113  // Collision
1114  return 1;
1115  }
1116  return 0;
1117 }
1118 
1119 // Add a molecular dot
1121  int const molecule,
1122  int const type,
1123  Vec3 const coor,
1124  ScValue const area,
1125  Vec3 const pcen,
1126  Atom const &atom)
1127 {
1128  DOT dot = { coor, Vec3(), area, 0, type, &atom };
1129  ScValue pradius = settings.rp, erl;
1130 
1131  // calculate outward pointing unit normal vector
1132  if(pradius <= 0)
1133  dot.outnml = coor - atom;
1134  else
1135  dot.outnml = (pcen - coor) / pradius;
1136 
1137  // determine whether buried
1138 
1139  // first check whether probe changed
1140  if(pcen.distance_squared(run_.prevp) <= 0.0) {
1141  dot.buried = run_.prevburied;
1142 
1143  } else {
1144 
1145  // check for collision with neighbors in other molecules
1146  dot.buried = 0;
1147  for(std::vector<Atom*>::const_iterator iNeighbor = atom.buried.begin();
1148  iNeighbor < atom.buried.end();
1149  ++iNeighbor) {
1150  erl = (*iNeighbor)->radius + pradius;
1151  ScValue d = pcen.distance_squared(**iNeighbor);
1152  if(d <= erl*erl) {
1153  dot.buried = 1;
1154  break;
1155  }
1156 
1157  }
1158 
1159  run_.prevp = pcen;
1160  run_.prevburied = dot.buried;
1161  }
1162 
1163  run_.dots[molecule].push_back(dot);
1164 }
1165 
1166 //
1167 // Calculate distance from point to line
1169  Vec3 const &cen,
1170  Vec3 const &axis,
1171  Vec3 const &pnt)
1172 {
1173  Vec3 vec = pnt - cen;
1174  ScValue dt = vec.dot(axis);
1175  ScValue d2 = vec.magnitude_squared() - pow(dt, 2);
1176  return d2 < 0.0 ? 0.0 : sqrt(d2);
1177 }
1178 
1179 // Generate sub arc of molecular dots centered around a defined point
1181  Vec3 const &cen,
1182  ScValue const rad,
1183  Vec3 const &axis,
1184  ScValue const density,
1185  Vec3 const &x,
1186  Vec3 const &v,
1187  std::vector<Vec3> &points)
1188 {
1189  Vec3 y;
1190  ScValue angle;
1191  ScValue dt1, dt2;
1192 
1193  y = axis.cross(x);
1194  dt1 = v.dot(x);
1195  dt2 = v.dot(y);
1196  angle = atan2(dt2, dt1);
1197 
1198  if(angle < 0.0)
1199  angle = angle + 2*PI;
1200 
1201  return SubDiv(cen, rad, x, y, angle, density, points);
1202 }
1203 
1204 // Subdivide defined arc and generate molecular dots
1206  Vec3 const &cen,
1207  ScValue const rad,
1208  Vec3 const &x,
1209  Vec3 const &y,
1210  ScValue const angle,
1211  ScValue const density,
1212  std::vector<Vec3> &points)
1213 {
1214  ScValue delta, a, c, s, ps;
1215  int i;
1216 
1217  delta = 1.0 / (sqrt(density) * rad);
1218  a = - delta / 2;
1219 
1220  for(i = 0; i < MAX_SUBDIV; ++i) {
1221  a = a + delta;
1222  if(a > angle)
1223  break;
1224 
1225  c = rad * cosf(a);
1226  s = rad * sinf(a);
1227  points.push_back(Vec3(cen + x*c + y*s));
1228  }
1229 
1230  if (a + delta < angle)
1231  throw ShapeComplementarityCalculatorException("Too many subdivisions");
1232 
1233  if (!points.empty())
1234  ps = rad * angle / points.size();
1235  else
1236  ps = 0.0;
1237 
1238  return ps;
1239 }
1240 
1241 // Generate an arbitrary unit vector perpendicular to axis
1243  Vec3 const &cen,
1244  ScValue const rad,
1245  Vec3 const &axis,
1246  ScValue const density,
1247  std::vector<Vec3> &points)
1248 {
1249  Vec3 v1, v2, x, y;
1250  ScValue dt;
1251 
1252  v1.x(pow(axis.y(), 2) + pow(axis.z(), 2));
1253  v1.y(pow(axis.x(), 2) + pow(axis.z(), 2));
1254  v1.z(pow(axis.x(), 2) + pow(axis.y(), 2));
1255  v1.normalize();
1256  dt = v1.dot(axis);
1257 
1258  if(ABS(dt) > 0.99) {
1259  v1.x(1.0);
1260  v1.y(0.0);
1261  v1.z(0.0);
1262  }
1263 
1264  v2 = axis.cross(v1);
1265  v2.normalize();
1266  x = axis.cross(v2);
1267  x.normalize();
1268  y = axis.cross(x);
1269 
1270  return SubDiv(cen, rad, x, y, 2.0 * PI, density, points);
1271 }
1272 
1273 ///////////////////////////////////////////////////////////////////////////
1274 // Private helpers
1275 ////////////////////////////////////////////////////////////////////////////
1276 
1277 Atom::Atom() : numeric::xyzVector < MolecularSurfaceCalculator::ScValue > (0.0)
1278 {
1279  natom = 0;
1280  nresidue = 0;
1281  molecule = 0;
1282  radius = 0;
1283  density = 0;
1284  atten = 0;
1285  access = 0;
1286 
1287  memset(atom, 0, sizeof(atom));
1288  memset(residue, 0, sizeof(residue));
1289 }
1290 
1292 
1293 // The End
1294 ////////////////////////////////////////////////////////////////////////////
1295 
1296 } // namespace sc
1297 } // namespace scoring
1298 } // namespace core
1299 
1300 #endif // INCLUDED_core_scoring_sc_MolecularSurfaceCalculator_cc
1301 
1302 // END //
1303