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 <gemmi/model.hpp>
11#include <gemmi/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() >= 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] = seqid.icode;
35 icode[1] = '\0';
36}
37
38inline mmdb::Manager* copy_to_mmdb(const Structure& st, mmdb::Manager* manager) {
39 for (const std::string& s : st.raw_remarks) {
40 std::string line = rtrim_str(s);
41 manager->PutPDBString(line.c_str());
42 }
43
44 for (int imodel = 0; imodel < (int) st.models.size(); ++imodel) {
45 const Model& model = st.models[imodel];
46 mmdb::PModel model2 = mmdb::newModel();
47 model2->SetMMDBManager(manager, imodel);
48
49 for (const Chain& chain : model.chains) {
50 mmdb::PChain chain2 = model2->CreateChain(chain.name.c_str());
51 for (const Residue& res : chain.residues) {
52 const char icode[2] = {res.seqid.icode, '\0'};
53 mmdb::PResidue res2 = chain2->GetResidueCreate(res.name.c_str(),
54 *res.seqid.num,
55 icode,
56 true);
57 for (const Atom& atom : res.atoms) {
58 mmdb::PAtom atom2 = mmdb::newAtom();
59 const char altloc[2] = {atom.altloc, '\0'};
60 std::string padded_name = atom.padded_name();
61 // padded_name() is padding from the left; MMDB from both sides
62 if (padded_name.size() < 4)
63 padded_name.resize(4, ' ');
64 atom2->SetAtomName(0, atom.serial, padded_name.c_str(),
65 altloc, res.segment.c_str(), atom.element.uname());
66 atom2->Het = res.het_flag == 'H';
67 atom2->SetCharge(atom.charge);
68 atom2->SetCoordinates(atom.pos.x, atom.pos.y, atom.pos.z,
69 atom.occ, atom.b_iso);
70 if (atom.aniso.nonzero()) {
71 atom2->u11 = atom.aniso.u11;
72 atom2->u22 = atom.aniso.u22;
73 atom2->u33 = atom.aniso.u33;
74 atom2->u12 = atom.aniso.u12;
75 atom2->u13 = atom.aniso.u13;
76 atom2->u23 = atom.aniso.u23;
77 atom2->WhatIsSet |= mmdb::ASET_Anis_tFSigma;
78 }
79 res2->AddAtom(atom2);
80 }
81 // TER
82 if (res.entity_type == EntityType::Polymer &&
83 (&res == &chain.residues.back() ||
84 (&res + 1)->entity_type != EntityType::Polymer)) {
85 mmdb::PAtom atom2 = mmdb::newAtom();
86 atom2->MakeTer();
87 atom2->serNum = res.atoms.back().serial + 1;
88 res2->AddAtom(atom2);
89 }
90 }
91 }
92 int n_model = manager->AddModel(model2);
93 // currently we don't setup CisPeps, so this doesn't do anything
94 manager->GetModel(n_model)->CopyCisPeps(model2);
95 manager->PutCell(st.cell.a, st.cell.b, st.cell.c,
96 st.cell.alpha, st.cell.beta, st.cell.gamma, 1);
97 manager->SetSpaceGroup(st.spacegroup_hm.c_str());
98 mmdb::Cryst* cryst = manager->GetCrystData();
99 auto z = st.info.find("_cell.Z_PDB");
100 if (z != st.info.end() && !z->second.empty()) {
101 cryst->Z = std::atoi(z->second.c_str());
102 cryst->WhatIsSet |= mmdb::CSET_ZValue;
103 }
104 if (st.has_origx && !st.origx.is_identity()) {
105 copy_transform_to_mmdb(st.origx, cryst->o, cryst->t);
106 cryst->WhatIsSet |= mmdb::CSET_OrigMatrix;
107 }
108 if (st.cell.explicit_matrices) {
109 copy_transform_to_mmdb(st.cell.frac, cryst->s, cryst->u);
110 cryst->WhatIsSet |= mmdb::CSET_ScaleMatrix;
111 }
112 if (!st.ncs.empty()) {
113 mmdb::mat33 m;
114 mmdb::vect3 v;
115 if (st.info.find("_struct_ncs_oper.id") != st.info.end()) {
117 cryst->AddNCSMatrix(m, v, 1);
118 }
119 for (const NcsOp& op : st.ncs) {
120 copy_transform_to_mmdb(op.tr, m, v);
121 cryst->AddNCSMatrix(m, v, op.given);
122 }
123 }
124 }
125 return manager;
126}
127
128
129// don't use const for mmdb objects - MMDB doesn't have const method
130inline Atom copy_atom_from_mmdb(mmdb::Atom& m_atom) {
131 Atom atom;
132 atom.name = m_atom.label_atom_id;
133 atom.altloc = m_atom.altLoc[0];
134 atom.charge = (signed char) m_atom.charge;
135 atom.element = Element(m_atom.element);
136 atom.serial = m_atom.serNum;
137 atom.pos = Position(m_atom.x, m_atom.y, m_atom.z);
138 atom.occ = (float) m_atom.occupancy;
139 atom.b_iso = (float) m_atom.tempFactor;
140 if (m_atom.WhatIsSet & mmdb::ASET_Anis_tFSigma) {
141 atom.aniso.u11 = (float) m_atom.u11;
142 atom.aniso.u22 = (float) m_atom.u22;
143 atom.aniso.u33 = (float) m_atom.u33;
144 atom.aniso.u12 = (float) m_atom.u12;
145 atom.aniso.u13 = (float) m_atom.u13;
146 atom.aniso.u23 = (float) m_atom.u23;
147 }
148 return atom;
149}
150
151inline Residue copy_residue_from_mmdb(mmdb::Residue& m_res) {
152 Residue res;
153 res.name = m_res.name;
154 res.seqid.num = m_res.seqNum;
155 if (m_res.insCode[0])
156 res.seqid.icode = m_res.insCode[0];
157 int n = m_res.GetNumberOfAtoms();
158 res.atoms.reserve(n);
159 bool first = true;
160 for (int i = 0; i < n; ++i)
161 if (mmdb::Atom* m_atom = m_res.GetAtom(i)) {
162 if (m_atom->isTer()) {
164 } else {
165 res.atoms.push_back(copy_atom_from_mmdb(*m_atom));
166 if (first) {
167 res.het_flag = m_atom->Het ? 'H' : 'A';
168 res.segment = m_atom->segID;
169 first = false;
170 }
171 }
172 }
173 return res;
174}
175
176inline Chain copy_chain_from_mmdb(mmdb::Chain& m_chain) {
177 Chain chain(m_chain.GetChainID());
178 int n = m_chain.GetNumberOfResidues();
179 chain.residues.reserve(n);
180 for (int i = 0; i < n; ++i)
181 if (mmdb::Residue* m_res = m_chain.GetResidue(i))
182 chain.residues.push_back(copy_residue_from_mmdb(*m_res));
183 // in MMDB we may have pseudo-atom TER that marks polymer end
184 for (auto i = chain.residues.begin(); i != chain.residues.end(); ++i)
185 if (i->entity_type == EntityType::Polymer) { // residue before TER
186 for (auto j = chain.residues.begin(); j != i; ++j)
187 j->entity_type = EntityType::Polymer;
188 for (auto j = i + 1; j != chain.residues.end(); ++j)
189 j->entity_type = j->is_water() ? EntityType::Water
191 break;
192 }
193 return chain;
194}
195
196inline Model copy_model_from_mmdb(mmdb::Model& m_model) {
197 Model model(std::to_string(m_model.GetSerNum()));
198 int n = m_model.GetNumberOfChains();
199 model.chains.reserve(n);
200 for (int i = 0; i < n; ++i)
201 if (mmdb::Chain* m_chain = m_model.GetChain(i))
202 model.chains.push_back(copy_chain_from_mmdb(*m_chain));
203 return model;
204}
205
206inline Structure copy_from_mmdb(mmdb::Manager* manager) {
208 const mmdb::Cryst& cryst = *manager->GetCrystData();
209 st.cell.set(cryst.a, cryst.b, cryst.c, cryst.alpha, cryst.beta, cryst.gamma);
210 if (cryst.WhatIsSet & mmdb::CSET_ZValue)
211 st.info.emplace("_cell.Z_PDB", std::to_string(cryst.Z));
212 st.spacegroup_hm = cryst.spaceGroup;
213 int n = manager->GetNumberOfModels();
214 st.models.reserve(n);
215 for (int i = 0; i < n; ++i)
216 if (mmdb::Model* m_model = manager->GetModel(i+1))
217 st.models.push_back(copy_model_from_mmdb(*m_model));
218 return st;
219}
220
221} // namespace gemmi
222#endif
223
224
225/*
226// Example 1.
227// Read a coordinate file using gemmi and write it to pdb using mmdb.
228
229#include <gemmi/mmread.hpp>
230#include <gemmi/mmdb.hpp>
231
232// two arguments expected: input and output paths.
233int main (int argc, char** argv) {
234 if (argc != 3)
235 return 1;
236
237 mmdb::InitMatType();
238 mmdb::Manager* manager = new mmdb::Manager();
239 try {
240 gemmi::Structure st = gemmi::read_structure_file(argv[1]);
241 st.merge_chain_parts();
242 gemmi::copy_to_mmdb(st, manager);
243 } catch(std::runtime_error& e) {
244 printf("File reading failed: %s\n", e.what());
245 return 1;
246 }
247
248 mmdb::ERROR_CODE rc = manager->WritePDBASCII(argv[2]);
249 if (rc)
250 printf(" ***** ERROR #%i WRITE:\n\n %s\n\n",
251 rc, mmdb::GetErrorDescription(rc));
252 return 0;
253}
254
255// Example 2.
256// Read a coordinate file using mmdb and write it to PDB using gemmi.
257#include <fstream>
258#include <gemmi/mmdb.hpp>
259#include <gemmi/to_pdb.hpp>
260
261// two arguments expected: input and output paths.
262int main (int argc, char** argv) {
263 if (argc != 3)
264 return 1;
265 mmdb::InitMatType();
266 mmdb::Manager manager;
267 manager.SetFlag(mmdb::MMDBF_PrintCIFWarnings |
268 mmdb::MMDBF_FixSpaceGroup |
269 mmdb::MMDBF_IgnoreHash |
270 mmdb::MMDBF_IgnoreNonCoorPDBErrors|
271 mmdb::MMDBF_DoNotProcessSpaceGroup);
272 mmdb::ERROR_CODE rc = manager.ReadCoorFile(argv[1]);
273 if (rc != mmdb::Error_NoError) {
274 printf(" ***** ERROR reading the file\n");
275 return 1;
276 }
277 gemmi::Structure st = gemmi::copy_from_mmdb(&manager);
278 std::ofstream os(argv[2]);
279 gemmi::write_pdb(st, os);
280 return 0;
281}
282*/
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:196
std::string rtrim_str(const std::string &str)
Definition util.hpp:118
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:151
Atom copy_atom_from_mmdb(mmdb::Atom &m_atom)
Definition mmdb.hpp:130
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:206
Chain copy_chain_from_mmdb(mmdb::Chain &m_chain)
Definition mmdb.hpp:176
mmdb::Manager * copy_to_mmdb(const Structure &st, mmdb::Manager *manager)
Definition mmdb.hpp:38
void fail(const std::string &msg)
Definition fail.hpp:59
Represents atom site in macromolecular structure (~100 bytes).
Definition model.hpp:112
SMat33< float > aniso
Definition model.hpp:127
char altloc
Definition model.hpp:115
signed char charge
Definition model.hpp:116
float b_iso
Definition model.hpp:126
Element element
Definition model.hpp:117
float occ
Definition model.hpp:124
Position pos
Definition model.hpp:123
std::string name
Definition model.hpp:114
std::vector< Residue > residues
Definition model.hpp:465
std::vector< Chain > chains
Definition model.hpp:700
Non-crystallographic symmetry operation (such as in the MTRIXn record)
Definition unitcell.hpp:120
Coordinates in Angstroms - orthogonal (Cartesian) coordinates.
Definition unitcell.hpp:32
std::string name
Definition seqid.hpp:91
std::string segment
Definition seqid.hpp:90
std::vector< Atom > atoms
Definition model.hpp:188
EntityType entity_type
Definition model.hpp:183
OptionalNum num
Definition seqid.hpp:55
char icode
Definition seqid.hpp:56