00001 #ifndef CCTBX_GEOMETRY_RESTRAINTS_ANGLE_H
00002 #define CCTBX_GEOMETRY_RESTRAINTS_ANGLE_H
00003
00004 #include <cctbx/sgtbx/rt_mx.h>
00005 #include <cctbx/geometry_restraints/utils.h>
00006 #include <scitbx/constants.h>
00007 #include <scitbx/optional_copy.h>
00008
00009 namespace cctbx { namespace geometry_restraints {
00010
00012 struct angle_proxy
00013 {
00015 typedef af::tiny<unsigned, 3> i_seqs_type;
00016
00018 angle_proxy() {}
00019
00021 angle_proxy(
00022 i_seqs_type const& i_seqs_,
00023 double angle_ideal_,
00024 double weight_)
00025 :
00026 i_seqs(i_seqs_),
00027 angle_ideal(angle_ideal_),
00028 weight(weight_)
00029 {}
00030
00032 angle_proxy(
00033 i_seqs_type const& i_seqs_,
00034 af::shared<sgtbx::rt_mx> const& sym_ops_,
00035 double angle_ideal_,
00036 double weight_)
00037 :
00038 i_seqs(i_seqs_),
00039 sym_ops(sym_ops_),
00040 angle_ideal(angle_ideal_),
00041 weight(weight_)
00042 {
00043 if ( sym_ops.get() ) {
00044 CCTBX_ASSERT(sym_ops.get()->size() == i_seqs.size());
00045 }
00046 }
00047
00049
00051 angle_proxy(
00052 i_seqs_type const& i_seqs_,
00053 angle_proxy const& proxy)
00054 :
00055 i_seqs(i_seqs_),
00056 angle_ideal(proxy.angle_ideal),
00057 weight(proxy.weight)
00058 {}
00059
00061
00063 angle_proxy(
00064 i_seqs_type const& i_seqs_,
00065 af::shared<sgtbx::rt_mx> const& sym_ops_,
00066 angle_proxy const& proxy)
00067 :
00068 i_seqs(i_seqs_),
00069 sym_ops(sym_ops_),
00070 angle_ideal(proxy.angle_ideal),
00071 weight(proxy.weight)
00072 {}
00073
00075 angle_proxy
00076 sort_i_seqs() const
00077 {
00078 angle_proxy result(*this);
00079 if (result.i_seqs[0] > result.i_seqs[2]) {
00080 std::swap(result.i_seqs[0], result.i_seqs[2]);
00081 if ( sym_ops.get() ) {
00082 std::swap(result.sym_ops[0], result.sym_ops[2]);
00083 }
00084 }
00085 return result;
00086 }
00087
00089 i_seqs_type i_seqs;
00091 scitbx::optional_copy<af::shared<sgtbx::rt_mx> > sym_ops;
00093 double angle_ideal;
00095 double weight;
00096 };
00097
00099 class angle
00100 {
00101 public:
00103 angle() {}
00104
00106 angle(
00107 af::tiny<scitbx::vec3<double>, 3> const& sites_,
00108 double angle_ideal_,
00109 double weight_)
00110 :
00111 sites(sites_),
00112 angle_ideal(angle_ideal_),
00113 weight(weight_)
00114 {
00115 init_angle_model();
00116 }
00117
00123 angle(
00124 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00125 angle_proxy const& proxy)
00126 :
00127 angle_ideal(proxy.angle_ideal),
00128 weight(proxy.weight)
00129 {
00130 for(int i=0;i<3;i++) {
00131 std::size_t i_seq = proxy.i_seqs[i];
00132 CCTBX_ASSERT(i_seq < sites_cart.size());
00133 sites[i] = sites_cart[i_seq];
00134 }
00135 init_angle_model();
00136 }
00137
00142 angle(
00143 uctbx::unit_cell const& unit_cell,
00144 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00145 angle_proxy const& proxy)
00146 :
00147 angle_ideal(proxy.angle_ideal),
00148 weight(proxy.weight)
00149 {
00150 for(int i=0;i<3;i++) {
00151 std::size_t i_seq = proxy.i_seqs[i];
00152 CCTBX_ASSERT(i_seq < sites_cart.size());
00153 sites[i] = sites_cart[i_seq];
00154 if ( proxy.sym_ops.get() ) {
00155 sgtbx::rt_mx rt_mx = proxy.sym_ops[i];
00156 if ( !rt_mx.is_unit_mx() ) {
00157 sites[i] = unit_cell.orthogonalize(
00158 rt_mx * unit_cell.fractionalize(sites[i]));
00159 }
00160 }
00161 }
00162 init_angle_model();
00163 }
00164
00166
00168 double
00169 residual() const { return weight * scitbx::fn::pow2(delta); }
00170
00172
00174 void
00175 grads_and_curvs_impl(
00176 scitbx::vec3<double>* grads,
00177 scitbx::vec3<double>* curvs,
00178 double epsilon=1.e-100) const
00179 {
00180 if (!have_angle_model) {
00181 std::fill_n(grads, 3U, scitbx::vec3<double>(0,0,0));
00182 if (curvs != 0) {
00183 std::fill_n(curvs, 3U, scitbx::vec3<double>(0,0,0));
00184 }
00185 }
00186 else {
00187 double
00188 sin_angle_model = std::sqrt(1-scitbx::fn::pow2(cos_angle_model));
00189 if (sin_angle_model < epsilon) {
00190 std::fill_n(grads, 3U, scitbx::vec3<double>(0,0,0));
00191 if (curvs != 0) {
00192 std::fill_n(curvs, 3U, scitbx::vec3<double>(0,0,0));
00193 }
00194 }
00195 else {
00196 using scitbx::constants::pi_180;
00197 scitbx::vec3<double>
00198 d_angle_d_site0, d_angle_d_site1, d_angle_d_site2;
00199 double grad_factor = -2 * weight * delta / pi_180;
00200 d_angle_d_site0 = (d_01_unit * cos_angle_model - d_21_unit) /
00201 (sin_angle_model * d_01_abs);
00202 grads[0] = grad_factor * d_angle_d_site0;
00203 d_angle_d_site2 = (d_21_unit * cos_angle_model - d_01_unit) /
00204 (sin_angle_model * d_21_abs);
00205 grads[2] = grad_factor * d_angle_d_site2;
00206 grads[1] = -(grads[0] + grads[2]);
00207 if (curvs != 0)
00208 {
00209 d_angle_d_site1 = grads[1] / grad_factor;
00210 scitbx::vec3<double> v111(1,1,1);
00211 double sinsqr = scitbx::fn::pow2(sin_angle_model);
00212
00213 scitbx::vec3<double> d2_angle_d_site00 =
00214 (2. * d_21_unit.each_mul(d_01_unit) +
00215 cos_angle_model * (v111 * sinsqr - d_21_unit.each_mul(d_21_unit)
00216 - (1. + 2. * sinsqr) * d_01_unit.each_mul(d_01_unit))) /
00217 (scitbx::fn::pow2(d_01_abs)*sin_angle_model*sinsqr);
00218
00219 scitbx::vec3<double> d2_angle_d_site22 =
00220 (2. * d_01_unit.each_mul(d_21_unit) +
00221 cos_angle_model * (v111 * sinsqr - d_01_unit.each_mul(d_01_unit)
00222 - (1. + 2. * sinsqr) * d_21_unit.each_mul(d_21_unit))) /
00223 (scitbx::fn::pow2(d_21_abs)*sin_angle_model*sinsqr);
00224
00225 double d01sqr = scitbx::fn::pow2(d_01_abs);
00226 double d21sqr = scitbx::fn::pow2(d_21_abs);
00227 scitbx::vec3<double> term1 = d_angle_d_site1 / sin_angle_model;
00228 scitbx::vec3<double> tvec1 = d_01_abs * d_21_unit;
00229 scitbx::vec3<double> tvec2 = d_21_abs * d_01_unit;
00230 scitbx::vec3<double> sumvec = d_01 + d_21;
00231 scitbx::vec3<double> d2_angle_d_site11 =
00232 (-2. * cos_angle_model *
00233 (tvec1.each_mul(tvec1) + tvec2.each_mul(tvec2)) +
00234 (tvec1+tvec2).each_mul(sumvec) +
00235 (d01sqr + d21sqr) * v111 * cos_angle_model +
00236 (d01sqr * d_21 + d21sqr * d_01).each_mul(term1) -
00237 d_01_abs * d_21_abs *
00238 (2. * v111 + cos_angle_model * term1.each_mul(sumvec))) /
00239 (sin_angle_model * d01sqr * d21sqr);
00240
00241 double curvfac = 2. * weight / pi_180;
00242 curvs[0] = curvfac *
00243 (d_angle_d_site0.each_mul(d_angle_d_site0) / pi_180 -
00244 delta * d2_angle_d_site00);
00245 curvs[1] = curvfac *
00246 (d_angle_d_site1.each_mul(d_angle_d_site1) / pi_180 -
00247 delta * d2_angle_d_site11);
00248 curvs[2] = curvfac *
00249 (d_angle_d_site2.each_mul(d_angle_d_site2) / pi_180 -
00250 delta * d2_angle_d_site22);
00251 }
00252 }
00253 }
00254 }
00255
00257
00270 af::shared<scitbx::vec3<double> >
00271 grads_and_curvs(double epsilon=1.e-100) const
00272 {
00273 af::shared<scitbx::vec3<double> > result(6);
00274 grads_and_curvs_impl(&result[0], &result[3], epsilon);
00275 return result;
00276 }
00277
00279
00281 af::tiny<scitbx::vec3<double>, 3>
00282 gradients(double epsilon=1.e-100) const
00283 {
00284 af::tiny<scitbx::vec3<double>, 3> result;
00285 grads_and_curvs_impl(result.begin(), 0, epsilon);
00286 return result;
00287 }
00288
00290
00292 void
00293 add_gradients(
00294 af::ref<scitbx::vec3<double> > const& gradient_array,
00295 angle_proxy::i_seqs_type const& i_seqs) const
00296 {
00297 af::tiny<scitbx::vec3<double>, 3> grads;
00298 grads_and_curvs_impl(grads.begin(), 0);
00299 for(int i=0;i<3;i++) {
00300 gradient_array[i_seqs[i]] += grads[i];
00301 }
00302 }
00303
00305
00310 void
00311 add_gradients(
00312 uctbx::unit_cell const& unit_cell,
00313 af::ref<scitbx::vec3<double> > const& gradient_array,
00314 angle_proxy const& proxy) const
00315 {
00316 angle_proxy::i_seqs_type const& i_seqs = proxy.i_seqs;
00317 scitbx::optional_copy<af::shared<sgtbx::rt_mx> > const&
00318 sym_ops = proxy.sym_ops;
00319 af::tiny<scitbx::vec3<double>, 3> grads;
00320 grads_and_curvs_impl(grads.begin(), 0);
00321 for(int i=0;i<3;i++) {
00322 if ( sym_ops.get() && !sym_ops[i].is_unit_mx() ) {
00323 scitbx::mat3<double> r_inv_cart_ = r_inv_cart(
00324 unit_cell, sym_ops[i]);
00325 gradient_array[i_seqs[i]] += grads[i] * r_inv_cart_;
00326 }
00327 else { gradient_array[i_seqs[i]] += grads[i]; }
00328 }
00329 }
00330
00332 af::tiny<scitbx::vec3<double>, 3> sites;
00334 double angle_ideal;
00336 double weight;
00338 bool have_angle_model;
00339 protected:
00340 double d_01_abs;
00341 double d_21_abs;
00342 scitbx::vec3<double> d_01;
00343 scitbx::vec3<double> d_21;
00344 scitbx::vec3<double> d_01_unit;
00345 scitbx::vec3<double> d_21_unit;
00346 double cos_angle_model;
00347 public:
00349 double angle_model;
00355 double delta;
00356
00357 protected:
00358 void
00359 init_angle_model()
00360 {
00361 have_angle_model = false;
00362 d_01_abs = 0;
00363 d_21_abs = 0;
00364 d_01.fill(0);
00365 d_21.fill(0);
00366 d_01_unit.fill(0);
00367 d_21_unit.fill(0);
00368 cos_angle_model = -9;
00369 angle_model = angle_ideal;
00370 delta = 0;
00371 d_01 = sites[0] - sites[1];
00372 d_01_abs = d_01.length();
00373 if (d_01_abs > 0) {
00374 d_21 = sites[2] - sites[1];
00375 d_21_abs = d_21.length();
00376 if (d_21_abs > 0) {
00377 d_01_unit = d_01 / d_01_abs;
00378 d_21_unit = d_21 / d_21_abs;
00379 cos_angle_model = std::max(-1.,std::min(1.,d_01_unit*d_21_unit));
00380 angle_model = std::acos(cos_angle_model)
00381 / scitbx::constants::pi_180;
00382 have_angle_model = true;
00383 delta = angle_delta_deg(angle_model, angle_ideal);
00384 }
00385 }
00386 }
00387 };
00388
00392 inline
00393 af::shared<double>
00394 angle_deltas(
00395 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00396 af::const_ref<angle_proxy> const& proxies)
00397 {
00398 return detail::generic_deltas<angle_proxy, angle>::get(
00399 sites_cart, proxies);
00400 }
00401
00405 inline
00406 af::shared<double>
00407 angle_deltas(
00408 uctbx::unit_cell const& unit_cell,
00409 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00410 af::const_ref<angle_proxy> const& proxies)
00411 {
00412 return detail::generic_deltas<angle_proxy, angle>::get(
00413 unit_cell, sites_cart, proxies);
00414 }
00415
00419 inline
00420 af::shared<double>
00421 angle_residuals(
00422 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00423 af::const_ref<angle_proxy> const& proxies)
00424 {
00425 return detail::generic_residuals<angle_proxy, angle>::get(
00426 sites_cart, proxies);
00427 }
00428
00432 inline
00433 af::shared<double>
00434 angle_residuals(
00435 uctbx::unit_cell const& unit_cell,
00436 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00437 af::const_ref<angle_proxy> const& proxies)
00438 {
00439 return detail::generic_residuals<angle_proxy, angle>::get(
00440 unit_cell, sites_cart, proxies);
00441 }
00442
00452 inline
00453 double
00454 angle_residual_sum(
00455 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00456 af::const_ref<angle_proxy> const& proxies,
00457 af::ref<scitbx::vec3<double> > const& gradient_array)
00458 {
00459 return detail::generic_residual_sum<angle_proxy, angle>::get(
00460 sites_cart, proxies, gradient_array);
00461 }
00462
00472 inline
00473 double
00474 angle_residual_sum(
00475 uctbx::unit_cell const& unit_cell,
00476 af::const_ref<scitbx::vec3<double> > const& sites_cart,
00477 af::const_ref<angle_proxy> const& proxies,
00478 af::ref<scitbx::vec3<double> > const& gradient_array)
00479 {
00480 return detail::generic_residual_sum<angle_proxy, angle>::get(
00481 unit_cell, sites_cart, proxies, gradient_array);
00482 }
00483
00484 }}
00485
00486 #endif // CCTBX_GEOMETRY_RESTRAINTS_ANGLE_H