IMP logo
IMP Reference Guide  develop.d36d3deffb,2026/11/09
The Integrative Modeling Platform
charmm_topology.h
Go to the documentation of this file.
1 /**
2  * \file IMP/atom/charmm_topology.h
3  * \brief Classes for handling CHARMM-style topology.
4  *
5  * Copyright 2007-2026 IMP Inventors. All rights reserved.
6  *
7  */
8 
9 #ifndef IMPATOM_CHARMM_TOPOLOGY_H
10 #define IMPATOM_CHARMM_TOPOLOGY_H
11 
12 #include "IMP/Object.h"
13 #include "Hierarchy.h"
14 #include "Atom.h"
15 #include <IMP/atom/atom_config.h>
16 #include <cereal/access.hpp>
17 
18 #include <string>
19 #include <vector>
20 #include <map>
21 
22 IMPATOM_BEGIN_NAMESPACE
23 
24 //! A single atom in a CHARMM topology.
25 /** Each atom has a name (unique to the residue), a CHARMM type (used to
26  look up parameters such as radii and bond lengths in the parameter file)
27  and an electrostatic charge.
28  \see CHARMMAtom
29  */
31  std::string name_;
32  std::string charmm_type_;
33  double charge_;
34 
35  friend class cereal::access;
36 
37  template<class Archive> void serialize(Archive &ar) {
38  ar(name_, charmm_type_, charge_);
39  }
40 
41  public:
43 
44  CHARMMAtomTopology(std::string name) : name_(name) {}
45 
46  CHARMMAtomTopology(std::string name, const CHARMMAtomTopology &other)
47  : name_(name), charmm_type_(other.charmm_type_), charge_(other.charge_) {}
48 
49  std::string get_name() const { return name_; }
50  std::string get_charmm_type() const { return charmm_type_; }
51  double get_charge() const { return charge_; }
52  void set_charmm_type(std::string charmm_type) { charmm_type_ = charmm_type; }
53  void set_charge(double charge) { charge_ = charge; }
55  out << "name: " << name_ << "; CHARMM type: " << charmm_type_
56  << "; charge: " << charge_;
57  });
58 };
59 
61 
63 
64 //! The end of a bond, angle, dihedral, improper, or internal coordinate.
65 /** An endpoint is an atom in a residue; bonds have two endpoints,
66  angles three, and dihedrals, impropers and internal coordinates, four.
67 
68  If residue_ is not nullptr, the endpoint is the named atom in the
69  pointed-to residue. (This is used for bonds that can span multiple
70  residues, perhaps in different chains, and is typically only created
71  by applying a two-residue patch such as DISU or LINK).
72 
73  If residue_ is nullptr, the endpoint is the named atom in the "current"
74  residue, unless the atom name has a +, -, 1: or 2: prefix.
75  Atoms prefixed with + are found in the next residue in the chain.
76  Atoms prefixed with - are found in the previous residue in the chain.
77  1: or 2: prefixes are used by two-residue patches to refer to atoms in
78  each residue patched by the patch.
79  */
80 class IMPATOMEXPORT CHARMMBondEndpoint {
81  std::string atom_name_;
82  Pointer<Object> residue_;
83 
84  public:
85  CHARMMBondEndpoint(std::string atom_name,
86  CHARMMResidueTopology *residue = nullptr);
87 
88  std::string get_atom_name() const { return atom_name_; }
89 
90  //! Map the endpoint to an Atom particle.
91  Atom get_atom(
92  const CHARMMResidueTopology *current_residue,
93  const CHARMMResidueTopology *previous_residue,
94  const CHARMMResidueTopology *next_residue,
95  const std::map<const CHARMMResidueTopology *, Hierarchy> &resmap) const;
96 
97  IMP_SHOWABLE_INLINE(CHARMMBondEndpoint, { out << atom_name_; });
98 };
99 
101 
102 //! A connection (bond, angle, dihedral) between some number of endpoints.
103 template <unsigned int D>
105  protected:
106  Vector<CHARMMBondEndpoint> endpoints_;
107 
108  public:
109  CHARMMConnection(const IMP::Strings &atoms) {
110  IMP_INTERNAL_CHECK(atoms.size() == D, "wrong number of bond endpoints");
111  for (Strings::const_iterator it = atoms.begin(); it != atoms.end(); ++it) {
112  endpoints_.push_back(CHARMMBondEndpoint(*it));
113  }
114  }
115 
116 #ifndef SWIG
118  : endpoints_(endpoints) {
119  IMP_INTERNAL_CHECK(endpoints.size() == D, "wrong number of bond endpoints");
120  }
121 #endif
122 
123  const IMP::atom::CHARMMBondEndpoint &get_endpoint(unsigned int i) const {
124  return endpoints_[i];
125  }
126 
127  //! Return true if the bond contains the named atom.
128  bool get_contains_atom(std::string name) const {
130  endpoints_.begin();
131  it != endpoints_.end(); ++it) {
132  if (it->get_atom_name() == name) {
133  return true;
134  }
135  }
136  return false;
137  }
138 
139 #ifndef SWIG
140  //! Map the bond to a list of Atom particles.
142  const CHARMMResidueTopology *current_residue,
143  const CHARMMResidueTopology *previous_residue,
144  const CHARMMResidueTopology *next_residue,
145  const std::map<const CHARMMResidueTopology *, Hierarchy> &resmap) const {
146  Atoms as;
148  endpoints_.begin();
149  it != endpoints_.end(); ++it) {
150  Atom a =
151  it->get_atom(current_residue, previous_residue, next_residue, resmap);
152  if (a) {
153  as.push_back(a);
154  } else {
155  return Atoms();
156  }
157  }
158  return as;
159  }
160 #endif
163  endpoints_.begin();
164  it != endpoints_.end(); ++it) {
165  if (it != endpoints_.begin()) {
166  out << "-";
167  }
168  out << it->get_atom_name();
169  }
170  });
171 };
172 
173 typedef CHARMMConnection<2> CHARMMBond;
174 typedef CHARMMConnection<3> CHARMMAngle;
175 typedef CHARMMConnection<4> CHARMMDihedral;
176 
177 IMP_VALUES(CHARMMBond, CHARMMBonds);
178 
179 IMP_VALUES(CHARMMAngle, CHARMMAngles);
180 
181 IMP_VALUES(CHARMMDihedral, CHARMMDihedrals);
182 
183 #ifdef SWIG
184 // Ugly, but SWIG needs a template instantiation before it is used as
185 // a base class
186 %template(CHARMMDihedral) CHARMMConnection<4>;
187 #endif
188 
189 //! A geometric relationship between four atoms.
190 /** The atoms (denoted i,j,k,l here) are uniquely positioned in 3D space
191  relative to each other by means of two distances, two angles, and
192  a dihedral.
193 
194  A regular internal coordinate stores the distances between ij and
195  kl respectively, and the angles between ijk and jkl.
196 
197  An improper internal coordinate stores the distances between ik and
198  kl respectively, and the angles between ikj and jkl.
199 
200  In both cases the dihedral is the angle between the two planes formed
201  by ijk and jkl.
202  */
204  float first_distance_, second_distance_, first_angle_, second_angle_,
205  dihedral_;
206  bool improper_;
207 
208  public:
209  CHARMMInternalCoordinate(const IMP::Strings &atoms, float first_distance,
210  float first_angle, float dihedral,
211  float second_angle, float second_distance,
212  bool improper)
213  : CHARMMConnection<4>(atoms),
214  first_distance_(first_distance),
215  second_distance_(second_distance),
216  first_angle_(first_angle),
217  second_angle_(second_angle),
218  dihedral_(dihedral),
219  improper_(improper) {}
221  float first_distance, float first_angle,
222  float dihedral, float second_angle,
223  float second_distance, bool improper)
224  : CHARMMConnection<4>(endpoints),
225  first_distance_(first_distance),
226  second_distance_(second_distance),
227  first_angle_(first_angle),
228  second_angle_(second_angle),
229  dihedral_(dihedral),
230  improper_(improper) {}
231 
232  float get_first_distance() const { return first_distance_; }
233  float get_second_distance() const { return second_distance_; }
234  float get_first_angle() const { return first_angle_; }
235  float get_second_angle() const { return second_angle_; }
236  float get_dihedral() const { return dihedral_; }
237  bool get_improper() const { return improper_; }
238 
241  out << "; distances: " << first_distance_ << ", " << second_distance_
242  << "; angles: " << first_angle_ << ", " << second_angle_
243  << "; dihedral: " << dihedral_;
244  if (improper_) {
245  out << "; improper";
246  }
247  });
248 };
250 
251 //! Base class for all CHARMM residue-based topology
252 class IMPATOMEXPORT CHARMMResidueTopologyBase : public IMP::Object {
253  std::string type_;
254 
255  protected:
257  CHARMMBonds bonds_;
258  CHARMMAngles angles_;
259  CHARMMDihedrals dihedrals_;
260  CHARMMDihedrals impropers_;
261  CHARMMInternalCoordinates internal_coordinates_;
262 
263  CHARMMResidueTopologyBase(std::string type)
264  : Object("CharmmResidueTopoBase%1%"), type_(type) {
265  set_name(std::string("CHARMM residue ") + type);
266  }
267 
268  public:
269  std::string get_type() const { return type_; }
270 
271  unsigned int get_number_of_atoms() const { return atoms_.size(); }
272  const CHARMMAtomTopology &get_atom(unsigned int i) const { return atoms_[i]; }
273 
274  void add_atom(const CHARMMAtomTopology &atom);
275 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
276  CHARMMAtomTopology &get_atom(std::string name);
277 #endif
278  const CHARMMAtomTopology &get_atom(AtomType type) const {
279  return get_atom(type.get_string());
280  }
281 
282  const CHARMMAtomTopology &get_atom(std::string name) const;
283 
284  unsigned int get_number_of_bonds() const { return bonds_.size(); }
285  void add_bond(const CHARMMBond &bond) { bonds_.push_back(bond); }
286 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
287  // cannot return non const ref to swig
288  CHARMMBond &get_bond(unsigned int index) { return bonds_[index]; }
289 #endif
290 
291  unsigned int get_number_of_angles() const { return angles_.size(); }
292  void add_angle(const CHARMMAngle &bond) { angles_.push_back(bond); }
293 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
294  // cannot return non const ref to swig
295  CHARMMAngle &get_angle(unsigned int index) { return angles_[index]; }
296 #endif
297  unsigned int get_number_of_dihedrals() const { return dihedrals_.size(); }
298  void add_dihedral(const CHARMMDihedral &bond) { dihedrals_.push_back(bond); }
299 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
300  // cannot return non const ref to swig
301  CHARMMDihedral &get_dihedral(unsigned int index) { return dihedrals_[index]; }
302 #endif
303  unsigned int get_number_of_impropers() const { return impropers_.size(); }
304  void add_improper(const CHARMMDihedral &bond) { impropers_.push_back(bond); }
305 #if !defined(SWIG) && !defined(IMP_DOXYGEN)
306  CHARMMDihedral &get_improper(unsigned int index) { return impropers_[index]; }
307 #endif
308 
309  unsigned int get_number_of_internal_coordinates() const {
310  return internal_coordinates_.size();
311  }
312  void add_internal_coordinate(const CHARMMInternalCoordinate &ic) {
313  internal_coordinates_.push_back(ic);
314  }
315  const CHARMMInternalCoordinate &get_internal_coordinate(unsigned int index)
316  const {
317  return internal_coordinates_[index];
318  }
319 
320  const CHARMMBond &get_bond(unsigned int index) const { return bonds_[index]; }
321  const CHARMMAngle &get_angle(unsigned int index) const {
322  return angles_[index];
323  }
324  const CHARMMDihedral &get_dihedral(unsigned int index) const {
325  return dihedrals_[index];
326  }
327  const CHARMMDihedral &get_improper(unsigned int index) const {
328  return impropers_[index];
329  }
330 
332 };
333 
335 
336 //! The ideal topology of a single residue
337 /** These residue topologies can be constructed manually (by adding
338  CHARMMAtomTopology objects with add_atom()). However, they are more
339  commonly populated automatically by reading a CHARMM topology file
340  (CHARMMParameters::get_residue_topology()).
341 
342  An ideal topology is intended to be created just once for each residue type.
343  To actually use a topology, create a CHARMMResidueTopology object, which
344  can be modified if necessary by adding/removing individual atoms, or
345  applying patches to, for example, modify terminal residues or
346  create disulfide bridges.
347  */
348 class IMPATOMEXPORT CHARMMIdealResidueTopology
349  : public CHARMMResidueTopologyBase {
350  std::string default_first_patch_, default_last_patch_;
351 
352  public:
353  CHARMMIdealResidueTopology(std::string type)
354  : CHARMMResidueTopologyBase(type) {}
356  : CHARMMResidueTopologyBase(type.get_string()) {}
357 
358  //! Remove the named atom
359  /** Any bonds/angles that involve this atom are also removed.
360  */
361  void remove_atom(std::string name);
362 
363  void set_default_first_patch(std::string patch) {
364  default_first_patch_ = patch;
365  }
366  void set_default_last_patch(std::string patch) {
367  default_last_patch_ = patch;
368  }
369  std::string get_default_first_patch() const { return default_first_patch_; }
370  std::string get_default_last_patch() const { return default_last_patch_; }
372 };
373 
375 
377 
378 //! A CHARMM patch residue.
379 /** Patch residues are similar to regular residues, except that they are
380  used to modify an existing residue. Any atoms they contain replace or
381  add to those in the residue; they can also remove atoms.
382  Atom names are as for regular residues, except for patches that affect
383  two residues (e.g. DISU, LINK) in which each atom contains a 1 or 2
384  prefix (plus an optional colon) to identify the residue which will be
385  patched; for example, both 1:CA and 1CA identify the CA atom in the
386  first residue.
387  */
388 class IMPATOMEXPORT CHARMMPatch : public CHARMMResidueTopologyBase {
389  Vector<std::string> deleted_atoms_;
390  std::map<std::string, Element> atom_elements_;
391 
392  public:
393  //! Construct a new, empty patch residue.
394  /** To get an existing patch, use CHARMMParameters::get_patch() instead.
395  */
396  CHARMMPatch(std::string type) : CHARMMResidueTopologyBase(type) {
397  set_name(std::string("CHARMM patching residue ") + type);
398  }
399 
400  void add_removed_atom(std::string name) { deleted_atoms_.push_back(name); }
401 
402  unsigned int get_number_of_removed_atoms() const {
403  return deleted_atoms_.size();
404  }
405 
406  std::string get_removed_atom(unsigned int i) const {
407  return deleted_atoms_[i];
408  }
409 
410  //! Set the element of the named atom in the patch.
411  /** When the patch is applied, an AtomType is created (if needed) for
412  each atom added to a residue, using this element.
413  */
414  void set_atom_element(std::string name, Element e) {
415  atom_elements_[name] = e;
416  }
417 
418  //! Get the element of the named atom in the patch.
419  /** \return UNKNOWN_ELEMENT if no element was set for this atom.
420  */
421  Element get_atom_element(std::string name) const {
422  std::map<std::string, Element>::const_iterator it =
423  atom_elements_.find(name);
424  return it == atom_elements_.end() ? UNKNOWN_ELEMENT : it->second;
425  }
426 
427  //! Apply the patch to the residue, modifying its topology accordingly.
428  /** \note Most CHARMM patches are designed to be applied in isolation;
429  it is usually an error to try to apply two different patches
430  to the same residue. Thus, by default \imp prohibits this.
431  To allow an already-patched residue to be re-patched, first
432  call CHARMMResidueTopology::set_patched(false).
433  */
434  void apply(CHARMMResidueTopology *res) const;
435 
436  //! Apply the patch to the given pair of residues.
437  /** This can only be used for special two-residue patches, such as
438  DISU or LINK. In a two-residue patch, each atom has a 1: or 2: prefix
439  to identify the residue it refers to.
440 
441  \throws ValueException if the patch is not a two-residue patch.
442  */
443  void apply(CHARMMResidueTopology *res1, CHARMMResidueTopology *res2) const;
445 };
446 
448 
449 //! The topology of a single residue in a model.
450 /** Each CHARMMResidueTopology object can represent an 'unknown' residue
451  (containing no atoms or bonds) or is a copy of an existing
452  CHARMMIdealResidueTopology. Optionally, patches can be applied to
453  residues (see CHARMMPatch::apply()) to add N- or C-termini, disulfide
454  bridges, sidechain modifications, etc.
455  */
456 class IMPATOMEXPORT CHARMMResidueTopology : public CHARMMIdealResidueTopology {
457  bool patched_;
458 
459  public:
460  //! Create an empty topology, containing no atoms or bonds.
462  : CHARMMIdealResidueTopology(type), patched_(false) {}
463 
464  //! Construct residue topology as a copy of an existing topology.
466  : CHARMMIdealResidueTopology(ideal->get_type()), patched_(false) {
467  set_default_first_patch(ideal->get_default_first_patch());
468  set_default_last_patch(ideal->get_default_last_patch());
469 
470  // Add atoms from existing topology
471  for (unsigned int i = 0; i < ideal->get_number_of_atoms(); ++i) {
472  add_atom(ideal->get_atom(i));
473  }
474  // Add angles/bonds/dihedrals/impropers
475  for (unsigned int i = 0; i < ideal->get_number_of_bonds(); ++i) {
476  add_bond(ideal->get_bond(i));
477  }
478  for (unsigned int i = 0; i < ideal->get_number_of_angles(); ++i) {
479  add_angle(ideal->get_angle(i));
480  }
481  for (unsigned int i = 0; i < ideal->get_number_of_dihedrals(); ++i) {
482  add_dihedral(ideal->get_dihedral(i));
483  }
484  for (unsigned int i = 0; i < ideal->get_number_of_impropers(); ++i) {
485  add_improper(ideal->get_improper(i));
486  }
487  for (unsigned int i = 0; i < ideal->get_number_of_internal_coordinates();
488  ++i) {
489  add_internal_coordinate(ideal->get_internal_coordinate(i));
490  }
491  }
492 
493  bool get_patched() const { return patched_; }
494  void set_patched(bool patched) { patched_ = patched; }
495 
497 };
498 
500 
501 IMPATOM_END_NAMESPACE
502 
503 #endif /* IMPATOM_CHARMM_TOPOLOGY_H */
CHARMMResidueTopology(CHARMMIdealResidueTopology *ideal)
Construct residue topology as a copy of an existing topology.
The ideal topology of a single residue.
#define IMP_SHOWABLE_INLINE(Name, how_to_show)
Declare the methods needed by an object that can be printed.
A connection (bond, angle, dihedral) between some number of endpoints.
A single atom in a CHARMM topology.
#define IMP_OBJECT_METHODS(Name)
Define the basic things needed by any Object.
Definition: object_macros.h:25
double get_dihedral(XYZ a, XYZ b, XYZ c, XYZ d)
Compute the dihedral angle (in radians) between the four particles.
Simple atom decorator.
A geometric relationship between four atoms.
The type of an atom.
Atom get_atom(Residue rd, AtomType at)
Return a particle atom from the residue.
A more IMP-like version of the std::vector.
Definition: Vector.h:50
The end of a bond, angle, dihedral, improper, or internal coordinate.
double get_angle(const Line3D &a, const Line3D &b)
Get angle in radians between two lines around their closest points.
The topology of a single residue in a model.
A smart pointer to a reference counted object.
Definition: Pointer.h:87
#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
Decorator for helping deal with a hierarchy of molecules.
#define IMP_VALUES(Name, PluralName)
Define the type for storing sets of values.
Definition: value_macros.h:23
Common base class for heavy weight IMP objects.
Definition: Object.h:111
Base class for all CHARMM residue-based topology.
A decorator for a particle representing an atom.
Definition: atom/Atom.h:238
The type for a residue.
CHARMMPatch(std::string type)
Construct a new, empty patch residue.
Element get_atom_element(std::string name) const
Get the element of the named atom in the patch.
bool get_contains_atom(std::string name) const
Return true if the bond contains the named atom.
A CHARMM patch residue.
std::ostream & show(Hierarchy h, std::ostream &out=std::cout)
Print the hierarchy using a given decorator to display each node.
#define IMP_OBJECTS(Name, PluralName)
Define the types for storing lists of object pointers.
Definition: object_macros.h:44
A shared base class to help in debugging and things.
Bond get_bond(Bonded a, Bonded b)
Get the bond between two particles.
CHARMMResidueTopology(ResidueType type)
Create an empty topology, containing no atoms or bonds.
void set_atom_element(std::string name, Element e)
Set the element of the named atom in the patch.
Element
The various elements currently supported/known.
Definition: element.h:23
Atoms get_atoms(const CHARMMResidueTopology *current_residue, const CHARMMResidueTopology *previous_residue, const CHARMMResidueTopology *next_residue, const std::map< const CHARMMResidueTopology *, Hierarchy > &resmap) const
Map the bond to a list of Atom particles.