Gemmi C++ API
Loading...
Searching...
No Matches
cif2mtz.hpp
Go to the documentation of this file.
1// Copyright 2021 Global Phasing Ltd.
2//
3// A class for converting SF-mmCIF to MTZ (merged or unmerged).
4
5#ifndef GEMMI_CIF2MTZ_HPP_
6#define GEMMI_CIF2MTZ_HPP_
7
8#include <map>
9#include <set>
10#include <unordered_map>
11#include <utility>
12#include "cifdoc.hpp" // for Loop, as_int, ...
13#include "fail.hpp" // for fail
14#include "intensit.hpp" // for DataType
15#include "logger.hpp" // for Logger
16#include "mtz.hpp" // for Mtz
17#include "numb.hpp" // for as_number
18#include "refln.hpp" // for ReflnBlock
19#include "version.hpp" // for GEMMI_VERSION
20
21namespace gemmi {
22
23// "Old-style" anomalous or unmerged data is expected to have only these tags.
25 if (rb.refln_loop == nullptr)
26 return false;
27 for (const std::string& tag : rb.refln_loop->tags) {
28 if (tag.size() < 7 + 6)
29 return false;
30 int tag_id = ialpha4_id(tag.c_str() + 7);
31 if (tag_id != ialpha4_id("inde") && // index_[hkl]
32 tag_id != ialpha4_id("wave") && // wavelength_id
33 tag_id != ialpha4_id("crys") && // crystal_id
34 tag_id != ialpha4_id("scal") && // scale_group_code
35 tag_id != ialpha4_id("stat") && // status
36 tag_id != ialpha4_id("inte") && // intensity_meas, intensity_sigma
38 (tag_id != ialpha4_id("F_me") && // F_meas_au, F_meas_sigma_au
39 tag != "_refln.pdbx_r_free_flag")))
40 return false;
41 }
42 return true;
43}
44
45
54 const SpaceGroup* sg) {
55 std::vector<int> positions;
56 positions.reserve(13); // usually less, but it doesn't matter
57 for (const char* tag : {"_refln.index_h", "_refln.index_k", "_refln.index_l"}) {
58 int pos = loop.find_tag_lc(tag);
59 if (pos == -1)
60 fail("while reading old anomalous: _refln.index_{h,k,l} not found");
61 positions.push_back(pos);
62 }
63 for (const char* tag : {"_refln.status", "_refln.pdbx_r_free_flag"}) {
64 int pos = loop.find_tag_lc(tag);
65 if (pos != -1)
66 positions.push_back(pos);
67 }
68
70 ret.tags.reserve(positions.size());
71 for (int p : positions)
72 ret.tags.push_back(loop.tags[p]);
73 const char* old_labels[2][2] = {
74 {"_refln.F_meas_au", "_refln.F_meas_sigma_au"},
75 {"_refln.intensity_meas", "_refln.intensity_sigma"}
76 };
77 const char* new_labels[2][4] = {
78 {"_refln.pdbx_F_plus", "_refln.pdbx_F_plus_sigma",
79 "_refln.pdbx_F_minus", "_refln.pdbx_F_minus_sigma"},
80 {"_refln.pdbx_I_plus", "_refln.pdbx_I_plus_sigma",
81 "_refln.pdbx_I_minus", "_refln.pdbx_I_minus_sigma"}
82 };
83 size_t common_tags = positions.size();
84 for (int n = 0; n < 2; ++n) {
85 int idx = loop.find_tag(old_labels[n][0]);
86 if (idx >= 0 && idx+1 < (int)loop.width() && loop.tags[idx+1] == old_labels[n][1]) {
87 positions.insert(positions.end(), {idx, idx+1, idx, idx+1});
88 ret.tags.insert(ret.tags.end(), new_labels[n], new_labels[n] + 4);
89 }
90 }
91 if (common_tags == positions.size())
92 fail("while reading old anomalous: _refln has neither F_meas_au nor intensity_meas");
93
94 ret.values.reserve(loop.length() * ret.width()); // upper bound
95 std::unordered_map<Miller, std::string*, MillerHash> seen;
96 if (!sg)
98 ReciprocalAsu asu(sg);
99 GroupOps gops = sg->operations();
100 for (size_t i = 0; i < loop.values.size(); i += loop.width()) {
101 const std::string* row = &loop.values[i];
102 Miller hkl;
103 for (size_t j = 0; j < 3; ++j)
104 hkl[j] = cif::as_int(row[positions[j]]);
105 auto hkl_sign = asu.to_asu_sign(hkl, gops);
106 // pointers don't change, .reserve() above prevents re-allocations
107 std::string* new_row = ret.values.data() + ret.values.size();
108 auto r = seen.emplace(hkl_sign.first, new_row);
109 bool sign = hkl_sign.second;
110 if (r.second) { // adding a new row
111 for (int p : positions)
112 ret.values.push_back(row[p]);
113 // Don't move hkl to asu here, only change the sign if F- is before F+.
114 if (!sign) // negative sign
115 for (int j = 0; j < 3; ++j)
116 new_row[j] = std::to_string(-hkl[j]);
117 size_t first_absent = common_tags + (sign ? 2 : 0);
118 for (size_t j = first_absent; j < ret.width(); j += 4) {
119 new_row[j] = ".";
120 new_row[j+1] = ".";
121 }
122 } else { // modifying existing row
123 std::string* modified_row = r.first->second;
124 if (sign) // positive sign - this hkl might be better
125 for (int j = 0; j < 3; ++j)
126 modified_row[j] = row[positions[j]];
127 // if a status or free flag value differs, set it to null
128 for (size_t j = 3; j < common_tags; ++j)
129 if (modified_row[j] != row[positions[j]])
130 modified_row[j] = ".";
131 for (size_t j = common_tags + (sign ? 0 : 2); j < ret.width(); j += 4) {
132 modified_row[j] = row[positions[j]];
133 modified_row[j+1] = row[positions[j+1]];
134 }
135 }
136 }
137
138 return ret;
139}
140
141
142struct CifToMtz {
143 // Alternative mmCIF tags for the same MTZ label should be consecutive
144 static const char** default_spec(bool for_merged) {
145 static const char* merged[] = {
146 "pdbx_r_free_flag FreeR_flag I 0",
147 "status FreeR_flag I 0 o=1,f=0",
148 "intensity_meas IMEAN J 1",
149 "F_squared_meas IMEAN J 1",
150 "intensity_sigma SIGIMEAN Q 1",
151 "F_squared_sigma SIGIMEAN Q 1",
152 "pdbx_I_plus I(+) K 1",
153 "pdbx_I_plus_sigma SIGI(+) M 1",
154 "pdbx_I_minus I(-) K 1",
155 "pdbx_I_minus_sigma SIGI(-) M 1",
156 "F_meas FP F 1",
157 "F_meas_au FP F 1",
158 "F_meas_sigma SIGFP Q 1",
159 "F_meas_sigma_au SIGFP Q 1",
160 "pdbx_F_plus F(+) G 1",
161 "pdbx_F_plus_sigma SIGF(+) L 1",
162 "pdbx_F_minus F(-) G 1",
163 "pdbx_F_minus_sigma SIGF(-) L 1",
164 "pdbx_anom_difference DP D 1",
165 "pdbx_anom_difference_sigma SIGDP Q 1",
166 "F_calc FC F 1",
167 "F_calc_au FC F 1",
168 "phase_calc PHIC P 1",
169 "pdbx_F_calc_with_solvent F-model F 1",
170 "pdbx_phase_calc_with_solvent PHIF-model P 1",
171 "fom FOM W 1",
172 "weight FOM W 1",
173 "pdbx_HL_A_iso HLA A 1",
174 "pdbx_HL_B_iso HLB A 1",
175 "pdbx_HL_C_iso HLC A 1",
176 "pdbx_HL_D_iso HLD A 1",
177 "pdbx_FWT FWT F 1",
178 "pdbx_PHWT PHWT P 1",
179 "pdbx_DELFWT DELFWT F 1",
180 "pdbx_DELPHWT PHDELWT P 1",
181 nullptr
182 };
183 static const char* unmerged[] = {
184 "intensity_meas I J 0", // for unmerged data is category refln
185 "intensity_net I J 0",
186 "intensity_sigma SIGI Q 0",
187 "pdbx_detector_x XDET R 0",
188 "pdbx_detector_y YDET R 0",
189 "pdbx_scan_angle ROT R 0",
190 nullptr
191 };
192 return for_merged ? merged : unmerged;
193 }
194
195 struct Entry {
196 std::string refln_tag;
197 std::string col_label;
200 std::vector<std::pair<std::string, float>> code_to_number;
201
202 Entry(const std::string& line) {
203 std::vector<std::string> tokens;
204 tokens.reserve(4);
205 split_str_into_multi(line, " \t\r\n", tokens);
206 if (tokens.size() != 4 && tokens.size() != 5)
207 fail("line should have 4 or 5 words: " + line);
208 if (tokens[2].size() != 1 || tokens[3].size() != 1 ||
209 (tokens[3][0] != '0' && tokens[3][0] != '1'))
210 fail("incorrect line: " + line);
211 refln_tag = tokens[0];
212 col_label = tokens[1];
213 col_type = tokens[2][0];
214 dataset_id = tokens[3][0] - '0';
215 // for compatibility with older spec
216 if (col_type == 's' && tokens.size() == 4) {
217 col_type = 'I';
218 tokens.push_back("o=1,f=0");
219 }
220 if (tokens.size() == 5) {
221 std::vector<std::string> items = split_str(tokens[4], ',');
222 code_to_number.reserve(items.size());
223 for (const std::string& item : items) {
224 size_t pos = item.find('=');
225 if (pos == std::string::npos)
226 fail("wrong mapping (", item, ") in: ", line);
227 float f;
228 auto result = fast_float::from_chars(item.c_str() + pos + 1,
229 item.c_str() + item.size(), f);
230 if (result.ec != std::errc())
231 fail("failed to parse value in ", item, " in: ", line);
232 code_to_number.emplace_back(item.substr(0, pos), f);
233 }
234 }
235 }
236
237 float translate_code_to_number(const std::string& v) const {
238 if (v.size() == 1) {
239 for (const auto& c2n : code_to_number)
240 if (c2n.first.size() == 1 && c2n.first[0] == v[0])
241 return c2n.second;
242 } else {
243 std::string s = cif::as_string(v);
244 for (const auto& c2n : code_to_number)
245 if (c2n.first == s)
246 return c2n.second;
247 }
248 return NAN;
249 }
250 };
251
252 bool force_unmerged = false;
253 std::string title;
254 std::vector<std::string> history = { "From gemmi-cif2mtz " GEMMI_VERSION };
255 double wavelength = NAN;
256 std::vector<std::string> spec_lines;
257
258 Mtz convert_block_to_mtz(const ReflnBlock& rb, Logger& logger) const {
259 Mtz mtz;
260 mtz.title = title.empty() ? "Converted from mmCIF block " + rb.block.name : title;
261 if (!history.empty()) {
262 mtz.history.reserve(mtz.history.size() + history.size());
263 mtz.history.insert(mtz.history.end(), history.begin(), history.end());
264 }
265 mtz.cell = rb.cell;
266 mtz.spacegroup = rb.spacegroup;
267 mtz.add_dataset("HKL_base");
268
269 const cif::Loop* loop = rb.refln_loop ? rb.refln_loop : rb.diffrn_refln_loop;
270 if (!loop)
271 fail("_refln category not found in mmCIF block: " + rb.block.name);
272 bool unmerged = force_unmerged || !rb.refln_loop;
273
274 if (!unmerged) {
275 Mtz::Dataset& ds = mtz.add_dataset("unknown");
276 if (!std::isnan(wavelength))
277 ds.wavelength = wavelength;
278 else if (rb.wavelength_count > 1)
279 logger.note("ignoring wavelengths, ", rb.wavelength_count,
280 " are present in block ", rb.block.name, '.');
281 else
282 ds.wavelength = rb.wavelength;
283 }
284
285 logger.level<7>("Searching tags with known MTZ equivalents ...");
286 std::vector<int> indices;
287 std::vector<const Entry*> entries; // used for code_to_number only
288 std::string tag = loop->tags[0];
289 const size_t tag_offset = rb.tag_offset();
290 // NOLINTNEXTLINE(readability-suspicious-call-argument): clang-tidy finds npos suspicious
291 auto set_tag = [&](const std::string& t) { tag.replace(tag_offset, std::string::npos, t); };
292
293 std::vector<Entry> spec_entries;
294 if (!spec_lines.empty()) {
295 spec_entries.reserve(spec_lines.size());
296 for (const std::string& line : spec_lines)
297 spec_entries.emplace_back(line);
298 } else {
299 const char** line = default_spec(!unmerged);
300 for (; *line != nullptr; ++line)
301 spec_entries.emplace_back(*line);
302 }
303
304 // always start with H, K, L
305 set_tag("index_h");
306 for (char c : {'h', 'k', 'l'}) {
307 tag.back() = c;
308 int index = loop->find_tag(tag);
309 if (index == -1)
310 fail("Miller index tag not found: " + tag);
311 indices.push_back(index);
312 entries.push_back(nullptr);
313 auto col = mtz.columns.emplace(mtz.columns.end());
314 col->dataset_id = 0;
315 col->type = 'H';
316 col->label = alpha_up(c);
317 }
318
319 // M/ISYM and BATCH
320 if (unmerged) {
321 auto col = mtz.columns.emplace(mtz.columns.end());
322 col->dataset_id = 0;
323 col->type = 'Y';
324 col->label = "M/ISYM";
325
326 col = mtz.columns.emplace(mtz.columns.end());
327 col->dataset_id = 0;
328 col->type = 'B';
329 col->label = "BATCH";
330 }
331
332 // other columns according to the spec
333 bool column_added = false;
334 for (const Entry& entry : spec_entries) {
335 set_tag(entry.refln_tag);
336 int index = loop->find_tag(tag);
337 if (index == -1)
338 continue;
339 if (mtz.column_with_label(entry.col_label))
340 continue;
341 column_added = true;
342 indices.push_back(index);
343 entries.push_back(entry.code_to_number.empty() ? nullptr : &entry);
344 auto col = mtz.columns.emplace(mtz.columns.end());
345 // dataset_id is meaningless in unmerged MTZ files
346 col->dataset_id = unmerged ? 0 : entry.dataset_id;
347 col->type = entry.col_type;
348 col->label = entry.col_label;
349 logger.level<7>(" ", tag, " -> ", col->label);
350 }
351 if (!column_added)
352 fail(force_unmerged ? "Unmerged d" : "D", "ata not found in block ", rb.block.name);
353
354 for (size_t i = 0; i != mtz.columns.size(); ++i) {
355 mtz.columns[i].parent = &mtz;
356 mtz.columns[i].idx = i;
357 }
358 mtz.nreflections = (int) loop->length();
359
360 std::unique_ptr<UnmergedHklMover> hkl_mover;
361 struct BatchInfo {
362 int sweep_id;
363 int frame_id;
364 };
365 std::vector<BatchInfo> batch_nums;
366 if (unmerged) {
367 hkl_mover.reset(new UnmergedHklMover(mtz.spacegroup));
368 set_tag("diffrn_id");
369 int sweep_id_index = loop->find_tag(tag);
370 if (sweep_id_index == -1)
371 logger.level<7>("No diffrn_id. Assuming a single sweep.");
372 set_tag("pdbx_image_id");
373 int image_id_index = loop->find_tag(tag);
374 if (logger.threshold >= 7) {
375 if (image_id_index == -1)
376 logger.mesg("No pdbx_image_id, setting BATCH to a dummy value.");
377 else
378 logger.mesg(" ", tag, " & diffrn_id -> BATCH");
379 }
380 struct SweepInfo {
381 std::set<int> frame_ids;
382 int offset;
383 float wavelength = 0.f;
384 std::string crystal_id = "unknown";
385 int dataset_id;
386 };
387 std::map<int, SweepInfo> sweeps;
388 cif::Block& block = const_cast<cif::Block&>(rb.block);
389 cif::Table tab_w0 = block.find("_diffrn.", {"id", "crystal_id"});
390 cif::Table tab_w1 = block.find("_diffrn_radiation.",
391 {"diffrn_id", "wavelength_id"});
392 cif::Table tab_w2 = block.find("_diffrn_radiation_wavelength.",
393 {"id", "wavelength"});
394 // store sweep and frame numbers corresponding to reflections
395 batch_nums.reserve(loop->length());
396 for (size_t i = 0; i < loop->values.size(); i += loop->tags.size()) {
397 int sweep_id = 1;
398 if (sweep_id_index >= 0)
400 int frame = 1;
401 if (image_id_index >= 0) {
402 double d = cif::as_number(loop->values[i + image_id_index], 1.);
403 frame = (int) std::ceil(d);
404 }
405 batch_nums.push_back({sweep_id, frame});
406 if (frame >= 0) {
408 // if new sweep was added - try to set crystal_id and wavelength
409 if (sweep.frame_ids.empty() && sweep_id_index >= 0) {
410 const std::string& sweep_str = loop->values[i + sweep_id_index];
411 try {
412 sweep.crystal_id = tab_w0.find_row(sweep_str).str(1);
413 } catch(std::exception&) {}
414 try {
415 const std::string& wave_id = tab_w1.find_row(sweep_str)[1];
416 const std::string& wavelen = tab_w2.find_row(wave_id)[1];
417 sweep.wavelength = (float) cif::as_number(wavelen, 0.);
418 } catch(std::exception&) {}
419 }
420 sweep.frame_ids.insert(frame);
421 }
422 }
423
424 // add datasets and set SweepInfo::dataset_id
425 for (auto& sweep_pair : sweeps) {
426 SweepInfo& si = sweep_pair.second;
427 Mtz::Dataset* ds = mtz.dataset_with_name(si.crystal_id);
428 if (ds == nullptr) {
429 ds = &mtz.add_dataset(si.crystal_id);
430 // both crystal name and dataset name are set to crystal_id
431 ds->project_name = "unknown";
432 ds->wavelength = si.wavelength;
433 }
434 si.dataset_id = ds->id;
435 }
436
437 // determine offset that makes frame numbers unique
438 int cap = 0;
439 for (auto& it : sweeps) {
440 it.second.offset = cap;
441 cap += *--it.second.frame_ids.end() + 1100;
442 cap -= cap % 1000;
443 }
444 // add offset to BatchInfo::frame_id
445 for (BatchInfo& p : batch_nums)
446 if (p.sweep_id >= 0 && p.frame_id >= 0)
447 p.frame_id += sweeps.at(p.sweep_id).offset;
448
449 // add MTZ batches
450 for (const auto& sweep_pair : sweeps) {
451 const SweepInfo& sweep_info = sweep_pair.second;
453 batch.set_dataset_id(sweep_info.dataset_id);
454 batch.set_cell(mtz.cell);
455 batch.set_wavelength(sweep_info.wavelength);
456 // FIXME should we set more properties in BATCH header?
457 int min_frame = *sweep_info.frame_ids.begin();
458 int max_frame = *--sweep_info.frame_ids.end();
459 if (2 * sweep_info.frame_ids.size() > size_t(max_frame - min_frame)) {
460 // probably consecutive range, even if some frames are missing
461 for (int n = min_frame; n <= max_frame; ++n) {
462 batch.number = n + sweep_info.offset;
463 mtz.batches.push_back(batch);
464 }
465 } else {
466 for (int n : sweep_info.frame_ids) {
467 batch.number = n + sweep_info.offset;
468 mtz.batches.push_back(batch);
469 }
470 }
471 }
472 } // - if (unmerged)
473
474 // fill in the data
475 mtz.data.resize(mtz.columns.size() * mtz.nreflections);
476 size_t k = 0, row = 0;
477 for (size_t i = 0; i < loop->values.size(); i += loop->tags.size()) {
478 if (unmerged) {
479 std::array<int, 3> hkl;
480 for (size_t ii = 0; ii != 3; ++ii)
481 hkl[ii] = cif::as_int(loop->values[i + indices[ii]]);
482 int isym = hkl_mover->move_to_asu(hkl);
483 for (size_t j = 0; j != 3; ++j)
484 mtz.data[k++] = (float) hkl[j];
485 mtz.data[k++] = (float) isym;
486 mtz.data[k++] = batch_nums.empty() ? 1.f : (float) batch_nums[row++].frame_id;
487 } else {
488 for (size_t j = 0; j != 3; ++j)
489 mtz.data[k++] = (float) cif::as_int(loop->values[i + indices[j]]);
490 }
491 for (size_t j = 3; j != indices.size(); ++j) {
492 const std::string& v = loop->values[i + indices[j]];
493 if (cif::is_null(v)) {
494 mtz.data[k] = (float) NAN;
495 } else if (entries[j] != nullptr) {
496 mtz.data[k] = entries[j]->translate_code_to_number(v);
497 } else {
498 mtz.data[k] = (float) cif::as_number(v);
499 if (std::isnan(mtz.data[k]))
500 logger.mesg("Value #", i + indices[j], " in the loop is not a number: ", v);
501 }
502 ++k;
503 }
504 }
505 return mtz;
506 }
507
510 *rb.refln_loop = transcript_old_anomalous_to_standard(*rb.refln_loop, rb.spacegroup);
511 Mtz mtz = convert_block_to_mtz(rb, logger);
512 if (mtz.is_merged() && mode == 'a') {
514 if (type_unique.first == DataType::Anomalous) {
516 logger.note("data in ", rb.block.name,
517 " is read as \"old-style\" anomalous (", rb.refln_loop->length(),
518 " -> ", type_unique.second, " rows).");
519 *rb.refln_loop = transcript_old_anomalous_to_standard(*rb.refln_loop,
520 rb.spacegroup);
521 // this is rare, so it's OK to run the conversion twice
522 mtz = convert_block_to_mtz(rb, logger);
523 } else {
524 logger.err("in ", rb.block.name, ", out of ",
525 rb.refln_loop->length(), " HKLs, only ", type_unique.second,
526 " are unique under symmetry; the rest are equivalent to Friedel mates");
527 }
528 } else if (type_unique.first == DataType::Unmerged) {
529 logger.err("in ", rb.block.name, ", out of ",
530 rb.refln_loop->length(), " HKLs, only ", type_unique.second,
531 " are unique under symmetry");
533 logger.mesg("Possibly unmerged data - you may use option --refln-to=unmerged");
534 }
535 }
536 return mtz;
537 }
538
539private:
540 static float status_to_freeflag(const std::string& str) {
541 char c = str[0];
542 if (c == '\'' || c == '"')
543 c = str[1];
544 if (c == 'o')
545 return 1.f;
546 if (c == 'f')
547 return 0.f;
548 return NAN;
549 }
550};
551
552} // namespace gemmi
553#endif
struct Document that represents the CIF file (but can also be read from a different representation,...
fail(), unreachable() and __declspec/__attribute__ macros
Class Intensities that reads multi-record data from MTZ, mmCIF or XDS_ASCII and merges it into mean o...
Logger - a tiny utility for passing messages through a callback.
MTZ reflection file format.
double as_number(const std::string &s, double nan=NAN)
Definition numb.hpp:19
std::string as_string(const std::string &value)
Definition cifdoc.hpp:80
bool is_null(const std::string &value)
Definition cifdoc.hpp:76
int as_int(const std::string &str)
Definition cifdoc.hpp:107
const SpaceGroup & get_spacegroup_p1()
Definition symmetry.hpp:893
std::array< int, 3 > Miller
A synonym for convenient passing of hkl.
Definition unitcell.hpp:129
std::pair< DataType, size_t > check_data_type_under_symmetry(const DataProxy &proxy)
Definition intensit.hpp:212
void split_str_into_multi(const std::string &str, const char *seps, std::vector< std::string > &result)
Definition util.hpp:165
cif::Loop transcript_old_anomalous_to_standard(const cif::Loop &loop, const SpaceGroup *sg)
Before _refln.pdbx_F_plus/minus was introduced, anomalous data was stored as two F_meas_au reflection...
Definition cif2mtz.hpp:53
bool possible_old_style(const ReflnBlock &rb, DataType data_type)
Definition cif2mtz.hpp:24
void fail(const std::string &msg)
Definition fail.hpp:59
std::vector< std::string > split_str(const std::string &str, S sep)
Definition util.hpp:157
constexpr int ialpha4_id(const char *s)
Definition util.hpp:306
char alpha_up(char c)
Definition util.hpp:61
Utilities for parsing CIF numbers (the CIF spec calls them 'numb').
Reads reflection data from the mmCIF format.
float translate_code_to_number(const std::string &v) const
Definition cif2mtz.hpp:237
std::vector< std::pair< std::string, float > > code_to_number
Definition cif2mtz.hpp:200
Entry(const std::string &line)
Definition cif2mtz.hpp:202
std::string refln_tag
Definition cif2mtz.hpp:196
std::string col_label
Definition cif2mtz.hpp:197
std::string title
Definition cif2mtz.hpp:253
std::vector< std::string > spec_lines
Definition cif2mtz.hpp:256
Mtz auto_convert_block_to_mtz(ReflnBlock &rb, Logger &logger, char mode) const
Definition cif2mtz.hpp:508
static const char ** default_spec(bool for_merged)
Definition cif2mtz.hpp:144
Mtz convert_block_to_mtz(const ReflnBlock &rb, Logger &logger) const
Definition cif2mtz.hpp:258
std::vector< std::string > history
Definition cif2mtz.hpp:254
double wavelength
Definition cif2mtz.hpp:255
Passes messages (including warnings/errors) to a callback function.
Definition logger.hpp:23
void mesg(Args const &... args) const
Send a message without any prefix.
Definition logger.hpp:44
GEMMI_COLD void err(Args const &... args) const
Send a warning/error (see Quirk above).
Definition logger.hpp:49
void level(Args const &... args) const
Send a message without any prefix on with a numeric threshold N.
Definition logger.hpp:36
int threshold
Pass messages of this level and all lower (more severe) levels: 8=all, 6=all but debug,...
Definition logger.hpp:28
void note(Args const &... args) const
Send a note (a notice, a significant message).
Definition logger.hpp:46
std::pair< Op::Miller, bool > to_asu_sign(const Op::Miller &hkl, const GroupOps &gops) const
Similar to to_asu(), but the second returned value is sign: true for + or centric.
Table find(const std::string &prefix, const std::vector< std::string > &tags)
Definition cifdoc.hpp:968
size_t width() const
Definition cifdoc.hpp:141
int find_tag(const std::string &tag) const
Definition cifdoc.hpp:137
std::vector< std::string > tags
Definition cifdoc.hpp:128
std::vector< std::string > values
Definition cifdoc.hpp:129
size_t length() const
Definition cifdoc.hpp:142
int find_tag_lc(const std::string &lctag) const
Definition cifdoc.hpp:132
Version number.
#define GEMMI_VERSION
Definition version.hpp:8