Loading [MathJax]/extensions/tex2jax.js
IMP logo
IMP Reference Guide  develop.ae08f42f4a,2025/04/07
The Integrative Modeling Platform
SlabWithToroidalPorePairScore.h
Go to the documentation of this file.
1 /**
2  * \file IMP/npctransport/SlabWithToroidalPorePairScore.h
3  * \brief a score for a slab with a toroidal pore
4  *
5 
6  * Copyright 2007-2025 IMP Inventors. All rights reserved.
7  *
8  */
9 
10 #ifndef IMPNPCTRANSPORT_SLAB_WITH_TOROIDAL_PORE_PAIR_SCORE_H
11 #define IMPNPCTRANSPORT_SLAB_WITH_TOROIDAL_PORE_PAIR_SCORE_H
12 
13 #include "npctransport_config.h"
14 #include "SlabWithToroidalPore.h"
15 #include <IMP/Model.h>
16 #include <IMP/Pointer.h>
17 #include <IMP/check_macros.h>
18 #include <IMP/PairScore.h>
19 #include <IMP/pair_macros.h>
20 #include "IMP/core/XYZR.h"
21 
22 IMPNPCTRANSPORT_BEGIN_NAMESPACE
23 
24 //! Score for a slab with a toroidal pore
25 class IMPNPCTRANSPORTEXPORT SlabWithToroidalPorePairScore
26 : public PairScore
27 {
28  double k_; // repulsion constant in kcal/mol/A
29 
30  // cached variables for currently evaluated slab (therefore, mutable):
31  mutable double top_; // top z - for caching
32  mutable double bottom_; // bottom z - for caching
33  mutable double midZ_; // vertical center of slab
34  mutable double R_; // torus major radius
35  mutable double rh_; // horizontal minor radius (ellipse horizontal semi-axis)
36  mutable double rv_; // vertical minor radius (ellipse vertical semi-axis)
37  mutable bool is_pore_radius_optimized_;
38 
39 public:
40  //! Constructs a horizontal slab with a toroidal pore,
41  //! centered at the z=0 plane
42  /**
43  Constructs a score over a horizontal slab with a ring toroidal pore
44 
45  @param k the slab repulsive force constant in kcal/mol/A
46  */
48  (double k);
49 
50  public:
51  //! evaluate score for particle pi in model m. If da is not null,
52  //! use it to accumulate derivatives in model.
53  virtual double evaluate_index
54  (Model *m,
55  ParticleIndexPair const& pip,
56  DerivativeAccumulator *da) const override;
57 
59  const ParticleIndexes &pis) const
60  override;
61 
62  //! evaluate score for particles pis[lower_bound..upper_bound] in
63  //! model m. If da is not null, use it to accumulate derivatives
64  //! in model.
65  virtual double evaluate_indexes
66  (Model *m,
67  const ParticleIndexPairs &pips,
69  unsigned int lower_bound,
70  unsigned int upper_bound,
71  bool all_indexes_checked=false) const override final;
72 
73  /**
74  Evaluate score for particles pis[lower_bound..upper_bound] in
75  model m. If da is not null, use it to accumulate derivatives
76  in model.
77 
78  Abort early and return maximal double value if score is larger
79  than max (note this assumes all score components are
80  non-negative, which is true for slab score)
81  */
83  ( Model *m,
84  const ParticleIndexPairs &p,
86  double max,
87  unsigned int lower_bound,
88  unsigned int upper_bound,
89  bool all_indexes_checked=false) const override
90  {
91  IMP_UNUSED(all_indexes_checked);
92  double ret = 0;
93  for (unsigned int i = lower_bound; i < upper_bound; ++i) {
94  ret += evaluate_if_good_index(m, p[i], da, max - ret);
95  if (ret > max) return std::numeric_limits<double>::max();
96  }
97  return ret;
98  }
99 
101 
102  private:
103 
104  /**
105  Computes the penetration depth between the specified sphere and an
106  axis aligned ellipsoid defined by the torus vertical minor radius and
107  horizontal minor radius, centered at the specified origin (based on
108  cached slab parameters)
109  Optionally stores a normalized translation vector in *out_translation
110 
111  @param sphere Sphere for which the distance is computed
112  @param origin Ellipsoid origin (a point on the major circle of the torus)
113  @param out_translation A pointer to the normalized translation
114  vector for removing the sphere out of the ellipsoid is
115  stored in *out_translation, if it is not a null pointer.
116  (0,0,0) is stored for 0 overlap.
117 
118  @return The penetration depth or 0 when the sphere does not overlap with
119  the ellipsoid.
120  */
121  inline double
122  get_sphere_ellipsoid_penetration_depth
123  (algebra::Sphere3D const& sphere,
124  algebra::Vector3D const& origin,
125  algebra::Vector3D* out_translation) const;
126 
127  // returns the penetration depth D of a sphere relative to the porus slab surface,
128  // or 0 if the sphere and the slab do not overlap (based on cached slab
129  // parameters)
130  //
131  // @param s the sphere to evaluate
132  // @param out_translation if not null, *out_translation
133  // is used to store the output normalized displacement vector for removing
134  // the sphere out of the slab (or 0,0,0 if no overlap)
135  // In other words, D*out_translation be the shortest displacement vector
136  // needed to remove the sphere out of the slab.
137  inline double get_sphere_penetration_depth
139  algebra::Vector3D* out_translation) const;
140 
141  // update internal variables holding slab params for fast access
142  // based on decorated particle swp
143  void update_cached_slab_params
144  (SlabWithToroidalPore slab) const;
145 };
146 
147 inline void
148 SlabWithToroidalPorePairScore::update_cached_slab_params
149 (SlabWithToroidalPore slab) const
150 {
151  //TODO: support slabs with non-zero x,y,z origin
152  double thickness= slab.get_thickness();
153  top_= 0.5*thickness;
154  bottom_= -0.5*thickness;
155  midZ_= 0;
156  R_= slab.get_pore_radius(); // major radius
157  rh_= slab.get_horizontal_minor_radius();
158  rv_= slab.get_vertical_minor_radius();
159  is_pore_radius_optimized_= slab.get_pore_radius_is_optimized();
160 }
161 
162 
163 //
164 inline double
165 SlabWithToroidalPorePairScore::evaluate_index
166 (Model *m,
167  const ParticleIndexPair& pip,
168  DerivativeAccumulator *da) const
169 {
171  SlabWithToroidalPore slab(m, pip[0]);
172  update_cached_slab_params(slab);
173  IMP::core::XYZR d(m, pip[1]);
175  return false;
176  algebra::Sphere3D d_sphere( d.get_sphere() );
177  algebra::Vector3D displacement;
178  double score=get_sphere_penetration_depth(d.get_sphere(),
179  da ? &displacement : nullptr);
180  IMP_LOG_TERSE("sphere " << d << " score " << score);
181  if(da && score>0.0){
182  algebra::Vector3D derivative_vector = -k_*displacement;
183  IMP_LOG_TERSE(" derivative vector " << derivative_vector);
184  d.add_to_derivatives(derivative_vector, *da);
185  if(is_pore_radius_optimized_){
186  double radial_displacement= displacement[0]*d_sphere[0]+displacement[1]*d_sphere[1]; // TODO: assumes slab origin at 0,0,0 - perhaps in the future extend to general case
187  slab.add_to_pore_radius_derivative(k_*radial_displacement, *da);
188  }
189 
190  }
191  return score;
192 }
193 
194 //
195 inline double
196 SlabWithToroidalPorePairScore::evaluate_indexes
197  (Model *m,
198  const ParticleIndexPairs &pips,
200  unsigned int lower_bound,
201  unsigned int upper_bound, bool) const
202 {
203  IMP_LOG_TERSE("SlabWithToroidalPore singleton - evaluate indexes"
204  << std::endl);
205  if(upper_bound<lower_bound){
206  return 0.0;
207  }
208  double ret(0.0);
209  double radial_displacements(0.0); // sum of pore radius displacemnets
210  algebra::Sphere3D const* spheres_table=
211  m->access_spheres_data();
212  algebra::Sphere3D* sphere_derivatives_table=
213  m->access_sphere_derivatives_data();
214  IMP::internal::BoolAttributeTableTraits::Container const&
215  is_optimizable_table= m->access_optimizeds_data
216  (core::XYZ::get_coordinate_key(0)); // x is indicator
217  ParticleIndex slab_pi(pips[lower_bound][0]);
218  SlabWithToroidalPore slab(m, slab_pi); // TODO: do this only in first round
219  update_cached_slab_params(slab);
220 
221  // Evaluate and sum score and derivative for all particles:
222  for (unsigned int i = lower_bound; i < upper_bound; ++i) {
223  ParticleIndex pi( pips[i][1]);
224  int pi_index=pi.get_index();
225  // Check attributes have valid values:
226  IMP_CHECK_CODE( {
227  IMP_INTERNAL_CHECK(pips[i][0]==slab_pi,
228  "All particles are assumed to be evaluated against"
229  " the same slab");
230  IMP::core::XYZR d(m, pi);
231  algebra::Sphere3D s=spheres_table[pi_index];
232  IMP_INTERNAL_CHECK(d.get_coordinates_are_optimized() == is_optimizable_table[pi],
233  "optimable table inconsistent with d.get_coordinates_are_optimized for particle " << d);
234  IMP_INTERNAL_CHECK((d.get_coordinates() - s.get_center()).get_magnitude()<.001,
235  "Different coords for particle " << d << " *** "
236  << d.get_coordinates() << " vs. " << s.get_center());
237  IMP_INTERNAL_CHECK(d.get_radius() == s.get_radius(),
238  "Different radii for particle " << d << " *** "
239  << d.get_radius() << " vs. " << s.get_radius());
240  } ); // IMP_CHECK_CODE
241  if(!is_optimizable_table[pi]) {
242  continue;
243  }
244  algebra::Vector3D displacement;
245  double cur_score = get_sphere_penetration_depth(spheres_table[pi_index],
246  da ? &displacement : nullptr);
247  IMP_LOG_TERSE("SlabWithToroidalPore singleton score for sphere / displacement "
248  << spheres_table[pi_index] << " is " << cur_score << " / "
249  << displacement << std::endl);
250  ret+= cur_score;
251  if(cur_score>0.0 && da) {
252  algebra::Vector3D derivative_vector = -k_*displacement;
253  // accumulate derivatives directly for speed
254  for(unsigned int j=0; j<3; j++) {
255  sphere_derivatives_table[pi_index][j] += (*da)(derivative_vector[j]);
256  }
257  algebra::Sphere3D const& s=spheres_table[pi_index];
258  radial_displacements+= displacement[0]*s[0] + displacement[1]*s[1]; // TODO: assumes slab origin at 0,0,0 - perhaps in דthe future extend to general case
259 
260  }
261  }
262  if(da && is_pore_radius_optimized_){
263  slab.add_to_pore_radius_derivative(k_ * radial_displacements, *da);
264  }
265  return ret;
266 }
267 
268 // sphere - sphere for which the distance is computed
269 // origin - ellipsoid origin (a point on the major circle of the torus)
270 double
271 SlabWithToroidalPorePairScore::
272 get_sphere_ellipsoid_penetration_depth
273 (algebra::Sphere3D const& sphere,
274  algebra::Vector3D const& origin,
275  algebra::Vector3D* out_translation) const
276 {
277  const double eps= 1e-9;
278  algebra::Vector3D v(sphere.get_center()-origin);
279  double dXY2= v[0]*v[0]+v[1]*v[1];
280  double dZ2= v[2]*v[2];
281  double dv2= dXY2 + dZ2 + eps;
282  // theta = atan(dXY/dZ) = asin(dXY/dv) = acos(dZ/dv)
283  double sinTheta2= dXY2/dv2;
284  double cosTheta2= dZ2/dv2;
285  double cur_r= std::sqrt(rv_*rv_*cosTheta2 + rh_*rh_*sinTheta2);
286  double dv=std::sqrt(dv2);
287  double surface_distance= dv - sphere.get_radius() - cur_r;
288  if(surface_distance>=0 ){
289  if(out_translation){
290  (*out_translation)=IMP::algebra::Vector3D(0,0,0);
291  }
292  return 0;
293  }
294  // Overlap:
295  if(out_translation){
296  (*out_translation)= v.get_unit_vector();
297  }
298  return -surface_distance; // penetration is negative the 'distance'
299 
300 
301 }
302 
303 // return - distance to nearest surface point
304 // out_translation - a normalized vector pointing to the nearest surface point
305 double
306 SlabWithToroidalPorePairScore::get_sphere_penetration_depth
307 (algebra::Sphere3D sphere,
308  algebra::Vector3D* out_translation) const
309 {
310  double const x=sphere[0];
311  double const y=sphere[1];
312  double const z=sphere[2];
313  double const sr=sphere.get_radius();
314  double sphere_dz_top= (z - sr) - top_;
315  double sphere_dz_bottom = (z + sr) - bottom_;
316  bool is_above_top=sphere_dz_top>0;
317  bool is_below_bottom=sphere_dz_bottom<0;
318  if(is_above_top || is_below_bottom) {
319  // early abort I - above or below slab
320  if(out_translation){
321  (*out_translation)=IMP::algebra::Vector3D(0,0,0);
322  }
323  return 0;
324  }
325  double d_xy2 = x*x + y*y; // distance from (0,0,z)
326  bool is_closer_to_top= (sphere_dz_bottom > -sphere_dz_top);
327  double abs_sphere_dz =
328  is_closer_to_top ? -sphere_dz_top : sphere_dz_bottom; // d_z is the mianimal vertical change in z that brings s either fully above or fully below the slab
329  if (d_xy2 > R_*R_) {
330  // early about II - radially outside torus major circle of radius R
331  if(out_translation){
332  (*out_translation)=IMP::algebra::Vector3D(0,0,is_closer_to_top?+1:-1);
333  }
334  return abs_sphere_dz;
335  }
336  double d_xy = std::sqrt(d_xy2);
337  // IV. In slab vertically + radially within torus major circle of radius R
338  const double eps = 1e-9;
339  double ret;
340  if (d_xy>eps) {
341  double scale= R_/d_xy;
342  IMP::algebra::Vector3D origin(x*scale, y*scale, midZ_);
343  ret= get_sphere_ellipsoid_penetration_depth
344  (sphere, origin, out_translation );
345  } else {
346  IMP::algebra::Vector3D origin(R_, 0.0, midZ_);
347  ret= get_sphere_ellipsoid_penetration_depth
348  (sphere, origin, out_translation );
349  }
350  return ret;
351 }
352 
353 
354 
355 IMPNPCTRANSPORT_END_NAMESPACE
356 
357 #endif /* IMPNPCTRANSPORT_SLAB_WITH_TOROIDAL_PORE_PAIR_SCORE_H */
void add_to_derivatives(const algebra::Vector3D &v, DerivativeAccumulator &d)
Add the vector v to the derivative vector of the x,y,z coordinates.
Definition: XYZ.h:82
void add_to_pore_radius_derivative(double v, DerivativeAccumulator &d)
Abstract class for scoring object(s) of type ParticleIndexPair.
Definition: PairScore.h:44
SphereD< 3 > Sphere3D
Typedef for Python.
Definition: SphereD.h:104
Macros for various classes.
#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:284
Storage of a model, its restraints, constraints and particles.
bool get_coordinates_are_optimized() const
Get whether the coordinates are optimized.
Definition: XYZ.h:89
A more IMP-like version of the std::vector.
Definition: Vector.h:50
#define IMP_LOG_TERSE(expr)
Definition: log_macros.h:72
#define IMP_INTERNAL_CHECK(expr, message)
An assertion to check for internal errors in IMP. An IMP::ErrorException will be thrown.
Definition: check_macros.h:139
Class for storing model, its restraints, constraints, and particles.
Definition: Model.h:86
const algebra::Sphere3D & get_sphere() const
Return a sphere object.
Definition: XYZR.h:67
virtual double evaluate_if_good_indexes(Model *m, const ParticleIndexPairs &o, DerivativeAccumulator *da, double max, unsigned int lower_bound, unsigned int upper_bound, bool all_indexes_checked=false) const
Float get_pore_radius() const
get cylindrical pore radius
#define IMP_UNUSED(variable)
Define PairScore.
virtual ModelObjectsTemp do_get_inputs(Model *m, const ParticleIndexes &pis) const =0
Overload this method to specify the inputs.
const algebra::Vector3D & get_coordinates() const
Convert it to a vector.
Definition: XYZ.h:109
#define IMP_CHECK_CODE(expr)
Only compile the code if checks are enabled.
Definition: check_macros.h:117
A nullptr-initialized pointer to an IMP Object.
Float get_thickness() const
returns whether the particle last entered the transport moiety from its
virtual double evaluate_indexes(Model *m, const ParticleIndexPairs &o, DerivativeAccumulator *da, unsigned int lower_bound, unsigned int upper_bound, bool all_indexes_checked=false) const
Compute the score and the derivative if needed over a set.
Helper macros for throwing and handling exceptions.
VectorD< 3 > Vector3D
Definition: VectorD.h:408
A decorator for a particle that's a slab with a toroidal pore.
virtual double evaluate_if_good_index(Model *m, const ParticleIndexPair &vt, DerivativeAccumulator *da, double max) const
Compute the score and the derivative if needed, only if "good".
Decorator for a sphere-like particle.
virtual double evaluate_index(Model *m, const ParticleIndexPair &vt, DerivativeAccumulator *da) const =0
Compute the score and the derivative if needed.
Class for adding derivatives from restraints to the model.
A decorator for a particle with x,y,z coordinates and a radius.
Definition: XYZR.h:27