votca 2024.2-dev
Loading...
Searching...
No Matches
h5mdtrajectoryreader.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18// Standard includes
19#include <memory>
20#include <string>
21#include <vector>
22
23// Third party includes
24#include <hdf5.h>
25
26#include <boost/regex.hpp>
27
28// Local private VOTCA includes
30
31namespace votca {
32namespace csg {
33
34using namespace std;
35
42
44 if (file_opened_) {
45 H5Fclose(file_id_);
46 file_opened_ = false;
47 }
48}
49
50bool H5MDTrajectoryReader::Open(const std::string &file) {
51 // Checks if we deal with hdf5 file.
52 if (!H5Fis_hdf5(file.c_str())) {
53 cout << file << " is not recognise as HDF5 file format" << endl;
54 return false;
55 }
56 file_id_ = H5Fopen(file.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);
57 file_opened_ = true;
58
59 // Check the version of the file.
60 hid_t g_h5md = H5Gopen(file_id_, "h5md", H5P_DEFAULT);
61 CheckError(g_h5md, "Unable to open /h5md group.");
62
63 hid_t at_version = H5Aopen(g_h5md, "version", H5P_DEFAULT);
64 CheckError(at_version, "Unable to read version attribute.");
65 int version[2] = {0, 0};
66 H5Aread(at_version, H5Aget_type(at_version), &version);
67 if (version[0] != 1 || version[1] > 1) {
68 cout << "Found H5MD version: " << version[0] << "." << version[1] << endl;
69 throw ios_base::failure("Wrong version of H5MD file.");
70 }
71
72 // Checks if the particles group exists and what is the number of members.
73 particle_group_ = H5Gopen(file_id_, "particles", H5P_DEFAULT);
74 CheckError(particle_group_, "Unable to open /particles group.");
75 hsize_t num_obj = 0;
76 H5Gget_num_objs(particle_group_, &num_obj);
77 if (num_obj == 0) {
78 throw ios_base::failure("The particles group is empty.");
79 }
80
81 // Check if the unit module is enabled.
82 hid_t modules = H5Gopen(g_h5md, "modules", H5P_DEFAULT);
83 std::cout << "open modules group" << modules << std::endl;
84 if (modules > 0) {
85 hid_t module_units = H5Gopen(modules, "units", H5P_DEFAULT);
86 if (module_units > 0) {
87 std::cout << "found units module - position and forces will be scaled "
88 << std::endl;
90 H5Gclose(module_units);
91 }
92 H5Gclose(modules);
93 }
94
95 first_frame_ = true;
96
97 // Handle errors by internal check up.
98 H5Eset_auto2(H5E_DEFAULT, nullptr, nullptr);
99
100 // Clean up.
101 H5Aclose(at_version);
102 H5Gclose(g_h5md);
103
104 return true;
105}
106
108 if (file_opened_) {
109 H5Fclose(file_id_);
110 file_opened_ = false;
111 }
112}
113
115 std::string particle_group_name_ = top.getParticleGroup();
116 if (particle_group_name_.compare("unassigned") == 0) {
117 throw ios_base::failure(
118 "Missing particle group in topology. Please set `h5md_particle_group` "
119 "tag with `name` attribute set to the particle group.");
120 }
121 std::string position_group_name = particle_group_name_ + "/position";
123 H5Gopen(particle_group_, position_group_name.c_str(), H5P_DEFAULT);
125 "Unable to open " + position_group_name + " group");
126 idx_frame_ = -1;
127 ds_atom_position_ = H5Dopen(atom_position_group_, "value", H5P_DEFAULT);
129 "Unable to open " + position_group_name + "/value dataset");
130
131 // Reads the box information.
132 std::string box_gr_name = particle_group_name_ + "/box";
133 hid_t g_box = H5Gopen(particle_group_, box_gr_name.c_str(), H5P_DEFAULT);
134 CheckError(g_box, "Unable to open " + box_gr_name + " group");
135 hid_t at_box_dimension = H5Aopen(g_box, "dimension", H5P_DEFAULT);
136 CheckError(at_box_dimension, "Unable to open dimension attribute.");
137 int dimension;
138 H5Aread(at_box_dimension, H5Aget_type(at_box_dimension), &dimension);
139 if (dimension != 3) {
140 throw ios_base::failure("Wrong dimension " +
141 boost::lexical_cast<std::string>(dimension));
142 }
143 // TODO: check if boundary is periodic.
144 std::string box_edges_name = particle_group_name_ + "/box/edges";
145 if (GroupExists(particle_group_, box_edges_name)) {
146 g_box = H5Gopen(particle_group_, box_gr_name.c_str(), H5P_DEFAULT);
147 edges_group_ = H5Gopen(g_box, "edges", H5P_DEFAULT);
148 ds_edges_group_ = H5Dopen(edges_group_, "value", H5P_DEFAULT);
149 cout << "H5MD: has /box/edges" << endl;
150 cout << "H5MD: time dependent box size" << endl;
152 } else {
153 cout << "H5MD: static box" << endl;
154 hid_t ds_edges = H5Dopen(g_box, "edges", H5P_DEFAULT);
155 CheckError(ds_edges, "Unable to open /box/edges");
156 std::unique_ptr<double[]> box = std::unique_ptr<double[]>{new double[3]};
157 ReadStaticData<double[]>(ds_edges, H5T_NATIVE_DOUBLE, box);
158 cout << "H5MD: Found box " << box[0] << " x " << box[1] << " x " << box[2]
159 << endl;
160 // Sets box size.
161 m = Eigen::Matrix3d::Zero();
162 m(0, 0) = box[0] * length_scaling_;
163 m(1, 1) = box[1] * length_scaling_;
164 m(2, 2) = box[2] * length_scaling_;
165 top.setBox(m);
167 }
168 H5Gclose(g_box);
169
170 // Gets the force group.
171 std::string force_group_name = particle_group_name_ + "/force";
172 if (GroupExists(particle_group_, force_group_name)) {
174 H5Gopen(particle_group_, force_group_name.c_str(), H5P_DEFAULT);
175 ds_atom_force_ = H5Dopen(atom_force_group_, "value", H5P_DEFAULT);
177 cout << "H5MD: has /force" << endl;
178 } else {
180 }
181
182 // Gets the velocity group.
183 std::string velocity_group_name = particle_group_name_ + "/velocity";
184 if (GroupExists(particle_group_, velocity_group_name)) {
186 H5Gopen(particle_group_, velocity_group_name.c_str(), H5P_DEFAULT);
187 ds_atom_velocity_ = H5Dopen(atom_velocity_group_, "value", H5P_DEFAULT);
189 cout << "H5MD: has /velocity" << endl;
190 } else {
192 }
193
194 // Gets the id group so that the atom id is taken from this group.
195 std::string id_group_name = particle_group_name_ + "/id";
196 if (GroupExists(particle_group_, id_group_name)) {
198 H5Gopen(particle_group_, id_group_name.c_str(), H5P_DEFAULT);
199 ds_atom_id_ = H5Dopen(atom_id_group_, "value", H5P_DEFAULT);
201 cout << "H5MD: has /id group" << endl;
202 } else {
204 }
205
206 // Reads unit system - if enabled.
211 }
212
213 // Gets number of particles and dimensions.
214 hid_t fs_atom_position_ = H5Dget_space(ds_atom_position_);
215 CheckError(fs_atom_position_, "Unable to open atom position space.");
216 hsize_t dims[3];
217 rank_ = H5Sget_simple_extent_dims(fs_atom_position_, dims, nullptr);
218 N_particles_ = dims[1];
219 vec_components_ = (int)dims[2];
220 max_idx_frame_ = dims[0] - 1;
221
222 // TODO: reads mass, charge and particle type.
223
225 N_particles_ != top.BeadCount()) {
226 cout << "Warning: The number of beads (" << N_particles_ << ")";
227 cout << " in the trajectory is different than defined in the topology ("
228 << top.BeadCount() << ")" << endl;
229 cout << "The number of beads from topology will be used!" << endl;
230 N_particles_ = top.BeadCount();
231 }
232}
233
234bool H5MDTrajectoryReader::FirstFrame(Topology &top) { // NOLINT const
235 // reference
236 if (first_frame_) {
237 first_frame_ = false;
238 Initialize(top);
239 }
240 NextFrame(top);
241 return true;
242}
243
245bool H5MDTrajectoryReader::NextFrame(Topology &top) { // NOLINT const reference
246 // Reads the position row.
247 idx_frame_++;
249 return false;
250 }
251
252 cout << '\r' << "Reading frame: " << idx_frame_ << "\n";
253 cout.flush();
254 // Set volume of box because top on workers somehow does not have this
255 // information.
257 std::unique_ptr<double[]> box = std::unique_ptr<double[]>{new double[3]};
258 ReadBox(ds_edges_group_, H5T_NATIVE_DOUBLE, idx_frame_, box);
259 m = Eigen::Matrix3d::Zero();
260 m(0, 0) = box.get()[0] * length_scaling_;
261 m(1, 1) = box.get()[1] * length_scaling_;
262 m(2, 2) = box.get()[2] * length_scaling_;
263 cout << "Time dependent box:" << endl;
264 cout << m << endl;
265 }
266 top.setBox(m);
267
268 double *positions;
269 double *forces = nullptr;
270 double *velocities = nullptr;
271 int *ids = nullptr;
272
273 try {
274 positions = ReadVectorData<double>(ds_atom_position_, H5T_NATIVE_DOUBLE,
275 idx_frame_);
276 } catch (const std::runtime_error &e) {
277 return false;
278 }
279
281 velocities = ReadVectorData<double>(ds_atom_velocity_, H5T_NATIVE_DOUBLE,
282 idx_frame_);
283 }
284
286 forces =
288 }
289
291 ids = ReadScalarData<int>(ds_atom_id_, H5T_NATIVE_INT, idx_frame_);
292 }
293
294 // Process atoms.
295 for (Index at_idx = 0; at_idx < N_particles_; at_idx++) {
296 double x, y, z;
297 Index array_index = at_idx * vec_components_;
298 x = positions[array_index] * length_scaling_;
299 y = positions[array_index + 1] * length_scaling_;
300 z = positions[array_index + 2] * length_scaling_;
301 // Set atom id, or it is an index of a row in dataset or from id dataset.
302 Index atom_id = at_idx;
304 if (ids[at_idx] == -1) { // ignore values where id == -1
305 continue;
306 }
307 atom_id = ids[at_idx];
308 }
309
310 // Topology has to be defined in the xml file or in other
311 // topology files. The h5md only stores the trajectory data.
312 Bead *b = top.getBead(atom_id);
313 if (b == nullptr) {
314 throw std::runtime_error("Bead not found: " +
315 boost::lexical_cast<std::string>(atom_id));
316 }
317
318 b->setPos(Eigen::Vector3d(x, y, z));
320 double vx, vy, vz;
321 vx = velocities[array_index] * velocity_scaling_;
322 vy = velocities[array_index + 1] * velocity_scaling_;
323 vz = velocities[array_index + 2] * velocity_scaling_;
324 b->setVel(Eigen::Vector3d(vx, vy, vz));
325 }
326
328 double fx, fy, fz;
329 fx = forces[array_index] * force_scaling_;
330 fy = forces[array_index + 1] * force_scaling_;
331 fz = forces[array_index + 2] * force_scaling_;
332 b->setF(Eigen::Vector3d(fx, fy, fz));
333 }
334 }
335
336 // Clean up pointers.
337 delete[] positions;
339 delete[] forces;
340 }
342 delete[] velocities;
343 }
345 delete[] ids;
346 }
347
348 return true;
349}
350
351void H5MDTrajectoryReader::ReadBox(hid_t ds, hid_t ds_data_type, Index row,
352 std::unique_ptr<double[]> &data_out) {
353 hsize_t offset[2];
354 offset[0] = row;
355 offset[1] = 0;
356 hsize_t ch_rows[2];
357 ch_rows[0] = 1;
358 ch_rows[1] = 3;
359 hid_t dsp = H5Dget_space(ds);
360 H5Sselect_hyperslab(dsp, H5S_SELECT_SET, offset, nullptr, ch_rows, nullptr);
361 hid_t mspace1 = H5Screate_simple(2, ch_rows, nullptr);
362 herr_t status =
363 H5Dread(ds, ds_data_type, mspace1, dsp, H5P_DEFAULT, data_out.get());
364 if (status < 0) {
365 throw std::runtime_error("Error ReadScalarData: " +
366 boost::lexical_cast<std::string>(status));
367 }
368}
369
371 const std::string &unit_type) {
372 hid_t unit_attr = H5Aopen(ds, "unit", H5P_DEFAULT);
373 double scaling_factor = 1.0;
374 if (unit_attr > 0) {
375 hid_t type_id = H5Aget_type(unit_attr);
376 hid_t atype_mem = H5Tget_native_type(type_id, H5T_DIR_ASCEND);
377 hsize_t str_size = H5Tget_size(type_id);
378 char buffer[80] = {0}; // buffer for attribute
379 H5Aread(unit_attr, atype_mem, &buffer);
380 H5Tclose(atype_mem);
381 H5Tclose(type_id);
382 std::string value = std::string(buffer, str_size);
383
384 // Read units
385 tools::Tokenizer tok(value, " ");
386 for (auto v : tok) {
387 boost::smatch suffix_match;
388 int unit_pow = 1;
389 if (boost::regex_match(v, suffix_match, suffix_units,
390 boost::match_extra)) {
391 // If the prefix has numeric suffix then use it in the formula for
392 // scaling factor.
393 unit_pow = std::stoi(suffix_match[2]);
394 }
395 auto votca_scaling_factor = votca_units_scaling_factors.find(v);
396 if (votca_scaling_factor != votca_units_scaling_factors.end()) {
397 scaling_factor =
398 scaling_factor * pow(votca_scaling_factor->second, unit_pow);
399 }
400 }
401 std::cout << "Found units " << value << " for " << unit_type << ".";
402 if (scaling_factor != 1.0) {
403 std::cout << " Values scaled by a factor " << scaling_factor;
404 }
405 std::cout << std::endl;
406 }
407
408 return scaling_factor;
409}
410
411} // namespace csg
412} // namespace votca
virtual void setPos(const Eigen::Vector3d &bead_position)
Definition basebead.h:161
information about a bead
Definition bead.h:50
void setF(const Eigen::Vector3d &bead_force)
Definition bead.h:356
void setVel(const Eigen::Vector3d &r)
Definition bead.h:315
void Close() override
Closes original trajectory file.
double ReadScaleFactor(const hid_t &ds, const std::string &unit_type)
void ReadBox(hid_t ds, hid_t ds_data_type, Index row, std::unique_ptr< double[]> &data_out)
void ReadStaticData(hid_t ds, hid_t ds_data_type, std::unique_ptr< T1 > &outbuf)
bool NextFrame(Topology &conf) override
Reads in the next frame.
T1 * ReadVectorData(hid_t ds, hid_t ds_data_type, Index row)
Reads dataset that contains vectors.
void CheckError(hid_t hid, std::string error_message)
T1 * ReadScalarData(hid_t ds, hid_t ds_data_type, Index row)
Reads dataset with scalar values.
std::unordered_map< std::string, double > votca_units_scaling_factors
bool GroupExists(hid_t file_id, std::string path)
void Initialize(Topology &top)
Initialize data structures.
bool Open(const std::string &file) override
Opens original trajectory file.
bool FirstFrame(Topology &conf) override
Reads in the first frame.
topology of the whole system
Definition topology.h:81
void setBox(const Eigen::Matrix3d &box, BoundaryCondition::eBoxtype boxtype=BoundaryCondition::typeAuto)
Definition topology.h:272
Index BeadCount() const
Definition topology.h:150
std::string getParticleGroup() const
Definition topology.h:343
Bead * getBead(const Index i)
Returns a pointer to the bead with index i.
Definition topology.h:227
break string into words
Definition tokenizer.h:72
STL namespace.
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26