faunus
io.h
1 #pragma once
2 
3 #include "particle.h"
4 #include "units.h"
5 #include "group.h"
6 #include <fstream>
7 #include <type_traits>
8 #include <concepts>
9 #include <spdlog/spdlog.h>
10 #include <numeric>
11 
12 namespace Faunus {
13 
14 class Space;
15 class Group;
16 
17 [[maybe_unused]] std::string addGrowingSuffix(
18  const std::string&);
19 
20 #ifndef __cplusplus
21 #define __cplusplus
22 #endif
23 #ifndef CPLUSPLUS
24 #define CPLUSPLUS
25 #endif
26 
27 namespace XdrFile {
28 #include <xdrfile_trr.h>
29 #include <xdrfile_xtc.h>
30 
32 {
33  void
34  operator()(XdrFile::XDRFILE* xdrfile);
35 };
36 
37 using XDRFILE_unique =
38  std::unique_ptr<XDRFILE, XdrFileDeleter>;
39 } // namespace XdrFile
40 
41 namespace IO {
42 
46 std::unique_ptr<std::ostream> openCompressedOutputStream(const std::string& filename,
47  bool throw_on_error = false);
48 
56 template <typename TKey, typename TValue>
57 void writeKeyValuePairs(std::ostream& stream, const std::map<TKey, TValue>& data,
58  const std::string& sep = " ", const std::string& end = "\n")
59 {
60  if (stream) {
61  for (const auto& [key, value] : data) {
62  stream << key << sep << value << end;
63  }
64  }
65 }
66 
74 template <typename TKey, typename TValue>
75 void writeKeyValuePairs(const std::string& filename, const std::map<TKey, TValue>& data)
76 {
77  if (!data.empty()) {
78  std::ofstream file(filename);
79  writeKeyValuePairs(file, data);
80  }
81 }
82 
83 } // namespace IO
84 
89 {
90  private:
91  virtual void loadHeader(std::istream& stream) = 0;
92  virtual void loadFooter(std::istream& stream);
93  virtual Particle loadParticle(std::istream& stream) = 0;
94  void checkLoadedParticles() const;
95 
96  protected:
97  static size_t getNumberOfAtoms(const std::string& line);
98  void getNextLine(std::istream& stream, std::string& destination,
99  const std::string& comment_identifiers);
100  size_t expected_number_of_particles = 0;
101 
102  void handleChargeMismatch(Particle& particle,
103  int atom_index) const;
104  static void handleRadiusMismatch(const Particle& particle, double radius,
105  int atom_index);
106 
107  public:
108  ParticleVector particles;
109  Point box_length = Point::Zero();
110  std::vector<std::string> comments;
111  bool prefer_charges_from_file =
112  true;
113 
114  ParticleVector& load(std::istream& stream);
115  ParticleVector& load(std::string_view filename);
116  virtual ~StructureFileReader() = default;
117 
118  bool box_dimension_support = false;
119  bool particle_charge_support = false;
120  bool particle_radius_support = false;
121 };
122 
130 {
131  private:
132  void loadHeader(std::istream& stream) override;
133  Particle loadParticle(std::istream& stream) override;
134  double bond_length = 0.0;
135  Point new_particle_position = Point::Zero();
136 
137  public:
138  explicit CoarseGrainedFastaFileReader(double bond_length,
139  const Point& initial_particle_position = Point(0, 0, 0));
140  void setBondLength(double bond_length);
141  std::string loadSequence(std::istream& stream);
142  static char getFastaLetter(std::istream& stream);
143 };
144 
146 {
147  private:
148  void loadHeader(std::istream& stream) override;
149  Particle loadParticle(std::istream& stream) override;
150 
151  public:
153 };
154 
156 {
157  private:
158  void loadHeader(std::istream& stream) override;
159  Particle loadParticle(std::istream& stream) override;
160 
161  public:
162  PQRReader();
163 };
164 
180 {
181  private:
182  void loadHeader(std::istream& stream) override;
183  Particle loadParticle(std::istream& stream) override;
184 };
185 
187 {
188  private:
189  void loadHeader(std::istream& stream) override;
190  Particle loadParticle(std::istream& stream) override;
191 };
192 
194 {
195  private:
196  void loadBoxInformation(
197  std::istream& stream);
198  void loadHeader(std::istream& stream) override;
199  Particle loadParticle(std::istream& stream) override;
200 
201  public:
202  GromacsReader();
203 };
204 
209 {
210  private:
211  virtual void saveHeader(std::ostream& stream,
212  int number_of_particles) const = 0;
213  virtual void
214  saveFooter(std::ostream& stream) const;
215  virtual void saveParticle(std::ostream& stream,
216  const Particle& particle) = 0;
217  void saveGroup(std::ostream& stream, const Group& group);
218 
219  template <class ParticleIter>
220  void saveParticles(std::ostream& stream, ParticleIter begin, ParticleIter end)
221  {
222  group_index = 0;
223  particle_index = 0;
224  std::for_each(begin, end, [&](const auto& particle) {
225  saveParticle(stream, particle);
226  particle_index++;
227  });
228  }
229 
230  protected:
231  static const std::string generated_by_faunus_comment;
232  bool particle_is_active = true;
233  std::string group_name;
234  std::size_t particle_index = 0;
235  std::size_t group_index = 0;
236  Point box_dimensions = Point::Zero();
237 
238  public:
239  template <std::forward_iterator ParticleIter>
240  void save(std::ostream& stream, ParticleIter begin, ParticleIter end, const Point& box_length)
241  {
242  if (auto number_of_particles = std::distance(begin, end); number_of_particles > 0) {
243  box_dimensions = box_length;
244  saveHeader(stream, number_of_particles);
245  saveParticles(stream, begin, end);
246  saveFooter(stream);
247  }
248  }
249 
250  void save(std::ostream& stream, const RequireGroups auto& groups, const Point& box_length)
251  {
252  group_index = 0;
253  particle_index = 0;
254  box_dimensions = box_length;
255 
256  auto group_sizes = groups | std::views::transform(&Group::capacity);
257  const auto number_of_particles =
258  std::accumulate(group_sizes.begin(), group_sizes.end(), 0u);
259  saveHeader(stream, number_of_particles);
260 
261  std::ranges::for_each(groups, [&](const auto& group) { saveGroup(stream, group); });
262  saveFooter(stream);
263  }
264 
265  template <class... Args> void save(const std::string& filename, const Args&... args)
266  {
267  if (std::ofstream stream(filename); stream) {
268  faunus_logger->debug("writing to {}", filename);
269  save(stream, args...);
270  }
271  else {
272  throw std::runtime_error("writeKeyValuePairs error: "s + filename);
273  }
274  }
275 
276  virtual ~StructureFileWriter() = default;
277 };
278 
280 {
281  private:
282  void saveHeader(std::ostream& stream, int number_of_particles) const override;
283  void saveParticle(std::ostream& stream, const Particle& particle) override;
284 };
285 
287 {
288  private:
289  void saveHeader(std::ostream& stream, int number_of_particles) const override;
290  void saveParticle(std::ostream& stream, const Particle& particle) override;
291 };
292 
298 {
299  private:
300  void saveHeader(std::ostream& stream, int number_of_particles) const override;
301  void saveParticle(std::ostream& stream, const Particle& particle) override;
302 };
303 
305 {
306  private:
307  void saveHeader(std::ostream& stream, int number_of_particles) const override;
308  void saveFooter(std::ostream& stream) const override;
309  void saveParticle(std::ostream& stream, const Particle& particle) override;
310 
311  public:
312  enum class Style
313  {
314  PQR_LEGACY,
315  PDB,
316  PQR
317  };
318  Style style = Style::PQR_LEGACY;
319  explicit PQRWriter(Style style = Style::PQR_LEGACY);
320 };
321 
323 {
324  private:
325  void saveHeader(std::ostream& stream, int number_of_particles) const override;
326  void saveFooter(std::ostream& stream) const override;
327  void saveParticle(std::ostream& stream, const Particle& particle) override;
328 };
329 
330 namespace PQRTrajectoryReader {
331 bool readAtomRecord(const std::string& record, Particle& particle,
332  double& radius);
333 void loadTrajectory(const std::string& filename,
334  std::vector<ParticleVector>& destination);
335 } // namespace PQRTrajectoryReader
336 
337 struct TrajectoryFrame;
338 
355 {
356  XdrFile::matrix xtc_box;
357  std::unique_ptr<XdrFile::rvec[]> xtc_coordinates;
358  int xtc_step = 0;
359  float xtc_time = 0.0;
360  int number_of_atoms = 0;
361  float precision = 1000.0;
362 
366  XTCTrajectoryFrame(int number_of_atoms);
371  XTCTrajectoryFrame(const TrajectoryFrame& frame);
372 
384  template <RequirePointIterator begin_iterator, class end_iterator>
385  XTCTrajectoryFrame(int step, float time, const Point& box, begin_iterator coordinates_begin,
386  end_iterator coordinates_end)
387  {
388  initNumberOfAtoms(std::distance(coordinates_begin, coordinates_end));
389  importFrame(step, time, box, coordinates_begin, coordinates_end);
390  }
391 
399  XTCTrajectoryFrame& operator=(const TrajectoryFrame& frame);
405  void importFrame(const TrajectoryFrame& frame);
406 
418  template <RequirePointIterator begin_iterator, class end_iterator>
419  void importFrame(const int step, const float time, const Point& box,
420  begin_iterator coordinates_begin, end_iterator coordinates_end)
421  {
422  importTimestamp(step, time);
423  importBox(box);
424  importCoordinates(coordinates_begin, coordinates_end, 0.5 * box);
425  }
426 
432  void exportFrame(TrajectoryFrame& frame) const;
433 
445  template <RequirePointIterator begin_iterator, RequirePointIterator end_iterator>
446  void exportFrame(int& step, float& time, Point& box, begin_iterator coordinates_begin,
447  end_iterator coordinates_end) const
448  {
449  exportTimestamp(step, time);
450  exportBox(box);
451  exportCoordinates(coordinates_begin, coordinates_end, 0.5 * box);
452  }
453 
454  protected:
455  using XTCFloat = float;
456  using XTCMatrix =
457  Eigen::Matrix<XTCFloat, 3, 3, Eigen::RowMajor>; //<! eigen equivalent of the box tensor
458  using XTCVector =
459  Eigen::Matrix<XTCFloat, 3, 1>; //<! eigen equivalent of the single set of coordinates
465  void importTimestamp(int step, float time);
470  void importBox(const Point& box);
477  void importCoordinates(const PointVector& coordinates, const Point& offset);
478 
488  template <RequirePointIterator begin_iterator, typename end_iterator>
489  void importCoordinates(begin_iterator begin, end_iterator end, const Point& offset) const
490  {
491  int i = 0;
492  std::ranges::for_each(begin, end, [&](const auto& position) {
493  if (i >= number_of_atoms) {
494  throw std::runtime_error("too many particles for XTC frame");
495  }
496  const XTCVector xtc_pos = ((position + offset) / 1.0_nm).template cast<XTCFloat>();
497  std::copy(xtc_pos.data(), xtc_pos.data() + DIM, xtc_coordinates.get()[i++]);
498  });
499  if (i != number_of_atoms) {
500  throw std::runtime_error("too few particles for XTC frame");
501  }
502  }
503 
509  void exportTimestamp(int& step, float& time) const;
514  void exportBox(Point& box) const;
522  void exportCoordinates(PointVector& coordinates, const Point& offset) const;
523 
533  template <RequirePointIterator begin_iterator, typename end_iterator>
534  void exportCoordinates(begin_iterator begin, end_iterator end, const Point& offset) const
535  {
536  // comparison of i is probably faster than prior call of std::distance
537  int i = 0;
538  for (auto coordinates_it = begin; coordinates_it != end; ++coordinates_it) {
539  if (i >= number_of_atoms) {
540  throw std::runtime_error("number of particles in the loaded XTC frame too low");
541  }
542  XTCVector xtc_atom_coordinates(xtc_coordinates.get()[i++]);
543  *coordinates_it = Point(xtc_atom_coordinates.cast<double>() * 1.0_nm) - offset;
544  }
545  if (i < number_of_atoms) {
546  throw std::runtime_error("number of particles in the loaded XTC frame too high");
547  }
548  }
549 
550  private:
554  void initNumberOfAtoms(int);
555 };
556 
564 {
567  int step = 0;
568  float timestamp = 0.0;
569 
570  TrajectoryFrame() = default;
571  TrajectoryFrame(const Point& box, const PointVector& coordinates, int step, float timestamp);
577  explicit TrajectoryFrame(const XTCTrajectoryFrame& xtc_frame);
584  void operator=(const XTCTrajectoryFrame& xtc_frame);
585 };
586 
596 {
597  int return_code = XdrFile::exdrOK;
598  XdrFile::XDRFILE_unique xdrfile;
599  std::unique_ptr<XTCTrajectoryFrame> xtc_frame;
601 
602  public:
603  std::string filename;
604 
607  explicit XTCReader(const std::string& filename);
608 
613  int getNumberOfCoordinates();
620  bool read(TrajectoryFrame& frame);
621 
634  template <RequirePointIterator begin_iterator, typename end_iterator>
635  bool read(int& step, float& time, Point& box, begin_iterator coordinates_begin,
636  end_iterator coordinates_end)
637  {
638  if (readFrame()) {
639  xtc_frame->exportFrame(step, time, box, coordinates_begin, coordinates_end);
640  return true;
641  }
642  return false;
643  }
644 
645  protected:
651  bool readFrame();
652 };
653 
662 {
663  int return_code = XdrFile::exdrOK;
664  XdrFile::XDRFILE_unique xdrfile;
665  std::unique_ptr<XTCTrajectoryFrame> xtc_frame;
666  int step_counter = 0;
668  float time_delta = 1.0_ps;
669 
670  public:
671  const std::string filename;
672 
675  explicit XTCWriter(const std::string& filename);
681  void write(const TrajectoryFrame& frame);
688  void writeNext(const TrajectoryFrame& frame);
689 
698  template <RequirePointIterator begin_iterator, typename end_iterator>
699  void writeNext(const Point& box, begin_iterator coordinates_begin, end_iterator coordinates_end)
700  {
701  if (!xtc_frame) {
702  auto number_of_atoms = std::ranges::distance(coordinates_begin, coordinates_end);
703  faunus_logger->trace("preparing xtc output for {} particles", number_of_atoms);
704  xtc_frame = std::make_unique<XTCTrajectoryFrame>(number_of_atoms);
705  }
706  xtc_frame->importFrame(step_counter, static_cast<float>(step_counter) * time_delta, box,
707  coordinates_begin, coordinates_end);
708  writeFrame();
709  ++step_counter;
710  }
711 
712  protected:
717  void writeFrame();
723  void writeFrameAt(int step, float time);
724 };
725 
726 std::vector<AtomData::index_type>
727 fastaToAtomIds(std::string_view fasta_sequence);
728 
735 ParticleVector fastaToParticles(std::string_view fasta_sequence, double bond_length = 7.0,
736  const Point& origin = {0, 0, 0});
737 
745 ParticleVector loadStructure(std::string_view filename, bool prefer_charges_from_file = true);
746 
752 std::unique_ptr<StructureFileWriter> createStructureFileWriter(const std::string& suffix);
753 
754 } // namespace Faunus
Eigen::Vector3d Point
3D vector used for positions, velocities, forces etc.
Definition: coordinates.h:7
Definition: io.h:304
Simple data structure to store a trajectory frame in native Faunus format.
Definition: io.h:563
Definition: io.h:155
XTCTrajectoryFrame(int step, float time, const Point &box, begin_iterator coordinates_begin, end_iterator coordinates_end)
Creates an XTC trajectory frame from scalar parameters and input iterator and converts data according...
Definition: io.h:385
std::vector< Point > PointVector
Vector of 3D vectors.
Definition: core.h:24
Definition: io.h:145
Base class to writeKeyValuePairs simple structure files such as XYZ, AAM, PQR etc.
Definition: io.h:208
void importFrame(const int step, const float time, const Point &box, begin_iterator coordinates_begin, end_iterator coordinates_end)
Imports data from scalar parameters and an input iterator over coordinates.
Definition: io.h:419
Definition: io.h:279
Definition: io.h:286
Modified XYZ format that also saves spherocylinder direction and patch (ported from Faunus v1) ...
Definition: io.h:297
XYZ file loader.
Definition: io.h:179
ParticleVector loadStructure(std::string_view filename, bool prefer_charges_from_file)
Load structure file into particle vector.
Definition: io.cpp:413
std::vector< Particle > ParticleVector
Storage type for collections of particles.
Definition: particle.h:253
std::string filename
name of the trajectory file, mainly for error reporting
Definition: io.h:603
void importCoordinates(begin_iterator begin, end_iterator end, const Point &offset) const
Imports and converts atomic coordinates.
Definition: io.h:489
Reads FASTA file and generate one particle per amino acid.
Definition: io.h:129
void operator()(XdrFile::XDRFILE *xdrfile)
Custom deleter for XDRFILE pointer that closes file.
Definition: io.cpp:1252
bool read(int &step, float &time, Point &box, begin_iterator coordinates_begin, end_iterator coordinates_end)
Reads the next frame in the trajectory.
Definition: io.h:635
std::unique_ptr< XdrFile::rvec[]> xtc_coordinates
C-style array of particle coordinates.
Definition: io.h:357
Particle class for storing positions, id, and other properties.
Definition: particle.h:220
Cell list class templates.
Definition: actions.cpp:11
End of Group class.
Definition: group.h:177
std::string addGrowingSuffix(const std::string &)
Add growing suffix filename until non-existing name is found.
void exportCoordinates(begin_iterator begin, end_iterator end, const Point &offset) const
Exports and converts atomic coordinates.
Definition: io.h:534
concept RequireGroups
Concept for a range of groups.
Definition: group.h:387
void writeNext(const Point &box, begin_iterator coordinates_begin, end_iterator coordinates_end)
Writes a next frame into the file using own automatic counter for step and timestamp.
Definition: io.h:699
Definition: io.h:193
std::vector< AtomData::index_type > fastaToAtomIds(std::string_view fasta_sequence)
Convert FASTA sequence to atom id sequence.
Definition: io.cpp:455
const std::string filename
name of the trajectory file, mainly for error reporting
Definition: io.h:671
Point box
simulation box
Definition: io.h:565
ParticleVector fastaToParticles(std::string_view fasta_sequence, double bond_length, const Point &origin)
Create particle vector from FASTA sequence with equally spaced atoms.
Definition: io.cpp:373
Base data structure for native XTC format as used in Gromacs and supported by the C library...
Definition: io.h:354
void exportFrame(int &step, float &time, Point &box, begin_iterator coordinates_begin, end_iterator coordinates_end) const
Exports data to scalar paramers and output iterator over atomic coordinates.
Definition: io.h:446
Definition: io.h:322
Base class to load simple structure files such as XYZ, AAM, PQR etc.
Definition: io.h:88
PointVector coordinates
coordinates of particles
Definition: io.h:566
Writes frames into an XTC file (GROMACS compressed trajectory file format).
Definition: io.h:661
Reads frames from an XTC file (GROMACS compressed trajectory file format).
Definition: io.h:595
XdrFile::matrix xtc_box
box tensor; only diagonal elements are used
Definition: io.h:356
std::unique_ptr< StructureFileWriter > createStructureFileWriter(const std::string &suffix)
Create structure writer.
Definition: io.cpp:388