Rosetta 3.5
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
BranchAngleOptimizer.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 protocols/branch_angle/BranchAgnleOptimizer.cc
11 /// @brief implementation of BranchAgnleOptimizer methods
12 /// @author Colin A. Smith (colin.smith@ucsf.edu)
13 
15 
16 // Protocols Headers
21 
22 // Core Headers
27 #include <core/id/AtomID.hh>
28 #include <basic/database/open.hh>
32 #include <core/pose/Pose.hh>
35 #include <basic/Tracer.hh>
36 
37 // Numeric Headers
38 #include <numeric/conversions.hh>
39 #include <numeric/NumericTraits.hh>
40 #include <numeric/xyz.functions.hh>
41 
42 // Utility Headers
43 #include <utility/io/izstream.hh>
44 #include <utility/io/ozstream.hh>
45 
46 #include <utility/vector1.hh>
47 
48 //Auto Headers
50 
51 using namespace core;
52 using namespace core::scoring::mm;
53 
54 static basic::Tracer TR("protocols.moves.branch_angle.BranchAngleOptimizer");
55 
56 namespace protocols {
57 namespace branch_angle {
58 
59 BranchAngleOptimizer::BranchAngleOptimizer(
61 ):
62  mm_bondangle_library_( mm_bondangle_library ),
63  bond_angle_residue_type_param_set_(NULL),
64  tolerance_(0.0000001),
65  initialized_(false)
66 {}
67 
69  mm_bondangle_library_( scoring::ScoringManager::get_instance()->get_MMBondAngleLibrary() ),
70  bond_angle_residue_type_param_set_(NULL),
71  tolerance_(0.0000001),
72  initialized_(false)
73 {}
74 
76  BranchAngleOptimizer const & src
77 ) :
78  mm_bondangle_library_(src.mm_bondangle_library_),
79  bond_angle_residue_type_param_set_(src.bond_angle_residue_type_param_set_), // not a true deep copy
80  coef1_(src.coef1_),
81  coef2_(src.coef2_),
82  coef_map1_(src.coef_map1_),
83  coef_map2_(src.coef_map2_),
84  undefined_coef1_(src.undefined_coef1_),
85  undefined_coef2_(src.undefined_coef2_),
86  tolerance_(src.tolerance_),
87  initialized_(src.initialized_)
88 {}
89 
91 {}
92 
95 {
96  runtime_assert(false)
97 
98  return *this;
99 }
100 
103 {
105 }
106 
109 {
111 }
112 
113 void
116 )
117 {
119 }
120 
121 void
124 )
125 {
127 }
128 
129 /// @detailed
130 /// Add a branch point to the object, if optimization parameters were found, returns an index
131 /// to them, otherwise returns 0. If other failure conditions are met, a warning is output
132 /// and the function returns 0.
133 Size
135  pose::Pose & pose,
136  id::AtomID main_atomid1,
137  id::AtomID center_atomid,
138  id::AtomID main_atomid2,
139  bool optimize_for_minimum // = false
140 )
141 {
142  Real static const pi(numeric::NumericTraits<Real>::pi());
143 
144  //TR << "Optimizing Backbone: " << main_atomid1 << center_atomid << main_atomid2 << std::endl;
145 
146  kinematics::tree::AtomCOP main_atom1(& pose.atom_tree().atom(main_atomid1));
147  kinematics::tree::AtomCOP const center_atom(& pose.atom_tree().atom(center_atomid));
148  kinematics::tree::AtomCOP main_atom2(& pose.atom_tree().atom(main_atomid2));
149 
150  Size coef_index(0);
151 
152  // make sure that the center atom has a complete set of connections
153  runtime_assert(!pose.residue(center_atomid.rsd()).has_incomplete_connection(center_atomid.atomno()));
154 
155  // count the number of atoms bonded to the central atom
156  Size num_neighbors(pose.residue(center_atomid.rsd()).n_bonded_neighbor_all_res(center_atomid.atomno()));
157 
158  Real m2_bond_angle(numeric::angle_radians(pose.xyz(main_atomid1), pose.xyz(center_atomid),
159  pose.xyz(main_atomid2)));
160 
161  if (num_neighbors == 3) {
162 
163  // lookup the branching atom
164  id::AtomID branch_atomid1;
165  branching_atomid1(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1);
166 
167  //TR << "Optimizing 1 Branching Atom: " << branch_atomid1 << std::endl;
168 
169  kinematics::tree::AtomCOP const branch_atom1(& pose.atom_tree().atom(branch_atomid1));
170 
171  // switch main atoms if necessary for having a working stub
172  if (branch_atom1->input_stub_atom2() == main_atom2) {
173  id::AtomID temp_atomid(main_atomid1);
174  kinematics::tree::AtomCOP temp_atom(main_atom1);
175  main_atomid1 = main_atomid2;
176  main_atom1 = main_atom2;
177  main_atomid2 = temp_atomid;
178  main_atom2 = temp_atom;
179  }
180 
181  // make sure both branching atom builds off main_atom1 & center_atom
182  if (!(branch_atom1->input_stub_atom1() == center_atom && branch_atom1->input_stub_atom2() == main_atom1 &&
183  branch_atom1->input_stub_atom3() == main_atom2)) {
184 
185  TR.Warning << "Warning: Branching atom (" << branch_atomid1 << ") for segment" << main_atomid1 << center_atomid
186  << main_atomid2 << "does not use segment atoms for input stub, ignoring" << std::endl;
187  return 0;
188  }
189 
190  // lookup the branching angle coefficients, returning 0 if they can't be found
191  BranchParam1 const params(param1(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1));
192 
193  std::map<BranchParam1, Size>::iterator coef_map_iter(coef_map1_.find(params));
194  if (coef_map_iter == coef_map1_.end()) {
195  undefined_coef1_.insert(params);
196  return 0;
197  } else {
198  coef_index = coef_map_iter->second;
199  }
200 
201  if (optimize_for_minimum) m2_bond_angle = coef1_[coef_index].overall_theta0();
202 
203  // calculate the new torsion offsets and bond angles
204  Real b1_torsion_offset;
205  Real b1_bond_angle;
206  coef1_[coef_index].evaluate(m2_bond_angle, b1_torsion_offset, b1_bond_angle);
207 
208  // set the corresponding degrees of freedom
209  id::DOF_ID const branch_atom1_PHI(branch_atomid1, id::PHI);
210  id::DOF_ID const branch_atom1_THETA(branch_atomid1, id::THETA);
211  pose.set_dof(branch_atom1_PHI, b1_torsion_offset);
212  pose.set_dof(branch_atom1_THETA, pi - b1_bond_angle);
213 
214  } else if (num_neighbors == 4) {
215 
216  // lookup the branching atoms
217  id::AtomID branch_atomid1;
218  id::AtomID branch_atomid2;
219  branching_atomids2(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1, branch_atomid2);
220 
221  //TR << "Optimizing 2 Branching Atoms: " << branch_atomid1 << branch_atomid2 << std::endl;
222 
223  kinematics::tree::AtomCOP branch_atom1(& pose.atom_tree().atom(branch_atomid1));
224  kinematics::tree::AtomCOP branch_atom2(& pose.atom_tree().atom(branch_atomid2));
225 
226  // switch main atoms (and branching atom chirality) if necessary for having a working stub
227  if (branch_atom1->input_stub_atom2() == main_atom2 && branch_atom2->input_stub_atom2() == main_atom2) {
228  id::AtomID temp_atomid(main_atomid1);
229  kinematics::tree::AtomCOP temp_atom(main_atom1);
230  main_atomid1 = main_atomid2;
231  main_atom1 = main_atom2;
232  main_atomid2 = temp_atomid;
233  main_atom2 = temp_atom;
234  temp_atomid = branch_atomid1;
235  temp_atom = branch_atom1;
236  branch_atomid1 = branch_atomid2;
237  branch_atom1 = branch_atom2;
238  branch_atomid2 = temp_atomid;
239  branch_atom2 = temp_atom;
240  }
241 
242  // make sure both branching atom builds off main_atom1 & center_atom
243  if (!(branch_atom1->input_stub_atom1() == center_atom && branch_atom1->input_stub_atom2() == main_atom1 &&
244  branch_atom2->input_stub_atom1() == center_atom && branch_atom2->input_stub_atom2() == main_atom1)) {
245 
246  TR.Warning << "Warning: Branching atoms (" << branch_atomid1 << branch_atomid2 << ") for segment" << main_atomid1
247  << center_atomid << main_atomid2 << "do not use segment atoms for input stub, ignoring" << std::endl;
248  return 0;
249  }
250 
251  // lookup the branching angle coefficients, returning 0 if they can't be found
252  BranchParam2 const params(param2(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1, branch_atomid2));
253 
254  std::map<BranchParam2, Size>::iterator coef_map_iter(coef_map2_.find(params));
255  if (coef_map_iter == coef_map2_.end()) {
256  undefined_coef2_.insert(params);
257  return 0;
258  } else {
259  coef_index = coef_map_iter->second;
260  }
261 
262  if (optimize_for_minimum) m2_bond_angle = coef2_[coef_index].overall_theta0();
263 
264  // calculate the new torsion offsets and bond angles
265  Real b1_torsion_offset;
266  Real b1_bond_angle;
267  Real b2_torsion_offset;
268  Real b2_bond_angle;
269  coef2_[coef_index].evaluate(m2_bond_angle, b1_torsion_offset, b1_bond_angle, b2_torsion_offset, b2_bond_angle);
270 
271  // set the corresponding degrees of freedom
272  id::DOF_ID const branch_atom1_PHI(branch_atom1->id(), id::PHI);
273  id::DOF_ID const branch_atom1_THETA(branch_atom1->id(), id::THETA);
274  id::DOF_ID const branch_atom2_PHI(branch_atom2->id(), id::PHI);
275  id::DOF_ID const branch_atom2_THETA(branch_atom2->id(), id::THETA);
276 
277  // use slightly different behavior depending on how the torsion offsets are ordered
278  if (branch_atom1->input_stub_atom3() == main_atom2 && branch_atom2->input_stub_atom3() == branch_atom1) {
279  pose.set_dof(branch_atom1_PHI, b1_torsion_offset);
280  pose.set_dof(branch_atom2_PHI, b2_torsion_offset - b1_torsion_offset);
281  // make sure there aren't any children after branch_atom2
282  // needs a const compatible next_child method
283  //runtime_assert(!center_atom->next_child(branch_atom2));
284  } else if (branch_atom1->input_stub_atom3() == branch_atom2 && branch_atom2->input_stub_atom3() == main_atom2) {
285  pose.set_dof(branch_atom1_PHI, b1_torsion_offset - b2_torsion_offset);
286  pose.set_dof(branch_atom2_PHI, b2_torsion_offset);
287  // make sure there aren't any children after branch_atom1
288  // needs a const compatible next_child method
289  //runtime_assert(!center_atom->next_child(branch_atom1));
290  } else {
291  TR.Warning << "Warning: Branching atoms (" << branch_atomid1 << branch_atomid2 << ") for segment" << main_atomid1
292  << center_atomid << main_atomid2 << "do not have main atom 2 as their direct descendent, ignoring"
293  << std::endl;
294  return 0;
295  }
296 
297  pose.set_dof(branch_atom1_THETA, pi - b1_bond_angle);
298  pose.set_dof(branch_atom2_THETA, pi - b2_bond_angle);
299 
300  } else {
301 
302  TR << "Warning: Segment" << main_atomid1 << center_atomid << main_atomid2
303  << "does not have 1 or 2 branching atoms, ignoring" << std::endl;
304  }
305 
306  return coef_index;
307 }
308 
309 /// @detailed
310 /// if overall parameters are not available, single bond parameters will be returned
313  core::pose::Pose const & pose,
314  core::id::AtomID main_atomid1,
315  core::id::AtomID center_atomid,
316  core::id::AtomID main_atomid2,
317  core::Real & Ktheta,
318  core::Real & theta0,
319  core::Real & energy0
320 )
321 {
322  //kinematics::tree::AtomCOP main_atom1(& pose.atom_tree().atom(main_atomid1));
323  //kinematics::tree::AtomCOP const center_atom(& pose.atom_tree().atom(center_atomid));
324  kinematics::tree::AtomCOP main_atom2(& pose.atom_tree().atom(main_atomid2));
325 
326  //TR << "Getting Overall Parameters: " << main_atomid1 << center_atomid << main_atomid2 << std::endl;
327 
328  Size coef_index(0);
329 
330  // make sure that the center atom has a complete set of connections
331  runtime_assert(!pose.residue(center_atomid.rsd()).has_incomplete_connection(center_atomid.atomno()));
332 
333  // count the number of atoms bonded to the central atom
334  Size num_neighbors(pose.residue(center_atomid.rsd()).n_bonded_neighbor_all_res(center_atomid.atomno()));
335 
336  if (num_neighbors == 3) {
337 
338  // lookup the branching atom
339  id::AtomID branch_atomid1;
340  branching_atomid1(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1);
341 
342  //TR << "1 Branching Atom: " << branch_atomid1 << std::endl;
343 
344  kinematics::tree::AtomCOP const branch_atom1(& pose.atom_tree().atom(branch_atomid1));
345 
346  // switch main atoms if necessary for having a working stub
347  if (branch_atom1->input_stub_atom2() == main_atom2) {
348  id::AtomID temp_atomid(main_atomid1);
349  main_atomid1 = main_atomid2;
350  main_atomid2 = temp_atomid;
351  }
352 
353  // lookup the branching angle coefficients
354  BranchParam1 const params(param1(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1));
355 
356  std::map<BranchParam1, Size>::iterator coef_map_iter(coef_map1_.find(params));
357  if (coef_map_iter == coef_map1_.end()) {
358  undefined_coef1_.insert(params);
359  Ktheta = params.m1_m2_Ktheta();
360  theta0 = params.m1_m2_theta0();
361  energy0 = 0;
362  } else {
363  coef_index = coef_map_iter->second;
364  Ktheta = coef1_[coef_index].overall_Ktheta();
365  theta0 = coef1_[coef_index].overall_theta0();
366  energy0 = coef1_[coef_index].overall_energy0();
367  }
368 
369  } else if (num_neighbors == 4) {
370 
371  // lookup the branching atoms
372  id::AtomID branch_atomid1;
373  id::AtomID branch_atomid2;
374  branching_atomids2(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1, branch_atomid2);
375 
376  //TR << "2 Branching Atoms: " << branch_atomid1 << branch_atomid2 << std::endl;
377 
378  kinematics::tree::AtomCOP branch_atom1(& pose.atom_tree().atom(branch_atomid1));
379  kinematics::tree::AtomCOP branch_atom2(& pose.atom_tree().atom(branch_atomid2));
380 
381  // switch main atoms (and branching atom chirality) if necessary for having a working stub
382  if (branch_atom1->input_stub_atom2() == main_atom2 && branch_atom2->input_stub_atom2() == main_atom2) {
383  id::AtomID temp_atomid(main_atomid1);
384  main_atomid1 = main_atomid2;
385  main_atomid2 = temp_atomid;
386  temp_atomid = branch_atomid1;
387  branch_atomid1 = branch_atomid2;
388  branch_atomid2 = temp_atomid;
389  }
390 
391  // lookup the branching angle coefficients
392  BranchParam2 const params(param2(pose, main_atomid1, center_atomid, main_atomid2, branch_atomid1, branch_atomid2));
393 
394  std::map<BranchParam2, Size>::iterator coef_map_iter(coef_map2_.find(params));
395  if (coef_map_iter == coef_map2_.end()) {
396  undefined_coef2_.insert(params);
397  Ktheta = params.m1_m2_Ktheta();
398  theta0 = params.m1_m2_theta0();
399  energy0 = 0;
400  } else {
401  coef_index = coef_map_iter->second;
402  Ktheta = coef2_[coef_index].overall_Ktheta();
403  theta0 = coef2_[coef_index].overall_theta0();
404  energy0 = coef2_[coef_index].overall_energy0();
405  }
406 
407  } else {
408 
409  TR << "Warning: Segment" << main_atomid1 << center_atomid << main_atomid2
410  << "does not have 1 or 2 branching atoms, ignoring" << std::endl;
411  }
412 
413  return coef_index;
414 }
415 
416 /// @detailed
417 /// use the MMBondAngleLibrary referenced by this object
420  pose::Pose const & pose,
421  id::AtomID const & main_atomid1,
422  id::AtomID const & center_atomid,
423  id::AtomID const & main_atomid2,
424  id::AtomID const & branch_atomid1
425 ) const
426 {
428 
429  Real m1_m2_Ktheta, m1_m2_theta0, m1_b1_Ktheta, m1_b1_theta0, m2_b1_Ktheta, m2_b1_theta0;
430 
432  pose.conformation(), main_atomid1, center_atomid, main_atomid2, m1_m2_Ktheta, m1_m2_theta0
433  );
435  pose.conformation(), main_atomid1, center_atomid, branch_atomid1, m1_b1_Ktheta, m1_b1_theta0
436  );
438  pose.conformation(), main_atomid2, center_atomid, branch_atomid1, m2_b1_Ktheta, m2_b1_theta0
439  );
440 
441  return BranchParam1(m1_m2_Ktheta, m1_m2_theta0, m1_b1_Ktheta, m1_b1_theta0, m2_b1_Ktheta, m2_b1_theta0,
442  tolerance_);
443  }
444 
445  int const main_mmtype1(pose.residue_type(main_atomid1.rsd()).atom(main_atomid1.atomno()).mm_atom_type_index());
446  int const center_mmtype(pose.residue_type(center_atomid.rsd()).atom(center_atomid.atomno()).mm_atom_type_index());
447  int const main_mmtype2(pose.residue_type(main_atomid2.rsd()).atom(main_atomid2.atomno()).mm_atom_type_index());
448  int const branch_mmtype1(pose.residue_type(branch_atomid1.rsd()).atom(branch_atomid1.atomno()).mm_atom_type_index());
449 
450  mm_bondangle_library_citer_pair m1_m2(mm_bondangle_library_.lookup(main_mmtype1, center_mmtype, main_mmtype2));
451  mm_bondangle_library_citer_pair m1_b1(mm_bondangle_library_.lookup(main_mmtype1, center_mmtype, branch_mmtype1));
452  mm_bondangle_library_citer_pair m2_b1(mm_bondangle_library_.lookup(main_mmtype2, center_mmtype, branch_mmtype1));
453 
454  BranchParam1 const params((m1_m2.first->second).key1(), (m1_m2.first->second).key2(),
455  (m1_b1.first->second).key1(), (m1_b1.first->second).key2(),
456  (m2_b1.first->second).key1(), (m2_b1.first->second).key2(),
457  tolerance_);
458 
459  return params;
460 }
461 
462 /// @detailed
463 /// use the MMBondAngleLibrary referenced by this object
466  pose::Pose const & pose,
467  id::AtomID const & main_atomid1,
468  id::AtomID const & center_atomid,
469  id::AtomID const & main_atomid2,
470  id::AtomID const & branch_atomid1,
471  id::AtomID const & branch_atomid2
472 ) const
473 {
475 
476  Real m1_m2_Ktheta, m1_m2_theta0, m1_b1_Ktheta, m1_b1_theta0, m2_b1_Ktheta, m2_b1_theta0,
477  m1_b2_Ktheta, m1_b2_theta0, m2_b2_Ktheta, m2_b2_theta0, b1_b2_Ktheta, b1_b2_theta0;
478 
480  pose.conformation(), main_atomid1, center_atomid, main_atomid2, m1_m2_Ktheta, m1_m2_theta0
481  );
483  pose.conformation(), main_atomid1, center_atomid, branch_atomid1, m1_b1_Ktheta, m1_b1_theta0
484  );
486  pose.conformation(), main_atomid2, center_atomid, branch_atomid1, m2_b1_Ktheta, m2_b1_theta0
487  );
489  pose.conformation(), main_atomid1, center_atomid, branch_atomid2, m1_b2_Ktheta, m1_b2_theta0
490  );
492  pose.conformation(), main_atomid2, center_atomid, branch_atomid2, m2_b2_Ktheta, m2_b2_theta0
493  );
495  pose.conformation(), branch_atomid1, center_atomid, branch_atomid2, b1_b2_Ktheta, b1_b2_theta0
496  );
497 
498  return BranchParam2(m1_m2_Ktheta, m1_m2_theta0, m1_b1_Ktheta, m1_b1_theta0, m2_b1_Ktheta, m2_b1_theta0,
499  m1_b2_Ktheta, m1_b2_theta0, m2_b2_Ktheta, m2_b2_theta0, b1_b2_Ktheta, b1_b2_theta0,
500  tolerance_);
501  }
502 
503  int const main_mmtype1(pose.residue_type(main_atomid1.rsd()).atom(main_atomid1.atomno()).mm_atom_type_index());
504  int const center_mmtype(pose.residue_type(center_atomid.rsd()).atom(center_atomid.atomno()).mm_atom_type_index());
505  int const main_mmtype2(pose.residue_type(main_atomid2.rsd()).atom(main_atomid2.atomno()).mm_atom_type_index());
506  int const branch_mmtype1(pose.residue_type(branch_atomid1.rsd()).atom(branch_atomid1.atomno()).mm_atom_type_index());
507  int const branch_mmtype2(pose.residue_type(branch_atomid2.rsd()).atom(branch_atomid2.atomno()).mm_atom_type_index());
508 
509  mm_bondangle_library_citer_pair m1_m2(mm_bondangle_library_.lookup(main_mmtype1, center_mmtype, main_mmtype2));
510  mm_bondangle_library_citer_pair m1_b1(mm_bondangle_library_.lookup(main_mmtype1, center_mmtype, branch_mmtype1));
511  mm_bondangle_library_citer_pair m2_b1(mm_bondangle_library_.lookup(main_mmtype2, center_mmtype, branch_mmtype1));
512  mm_bondangle_library_citer_pair m1_b2(mm_bondangle_library_.lookup(main_mmtype1, center_mmtype, branch_mmtype2));
513  mm_bondangle_library_citer_pair m2_b2(mm_bondangle_library_.lookup(main_mmtype2, center_mmtype, branch_mmtype2));
514  mm_bondangle_library_citer_pair b1_b2(mm_bondangle_library_.lookup(branch_mmtype1, center_mmtype, branch_mmtype2));
515 
516  BranchParam2 const params((m1_m2.first->second).key1(), (m1_m2.first->second).key2(),
517  (m1_b1.first->second).key1(), (m1_b1.first->second).key2(),
518  (m2_b1.first->second).key1(), (m2_b1.first->second).key2(),
519  (m1_b2.first->second).key1(), (m1_b2.first->second).key2(),
520  (m2_b2.first->second).key1(), (m2_b2.first->second).key2(),
521  (b1_b2.first->second).key1(), (b1_b2.first->second).key2(),
522  tolerance_);
523 
524  return params;
525 }
526 
527 /// @detailed
528 /// get number of single branching atom coefficients
531 {
532  return coef1_.size();
533 }
534 
535 /// @detailed
536 /// get number of double branching atom coefficients
539 {
540  return coef2_.size();
541 }
542 
543 /// @detailed
544 /// get number of undefined single branching atom coefficients
547 {
548  return undefined_coef1_.size();
549 }
550 
551 /// @detailed
552 /// get number of undefined double branching atom coefficients
555 {
556  return undefined_coef2_.size();
557 }
558 
559 /// @detailed
560 /// read known parameters from the database
561 void
563 {
566  read_coef1_user();
567  read_coef2_user();
568  initialized_ = true;
569 }
570 
571 /// @detailed
572 /// write undefined parameters to the database
573 void
575 {
576  if (undefined_coef1_.size()) {
578  TR << "Wrote " << undefined_coef1_.size() << " undefined single branch parameters to the database" << std::endl;
579  }
580  if (undefined_coef2_.size()) {
582  TR << "Wrote " << undefined_coef2_.size() << " undefined double branch parameters to the database" << std::endl;
583  }
584 }
585 
586 /// @detailed
587 /// reads records of the format:
588 ///
589 /// m1_m2_Ktheta(kcal/radians^2) m1_m2_theta0(degrees)
590 /// m1_b1_Ktheta(kcal/radians^2) m1_b1_theta0(degrees)
591 /// m2_b1_Ktheta(kcal/radians^2) m2_b1_theta0(degrees)
592 ///
593 /// overall_Ktheta(kcal/radians^2) overall_theta0(degrees) overall_energy0(kcal/mol)
594 ///
595 /// b1_torsion_offset_A(radians) b1_torsion_offset_B(unitless) b1_torsion_offset_C(radians^-1)
596 /// b1_bond_angle_A(radians) b1_bond_angle_B(unitless) b1_bond_angle_C(radians^-1)
597 void
599  std::istream & in
600 )
601 {
602  Real m1_m2_Ktheta;
603  Real m1_m2_theta0;
604  Real m1_b1_Ktheta;
605  Real m1_b1_theta0;
606  Real m2_b1_Ktheta;
607  Real m2_b1_theta0;
608 
609  Real overall_Ktheta;
610  Real overall_theta0;
611  Real overall_energy0;
612  Real b1_torsion_offset_A;
613  Real b1_torsion_offset_B;
614  Real b1_torsion_offset_C;
615  Real b1_bond_angle_A;
616  Real b1_bond_angle_B;
617  Real b1_bond_angle_C;
618 
619  while (in >> m1_m2_Ktheta >> m1_m2_theta0
620  >> m1_b1_Ktheta >> m1_b1_theta0
621  >> m2_b1_Ktheta >> m2_b1_theta0
622  >> overall_Ktheta >> overall_theta0 >> overall_energy0
623  >> b1_torsion_offset_A >> b1_torsion_offset_B >> b1_torsion_offset_C
624  >> b1_bond_angle_A >> b1_bond_angle_B >> b1_bond_angle_C) {
625 
626  numeric::conversions::to_radians(m1_m2_theta0);
627  numeric::conversions::to_radians(m1_b1_theta0);
628  numeric::conversions::to_radians(m2_b1_theta0);
629  numeric::conversions::to_radians(overall_theta0);
630 
631  BranchParam1 const params(m1_m2_Ktheta, m1_m2_theta0,
632  m1_b1_Ktheta, m1_b1_theta0,
633  m2_b1_Ktheta, m2_b1_theta0,
634  tolerance_);
635 
636  BranchCoef1 const coefs(overall_Ktheta, overall_theta0, overall_energy0,
637  b1_torsion_offset_A, b1_torsion_offset_B, b1_torsion_offset_C,
638  b1_bond_angle_A, b1_bond_angle_B, b1_bond_angle_C);
639 
640  std::map<BranchParam1, core::Size>::iterator iter(coef_map1_.find(params));
641 
642  if (iter == coef_map1_.end()) {
643  // if the parameter key doesn't exist, add a new coefficient record and map entry
644  coef1_.push_back(coefs);
645  coef_map1_[params] = coef1_.size();
646  } else {
647  // if the parameter key already exists, replace the existing coefficients
648  coef1_[iter->second] = coefs;
649  }
650  }
651 }
652 
653 /// @detailed
654 /// read from an uncompressed or gzip-compressed file, returns false on failure
655 bool
657  std::string const & filename
658 )
659 {
660  utility::io::izstream infile(filename);
661 
662  if (infile) {
663  read_coef1(infile);
664  infile.close();
665  return true;
666  }
667 
668  return false;
669 }
670 
671 /// @detailed
672 /// read from sampling/branch_angle/branch_angle_1.txt
673 void
675 {
676  utility::io::izstream infile;
677  basic::database::open(infile, "sampling/branch_angle/branch_angle_1.txt");
678  read_coef1(infile);
679  infile.close();
680 }
681 
682 /// @detailed
683 /// read from branch_angle/branch_angle_1_user.txt
684 bool
686 {
687  return read_coef1(basic::database::full_name("branch_angle/branch_angle_1_user.txt", false));
688 }
689 
690 /// @detailed
691 /// reads records of the format:
692 ///
693 /// m1_m2_Ktheta(kcal/radians^2) m1_m2_theta0(degrees)
694 /// m1_b1_Ktheta(kcal/radians^2) m1_b1_theta0(degrees)
695 /// m2_b1_Ktheta(kcal/radians^2) m2_b1_theta0(degrees)
696 /// m1_b2_Ktheta(kcal/radians^2) m1_b2_theta0(degrees)
697 /// m2_b2_Ktheta(kcal/radians^2) m2_b2_theta0(degrees)
698 /// b1_b2_Ktheta(kcal/radians^2) b1_b2_theta0(degrees)
699 ///
700 /// overall_Ktheta(kcal/radians^2) overall_theta0(degrees) overall_energy0(kcal/mol)
701 ///
702 /// b1_torsion_offset_A(radians) b1_torsion_offset_B(unitless) b1_torsion_offset_C(radians^-1)
703 /// b1_bond_angle_A(radians) b1_bond_angle_B(unitless) b1_bond_angle_C(radians^-1)
704 /// b2_torsion_offset_A(radians) b2_torsion_offset_B(unitless) b2_torsion_offset_C(radians^-1)
705 /// b2_bond_angle_A(radians) b2_bond_angle_B(unitless) b2_bond_angle_C(radians^-1)
706 void
708  std::istream & in
709 )
710 {
711  Real m1_m2_Ktheta;
712  Real m1_m2_theta0;
713  Real m1_b1_Ktheta;
714  Real m1_b1_theta0;
715  Real m2_b1_Ktheta;
716  Real m2_b1_theta0;
717  Real m1_b2_Ktheta;
718  Real m1_b2_theta0;
719  Real m2_b2_Ktheta;
720  Real m2_b2_theta0;
721  Real b1_b2_Ktheta;
722  Real b1_b2_theta0;
723 
724  Real overall_Ktheta;
725  Real overall_theta0;
726  Real overall_energy0;
727  Real b1_torsion_offset_A;
728  Real b1_torsion_offset_B;
729  Real b1_torsion_offset_C;
730  Real b1_bond_angle_A;
731  Real b1_bond_angle_B;
732  Real b1_bond_angle_C;
733  Real b2_torsion_offset_A;
734  Real b2_torsion_offset_B;
735  Real b2_torsion_offset_C;
736  Real b2_bond_angle_A;
737  Real b2_bond_angle_B;
738  Real b2_bond_angle_C;
739 
740  while (in >> m1_m2_Ktheta >> m1_m2_theta0
741  >> m1_b1_Ktheta >> m1_b1_theta0
742  >> m2_b1_Ktheta >> m2_b1_theta0
743  >> m1_b2_Ktheta >> m1_b2_theta0
744  >> m2_b2_Ktheta >> m2_b2_theta0
745  >> b1_b2_Ktheta >> b1_b2_theta0
746  >> overall_Ktheta >> overall_theta0 >> overall_energy0
747  >> b1_torsion_offset_A >> b1_torsion_offset_B >> b1_torsion_offset_C
748  >> b1_bond_angle_A >> b1_bond_angle_B >> b1_bond_angle_C
749  >> b2_torsion_offset_A >> b2_torsion_offset_B >> b2_torsion_offset_C
750  >> b2_bond_angle_A >> b2_bond_angle_B >> b2_bond_angle_C) {
751 
752  numeric::conversions::to_radians(m1_m2_theta0);
753  numeric::conversions::to_radians(m1_b1_theta0);
754  numeric::conversions::to_radians(m2_b1_theta0);
755  numeric::conversions::to_radians(m1_b2_theta0);
756  numeric::conversions::to_radians(m2_b2_theta0);
757  numeric::conversions::to_radians(b1_b2_theta0);
758  numeric::conversions::to_radians(overall_theta0);
759 
760  BranchParam2 const params(m1_m2_Ktheta, m1_m2_theta0,
761  m1_b1_Ktheta, m1_b1_theta0,
762  m2_b1_Ktheta, m2_b1_theta0,
763  m1_b2_Ktheta, m1_b2_theta0,
764  m2_b2_Ktheta, m2_b2_theta0,
765  b1_b2_Ktheta, b1_b2_theta0,
766  tolerance_);
767 
768  BranchCoef2 const coefs(overall_Ktheta, overall_theta0, overall_energy0,
769  b1_torsion_offset_A, b1_torsion_offset_B, b1_torsion_offset_C,
770  b1_bond_angle_A, b1_bond_angle_B, b1_bond_angle_C,
771  b2_torsion_offset_A, b2_torsion_offset_B, b2_torsion_offset_C,
772  b2_bond_angle_A, b2_bond_angle_B, b2_bond_angle_C);
773 
774  std::map<BranchParam2, core::Size>::iterator iter(coef_map2_.find(params));
775 
776  if (iter == coef_map2_.end()) {
777  // if the parameter key doesn't exist, add a new coefficient record and map entry
778  coef2_.push_back(coefs);
779  coef_map2_[params] = coef2_.size();
780  } else {
781  // if the parameter key already exists, replace the existing coefficients
782  coef2_[iter->second] = coefs;
783  }
784  }
785 }
786 
787 /// @detailed
788 /// read from an uncompressed or gzip-compressed file, returns false on failure
789 bool
791  std::string const & filename
792 )
793 {
794  utility::io::izstream infile(filename);
795 
796  if (infile) {
797  read_coef2(infile);
798  infile.close();
799  return true;
800  }
801 
802  return false;
803 }
804 
805 /// @detailed
806 /// read from sampling/branch_angle/branch_angle_2.txt, fails hard
807 void
809 {
810  utility::io::izstream infile;
811  basic::database::open(infile, "sampling/branch_angle/branch_angle_2.txt");
812  read_coef2(infile);
813  infile.close();
814 }
815 
816 /// @detailed
817 /// read from branch_angle/branch_angle_2_user.txt
818 bool
820 {
821  return read_coef2(basic::database::full_name("branch_angle/branch_angle_2_user.txt", false));
822 }
823 
824 /// @detailed
825 /// reads records of the format:
826 ///
827 /// m1_m2_Ktheta(kcal/radians^2) m1_m2_theta0(degrees)
828 /// m1_b1_Ktheta(kcal/radians^2) m1_b1_theta0(degrees)
829 /// m2_b1_Ktheta(kcal/radians^2) m2_b1_theta0(degrees)
830 void
832  std::istream & in
833 )
834 {
835  Real m1_m2_Ktheta;
836  Real m1_m2_theta0;
837  Real m1_b1_Ktheta;
838  Real m1_b1_theta0;
839  Real m2_b1_Ktheta;
840  Real m2_b1_theta0;
841 
842  while (in >> m1_m2_Ktheta >> m1_m2_theta0
843  >> m1_b1_Ktheta >> m1_b1_theta0
844  >> m2_b1_Ktheta >> m2_b1_theta0) {
845 
846  numeric::conversions::to_radians(m1_m2_theta0);
847  numeric::conversions::to_radians(m1_b1_theta0);
848  numeric::conversions::to_radians(m2_b1_theta0);
849 
850  BranchParam1 const params(m1_m2_Ktheta, m1_m2_theta0,
851  m1_b1_Ktheta, m1_b1_theta0,
852  m2_b1_Ktheta, m2_b1_theta0,
853  tolerance_);
854 
855  undefined_coef1_.insert(params);
856  }
857 }
858 
859 /// @detailed
860 /// read from an uncompressed or gzip-compressed file, returns false on failure
861 bool
863  std::string const & filename
864 )
865 {
866  utility::io::izstream infile(filename);
867 
868  if (infile) {
869  read_undefined_coef1(infile);
870  infile.close();
871  return true;
872  }
873 
874  return false;
875 }
876 
877 /// @detailed
878 /// read from branch_angle/branch_angle_1_undefined.txt
879 bool
881 {
882  return read_undefined_coef1(basic::database::full_name("branch_angle/branch_angle_1_undefined.txt", false));
883 }
884 
885 /// @detailed
886 /// for every set of parameters, dump out three lines in the format:
887 ///
888 /// m1_m2_Ktheta(kcal/radians^2) m1_m2_theta0(degrees)
889 /// m1_b1_Ktheta(kcal/radians^2) m1_b1_theta0(degrees)
890 /// m2_b1_Ktheta(kcal/radians^2) m2_b1_theta0(degrees)
891 ///
892 /// each set of parameters will be followed by a blank line
893 void
895  std::ostream & out
896 ) const
897 {
898  std::streamsize oldprecision = out.precision();
899  out << std::setprecision(16);
900  for (std::set<BranchParam1>::const_iterator iter(undefined_coef1_.begin()); iter != undefined_coef1_.end(); ++iter) {
901  BranchParam1 const & params(*iter);
902  out << params.m1_m2_Ktheta() << " " << numeric::conversions::degrees(params.m1_m2_theta0()) << std::endl;
903  out << params.m1_b1_Ktheta() << " " << numeric::conversions::degrees(params.m1_b1_theta0()) << std::endl;
904  out << params.m2_b1_Ktheta() << " " << numeric::conversions::degrees(params.m2_b1_theta0()) << std::endl;
905  out << std::endl;
906  }
907  out << std::setprecision(oldprecision);
908 }
909 
910 /// @detailed
911 /// write to an uncompressed or gzip-compressed file, returns false on failure
912 bool
914  std::string const & filename
915 ) const
916 {
917  utility::io::ozstream outfile(filename);
918 
919  if (outfile) {
920  write_undefined_coef1(outfile);
921  outfile.close();
922  return true;
923  }
924 
925  return false;
926 }
927 
928 /// @detailed
929 /// overwrite branch_angle/branch_angle_1_undefined.txt if undefined parameters exist
930 bool
932 {
933  if (undefined_coef1_.size()) {
934  return write_undefined_coef1(basic::database::full_name("branch_angle/branch_angle_1_undefined.txt", false));
935  }
936 
937  return true;
938 }
939 
940 /// @detailed
941 /// reads records of the format:
942 ///
943 /// m1_m2_Ktheta(kcal/radians^2) m1_m2_theta0(degrees)
944 /// m1_b1_Ktheta(kcal/radians^2) m1_b1_theta0(degrees)
945 /// m2_b1_Ktheta(kcal/radians^2) m2_b1_theta0(degrees)
946 /// m1_b2_Ktheta(kcal/radians^2) m1_b2_theta0(degrees)
947 /// m2_b2_Ktheta(kcal/radians^2) m2_b2_theta0(degrees)
948 /// b1_b2_Ktheta(kcal/radians^2) b1_b2_theta0(degrees)
949 void
951  std::istream & in
952 )
953 {
954  Real m1_m2_Ktheta;
955  Real m1_m2_theta0;
956  Real m1_b1_Ktheta;
957  Real m1_b1_theta0;
958  Real m2_b1_Ktheta;
959  Real m2_b1_theta0;
960  Real m1_b2_Ktheta;
961  Real m1_b2_theta0;
962  Real m2_b2_Ktheta;
963  Real m2_b2_theta0;
964  Real b1_b2_Ktheta;
965  Real b1_b2_theta0;
966 
967  while (in >> m1_m2_Ktheta >> m1_m2_theta0
968  >> m1_b1_Ktheta >> m1_b1_theta0
969  >> m2_b1_Ktheta >> m2_b1_theta0
970  >> m1_b2_Ktheta >> m1_b2_theta0
971  >> m2_b2_Ktheta >> m2_b2_theta0
972  >> b1_b2_Ktheta >> b1_b2_theta0) {
973 
974  numeric::conversions::to_radians(m1_m2_theta0);
975  numeric::conversions::to_radians(m1_b1_theta0);
976  numeric::conversions::to_radians(m2_b1_theta0);
977  numeric::conversions::to_radians(m1_b2_theta0);
978  numeric::conversions::to_radians(m2_b2_theta0);
979  numeric::conversions::to_radians(b1_b2_theta0);
980 
981  BranchParam2 const params(m1_m2_Ktheta, m1_m2_theta0,
982  m1_b1_Ktheta, m1_b1_theta0,
983  m2_b1_Ktheta, m2_b1_theta0,
984  m1_b2_Ktheta, m1_b2_theta0,
985  m2_b2_Ktheta, m2_b2_theta0,
986  b1_b2_Ktheta, b1_b2_theta0,
987  tolerance_);
988 
989  undefined_coef2_.insert(params);
990  }
991 }
992 
993 /// @detailed
994 /// read from an uncompressed or gzip-compressed file, returns false on failure
995 bool
997  std::string const & filename
998 )
999 {
1000  utility::io::izstream infile(filename);
1001 
1002  if (infile) {
1003  read_undefined_coef2(infile);
1004  infile.close();
1005  return true;
1006  }
1007 
1008  return false;
1009 }
1010 
1011 /// @detailed
1012 /// read from branch_angle/branch_angle_2_undefined.txt
1013 bool
1015 {
1016  return read_undefined_coef2(basic::database::full_name("branch_angle/branch_angle_2_undefined.txt", false));
1017 }
1018 
1019 /// @detailed
1020 /// for every set of parameters, dump out three lines in the format:
1021 ///
1022 /// m1_m2_Ktheta(kcal/radians^2) m1_m2_theta0(degrees)
1023 /// m1_b1_Ktheta(kcal/radians^2) m1_b1_theta0(degrees)
1024 /// m2_b1_Ktheta(kcal/radians^2) m2_b1_theta0(degrees)
1025 /// m1_b2_Ktheta(kcal/radians^2) m1_b2_theta0(degrees)
1026 /// m2_b2_Ktheta(kcal/radians^2) m2_b2_theta0(degrees)
1027 /// b1_b2_Ktheta(kcal/radians^2) b1_b2_theta0(degrees)
1028 ///
1029 /// each set of parameters will be followed by a blank line
1030 void
1032  std::ostream & out
1033 ) const
1034 {
1035  std::streamsize oldprecision = out.precision();
1036  out << std::setprecision(16);
1037  for (std::set<BranchParam2>::const_iterator iter(undefined_coef2_.begin()); iter != undefined_coef2_.end(); ++iter) {
1038  BranchParam2 const & params(*iter);
1039  out << params.m1_m2_Ktheta() << " " << numeric::conversions::degrees(params.m1_m2_theta0()) << std::endl;
1040  out << params.m1_b1_Ktheta() << " " << numeric::conversions::degrees(params.m1_b1_theta0()) << std::endl;
1041  out << params.m2_b1_Ktheta() << " " << numeric::conversions::degrees(params.m2_b1_theta0()) << std::endl;
1042  out << params.m1_b2_Ktheta() << " " << numeric::conversions::degrees(params.m1_b2_theta0()) << std::endl;
1043  out << params.m2_b2_Ktheta() << " " << numeric::conversions::degrees(params.m2_b2_theta0()) << std::endl;
1044  out << params.b1_b2_Ktheta() << " " << numeric::conversions::degrees(params.b1_b2_theta0()) << std::endl;
1045  out << std::endl;
1046  }
1047  out << std::setprecision(oldprecision);
1048 }
1049 
1050 /// @detailed
1051 /// write to an uncompressed or gzip-compressed file, returns false on failure
1052 bool
1054  std::string const & filename
1055 ) const
1056 {
1057  utility::io::ozstream outfile(filename);
1058 
1059  if (outfile) {
1060  write_undefined_coef2(outfile);
1061  outfile.close();
1062  return true;
1063  }
1064 
1065  return false;
1066 }
1067 
1068 /// @detailed
1069 /// overwrite branch_angle/branch_angle_2_undefined.txt if undefined parameters exist
1070 bool
1072 {
1073  if (undefined_coef2_.size()) {
1074  return write_undefined_coef2(basic::database::full_name("branch_angle/branch_angle_2_undefined.txt", false));
1075  }
1076 
1077  return true;
1078 }
1079 
1080 /// @detailed
1081 /// get ordered branching atom around an atom with 3 neighbors
1082 void
1084  pose::Pose const & pose,
1085  id::AtomID main_atomid1,
1086  id::AtomID center_atomid,
1087  id::AtomID main_atomid2,
1088  id::AtomID & branch_atomid1
1089 )
1090 {
1091  utility::vector1<id::AtomID> neighbors(pose.conformation().bonded_neighbor_all_res(center_atomid));
1092 
1093  runtime_assert(neighbors.size() == 3);
1094 
1095  bool found_main_atomid1(false);
1096  bool found_main_atomid2(false);
1097 
1098  for (Size i = 1; i <= 3; ++i) {
1099  id::AtomID & atomid(neighbors[i]);
1100  if (atomid == main_atomid1) {
1101  found_main_atomid1 = true;
1102  } else if (atomid == main_atomid2) {
1103  found_main_atomid2 = true;
1104  } else {
1105  branch_atomid1 = atomid;
1106  }
1107  }
1108 
1109  runtime_assert(found_main_atomid1 && found_main_atomid2);
1110 }
1111 
1112 /// @detailed
1113 /// get ordered branching atoms such that the torsion offset from main_atomid2 to branch_atomid1
1114 /// is less than that to branch_atomid2, given that both are in the range [0, 2*pi). In other
1115 /// words the atoms will be returned clockwise from main_atomid2, when looking from main_atomid1
1116 /// to center_atomid.
1117 void
1119  pose::Pose const & pose,
1120  id::AtomID main_atomid1,
1121  id::AtomID center_atomid,
1122  id::AtomID main_atomid2,
1123  id::AtomID & branch_atomid1,
1124  id::AtomID & branch_atomid2
1125 )
1126 {
1127  Real static const pi_2(numeric::NumericTraits<Real>::pi_2());
1128 
1129  utility::vector1<id::AtomID> neighbors(pose.conformation().bonded_neighbor_all_res(center_atomid));
1130 
1131  runtime_assert(neighbors.size() == 4);
1132 
1133  bool found_main_atomid1(false);
1134  bool found_main_atomid2(false);
1135  bool found_branch_atomid1(false);
1136 
1137  for (Size i = 1; i <= 4; ++i) {
1138  id::AtomID & atomid(neighbors[i]);
1139  if (atomid == main_atomid1) {
1140  found_main_atomid1 = true;
1141  } else if (atomid == main_atomid2) {
1142  found_main_atomid2 = true;
1143  } else if (!found_branch_atomid1) {
1144  branch_atomid1 = atomid;
1145  found_branch_atomid1 = true;
1146  } else {
1147  branch_atomid2 = atomid;
1148  }
1149  }
1150 
1151  runtime_assert(found_main_atomid1 && found_main_atomid2);
1152 
1153  // get dihedral offsets of the two branching atoms adjusted to [0, 2*pi)
1154  Real dihedral1(numeric::dihedral_radians(pose.xyz(main_atomid2), pose.xyz(main_atomid1), pose.xyz(center_atomid),
1155  pose.xyz(branch_atomid1)));
1156  if (dihedral1 < 0) dihedral1 += pi_2;
1157  Real dihedral2(numeric::dihedral_radians(pose.xyz(main_atomid2), pose.xyz(main_atomid1), pose.xyz(center_atomid),
1158  pose.xyz(branch_atomid2)));
1159  if (dihedral2 < 0) dihedral2 += pi_2;
1160 
1161  // switch the atoms if their dihedral offsets are in the wrong order
1162  if (dihedral2 < dihedral1) {
1163  id::AtomID temp_atomid(branch_atomid1);
1164  branch_atomid1 = branch_atomid2;
1165  branch_atomid2 = temp_atomid;
1166  }
1167 }
1168 
1169 /// @detailed
1170 /// get ordered branching atoms such that the torsion offset from main_atom2 to branch_atom1
1171 /// is less than that to branch_atom2, given that both are in the range [0, 2*pi). In other
1172 /// words the atoms will be returned clockwise from main_atom2, when looking from main_atom1
1173 /// to the central atom.
1174 void
1176  kinematics::tree::AtomCOP const main_atom2,
1177  kinematics::tree::AtomCOP & branch_atom1,
1178  kinematics::tree::AtomCOP & branch_atom2
1179 )
1180 {
1181  Real static const pi_2(numeric::NumericTraits<Real>::pi_2());
1182 
1183  kinematics::tree::AtomCOP const parent(main_atom2->parent());
1184 
1185  // check to see if parent exsits and that the correct number of bonded atoms are present
1186  runtime_assert(parent);
1187  runtime_assert(parent->get_nonjump_atom(3));
1188  runtime_assert(!parent->get_nonjump_atom(4));
1189 
1190  branch_atom1 = 0;
1191  branch_atom2 = 0;
1192 
1193  // iterate through the bonded atoms and find the two siblings
1194  for (Size i = 1; i <= 3; ++i) {
1195  kinematics::tree::AtomCOP const sibling(parent->get_nonjump_atom(i));
1196  TR << sibling->id() << std::endl;
1197  if (sibling != main_atom2) {
1198  if (!branch_atom1) {
1199  branch_atom1 = sibling;
1200  } else {
1201  branch_atom2 = sibling;
1202  }
1203  }
1204  }
1205 
1206  runtime_assert(branch_atom1);
1207  runtime_assert(branch_atom2);
1208 
1209  // get dihedral offsets of the two branching atoms adjusted to [0, 2*pi)
1210  Real dihedral1(parent->dihedral_between_bonded_children(main_atom2, branch_atom1));
1211  if (dihedral1 < 0) dihedral1 += pi_2;
1212  Real dihedral2(parent->dihedral_between_bonded_children(main_atom2, branch_atom2));
1213  if (dihedral2 < 0) dihedral2 += pi_2;
1214 
1215  // switch the atoms if their dihedral offsets are in the wrong order
1216  if (dihedral2 < dihedral1) {
1217  kinematics::tree::AtomCOP const temp_atom(branch_atom1);
1218  branch_atom1 = branch_atom2;
1219  branch_atom2 = temp_atom;
1220  }
1221 }
1222 
1223 } // branch_angle
1224 } // protocols