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