Repository navigation
MDC API
Beyond the possibility of decompressing mdc-compressed files with CLI, it's also possible to access the data from programming language via appropriate API. Before introducing details of specific APIs the general idea is presented. The first thing to do is always open the MDC file via the appropriate module (class in the case of C++, function + struct in the case of C). The handle (object/struct instance) to this file cannot be used directly to perform queries, but instead, it offers a mechanism to obtain another module (query engine) that may be used to query. Multiple query engines may be obtained from a single MDC file handle. To get a query engine, the programmer must provide a list of segment IDs (strings) and a list of atom IDs (ints). Both can be empty (if both are empty, the query engine will select all the atoms from the MDC file). Having query engine programmer may query for a list of frame IDs (resulting frames are in the order of this list). The distinction between a file handler and a query engine is for multithreaded purposes. There should be (usually) a single file handler for a given MDC file, and to perform queries from multiple threads, each thread should have its own query engine.
The C++ API is enclosed in mdc namespace.
There are two main classes:
-
reader- realizes MDC file handler -
query_engine- used to perform queries
-
reader(const std::string& path)- constructor that opens MDC file.In the case of failure, it sets an internal error message that may be checked with theget_current_errormethod -
const std::string& get_current_error() const- returns a current error message; if it's empty, it means there were no errors; in the opposite case, it returns the last error message; errors are never cleared and are considered unrecoverable (using areaderobject that is in erroneous state for anything other than destruction is undefined behavior) -
std::unique_ptr<query_engine> get_query_engine(const std::vector<std::string>& segment_ids, const std::vector<uint32_t>& atom_ids) const;- returns a newquery_engivethat may be used to perform queries for specified segment IDs and atom IDs (1-base indexing). Ifsegment_idsandatom_idsare empty, all atoms are returned from queries.
-
const std::vector<segment_desc_t>& get_segments() const- returns a description of segments in the MDC file; each segment is described by size (in number of atoms), and type (water, ios, etc.); details onsegment_desc_ttype are below -
const std::vector<uint32_t>& get_original_atom_ids() const- allows for remapping the atoms as ordered in the query result to the original numbering -
uint32_t get_no_frames() const- returns the number of frames in MDC file -
bool query(std::span<const uint32_t> frame_ids, query_result& result)- performs a query for atoms specified withget_query_enginefor a given frame IDs; the result of a query is represented asquery_resulttype, and its details are below, in the case of failure returnsfalseand details should be checked withget_current_errormethod -
bool query(std::initializer_list<uint32_t> frame_ids, query_result& result)- same as above, but allows to pass frame IDs asstd::initializer_list -
const std::string& get_current_error() const- returns a current error message; if it's empty, it means there were no errors; in the opposite case, it returns the last error message; errors are never cleared and are considered unrecoverable (using aquery_engineobject that is in erroneous state for anything other than destruction is undefined behavior)
It is a structure with the following fields
-
segment_type_t type- is an enum with segment type (one ofunknown,molecule,ion,water,none -
std::string name- the name of a segment -
uint32_t size- number of atoms in segment
query_result represents the result of a single query. It's a struct (with some private fields related to storage), that contains the following field:
-
std::vector<frame> frames- frames being the result of a query, typeframeis described below. The order of frames is the same as the order of frame IDs in the first parameter of the performedquerymethod.
frame is a struct with the following fields:
-
std::span<atom_coords> coords- coordinates of atoms (atom_coordsis as struct with three floats:x,y,z) -
int step- frame step -
float time- frame time -
float box[3][3]- frame box -
float prec- frame precision
Compilation
TODO
linking
TODO
example (TODO: make it better)
#include <iostream>
#include <string>
#include <fstream>
#include <cassert>
#include "mdc_reader.h"
struct Params
{
std::string input_file;
std::string output_file;
};
void help(char* prog)
{
printf("Usage: %s -i input_file -o output_file\n", prog);
}
bool parse_args(int argc, char* argv[], Params& params)
{
for (int i = 1; i < argc; i++)
{
if (std::string(argv[i]) == "-i")
{
if (i + 1 < argc)
{
params.input_file = argv[i + 1];
i++;
}
else
{
std::cout << "Error: -i option requires one argument" << std::endl;
help(argv[0]);
return false;
}
}
else if (std::string(argv[i]) == "-o")
{
if (i + 1 < argc)
{
params.output_file = argv[i + 1];
i++;
}
else
{
std::cout << "Error: -o option requires one argument" << std::endl;
help(argv[0]);
return false;
}
}
else
{
std::cout << "Error: unknown option " << argv[i] << std::endl;
help(argv[0]);
return false;
}
}
if (params.input_file.empty())
{
printf("Error: -i is required\n");
help(argv[0]);
return false;
}
if (params.output_file.empty())
{
printf("Error: -o is required\n");
help(argv[0]);
return false;
}
return true;
}
int main(int argc, char* argv[])
{
Params params;
if (!parse_args(argc, argv, params))
return 1;
mdc::reader reader(params.input_file);
if (const auto& err = reader.get_current_error(); !err.empty())
{
std::cerr << err;
return 1;
}
//query for all segments and all atoms
auto query_engine_ptr = reader.get_query_engine({},{});
auto& query_engine = *query_engine_ptr;
if (const auto& err = query_engine.get_current_error(); !err.empty())
{
std::cerr << err;
return 1;
}
uint32_t no_segments = query_engine.get_segments().size();
uint32_t no_frames = query_engine.get_no_frames();
std::ofstream output(params.output_file, std::ios::binary);
if (!output)
{
std::cerr << "Error: Cannot open output file\n";
return 1;
}
mdc::query_result result;
for (uint32_t frame_id = 0; frame_id < no_frames; frame_id++)
{
int tot_atoms = 0;
for (uint32_t segment_id = 0; segment_id < no_segments; segment_id++)
{
if (!query_engine.query({ frame_id }, result))
{
std::cerr << "Error: " << query_engine.get_current_error();
return 1;
}
assert(result.frames.size() == 1);
auto coords = result.frames[0].coords;
auto n_atoms = coords.size();
tot_atoms += n_atoms;
for(const auto & coord : coords)
{
//warning: this is not endian aware
output.write(reinterpret_cast<const char*>(&coord.x), sizeof(coord.x));
output.write(reinterpret_cast<const char*>(&coord.y), sizeof(coord.y));
output.write(reinterpret_cast<const char*>(&coord.z), sizeof(coord.y));
}
}
std::cerr << "Frame time: " << result.frames[0].time << "\tNo. atoms: " << tot_atoms << "\n";
}
return 0;
}