Build Packages / Unit tests (push) Skipped
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 8m34s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 10m0s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 10m23s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 10m23s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 11m16s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 11m49s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 8m32s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 9m15s
Build Packages / XDS test (durin plugin) (push) Successful in 7m16s
Build Packages / Generate python client (push) Successful in 16s
Build Packages / build:rpm (rocky9) (push) Successful in 10m12s
Build Packages / Create release (push) Skipped
Build Packages / Build documentation (push) Successful in 47s
Build Packages / DIALS test (push) Successful in 10m18s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 5m46s
Build Packages / build:rpm (rocky8) (push) Successful in 1h41m2s
Build Packages / XDS test (neggia plugin) (push) Successful in 1h59m18s
This is an UNSTABLE release. The release has significant modifications for data processing - in case of troubles go back to 1.0.0-rc.144. jfjoch_process: Generate a dedicated file (_process.h5), which can be used as a replacement for the _master.h5 file for a reanalyzed dataset. jfjoch_process: Improve the performance of scaling and merging, implement on the fly scaling. jfjoch_writer: All final data analysis results are repopulated in the _master.h5 file. jfjoch_scale: Dedicated tool for rescaling/merging existing data. jfjoch_viewer: Fix bugs where pixel labels where displayed on a wrong pixel. WARNING! Scaling and merging are experimental at the moment, and may not provide reasonable results for the time being. Reviewed-on: #56
601 lines
19 KiB
C++
601 lines
19 KiB
C++
// Copyright 2019 Global Phasing Ltd.
|
|
//
|
|
// MTZ reflection file format.
|
|
|
|
#ifndef GEMMI_MTZ_HPP_
|
|
#define GEMMI_MTZ_HPP_
|
|
|
|
#include <cassert>
|
|
#include <cmath> // for isnan
|
|
#include <cstdint> // for int32_t
|
|
#include <algorithm> // for copy
|
|
#include <array>
|
|
#include <initializer_list>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "fail.hpp" // for fail
|
|
#include "input.hpp" // for AnyStream, FileStream, CharArray
|
|
#include "iterator.hpp" // for StrideIter
|
|
#include "logger.hpp" // for Logger
|
|
#include "math.hpp" // for rad, Mat33
|
|
#include "symmetry.hpp" // for find_spacegroup_by_name, SpaceGroup
|
|
#include "unitcell.hpp" // for UnitCell
|
|
#include "util.hpp" // for ialpha4_id, rtrim_str, ialpha3_id, ...
|
|
|
|
namespace gemmi {
|
|
|
|
// Unmerged MTZ files always store in-asu hkl indices and symmetry operation
|
|
// encoded in the M/ISYM column. Here is a helper for writing such files.
|
|
struct UnmergedHklMover {
|
|
UnmergedHklMover(const SpaceGroup* spacegroup) : asu_(spacegroup) {
|
|
if (spacegroup)
|
|
group_ops_ = spacegroup->operations();
|
|
}
|
|
|
|
// Modifies hkl and returns ISYM value for M/ISYM
|
|
int move_to_asu(std::array<int, 3>& hkl) {
|
|
std::pair<Miller, int> hkl_isym = asu_.to_asu(hkl, group_ops_);
|
|
hkl = hkl_isym.first;
|
|
return hkl_isym.second;
|
|
}
|
|
|
|
private:
|
|
ReciprocalAsu asu_;
|
|
GroupOps group_ops_;
|
|
};
|
|
|
|
struct MtzMetadata {
|
|
std::string source_path; // input file path, if known
|
|
bool same_byte_order = true;
|
|
bool indices_switched_to_original = false;
|
|
std::int64_t header_offset = 0;
|
|
std::string version_stamp;
|
|
std::string title;
|
|
int nreflections = 0;
|
|
std::array<int, 5> sort_order = {};
|
|
double min_1_d2 = NAN;
|
|
double max_1_d2 = NAN;
|
|
float valm = NAN;
|
|
int nsymop = 0;
|
|
UnitCell cell;
|
|
int spacegroup_number = 0;
|
|
std::string spacegroup_name;
|
|
std::vector<Op> symops;
|
|
const SpaceGroup* spacegroup = nullptr;
|
|
std::vector<std::string> history;
|
|
std::string appended_text;
|
|
// used to report non-critical problems when reading a file (also used in mtz2cif)
|
|
Logger logger;
|
|
};
|
|
|
|
struct GEMMI_DLL Mtz : public MtzMetadata {
|
|
struct Dataset {
|
|
int id;
|
|
std::string project_name;
|
|
std::string crystal_name;
|
|
std::string dataset_name;
|
|
UnitCell cell;
|
|
double wavelength; // 0 means not set
|
|
};
|
|
|
|
struct Column {
|
|
int dataset_id;
|
|
char type;
|
|
std::string label;
|
|
float min_value = NAN;
|
|
float max_value = NAN;
|
|
std::string source; // from COLSRC
|
|
Mtz* parent;
|
|
std::size_t idx;
|
|
|
|
Dataset& dataset() { return parent->dataset(dataset_id); }
|
|
const Dataset& dataset() const { return parent->dataset(dataset_id); }
|
|
bool has_data() const { return parent->has_data(); }
|
|
int size() const { return has_data() ? parent->nreflections : 0; }
|
|
size_t stride() const { return parent->columns.size(); }
|
|
float& operator[](std::size_t n) { return parent->data[idx + n * stride()]; }
|
|
float operator[](std::size_t n) const { return parent->data[idx + n * stride()]; }
|
|
float& at(std::size_t n) { return parent->data.at(idx + n * stride()); }
|
|
float at(std::size_t n) const { return parent->data.at(idx + n * stride()); }
|
|
bool is_integer() const {
|
|
return type == 'H' || type == 'B' || type == 'Y' || type == 'I';
|
|
}
|
|
|
|
const Column* get_next_column_if_type(char next_type) const {
|
|
if (idx + 1 < parent->columns.size()) {
|
|
const Column& next_col = parent->columns[idx + 1];
|
|
if (next_col.dataset_id == dataset_id && next_col.type == next_type)
|
|
return &next_col;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
using iterator = StrideIter<float>;
|
|
iterator begin() {
|
|
assert(parent);
|
|
assert(&parent->columns[idx] == this);
|
|
return iterator({parent->data.data(), idx, stride()});
|
|
}
|
|
iterator end() {
|
|
return iterator({parent->data.data() + parent->data.size(), idx,
|
|
stride()});
|
|
}
|
|
using const_iterator = StrideIter<const float>;
|
|
const_iterator begin() const { return const_cast<Column*>(this)->begin(); }
|
|
const_iterator end() const { return const_cast<Column*>(this)->end(); }
|
|
};
|
|
|
|
struct Batch {
|
|
Batch() {
|
|
ints.resize(29, 0);
|
|
floats.resize(156, 0.);
|
|
// write the same values that are written by CCP4 progs such as COMBAT
|
|
ints[0] = 29 + 156;
|
|
ints[1] = 29;
|
|
ints[2] = 156;
|
|
// COMBAT sets BSCALE=1, but Pointless sets it to 0.
|
|
//floats[43] = 1.f; // batch scale
|
|
}
|
|
int number = 0;
|
|
std::string title;
|
|
std::vector<int> ints;
|
|
std::vector<float> floats;
|
|
std::vector<std::string> axes;
|
|
|
|
UnitCell get_cell() const {
|
|
return UnitCell(floats[0], floats[1], floats[2],
|
|
floats[3], floats[4], floats[5]);
|
|
}
|
|
void set_cell(const UnitCell& uc) {
|
|
floats[0] = (float) uc.a;
|
|
floats[1] = (float) uc.b;
|
|
floats[2] = (float) uc.c;
|
|
floats[3] = (float) uc.alpha;
|
|
floats[4] = (float) uc.beta;
|
|
floats[5] = (float) uc.gamma;
|
|
}
|
|
|
|
int dataset_id() const { return ints[20]; }
|
|
void set_dataset_id(int id) { ints[20] = id; }
|
|
float wavelength() const { return floats[86]; }
|
|
void set_wavelength(float lambda) { floats[86] = lambda; }
|
|
float phi_start() const { return floats[36]; }
|
|
float phi_end() const { return floats[37]; }
|
|
Mat33 matrix_U() const {
|
|
return Mat33(floats[6], floats[9], floats[12],
|
|
floats[7], floats[10], floats[13],
|
|
floats[8], floats[11], floats[14]);
|
|
}
|
|
};
|
|
|
|
std::vector<Dataset> datasets;
|
|
std::vector<Column> columns;
|
|
std::vector<Batch> batches;
|
|
std::vector<float> data;
|
|
|
|
explicit Mtz(bool with_base=false) {
|
|
if (with_base)
|
|
add_base();
|
|
}
|
|
Mtz(Mtz&& o) noexcept { *this = std::move(o); }
|
|
Mtz& operator=(Mtz&& o) noexcept {
|
|
MtzMetadata::operator=(std::move(o));
|
|
datasets = std::move(o.datasets);
|
|
columns = std::move(o.columns);
|
|
batches = std::move(o.batches);
|
|
data = std::move(o.data);
|
|
for (Mtz::Column& col : columns)
|
|
col.parent = this;
|
|
return *this;
|
|
}
|
|
|
|
// explicit to be aware where we make copies
|
|
explicit Mtz(const Mtz& o) : MtzMetadata(o) {
|
|
datasets = o.datasets;
|
|
columns = o.columns;
|
|
batches = o.batches;
|
|
data = o.data;
|
|
for (Mtz::Column& col : columns)
|
|
col.parent = this;
|
|
}
|
|
|
|
Mtz& operator=(Mtz const&) = delete;
|
|
|
|
void add_base() {
|
|
datasets.push_back({0, "HKL_base", "HKL_base", "HKL_base", cell, 0.});
|
|
for (int i = 0; i != 3; ++i)
|
|
add_column(std::string(1, "HKL"[i]), 'H', 0, i, false);
|
|
}
|
|
|
|
// Functions to use after MTZ headers (and data) is read.
|
|
|
|
double resolution_high() const { return std::sqrt(1.0 / max_1_d2); }
|
|
double resolution_low() const { return std::sqrt(1.0 / min_1_d2); }
|
|
|
|
UnitCell& get_cell(int dataset=-1) {
|
|
for (Dataset& ds : datasets)
|
|
if (ds.id == dataset && ds.cell.is_crystal() && ds.cell.a > 0)
|
|
return ds.cell;
|
|
return cell;
|
|
}
|
|
|
|
const UnitCell& get_cell(int dataset=-1) const {
|
|
return const_cast<Mtz*>(this)->get_cell(dataset);
|
|
}
|
|
|
|
void set_cell_for_all(const UnitCell& new_cell) {
|
|
cell = new_cell;
|
|
cell.set_cell_images_from_spacegroup(spacegroup); // probably not needed
|
|
for (Dataset& ds : datasets)
|
|
ds.cell = cell;
|
|
}
|
|
|
|
UnitCellParameters get_average_cell_from_batch_headers(double* rmsd) const;
|
|
|
|
void set_spacegroup(const SpaceGroup* new_sg) {
|
|
spacegroup = new_sg;
|
|
spacegroup_number = new_sg ? spacegroup->ccp4 : 0;
|
|
spacegroup_name = new_sg ? spacegroup->hm : "";
|
|
}
|
|
|
|
Dataset& last_dataset() {
|
|
if (datasets.empty())
|
|
fail("MTZ dataset not found (missing DATASET header line?).");
|
|
return datasets.back();
|
|
}
|
|
|
|
Dataset& dataset(int id) {
|
|
if ((size_t)id < datasets.size() && datasets[id].id == id)
|
|
return datasets[id];
|
|
for (Dataset& d : datasets)
|
|
if (d.id == id)
|
|
return d;
|
|
fail("MTZ file has no dataset with ID " + std::to_string(id));
|
|
}
|
|
const Dataset& dataset(int id) const {
|
|
return const_cast<Mtz*>(this)->dataset(id);
|
|
}
|
|
|
|
Dataset* dataset_with_name(const std::string& name) {
|
|
for (Dataset& d : datasets)
|
|
if (d.dataset_name == name)
|
|
return &d;
|
|
return nullptr;
|
|
}
|
|
const Dataset* dataset_with_name(const std::string& label) const {
|
|
return const_cast<Mtz*>(this)->dataset_with_name(label);
|
|
}
|
|
|
|
int count(const std::string& label) const {
|
|
int n = 0;
|
|
for (const Column& col : columns)
|
|
if (col.label == label)
|
|
++n;
|
|
return n;
|
|
}
|
|
|
|
int count_type(char type) const {
|
|
int n = 0;
|
|
for (const Column& col : columns)
|
|
if (col.type == type)
|
|
++n;
|
|
return n;
|
|
}
|
|
|
|
Column* column_with_label(const std::string& label, const Dataset* ds=nullptr, char type='*') {
|
|
for (Column& col : columns)
|
|
if (col.label == label && (!ds || ds->id == col.dataset_id)
|
|
&& (type == '*' || type == col.type))
|
|
return &col;
|
|
return nullptr;
|
|
}
|
|
const Column* column_with_label(const std::string& label, const Dataset* ds=nullptr,
|
|
char type='*') const {
|
|
return const_cast<Mtz*>(this)->column_with_label(label, ds, type);
|
|
}
|
|
|
|
const Column& get_column_with_label(const std::string& label, const Dataset* ds=nullptr) const {
|
|
if (const Column* col = column_with_label(label, ds))
|
|
return *col;
|
|
fail("Column label not found: " + label);
|
|
}
|
|
|
|
std::vector<const Column*> columns_with_type(char type) const {
|
|
std::vector<const Column*> cols;
|
|
for (const Column& col : columns)
|
|
if (col.type == type)
|
|
cols.push_back(&col);
|
|
return cols;
|
|
}
|
|
|
|
std::vector<int> positions_of_columns_with_type(char col_type) const {
|
|
std::vector<int> cols;
|
|
for (int i = 0; i < (int) columns.size(); ++i)
|
|
if (columns[i].type == col_type)
|
|
cols.push_back(i);
|
|
return cols;
|
|
}
|
|
|
|
// F(+)/(-) pairs should have type G (and L for sigma),
|
|
// I(+)/(-) -- K (M for sigma), but E(+)/(-) has no special column type,
|
|
// so here we use column labels not types.
|
|
std::vector<std::pair<int,int>> positions_of_plus_minus_columns() const {
|
|
std::vector<std::pair<int,int>> r;
|
|
for (int i = 0; i < (int) columns.size(); ++i) {
|
|
const Column& col = columns[i];
|
|
size_t sign_pos = col.label.find("(+)");
|
|
if (sign_pos != std::string::npos) {
|
|
std::string minus_label = columns[i].label;
|
|
minus_label[sign_pos+1] = '-';
|
|
for (int j = 0; j < (int) columns.size(); ++j)
|
|
if (columns[j].label == minus_label &&
|
|
columns[j].type == col.type &&
|
|
columns[j].dataset_id == col.dataset_id) {
|
|
r.emplace_back(i, j);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
/// the order of labels matters
|
|
const Column* column_with_one_of_labels(std::initializer_list<const char*> labels,
|
|
char type='*') const {
|
|
for (const char* label : labels)
|
|
if (const Column* col = column_with_label(label, nullptr, type))
|
|
return col;
|
|
return nullptr;
|
|
}
|
|
|
|
/// the order of labels doesn't matter
|
|
Column* column_with_type_and_any_of_labels(char type, std::initializer_list<const char*> labels) {
|
|
for (Column& col : columns)
|
|
if (col.type == type) {
|
|
for (const char* label : labels)
|
|
if (col.label == label)
|
|
return &col;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Column* rfree_column() {
|
|
// cf. MtzToCif::default_spec in mtz2cif.hpp
|
|
return column_with_type_and_any_of_labels('I',
|
|
{"FREE", "RFREE", "FREER", "FreeR_flag", "R-free-flags", "FreeRflag", "R_FREE_FLAGS"});
|
|
}
|
|
const Column* rfree_column() const {
|
|
return const_cast<Mtz*>(this)->rfree_column();
|
|
}
|
|
|
|
Column* imean_column() {
|
|
return column_with_type_and_any_of_labels('J', {"IMEAN", "I", "IOBS", "I-obs"});
|
|
}
|
|
const Column* imean_column() const {
|
|
return const_cast<Mtz*>(this)->imean_column();
|
|
}
|
|
|
|
Column* iplus_column() {
|
|
return column_with_type_and_any_of_labels('K', {"I(+)", "IOBS(+)", "I-obs(+)", "Iplus"});
|
|
}
|
|
const Column* iplus_column() const {
|
|
return const_cast<Mtz*>(this)->iplus_column();
|
|
}
|
|
|
|
Column* iminus_column() {
|
|
return column_with_type_and_any_of_labels('K', {"I(-)", "IOBS(-)", "I-obs(-)", "Iminus"});
|
|
}
|
|
const Column* iminus_column() const {
|
|
return const_cast<Mtz*>(this)->iminus_column();
|
|
}
|
|
|
|
bool has_data() const {
|
|
return data.size() == columns.size() * nreflections;
|
|
}
|
|
|
|
bool is_merged() const { return batches.empty(); }
|
|
|
|
/// Calculates min/max for all combinations of reflections and unit cells,
|
|
/// where unit cells are a global CELL and per-dataset DCELL.
|
|
std::array<double,2> calculate_min_max_1_d2() const;
|
|
|
|
void update_reso() {
|
|
std::array<double,2> reso = calculate_min_max_1_d2();
|
|
min_1_d2 = reso[0];
|
|
max_1_d2 = reso[1];
|
|
}
|
|
|
|
// Functions for reading MTZ headers and data.
|
|
|
|
void toggle_endianness() {
|
|
same_byte_order = !same_byte_order;
|
|
swap_eight_bytes(&header_offset);
|
|
}
|
|
|
|
void read_first_bytes(AnyStream& stream);
|
|
|
|
/// read headers until END
|
|
void read_main_headers(AnyStream& stream, std::vector<std::string>* save_headers);
|
|
|
|
/// read the part between END and MTZENDOFHEADERS
|
|
void read_history_and_batch_headers(AnyStream& stream);
|
|
|
|
void setup_spacegroup();
|
|
|
|
void read_raw_data(AnyStream& stream, bool do_read=true);
|
|
|
|
void read_all_headers(AnyStream& stream);
|
|
|
|
void read_stream(AnyStream& stream, bool with_data);
|
|
|
|
void read_file(const std::string& path) {
|
|
try {
|
|
source_path = path;
|
|
FileStream stream(path.c_str(), "rb");
|
|
read_stream(stream, true);
|
|
} catch (std::system_error&) {
|
|
throw; // system_error::what() includes path, don't add anything
|
|
} catch (std::runtime_error& e) {
|
|
fail(std::string(e.what()) + ": " + path);
|
|
}
|
|
}
|
|
|
|
template<typename Input>
|
|
void read_input(Input&& input, bool with_data) {
|
|
source_path = input.path();
|
|
read_stream(*input.create_stream(), with_data);
|
|
}
|
|
|
|
/// the same as read_input(MaybeGzipped(path), with_data)
|
|
void read_file_gz(const std::string& path, bool with_data=true);
|
|
|
|
std::vector<int> sorted_row_indices(int use_first=3) const;
|
|
bool sort(int use_first=3);
|
|
|
|
Miller get_hkl(size_t offset) const {
|
|
return {{(int)data[offset], (int)data[offset+1], (int)data[offset+2]}};
|
|
}
|
|
void set_hkl(size_t offset, const Miller& hkl) {
|
|
for (int i = 0; i != 3; ++i)
|
|
data[offset + i] = static_cast<float>(hkl[i]);
|
|
}
|
|
|
|
/// Returns offset of the first hkl or (size_t)-1. Can be slow.
|
|
size_t find_offset_of_hkl(const Miller& hkl, size_t start=0) const;
|
|
|
|
/// (for merged MTZ only) change HKL to ASU equivalent, adjust phases, etc
|
|
void ensure_asu(bool tnt_asu=false);
|
|
|
|
/// Reindex data, usually followed by ensure_asu(). Outputs messages through logger.
|
|
void reindex(const Op& op);
|
|
|
|
/// Change symmetry to P1 and expand reflections. Does not sort.
|
|
/// Similar to command EXPAND in SFTOOLS.
|
|
void expand_to_p1();
|
|
|
|
/// (for unmerged MTZ only) change HKL according to M/ISYM
|
|
bool switch_to_original_hkl();
|
|
|
|
/// (for unmerged MTZ only) change HKL to ASU equivalent and set ISYM
|
|
bool switch_to_asu_hkl();
|
|
|
|
Dataset& add_dataset(const std::string& name) {
|
|
int id = 0;
|
|
for (const Dataset& d : datasets)
|
|
if (d.id >= id)
|
|
id = d.id + 1;
|
|
datasets.push_back({id, name, name, name, cell, 0.0});
|
|
return datasets.back();
|
|
}
|
|
|
|
Column& add_column(const std::string& label, char type,
|
|
int dataset_id, int pos, bool expand_data);
|
|
|
|
// extra_col are columns right after src_col that are also copied.
|
|
Column& replace_column(size_t dest_idx, const Column& src_col,
|
|
const std::vector<std::string>& trailing_cols={});
|
|
|
|
// If dest_idx < 0 - columns are appended at the end
|
|
// append new column(s), otherwise overwrite existing ones.
|
|
Column& copy_column(int dest_idx, const Column& src_col,
|
|
const std::vector<std::string>& trailing_cols={});
|
|
|
|
void remove_column(size_t idx);
|
|
|
|
template <typename Func>
|
|
void remove_rows_if(Func condition) {
|
|
if (!has_data())
|
|
fail("No data.");
|
|
auto out = data.begin();
|
|
size_t width = columns.size();
|
|
for (auto r = data.begin(); r < data.end(); r += width)
|
|
if (!condition(&*r)) {
|
|
if (r != out)
|
|
std::copy(r, r + width, out);
|
|
out += width;
|
|
}
|
|
data.erase(out, data.end());
|
|
nreflections = int(data.size() / width);
|
|
}
|
|
|
|
void expand_data_rows(size_t added, int pos_=-1) {
|
|
size_t old_row_size = columns.size() - added;
|
|
if (data.size() != old_row_size * nreflections)
|
|
fail("Internal error");
|
|
size_t pos = pos_ == -1 ? old_row_size : (size_t) pos_;
|
|
if (pos > old_row_size)
|
|
fail("expand_data_rows(): pos out of range");
|
|
vector_insert_columns(data, old_row_size, (size_t)nreflections, added, pos, NAN);
|
|
}
|
|
|
|
void set_data(const float* new_data, size_t n) {
|
|
size_t ncols = columns.size();
|
|
if (n % ncols != 0)
|
|
fail("Mtz.set_data(): expected " + std::to_string(ncols) + " columns.");
|
|
nreflections = int(n / ncols);
|
|
data.assign(new_data, new_data + n);
|
|
}
|
|
|
|
// Function for writing MTZ file
|
|
void write_to_cstream(std::FILE* stream) const;
|
|
void write_to_string(std::string& str) const;
|
|
void write_to_file(const std::string& path) const;
|
|
size_t size_to_write() const;
|
|
size_t write_to_buffer(char* buf, size_t maxlen) const;
|
|
|
|
private:
|
|
template<typename Write> void write_to_stream(Write write) const;
|
|
};
|
|
|
|
|
|
inline Mtz read_mtz_file(const std::string& path) {
|
|
Mtz mtz;
|
|
mtz.read_file(path);
|
|
return mtz;
|
|
}
|
|
|
|
template<typename Input>
|
|
Mtz read_mtz(Input&& input, bool with_data) {
|
|
Mtz mtz;
|
|
mtz.read_input(std::forward<Input>(input), with_data);
|
|
return mtz;
|
|
}
|
|
|
|
// Abstraction of data source, cf. ReflnDataProxy.
|
|
struct MtzDataProxy {
|
|
const Mtz& mtz_;
|
|
size_t stride() const { return mtz_.columns.size(); }
|
|
size_t size() const { return mtz_.data.size(); }
|
|
using num_type = float;
|
|
float get_num(size_t n) const { return mtz_.data[n]; }
|
|
const UnitCell& unit_cell() const { return mtz_.cell; }
|
|
const SpaceGroup* spacegroup() const { return mtz_.spacegroup; }
|
|
Miller get_hkl(size_t offset) const { return mtz_.get_hkl(offset); }
|
|
|
|
size_t column_index(const std::string& label) const {
|
|
if (const Mtz::Column* col = mtz_.column_with_label(label))
|
|
return col->idx;
|
|
fail("MTZ file has no column with label: " + label);
|
|
}
|
|
};
|
|
|
|
// Like above, but here the data is stored outside of the Mtz class
|
|
struct MtzExternalDataProxy : MtzDataProxy {
|
|
const float* data_;
|
|
MtzExternalDataProxy(const Mtz& mtz, const float* data)
|
|
: MtzDataProxy{mtz}, data_(data) {}
|
|
size_t size() const { return mtz_.columns.size() * mtz_.nreflections; }
|
|
float get_num(size_t n) const { return data_[n]; }
|
|
Miller get_hkl(size_t offset) const {
|
|
return {{(int)data_[offset + 0],
|
|
(int)data_[offset + 1],
|
|
(int)data_[offset + 2]}};
|
|
}
|
|
};
|
|
|
|
inline MtzDataProxy data_proxy(const Mtz& mtz) { return {mtz}; }
|
|
|
|
} // namespace gemmi
|
|
|
|
#endif
|