IMP logo
IMP Reference Guide  2.9.0
The Integrative Modeling Platform
SitesPairScore.h
Go to the documentation of this file.
1 /**
2  * \file SitesPairScore.h
3  * \brief A Score on the distance between a pair of particles.
4  *
5  * Copyright 2007-2018 IMP Inventors. All rights reserved.
6  */
7 
8 // TODO: verify if energy units are kcal/mol or KT
9 
10 #ifndef IMPNPCTRANSPORT_SITES_PAIR_SCORE_H
11 #define IMPNPCTRANSPORT_SITES_PAIR_SCORE_H
12 
13 #include "npctransport_config.h"
16 #include "internal/RigidBodyInfo.h"
17 #include "internal/sites.h"
18 #include <IMP/PairScore.h>
19 #include <IMP/UnaryFunction.h>
20 #include <IMP/Pointer.h>
21 #include <IMP/core/XYZR.h>
22 #include <IMP/core/Typed.h>
25 #include <IMP/generic.h>
28 #include <IMP/set_map_macros.h>
30 #include <IMP/atom/estimates.h>
31 #include <boost/unordered_set.hpp>
32 
33 #include <boost/array.hpp>
34 
35 IMPNPCTRANSPORT_BEGIN_NAMESPACE
36 
37 
38 /** \brief Apply a function to the distance between two particles with
39  a set of specific binding sites
40 
41  The sites are expressed in the local reference frame of
42  the two rigid bodies. Care must be taken to pass the bodies
43  in the appropriate order. See construction documentation for more details.
44 */
45 class IMPNPCTRANSPORTEXPORT SitesPairScore
47 {
48  private:
50 
51 
52  /************************* class variables ****************/
53  bool is_orientational_score_; // if true, use orientation-dependent score
56  sites0_, sites1_;
57 
58 
59  //! Maximal square of distance between particles with interacting sites
60  //! based on the interaction range and list of sites
61  double ubound_distance2_;
62 
63  // //! Cache:
64  // typedef IMP_KERNEL_LARGE_UNORDERED_MAP<ParticleIndex,internal::RigidBodyInfo>
65  // t_particles_rb_cache;
66  // mutable t_particles_rb_cache particles_rb_cache_;
67  // mutable bool is_cache_active_;
68  // mutable unsigned int cur_cache_id_; // to keep track of caching rounds
69 
70  public:
71 
72  /**
73  For positive sigmas, this is an orientation dependent score between two
74  spherical particles that
75  contain a fixed set of interaction sites, sites0 and sites1 (for
76  first and second particle, resp.).
77 
78  The interaction is composed of a non-specific interaction term
79  between the bead spheres, and the sum of interactions between
80  specific interactions sites of each bead.
81 
82  If \sigma{0} and \sigma{1} are positive, the attractive force
83  between pairs of sites at an optimal orientation (sites facing
84  each other) depends on their distance x. The attraction force
85  magnitude is k*x when x<=0.5*range and k*(range-x) when x is
86  between 0.5*range and range. When site0 is rotated by \sigma <
87  \sigma{0}, this force decays further by a factor
88  (cos{\sigma}-cos{\sigma}0)/(1.0-cos{\sigma0}). The force decays
89  similarly when site1 is rotated by \sigma < \sigma{1}. The
90  maximal potential energy difference due to such pair of
91  interacting sites is:
92  max\DELTA{U}_{site_site} = 0.25 * k * range^2 [kcal/mol]
93 
94  If sigma0_deg or sigma1_deg are non-positive, the attractive
95  force between pairs of sites within the attraction range is a
96  constant k, and the maximal interaction energy is instead:
97  max\DELTA{U}_{site-site} = k * range [kCal/mol]
98 
99  In addition to site-site interaction, there is a constant
100  attractive force k_nonspec_attraction between the sphere surfaces
101  up to a range range_nonspec_attraction, with maximal energy
102  contribution:
103  max\DELTA{U}_{non-specific} = 0.5 * k_nonspec_attraction * range_nonspec_attraction [kcal/mol]
104 
105  Note that for a specific pair of particles, each particle might have
106  a different reference frame (rigid body translation and rotation),
107  which is applied to the sites list upon score evaluation.
108 
109  @param range Maximal range of site specific attraction in any direction
110  of specific sites placed on particles
111  @param k Maximal site specific attraction coefficient (in
112  kcal/mol/A^2 when sigma0_deg and sigma1_deg are positive,
113  or kcal/mol/A otherwise, i.e., for orientation-independent interactions)
114  @param sigma0_deg, sigma1_deg Maximal rotational range of sites 0 and 1, respectively,
115  on the particle surface, specified in degrees. If either is 0,
116  the pair score between site centers is used with a constant k.
117  @param range_nonspec_attraction Range for non-specific attraction term
118  between particles that contain the sites
119  @param k_nonspec_attraction Non-specific attraction coefficient between particles
120  (constant force in kCal/mol/A within specified range)
121  @param k_nonspec_repulsion Repulsion coefficient between particles (constant force
122  applied when particle spheres overlap in kCal/mol/A)
123  @param sites0 List of sites on the first particle, in its local reference frame
124  @param sites1 List of sites on the second particle, in its local reference frame
125  */
126  SitesPairScore(double range, double k,
127  double sigma0_deg, double sigma1_deg,
128  double range_nonspec_attraction, double k_nonspec_attraction,
129  double k_nonspec_repulsion,
130  const algebra::Sphere3Ds &sites0,
131  const algebra::Sphere3Ds &sites1);
132 
133 
134  public:
135 
136  virtual double evaluate_indexes(Model *m, const ParticleIndexPairs &p,
138  unsigned int lower_bound,
139  unsigned int upper_bound) const IMP_FINAL;
140 
141  //! evaluated indexes for the range from lower_bound to upper_bound
142  //! in p, if score>max then return max value of double
144  ( Model *m, const ParticleIndexPairs &p,
146  double max, unsigned int lower_bound, unsigned int upper_bound) const
147  {
148  // activate_cache();
149  double ret = 0.0;
150  for (unsigned int i = lower_bound; i < upper_bound; ++i) {
151  ret += evaluate_if_good_index(m, p[i], da, max - ret);
152  if (ret > max) return std::numeric_limits<double>::max();
153  }
154  // deactivate_cache();
155  return ret;
156  }
157 
158 
159  /** evaluate the score for the pair of model particle indexes in p,
160  updating score derivatives to da
161  */
162  virtual double evaluate_index(Model *m, const ParticleIndexPair &p,
164 
165 #ifndef SWIG
166  /**
167  EvaluatE all site-site interactions
168  for evaluate_index() for the pair pip in model m. If da is not nullptr,
169  it accumulated appropriate derivatives. If contacts_accumulator is not null,
170  then the number of individual contacts and occupied sites is asscumulated there.
171 
172  @param sphere_table An array storing of sphere coordinates by particle index
173  @param quaternions_tables An array of quaternions by particle index
174  @param sphere_table An array storing of sphere coordinate derivatives by particle index
175  @param torque_tables An array of torques by particle index
176  @param pip the pair of particle indexes in m
177  @param da optional accumulator for force and torque derivatives
178  @param contacts_accumulator A pointer to a tuple of output values
179  [num-contacts, sites1-bound, sites1-bound].
180  num-contacts is the total number of site-site contacts between pip.
181  sites0-bound and sites1-bound are vectors of contact counts
182  for each site of pip[0] and pip[1], resp.
183  Ignored if Null
184 
185  @return the site-site contributions for the score for the pair
186  pip in model m.
187  */
188  double
189  evaluate_site_contributions_with_internal_tables
190  (algebra::Sphere3D const* spheres_table,
191  double const**quaternions_tables,
192  algebra::Sphere3D *sphere_derivatives_table,
193  double **torques_tables,
194  const ParticleIndexPair &pip,
196  boost::tuple< unsigned int, std::vector<unsigned int>, std::vector<unsigned int> >
197  (*contacts_accumulator) = nullptr
198  ) const;
199 
200  /**
201  EvaluatE all site-site interactions
202  for evaluate_index() for the pair pip in model m. If da is not nullptr,
203  it accumulated appropriate derivatives. If contacts_accumulator is not null,
204  then the number of individual contacts and occupied sites is asscumulated there.
205 
206  @param m the model
207  @param pip the pair of particle indexes in m
208  @param da optional accumulator for force and torque derivatives
209  @param contacts_accumulator A pointer to a tuple of output values
210  [num-contacts, sites1-bound, sites1-bound].
211  num-contacts is the total number of site-site contacts between pip.
212  sites0-bound and sites1-bound are vectors of contact counts
213  for each site of pip[0] and pip[1], resp.
214  Ignored if Null
215 
216  @return the site-site contributions for the score for the pair
217  pip in model m.
218  */
219  double
220  evaluate_site_contributions
221  (Model* m,
222  const ParticleIndexPair &pip,
224  boost::tuple< unsigned int, std::vector<unsigned int>, std::vector<unsigned int> >
225  (*contacts_accumulator)
226  ) const;
227 
228 #endif
229 
231  const ParticleIndexes &pis) const;
232 
233  // Restraints do_create_current_decomposition(Model *m,
234  // const ParticleIndexPair &vt)
235  // const IMP_OVERRIDE;
236 
237  //! return the range for site-site attraction
238  double get_sites_range() const { return params_.r; }
239 
240  //! return the k for site-site attraction
241  double get_sites_k() const { return params_.k; }
242 
243  SitesPairScoreParameters get_parameters() const {return params_;}
244 
245  public:
247 
248  private:
249 
250  /** evaluate the score for the pair of model particle indexes in p,
251  updating score derivatives to da, and using internal attribute
252  tables in Model
253  */
254  inline double evaluate_index_with_internal_tables
255  ( Model* m,
256  algebra::Sphere3D const* spheres_table,
257  double const **quaternions_tables,
258  algebra::Sphere3D *sphere_derivatives_table,
259  double **torques_tables,
260  const ParticleIndexPair &p,
261  DerivativeAccumulator *da) const;
262 
263  // gets the rigid body information (e.g., translation, inverse rotation)
264  // associated with particle m.pi, possibly from cache (depending on internal
265  // cache definitions)
266  inline internal::RigidBodyInfo
267  get_rigid_body_info
268  (algebra::Sphere3D const* spheres_table,
269  double const** quaternions_tables,
270  ParticleIndex pi) const;
271 
272  public:
273  // sets the sites associated with each partner to sites0
274  // and sites1, respectively (in local reference frame)
275  void set_sites(const algebra::Sphere3Ds &sites0,
276  const algebra::Sphere3Ds &sites1){
277  sites0_= sites0;
278  sites1_= sites1;
279  }
280 
281  // sets the sites associated with the first partner
282  // (in local reference frame)
283  void set_sites0(const algebra::Sphere3Ds &sites0){
284  sites0_= sites0;
285  }
286 
287  // sets the sites associated with the first partner
288  // (in local reference frame)
289  void set_sites1(const algebra::Sphere3Ds &sites1){
290  sites1_= sites1;
291  }
292 
293  private:
294  /* // maintain a cache for evaluate_index() till call to deactivate_cache() */
295  /* inline void activate_cache() const */
296  /* { */
297  /* // (note: cache is mutable) */
298  /* is_cache_active_ = true; cur_cache_id_++; */
299  /* } */
300 
301  /* inline void deactivate_cache() const */
302  /* { */
303  /* // (note: cache is mutable) */
304  /* is_cache_active_ = false; */
305  /* } */
306 
307 
308 };
309 
310 //!
311 inline double
312 SitesPairScore::evaluate_index
313 (Model *m, const ParticleIndexPair &p,
314  DerivativeAccumulator *da) const{
315  // get internal tables:
316  algebra::Sphere3D const* spheres_table=
317  m->access_spheres_data();
318  double const* quaternions_tables[4];
319  for(unsigned int i = 0; i < 4; i++){
320  quaternions_tables[i]=
321  core::RigidBody::access_quaternion_i_data(m, i);
322  }
323  algebra::Sphere3D* sphere_derivatives_table=
324  m->access_sphere_derivatives_data();
325  double* torques_tables[3];
326  for(unsigned int i = 0; i < 3; i++){
327  torques_tables[i]=
328  core::RigidBody::access_torque_i_data(m, i);
329  }
330  // evaluate:
331  return evaluate_index_with_internal_tables(m,
332  spheres_table,
333  quaternions_tables,
334  sphere_derivatives_table,
335  torques_tables,
336  p,
337  da);
338 }
339 
340 
341 
342 
343 /**
344  the sites of each particle are transformed to a common frame of reference
345  (using the reference frame of each particle), and the site-specific
346  attraction, and the inter-particle non specific attraction and repulsion
347  are evaluated and summed.
348 */
349 inline double
350 SitesPairScore::evaluate_index_with_internal_tables
351 ( Model* m,
352  algebra::Sphere3D const* spheres_table,
353  double const** quaternions_tables,
354  algebra::Sphere3D *sphere_derivatives_table,
355  double **torques_tables,
356  const ParticleIndexPair &pip,
357  DerivativeAccumulator *da) const {
359 
360  // I. evaluate non-specific attraction and repulsion between
361  // parent particles before computing for specific sites :
362  double non_specific_score = P::evaluate_index(m, pip, da);
364  lips_cache= P::get_evaluation_cache();
365 
366  // II. Return if parent particles are out of site-specific interaction range
367  // using cache to avoid some redundant calcs
368  double const& distance2= lips_cache.particles_delta_squared;
369  IMP_LOG(PROGRESS, "distance2 " << distance2
370  << " ; distance upper-bound " << ubound_distance2_ << std::endl);
371  if (distance2 > ubound_distance2_) {
372  IMP_LOG(PROGRESS, "Sites contribution is 0.0 and non-specific score is "
373  << non_specific_score << std::endl);
374  return non_specific_score;
375  }
376 
377  double site_score=evaluate_site_contributions_with_internal_tables
378  (spheres_table,
379  quaternions_tables,
380  sphere_derivatives_table,
381  torques_tables,
382  pip, da);
383  // III. evaluate site-specific contributions :
384  return site_score + non_specific_score;
385 }
386 
387 #ifndef SWIG
388 
389 //!
390 inline double
391 SitesPairScore::evaluate_site_contributions_with_internal_tables
392 ( algebra::Sphere3D const* spheres_table,
393  double const**quaternions_tables,
394  algebra::Sphere3D *sphere_derivatives_table,
395  double **torques_tables,
396  const ParticleIndexPair &pip,
398  boost::tuple<unsigned int,
399  std::vector<unsigned int>,
400  std::vector<unsigned int> >
401  * contacts_accumulator
402  ) const
403 {
405  // interaction statistics variables
406  static unsigned int n_contacts;
407  static std::vector<unsigned int> occupied_sites0; // how many contacts at each site of particle 0
408  static std::vector<unsigned int> occupied_sites1; // how many contacts at each site of particle 1
409  if(contacts_accumulator){
410  n_contacts= 0;
411  occupied_sites0.resize(sites0_.size());
412  occupied_sites1.resize(sites1_.size());
413  std::fill(occupied_sites0.begin(), occupied_sites0.end(),0);
414  std::fill(occupied_sites1.begin(), occupied_sites1.end(),0);
415  }
416 
417  // bring sites_ to the frame of reference of nn_sites_ and nn_
418  ParticleIndex pi0 = pip[0];
419  ParticleIndex pi1 = pip[1];
420  // get rbi0/1 info, update if needed
421  internal::RigidBodyInfo rbi0 = get_rigid_body_info(spheres_table,
422  quaternions_tables,
423  pi0);
424  internal::RigidBodyInfo rbi1 = get_rigid_body_info(spheres_table,
425  quaternions_tables,
426  pi1);
427  IMP_LOG_PROGRESS( "RBI0.pi " << rbi0.pi
428  << " RB0.cache_id " << rbi0.cache_id
429  << "RBI0.tr " << rbi0.tr << std::endl);
430  IMP_LOG_PROGRESS( "RBI1.cache_id " << rbi1.cache_id
431  << "RBI1.pi " << rbi1.pi
432  << "RBI1.tr " << rbi1.tr << std::endl);
433  // sum over specific interactions between all pairs of sites:
434  double sum = 0;
435  if(is_orientational_score_){
436  // Pre-compute a few variables that do not depend on either both sites or on site1
437  algebra::Vector3D const& gRB0= rbi0.tr.get_translation();
438  algebra::Vector3D const& gRB1= rbi1.tr.get_translation();
439  algebra::Vector3D gUnitRB0RB1= gRB1-gRB0;
440  double distRB0RB1= get_magnitude_and_normalize_in_place(gUnitRB0RB1); // distance between centers
441  for (unsigned int i = 0; i < sites0_.size(); ++i) {
442  algebra::Vector3D gSite0 = rbi0.tr.get_transformed(sites0_[i].get_center());
443  algebra::Vector3D gUnitRB0Site0= (gSite0-gRB0)*rbi0.iradius;
444  double cosSigma0 = gUnitRB0Site0*gUnitRB0RB1;
445  if(cosSigma0 < params_.cosSigma1_max) { // not in range... - note the indexing is not an error - sigma0 is equivalent to params_.sigma1
446  continue;
447  }
448  double kFactor0=internal::get_k_factor(cosSigma0, params_.cosSigma1_max); // note the indexing is not an error - sigma0 is equivalent to params_.sigma1
449  algebra::Vector3D gRotSigma0;
450  double dKFactor0;
451  if(da){
452  gRotSigma0 = get_vector_product(gUnitRB0Site0,gUnitRB0RB1);
453  double absSinSigma0 = get_magnitude_and_normalize_in_place(gRotSigma0);
454  dKFactor0=internal::get_derivative_k_factor(absSinSigma0, params_.cosSigma1_max);
455  }
456  for(unsigned int j = 0 ; j < sites1_.size(); ++j) {
457  algebra::Vector3D gSite1 = rbi1.tr.get_transformed(sites1_[j].get_center());
458  IMP_LOG_PROGRESS( "Evaluating sites at global coordinates: " << gSite0
459  << " ; " << gSite1 << std::endl );
460  double cur_score;
461  cur_score =
462  internal::evaluate_pair_of_sites(params_,
463  rbi0, rbi1,
464  gSite1,
465  gUnitRB0RB1, distRB0RB1,
466  gRotSigma0,
467  kFactor0, dKFactor0,
468  da,
469  sphere_derivatives_table,
470  torques_tables);
471  sum += cur_score;
472  if(contacts_accumulator && cur_score!=0.0){
473  n_contacts++;
474  occupied_sites0[i]++;
475  occupied_sites1[i]++;
476  }
477  } // j
478  } // i
479  } // is_orientational_score_
480  else
481  {
482  for (unsigned int i = 0; i < sites0_.size(); ++i) {
483  algebra::Vector3D g0 = rbi0.tr.get_transformed(sites0_[i].get_center());
484  for(unsigned int j = 0 ; j < sites1_.size(); ++j) {
485  algebra::Vector3D g1 = rbi1.tr.get_transformed(sites1_[j].get_center());
486  IMP_LOG_PROGRESS( "Evaluating sites at global coordinates: " << g0 << " ; " << g1 << std::endl );
487  double cur_score;
488  // old score
489  cur_score =
490  internal::evaluate_one_site_3(params_.k,
491  params_.r,
492  rbi0, rbi1,
493  sites0_[i], sites1_[j],
494  g0, g1,
495  da,
496  sphere_derivatives_table,
497  torques_tables);
498 
499  sum += cur_score;
500  if(contacts_accumulator && cur_score!=0.0){
501  n_contacts++;
502  occupied_sites0[i]++;
503  occupied_sites1[i]++;
504  }
505  }// j
506  }// i
507  } // else
508  if(contacts_accumulator){
509  (*contacts_accumulator)=
510  boost::make_tuple(n_contacts,
511  occupied_sites0,
512  occupied_sites1);
513  }
514 
515  IMP_LOG_PROGRESS( "Sum " << sum << std::endl);
516  return sum;
517 }
518 
519 
520 //!
521 inline double
522 SitesPairScore::evaluate_site_contributions
523 (Model* m,
524  const ParticleIndexPair &pip,
526  boost::tuple< unsigned int, std::vector<unsigned int>, std::vector<unsigned int> >
527  (*contacts_accumulator)
528  ) const
529 {
530  // Get internal tables
531  algebra::Sphere3D const* spheres_table=
532  m->access_spheres_data();
533  double const* quaternions_tables[4];
534  for(unsigned int i = 0; i < 4; i++){
535  quaternions_tables[i]=
536  core::RigidBody::access_quaternion_i_data(m, i);
537  }
538  algebra::Sphere3D* sphere_derivatives_table=
539  m->access_sphere_derivatives_data();
540  double* torques_tables[3];
541  for(unsigned int i = 0; i < 3; i++){
542  torques_tables[i]=
543  core::RigidBody::access_torque_i_data(m, i);
544  }
545  // evaluate:
546  return evaluate_site_contributions_with_internal_tables
547  (spheres_table,
548  quaternions_tables,
549  sphere_derivatives_table,
550  torques_tables,
551  pip,
552  da,
553  contacts_accumulator);
554 }
555 
556 #endif // ifndef SWIG
557 
558 //!
559 inline internal::RigidBodyInfo
560 SitesPairScore::get_rigid_body_info
561 (algebra::Sphere3D const* spheres_table,
562  double const **quaternions_tables,
563  ParticleIndex pi) const
564 {
565  // TODO: add usage check that it has valid quaternions
566  // IMP_USAGE_CHECK(core::RigidBody::get_is_setup(m, pi),
567  // "PI " << pi.get_index() << " not a rigid body");
568  /* if(is_cache_active_){ */
569  /* std::pair<t_particles_rb_cache::iterator, bool> */
570  /* p = particles_rb_cache_.insert */
571  /* (std::make_pair(pi, internal::RigidBodyInfo())); */
572  /* internal::RigidBodyInfo& rbi_cached = p.first->second; */
573  /* bool const rbi_in_cache = !p.second; */
574  /* if(!rbi_in_cache || */
575  /* rbi_cached.cache_id != cur_cache_id_) // = cached version is outdated */
576  /* { */
577  /* rbi_cached.set_particle(spheres_table, */
578  /* quaternions_tables, */
579  /* pi, */
580  /* cur_cache_id_); */
581  /* } */
582  /* return rbi_cached; */
583  /* } */
584  /* else // if is_cache_active_ */
585  /* { */
586  return internal::RigidBodyInfo(spheres_table,
587  quaternions_tables,
588  pi,
589  INVALID_CACHE_ID);
590  /* } */
591 }
592 
593 
594 
595 IMPNPCTRANSPORT_END_NAMESPACE
596 
597 #endif /* IMPNPCTRANSPORT_SITES_PAIR_SCORE_H */
ModelObjectsTemp do_get_inputs(Model *m, const ParticleIndexes &pis) const
Overload this method to specify the inputs.
Apply a PairScore to each Pair in a list.
virtual double evaluate_if_good_indexes(Model *m, const ParticleIndexPairs &o, DerivativeAccumulator *da, double max, unsigned int lower_bound, unsigned int upper_bound) const
#define IMP_FINAL
Have the compiler report an error if anything overrides this method.
#define IMP_OBJECT_METHODS(Name)
Define the basic things needed by any Object.
Definition: object_macros.h:25
#define IMP_OBJECT_LOG
Set the log level to the object's log level.
Definition: log_macros.h:297
Single variable function.
#define IMP_LOG_PROGRESS(expr)
Definition: log_macros.h:105
A particle with a user-defined type.
Macros to choose the best set or map for different purposes.
Class for storing model, its restraints, constraints, and particles.
Definition: Model.h:72
virtual double evaluate_indexes(Model *m, const ParticleIndexPairs &pips, DerivativeAccumulator *da, unsigned int lower_bound, unsigned int upper_bound) const
double get_magnitude_and_normalize_in_place(VT &vt)
Returns the magnitude of vt and turns it to a unit vector in place.
Definition: VectorBaseD.h:260
Apply a function to the distance between two particles with a set of specific binding sites...
Vector3D get_vector_product(const Vector3D &p1, const Vector3D &p2)
Return the vector product (cross product) of two vectors.
Definition: Vector3D.h:31
Define PairScore.
Represent an XYZR particle with a sphere.
Simple 3D transformation class.
A score on the distance between the surfaces of two spheres.
A nullptr-initialized pointer to an IMP Object.
double get_sites_range() const
return the range for site-site attraction
double get_sites_k() const
return the k for site-site attraction
VectorD< 3 > Vector3D
Definition: VectorD.h:395
double evaluate_index(algebra::Sphere3D const &s0, algebra::Sphere3D const &s1, algebra::Sphere3D &ds0, algebra::Sphere3D &ds1, DerivativeAccumulator *da) const
Functions to search over vectors.
A Score on the distance between a pair of particles.
A summary of useful information about rigid bodies and their transformation for eg, caching purposes for SitesPairScore.
Decorator for a sphere-like particle.
#define IMP_OVERRIDE
Cause a compile error if this method does not override a parent method.
Class for adding derivatives from restraints to the model.
Estimates of various physical quantities.
Various important functionality for implementing decorators.