Gemmi C++ API
Loading...
Searching...
No Matches
mmdb.hpp
Go to the documentation of this file.
1// Copyright 2020-2022 Global Phasing Ltd.
2//
3// Converts between gemmi::Structure and mmdb::Manager.
4
5#ifndef GEMMI_MMDB_HPP_
6#define GEMMI_MMDB_HPP_
7
8#include <cstdlib> // for atoi
9#include <cstring> // for memcpy
10#include "model.hpp"
11#include "util.hpp" // for rtrim_str
12#include <mmdb2/mmdb_manager.h>
13
14namespace gemmi {
15
16inline void copy_transform_to_mmdb(const Transform& tr,
17 mmdb::mat33& mat, mmdb::vect3& vec) {
18 for (int i = 0; i < 3; ++i) {
19 for (int j = 0; j < 3; ++j)
20 mat[i][j] = tr.mat[i][j];
21 vec[i] = tr.vec.at(i);
22 }
23}
24
25template<int N>
26void strcpy_to_mmdb(char (&dest)[N], const std::string& src) {
27 if (src.size() + 1 >= N)
28 fail("This string is too long: " + src);
29 std::memcpy(dest, src.c_str(), src.size() + 1);
30}
31
32inline void set_seqid_in_mmdb(int* seqnum, mmdb::InsCode& icode, SeqId seqid) {
33 *seqnum = *seqid.num;
34 icode[0] = icode[1] = '\0';
35 if (seqid.has_icode())
36 icode[0] = seqid.icode;
37}
38
39inline SeqId seqid_from_mmdb(int seqnum, const mmdb::InsCode& inscode) {
40 return SeqId(seqnum, inscode[0] ? inscode[0] : ' ');
41}
42
43inline mmdb::CisPep* cispep_to_mmdb(const CisPep& g, int ser_num, int model_num) {
44 mmdb::CisPep* m = new mmdb::CisPep();
45 m->serNum = ser_num;
46 strcpy_to_mmdb(m->pep1, g.partner_c.res_id.name);
47 strcpy_to_mmdb(m->chainID1, g.partner_c.chain_name);
48 set_seqid_in_mmdb(&m->seqNum1, m->icode1, g.partner_c.res_id.seqid);
49 strcpy_to_mmdb(m->pep2, g.partner_n.res_id.name);
50 strcpy_to_mmdb(m->chainID2, g.partner_n.chain_name);
51 set_seqid_in_mmdb(&m->seqNum2, m->icode2, g.partner_n.res_id.seqid);
52 m->modNum = model_num;
53 m->measure = g.reported_angle;
54 return m;
55}
56
57inline CisPep cispep_from_mmdb(const mmdb::CisPep& m, int model_num) {
58 CisPep g;
59 g.partner_c.res_id.name = m.pep1;
60 g.partner_c.res_id.seqid = seqid_from_mmdb(m.seqNum1, m.icode1);
61 g.partner_c.chain_name = m.chainID1;
62 g.partner_n.res_id.name = m.pep2;
63 g.partner_n.res_id.seqid = seqid_from_mmdb(m.seqNum2, m.icode2);
64 g.partner_n.chain_name = m.chainID2;
65 g.model_num = model_num;
66 g.reported_angle = m.measure;
67 return g;
68}
69
70inline void transfer_links_to_mmdb(const Structure& st, mmdb::Manager* mol) {
71 // based on code provided by Paul Emsley
72 for (const Connection& con : st.connections) {
73 if (!con.partner1.res_id.seqid.num || !con.partner2.res_id.seqid.num)
74 continue;
75 mmdb::Link link{};
76 // partner1
77 strcpy_to_mmdb(link.atName1, con.partner1.atom_name);
78 link.aloc1[0] = con.partner1.altloc;
79 set_seqid_in_mmdb(&link.seqNum1, link.insCode1, con.partner1.res_id.seqid);
80 strcpy_to_mmdb(link.resName1, con.partner1.res_id.name);
81 strcpy_to_mmdb(link.chainID1, con.partner1.chain_name);
82 // partner2
83 strcpy_to_mmdb(link.atName2, con.partner2.atom_name);
84 link.aloc2[0] = con.partner2.altloc;
85 set_seqid_in_mmdb(&link.seqNum2, link.insCode2, con.partner2.res_id.seqid);
86 strcpy_to_mmdb(link.resName2, con.partner2.res_id.name);
87 strcpy_to_mmdb(link.chainID2, con.partner2.chain_name);
88 if (con.reported_distance > 0)
89 link.dist = con.reported_distance;
90 if (con.asu == Asu::Different) {
91 link.s2 = con.reported_sym[0];
92 link.i2 = link.i1 + con.reported_sym[1];
93 link.j2 = link.j1 + con.reported_sym[2];
94 link.k2 = link.k1 + con.reported_sym[3];
95 }
96 // add links to models
97 for (int imod = 1; imod <= mol->GetNumberOfModels(); imod++)
98 if (mmdb::Model* model_p = mol->GetModel(imod))
99 model_p->AddLink(new mmdb::Link(link));
100 }
101}
102
103// ignoring LinkR's here
104inline void transfer_links_from_mmdb(mmdb::LinkContainer& mmdb_links, Structure& st) {
105 for (int i = 0; i < mmdb_links.Length(); ++i) {
106 mmdb::Link& link = *static_cast<mmdb::Link*>(mmdb_links.GetContainerClass(i));
108 // partner1
109 con.partner1.atom_name = link.atName1;
110 con.partner1.altloc = link.aloc1[0];
111 con.partner1.res_id.seqid = seqid_from_mmdb(link.seqNum1, link.insCode1);
112 con.partner1.res_id.name = link.resName1;
113 con.partner1.chain_name = link.chainID1;
114 // partner2
115 con.partner2.atom_name = link.atName2;
116 con.partner2.altloc = link.aloc2[0];
117 con.partner2.res_id.seqid = seqid_from_mmdb(link.seqNum2, link.insCode2);
118 con.partner2.res_id.name = link.resName2;
119 con.partner2.chain_name = link.chainID2;
120 con.reported_distance = link.dist;
121 if (link.s1 == link.s2 && link.i1 == link.i2 && link.j1 == link.j2 && link.k1 == link.k2) {
122 con.asu = Asu::Same;
123 } else {
124 con.asu = Asu::Different;
125 if (link.s1 == 1)
126 con.reported_sym[0] = link.s2;
127 con.reported_sym[1] = link.i2 - link.i1;
128 con.reported_sym[2] = link.j2 - link.j1;
129 con.reported_sym[3] = link.k2 - link.k1;
130 }
131 // for LinkR we'd also have:
132 // con.link_id = link.linkRID;
133 st.connections.push_back(con);
134 }
135}
136
137inline void copy_to_mmdb(const Structure& st, mmdb::Manager* manager) {
138 for (const std::string& s : st.raw_remarks) {
139 std::string line = rtrim_str(s);
140 manager->PutPDBString(line.c_str());
141 }
142
143 for (int imodel = 0; imodel < (int) st.models.size(); ++imodel) {
144 const Model& model = st.models[imodel];
145 mmdb::Model* model2 = mmdb::newModel();
146 manager->AddModel(model2);
147
148 for (const Chain& chain : model.chains) {
149 mmdb::Chain* chain2 = model2->CreateChain(chain.name.c_str());
150 for (const Residue& res : chain.residues) {
151 char icode[2] = {};
152 if (res.seqid.has_icode())
153 icode[0] = res.seqid.icode;
154 mmdb::Residue* res2 = chain2->GetResidueCreate(res.name.c_str(),
155 *res.seqid.num,
156 icode,
157 true);
158 for (const Atom& atom : res.atoms) {
159 mmdb::Atom* atom2 = mmdb::newAtom();
160 const char altloc[2] = {atom.altloc, '\0'};
161 std::string padded_name = atom.padded_name();
162 // padded_name() is padding from the left; MMDB from both sides
163 if (padded_name.size() < 4)
164 padded_name.resize(4, ' ');
165 atom2->SetAtomName(0, atom.serial, padded_name.c_str(),
166 altloc, res.segment.c_str(), atom.element.uname());
167 atom2->Het = res.het_flag == 'H';
168 atom2->SetCharge(atom.charge);
169 atom2->SetCoordinates(atom.pos.x, atom.pos.y, atom.pos.z,
170 atom.occ, atom.b_iso);
171 if (atom.aniso.nonzero()) {
172 atom2->u11 = atom.aniso.u11;
173 atom2->u22 = atom.aniso.u22;
174 atom2->u33 = atom.aniso.u33;
175 atom2->u12 = atom.aniso.u12;
176 atom2->u13 = atom.aniso.u13;
177 atom2->u23 = atom.aniso.u23;
178 atom2->WhatIsSet |= mmdb::ASET_Anis_tFSigma;
179 }
180 res2->AddAtom(atom2);
181 }
182 // TER
183 if (res.entity_type == EntityType::Polymer &&
184 (&res == &chain.residues.back() ||
185 (&res + 1)->entity_type != EntityType::Polymer)) {
186 mmdb::Atom* atom2 = mmdb::newAtom();
187 atom2->MakeTer();
188 atom2->serNum = res.atoms.back().serial + 1;
189 res2->AddAtom(atom2);
190 }
191 }
192 }
193 manager->PutCell(st.cell.a, st.cell.b, st.cell.c,
194 st.cell.alpha, st.cell.beta, st.cell.gamma, 1);
195 manager->SetSpaceGroup(st.spacegroup_hm.c_str());
196 mmdb::Cryst* cryst = manager->GetCrystData();
197 auto z = st.info.find("_cell.Z_PDB");
198 if (z != st.info.end() && !z->second.empty()) {
199 cryst->Z = std::atoi(z->second.c_str());
200 cryst->WhatIsSet |= mmdb::CSET_ZValue;
201 }
202 if (st.has_origx && !st.origx.is_identity()) {
203 copy_transform_to_mmdb(st.origx, cryst->o, cryst->t);
204 cryst->WhatIsSet |= mmdb::CSET_OrigMatrix;
205 }
206 if (st.cell.explicit_matrices) {
207 copy_transform_to_mmdb(st.cell.frac, cryst->s, cryst->u);
208 cryst->WhatIsSet |= mmdb::CSET_ScaleMatrix;
209 }
210 if (!st.ncs.empty()) {
211 mmdb::mat33 m;
212 mmdb::vect3 v;
213 if (st.info.find("_struct_ncs_oper.id") != st.info.end()) {
215 cryst->AddNCSMatrix(m, v, 1);
216 }
217 for (const NcsOp& op : st.ncs) {
218 copy_transform_to_mmdb(op.tr, m, v);
219 cryst->AddNCSMatrix(m, v, op.given);
220 }
221 }
222 }
223 if (!st.cispeps.empty() && !st.models.empty()) {
224 int ser_num = 0;
225 for (const CisPep& cispep : st.cispeps) {
226 // In the PDB, CISPEP records have modNum=0 if there is only one model
227 int modnum = st.models.size() > 1 ? cispep.model_num : 0;
228 int model_no = std::max(1, modnum); // GetModel() takes 1-based index
229 if (mmdb::Model* m_model = manager->GetModel(model_no))
230 m_model->AddCisPep(cispep_to_mmdb(cispep, ++ser_num, modnum));
231 }
232 }
234}
235
236
237// don't use const for mmdb objects - MMDB doesn't have const method
238inline Atom copy_atom_from_mmdb(mmdb::Atom& m_atom) {
239 Atom atom;
240 atom.name = m_atom.label_atom_id;
241 atom.altloc = m_atom.altLoc[0];
242 atom.charge = (signed char) m_atom.charge;
243 atom.element = Element(m_atom.element);
244 atom.serial = m_atom.serNum;
245 atom.pos = Position(m_atom.x, m_atom.y, m_atom.z);
246 atom.occ = (float) m_atom.occupancy;
247 atom.b_iso = (float) m_atom.tempFactor;
248 if (m_atom.WhatIsSet & mmdb::ASET_Anis_tFSigma) {
249 atom.aniso.u11 = (float) m_atom.u11;
250 atom.aniso.u22 = (float) m_atom.u22;
251 atom.aniso.u33 = (float) m_atom.u33;
252 atom.aniso.u12 = (float) m_atom.u12;
253 atom.aniso.u13 = (float) m_atom.u13;
254 atom.aniso.u23 = (float) m_atom.u23;
255 }
256 return atom;
257}
258
259inline Residue copy_residue_from_mmdb(mmdb::Residue& m_res) {
260 Residue res;
261 res.name = m_res.name;
262 res.seqid = seqid_from_mmdb(m_res.seqNum, m_res.insCode);
263 int n = m_res.GetNumberOfAtoms();
264 res.atoms.reserve(n);
265 bool first = true;
266 for (int i = 0; i < n; ++i)
267 if (mmdb::Atom* m_atom = m_res.GetAtom(i)) {
268 if (m_atom->isTer()) {
270 } else {
271 res.atoms.push_back(copy_atom_from_mmdb(*m_atom));
272 if (first) {
273 res.het_flag = m_atom->Het ? 'H' : 'A';
274 res.segment = m_atom->segID;
275 first = false;
276 }
277 }
278 }
279 return res;
280}
281
282inline Chain copy_chain_from_mmdb(mmdb::Chain& m_chain) {
283 Chain chain(m_chain.GetChainID());
284 int n = m_chain.GetNumberOfResidues();
285 chain.residues.reserve(n);
286 for (int i = 0; i < n; ++i)
287 if (mmdb::Residue* m_res = m_chain.GetResidue(i))
288 chain.residues.push_back(copy_residue_from_mmdb(*m_res));
289 // in MMDB we may have pseudo-atom TER that marks polymer end
290 for (auto i = chain.residues.begin(); i != chain.residues.end(); ++i)
291 if (i->entity_type == EntityType::Polymer) { // residue before TER
292 for (auto j = chain.residues.begin(); j != i; ++j)
293 j->entity_type = EntityType::Polymer;
294 for (auto j = i + 1; j != chain.residues.end(); ++j)
295 j->entity_type = j->is_water() ? EntityType::Water
297 break;
298 }
299 return chain;
300}
301
302inline Model copy_model_from_mmdb(mmdb::Model& m_model) {
303 Model model(m_model.GetSerNum());
304 int n = m_model.GetNumberOfChains();
305 model.chains.reserve(n);
306 for (int i = 0; i < n; ++i)
307 if (mmdb::Chain* m_chain = m_model.GetChain(i))
308 model.chains.push_back(copy_chain_from_mmdb(*m_chain));
309 return model;
310}
311
312inline Structure copy_from_mmdb(mmdb::Manager* manager) {
314 const mmdb::Cryst& cryst = *manager->GetCrystData();
315 st.cell.set(cryst.a, cryst.b, cryst.c, cryst.alpha, cryst.beta, cryst.gamma);
316 if (cryst.WhatIsSet & mmdb::CSET_ZValue)
317 st.info.emplace("_cell.Z_PDB", std::to_string(cryst.Z));
318 st.spacegroup_hm = cryst.spaceGroup;
319 int n = manager->GetNumberOfModels();
320 st.models.reserve(n);
321 for (int i = 1; i <= n; ++i)
322 if (mmdb::Model* m_model = manager->GetModel(i)) {
323 st.models.push_back(copy_model_from_mmdb(*m_model));
324 int model_num = st.models.back().num;
325 for (int j = 1; j <= m_model->GetNumberOfCisPeps(); ++j)
326 if (const mmdb::CisPep* m_cispep = m_model->GetCisPep(j))
327 st.cispeps.push_back(cispep_from_mmdb(*m_cispep, model_num));
328 }
329 if (n > 0) {
330 mmdb::Model* mmdb_model = manager->GetModel(1);
332 }
333 return st;
334}
335
336} // namespace gemmi
337#endif
338
339
340/*
341// Example 1.
342// Read a coordinate file using gemmi and write it to pdb using mmdb.
343
344#include <gemmi/mmread_gz.hpp>
345#include <gemmi/mmdb.hpp>
346
347// two arguments expected: input and output paths.
348int main (int argc, char** argv) {
349 if (argc != 3)
350 return 1;
351
352 mmdb::InitMatType();
353 mmdb::Manager* manager = new mmdb::Manager();
354 try {
355 gemmi::Structure st = gemmi::read_structure_gz(argv[1]);
356 st.merge_chain_parts();
357 gemmi::copy_to_mmdb(st, manager);
358 } catch(std::runtime_error& e) {
359 printf("File reading failed: %s\n", e.what());
360 return 1;
361 }
362
363 mmdb::ERROR_CODE rc = manager->WritePDBASCII(argv[2]);
364 if (rc)
365 printf(" ***** ERROR #%i WRITE:\n\n %s\n\n",
366 rc, mmdb::GetErrorDescription(rc));
367 return 0;
368}
369
370// Example 2.
371// Read a coordinate file using mmdb and write it to PDB using gemmi.
372#include <fstream>
373#include <gemmi/mmdb.hpp>
374#include <gemmi/to_pdb.hpp>
375
376// two arguments expected: input and output paths.
377int main (int argc, char** argv) {
378 if (argc != 3)
379 return 1;
380 mmdb::InitMatType();
381 mmdb::Manager manager;
382 manager.SetFlag(mmdb::MMDBF_PrintCIFWarnings |
383 mmdb::MMDBF_FixSpaceGroup |
384 mmdb::MMDBF_IgnoreHash |
385 mmdb::MMDBF_IgnoreNonCoorPDBErrors|
386 mmdb::MMDBF_DoNotProcessSpaceGroup);
387 mmdb::ERROR_CODE rc = manager.ReadCoorFile(argv[1]);
388 if (rc != mmdb::Error_NoError) {
389 printf(" ***** ERROR reading the file\n");
390 return 1;
391 }
392 gemmi::Structure st = gemmi::copy_from_mmdb(&manager);
393 std::ofstream os(argv[2]);
394 gemmi::write_pdb(st, os);
395 return 0;
396}
397*/
Data structures to store macromolecular structure models.
void strcpy_to_mmdb(char(&dest)[N], const std::string &src)
Definition mmdb.hpp:26
Model copy_model_from_mmdb(mmdb::Model &m_model)
Definition mmdb.hpp:302
void transfer_links_to_mmdb(const Structure &st, mmdb::Manager *mol)
Definition mmdb.hpp:70
std::string rtrim_str(const std::string &str)
Definition util.hpp:119
void set_seqid_in_mmdb(int *seqnum, mmdb::InsCode &icode, SeqId seqid)
Definition mmdb.hpp:32
Residue copy_residue_from_mmdb(mmdb::Residue &m_res)
Definition mmdb.hpp:259
SeqId seqid_from_mmdb(int seqnum, const mmdb::InsCode &inscode)
Definition mmdb.hpp:39
CisPep cispep_from_mmdb(const mmdb::CisPep &m, int model_num)
Definition mmdb.hpp:57
Atom copy_atom_from_mmdb(mmdb::Atom &m_atom)
Definition mmdb.hpp:238
void copy_to_mmdb(const Structure &st, mmdb::Manager *manager)
Definition mmdb.hpp:137
void copy_transform_to_mmdb(const Transform &tr, mmdb::mat33 &mat, mmdb::vect3 &vec)
Definition mmdb.hpp:16
Structure copy_from_mmdb(mmdb::Manager *manager)
Definition mmdb.hpp:312
Chain copy_chain_from_mmdb(mmdb::Chain &m_chain)
Definition mmdb.hpp:282
void fail(const std::string &msg)
Definition fail.hpp:59
mmdb::CisPep * cispep_to_mmdb(const CisPep &g, int ser_num, int model_num)
Definition mmdb.hpp:43
void transfer_links_from_mmdb(mmdb::LinkContainer &mmdb_links, Structure &st)
Definition mmdb.hpp:104
Represents atom site in macromolecular structure (~100 bytes).
Definition model.hpp:120
SMat33< float > aniso
Definition model.hpp:135
char altloc
Definition model.hpp:123
signed char charge
Definition model.hpp:124
float b_iso
Definition model.hpp:134
Element element
Definition model.hpp:125
float occ
Definition model.hpp:132
Position pos
Definition model.hpp:131
std::string name
Definition model.hpp:122
std::vector< Residue > residues
Definition model.hpp:486
std::vector< Chain > chains
Definition model.hpp:722
Non-crystallographic symmetry operation (such as in the MTRIXn record)
Definition unitcell.hpp:121
Coordinates in Angstroms - orthogonal (Cartesian) coordinates.
Definition unitcell.hpp:32
std::string name
Definition seqid.hpp:93
std::string segment
Definition seqid.hpp:92
std::vector< Atom > atoms
Definition model.hpp:196
EntityType entity_type
Definition model.hpp:191
OptionalNum num
Definition seqid.hpp:56
char has_icode() const
Definition seqid.hpp:79
char icode
Definition seqid.hpp:57
Utilities. Mostly for working with strings and vectors.