From ccf1382896f4ac4f7fd612d64db750983fc70975 Mon Sep 17 00:00:00 2001
From: Giovanni La Mura <giovanni.lamura@inaf.it>
Date: Tue, 20 Feb 2024 18:30:34 +0100
Subject: [PATCH] Implement trapping configuration binary I/O and add it to
 library Makefile

---
 src/include/tfrfme.h             | 132 +++++++++++++-
 src/libnptm/Makefile             |   4 +-
 src/libnptm/TransitionMatrix.cpp |   1 +
 src/libnptm/tfrfme.cpp           | 297 ++++++++++++++++++++++++++++++-
 4 files changed, 414 insertions(+), 20 deletions(-)

diff --git a/src/include/tfrfme.h b/src/include/tfrfme.h
index 8bb9a7fc..9ebac454 100644
--- a/src/include/tfrfme.h
+++ b/src/include/tfrfme.h
@@ -6,6 +6,50 @@
 #ifndef INCLUDE_TFRFME_H_
 #define INCLUDE_TFRFME_H_
 
+/**
+ * \brief Exception for unrecognized parameters.
+ */
+class MatrixOutOfBoundsException: public std::exception {
+protected:
+  //! Description of the problem.
+  std::string message;
+public:
+  /**
+   * \brief Exception instance constructor.
+   *
+   * \param problem: `string` Description of the problem that occurred.
+   */
+  MatrixOutOfBoundsException(std::string problem) { message = problem; }
+  /**
+   * \brief Exception message.
+   */
+  virtual const char* what() const throw() {
+    return message.c_str();
+  }
+};
+
+/**
+ * \brief Exception for unrecognized parameters.
+ */
+class UnrecognizedParameterException: public std::exception {
+protected:
+  //! Description of the problem.
+  std::string message;
+public:
+  /**
+   * \brief Exception instance constructor.
+   *
+   * \param problem: `string` Description of the problem that occurred.
+   */
+  UnrecognizedParameterException(std::string problem) { message = problem; }
+  /**
+   * \brief Exception message.
+   */
+  virtual const char* what() const throw() {
+    return message.c_str();
+  }
+};
+
 /*! \brief Class to represent the trapping configuration.
  */
 class TFRFME {
@@ -51,28 +95,56 @@ class TFRFME {
   //! Vector of computed z positions
   double *zv;
   //! QUESTION: definition?
-  complex<double> **wsum;
+  std::complex<double> **wsum;
+
+  /*! \brief Load a configuration instance from a HDF5 binary file.
+   *
+   * \param file_name: `string` Name of the file to be loaded.
+   * \return instance: `TFRFME *` Pointer to a new trapping configuration
+   * instance.
+   */
+  static TFRFME *from_hdf5(std::string file_name);
+
+  /*! \brief Load a configuration instance from a legacy binary file.
+   *
+   * \param file_name: `string` Name of the file to be loaded.
+   * \return instance: `TFRFME *` Pointer to a new trapping configuration
+   * instance.
+   */
+  static TFRFME *from_legacy(std::string file_name);
+
+  /*! \brief Save a configuration instance to a HDF5 binary file.
+   *
+   * \param file_name: `string` Name of the file to be loaded.
+   */
+  void write_hdf5(std::string file_name);
+
+  /*! \brief Save a configuration instance to a legacy binary file.
+   *
+   * \param file_name: `string` Name of the file to be loaded.
+   */
+  void write_legacy(std::string file_name);
 
  public:
-  /*! \briesf Trapping configuration instance constructor.
+  /*! \brief Trapping configuration instance constructor.
    */
   TFRFME(
 	 int _lmode, int _lm, int _nkv, int _nxv, int _nyv, int _nzv
 );
   
-  /*! \briesf Trapping configuration instance destroyer.
+  /*! \brief Trapping configuration instance destroyer.
    */
   ~TFRFME();
 
-  /*! \briesf Load a trapping configuration instance from binary file.
+  /*! \brief Load a trapping configuration instance from binary file.
    *
    * \param file_name: `string` Name of the file.
-   * \param format: `string` Format of the file (can be either "HDF5"
+   * \param mode: `string` Format of the file (can be either "HDF5"
    * or "LGEACY". Default is "LEGACY").
    * \return instance: `TFRFME *` Pointer to anewly created configuration
    * instance.
    */
-  TFRFME* from_binary(std::string file_name, std::string format="LEGACY");
+  static TFRFME* from_binary(std::string file_name, std::string mode="LEGACY");
 
   /*! \brief Get an element from the WSUM matrix.
    *
@@ -89,6 +161,27 @@ class TFRFME {
    */
   double get_param(std::string param_name);
 
+  /*! \brief Get the X coordinate of the computed point at given index.
+   *
+   * \param index: `int` Index of the requested point.
+   * \return x: `double` X coordinate of the requested point.
+   */
+  double get_x(int index) { return xv[index]; }
+
+  /*! \brief Get the Y coordinate of the computed point at given index.
+   *
+   * \param index: `int` Index of the requested point.
+   * \return y: `double` Y coordinate of the requested point.
+   */
+  double get_y(int index) { return yv[index]; }
+
+  /*! \brief Get the Z coordinate of the computed point at given index.
+   *
+   * \param index: `int` Index of the requested point.
+   * \return z: `double` Z coordinate of the requested point.
+   */
+  double get_z(int index) { return zv[index]; }
+
   /*! \brief Set an element in the WSUM matrix.
    *
    * \param row: `int` Row index of the element to be read.
@@ -104,12 +197,33 @@ class TFRFME {
    */
   void set_param(std::string param_name, double value);
 
-  /*! \briesf Write a trapping configuration instance to binary file.
+  /*! \brief Set the X coordinate of the computed point at given index.
+   *
+   * \param index: `int` Index of the requested point.
+   * \param value: `double` X coordinate of the requested point.
+   */
+  void set_x(int index, double value) { xv[index] = value; }
+
+  /*! \brief Set the Y coordinate of the computed point at given index.
+   *
+   * \param index: `int` Index of the requested point.
+   * \param value: `double` Y coordinate of the requested point.
+   */
+  void set_y(int index, double value) { yv[index] = value; }
+
+  /*! \brief Set the Z coordinate of the computed point at given index.
+   *
+   * \param index: `int` Index of the requested point.
+   * \param value: `double` Z coordinate of the requested point.
+   */
+  void set_z(int index, double value) { zv[index] = value; }
+
+  /*! \brief Write a trapping configuration instance to binary file.
    *
    * \param file_name: `string` Name of the file.
-   * \param format: `string` Format of the file (can be either "HDF5"
+   * \param mode: `string` Format of the file (can be either "HDF5"
    * or "LGEACY". Default is "LEGACY").
    */
-  void write_binary(std::string file_name, std::string format="LEGACY");
+  void write_binary(std::string file_name, std::string mode="LEGACY");
 };
 #endif
diff --git a/src/libnptm/Makefile b/src/libnptm/Makefile
index 33dfa35e..92633f8f 100644
--- a/src/libnptm/Makefile
+++ b/src/libnptm/Makefile
@@ -19,9 +19,9 @@ endif
 include ../make.inc
 
 
-CXX_NPTM_OBJS=$(OBJDIR)/Commons.o $(OBJDIR)/Configuration.o $(OBJDIR)/file_io.o $(OBJDIR)/Parsers.o $(OBJDIR)/sph_subs.o $(OBJDIR)/clu_subs.o $(OBJDIR)/tra_subs.o $(OBJDIR)/TransitionMatrix.o
+CXX_NPTM_OBJS=$(OBJDIR)/Commons.o $(OBJDIR)/Configuration.o $(OBJDIR)/file_io.o $(OBJDIR)/Parsers.o $(OBJDIR)/sph_subs.o $(OBJDIR)/clu_subs.o $(OBJDIR)/tfrfme.o $(OBJDIR)/tra_subs.o $(OBJDIR)/TransitionMatrix.o
 
-CXX_NPTM_DYNOBJS=$(DYNOBJDIR)/Commons.o $(DYNOBJDIR)/Configuration.o $(DYNOBJDIR)/file_io.o $(DYNOBJDIR)/Parsers.o $(DYNOBJDIR)/sph_subs.o $(DYNOBJDIR)/clu_subs.o $(DYNOBJDIR)/tra_subs.o $(DYNOBJDIR)/TransitionMatrix.o
+CXX_NPTM_DYNOBJS=$(DYNOBJDIR)/Commons.o $(DYNOBJDIR)/Configuration.o $(DYNOBJDIR)/file_io.o $(DYNOBJDIR)/Parsers.o $(DYNOBJDIR)/sph_subs.o $(DYNOBJDIR)/clu_subs.o $(DYNOBJDIR)/tfrfme.o $(DYNOBJDIR)/tra_subs.o $(DYNOBJDIR)/TransitionMatrix.o
 
 
 all: $(BUILDDIR_NPTM)/libnptm.a $(BUILDDIR_NPTM)/libnptm.so
diff --git a/src/libnptm/TransitionMatrix.cpp b/src/libnptm/TransitionMatrix.cpp
index 68860781..3e192fa8 100644
--- a/src/libnptm/TransitionMatrix.cpp
+++ b/src/libnptm/TransitionMatrix.cpp
@@ -6,6 +6,7 @@
 #include <exception>
 #include <fstream>
 #include <hdf5.h>
+#include <string>
 
 #ifndef INCLUDE_LIST_H_
 #include "../include/List.h"
diff --git a/src/libnptm/tfrfme.cpp b/src/libnptm/tfrfme.cpp
index 1dffdf06..d39791a6 100644
--- a/src/libnptm/tfrfme.cpp
+++ b/src/libnptm/tfrfme.cpp
@@ -3,11 +3,22 @@
  * \brief Implementation of the trapping configuration objects.
  */
 
-#include<complex>
-#include<string>
+#include <complex>
+#include <exception>
+#include <fstream>
+#include <hdf5.h>
+#include <string>
+
+#ifndef INCLUDE_LIST_H_
+#include "../include/List.h"
+#endif
 
 #ifndef INCLUDE_TFRFME_H_
-#include "../include/tfrfme.cpp"
+#include "../include/tfrfme.h"
+#endif
+
+#ifndef INCLUDE_FILE_IO_H_
+#include "../include/file_io.h"
 #endif
 
 using namespace std;
@@ -46,24 +57,292 @@ TFRFME::~TFRFME() {
   delete[] wsum;
 }
 
-TFRFME::TFRFME* from_binary(string file_name, string format) {
+TFRFME* TFRFME::from_binary(string file_name, string mode) {
+  TFRFME *instance = NULL;
+  if (mode.compare("LEGACY") == 0) {
+    instance = from_legacy(file_name);
+  } else if (mode.compare("HDF5") == 0) {
+    instance = from_hdf5(file_name);
+  } /* else {
+    string message = "Unknown format mode: \"" + mode + "\"";
+    throw UnrecognizedFormatException(message);
+    } */
+  return instance;
+}
+
+TFRFME* TFRFME::from_hdf5(string file_name) {
+  TFRFME *instance = NULL;
+  unsigned int flags = H5F_ACC_RDONLY;
+  HDFFile *hdf_file = new HDFFile(file_name, flags);
+  herr_t status = hdf_file->get_status();
+  if (status == 0) {
+    int lmode, lm, nkv, nxv, nyv, nzv;
+    double vk, exri, an, ff, tra, spd, frsh, exril;
+    status = hdf_file->read("LMODE", "INT32", &lmode);
+    status = hdf_file->read("LM", "INT32", &lm);
+    status = hdf_file->read("NKV", "INT32", &nkv);
+    status = hdf_file->read("NXV", "INT32", &nxv);
+    status = hdf_file->read("NYV", "INT32", &nyv);
+    status = hdf_file->read("NZV", "INT32", &nzv);
+    status = hdf_file->read("VK", "FLOAT64", &vk);
+    status = hdf_file->read("EXRI", "FLOAT64", &exri);
+    status = hdf_file->read("AN", "FLOAT64", &an);
+    status = hdf_file->read("FF", "FLOAT64", &ff);
+    status = hdf_file->read("TRA", "FLOAT64", &tra);
+    status = hdf_file->read("SPD", "FLOAT64", &spd);
+    status = hdf_file->read("FRSH", "FLOAT64", &frsh);
+    status = hdf_file->read("EXRIL", "FLOAT64", &exril);
+    instance = new TFRFME(lmode, lm, nkv, nxv, nyv, nzv);
+    instance->set_param("vk", vk);
+    instance->set_param("exri", exri);
+    instance->set_param("an", an);
+    instance->set_param("ff", ff);
+    instance->set_param("tra", tra);
+    instance->set_param("spd", spd);
+    instance->set_param("frsh", frsh);
+    instance->set_param("exril", exril);
+    // DA QUI: aggiungere lettura di vettori e matrice.
+  }
+  return instance;
+}
+
+TFRFME* TFRFME::from_legacy(string file_name) {
+  fstream input;
   TFRFME *instance = NULL;
+  input.open(file_name.c_str(), ios::in | ios::binary);
+  if (input.is_open()) {
+    int lmode, lm, nkv, nxv, nyv, nzv;
+    double vk, exri, an, ff, tra, spd, frsh, exril;
+    double dval, rval, ival;
+    input.read(reinterpret_cast<char *>(&lmode), sizeof(int));
+    input.read(reinterpret_cast<char *>(&lm), sizeof(int));
+    input.read(reinterpret_cast<char *>(&nkv), sizeof(int));
+    input.read(reinterpret_cast<char *>(&nxv), sizeof(int));
+    input.read(reinterpret_cast<char *>(&nyv), sizeof(int));
+    input.read(reinterpret_cast<char *>(&nzv), sizeof(int));
+    input.read(reinterpret_cast<char *>(&vk), sizeof(double));
+    input.read(reinterpret_cast<char *>(&exri), sizeof(double));
+    input.read(reinterpret_cast<char *>(&an), sizeof(double));
+    input.read(reinterpret_cast<char *>(&ff), sizeof(double));
+    input.read(reinterpret_cast<char *>(&tra), sizeof(double));
+    input.read(reinterpret_cast<char *>(&spd), sizeof(double));
+    input.read(reinterpret_cast<char *>(&frsh), sizeof(double));
+    input.read(reinterpret_cast<char *>(&exril), sizeof(double));
+    instance = new TFRFME(lmode, lm, nkv, nxv, nyv, nzv);
+    instance->set_param("vk", vk);
+    instance->set_param("exri", exri);
+    instance->set_param("an", an);
+    instance->set_param("ff", ff);
+    instance->set_param("tra", tra);
+    instance->set_param("spd", spd);
+    instance->set_param("frsh", frsh);
+    instance->set_param("exril", exril);
+    for (int xi = 0; xi < nxv; xi++) {
+      input.read(reinterpret_cast<char *>(&dval), sizeof(double));
+      instance->set_x(xi, dval);
+    }
+    for (int yi = 0; yi < nyv; yi++) {
+      input.read(reinterpret_cast<char *>(&dval), sizeof(double));
+      instance->set_y(yi, dval);
+    }
+    for (int zi = 0; zi < nzv; zi++) {
+      input.read(reinterpret_cast<char *>(&dval), sizeof(double));
+      instance->set_z(zi, dval);
+    }
+    int _nlmmt = 2 * lm * (lm + 2);
+    int _nrvc = nxv * nyv * nzv;
+    for (int wi = 0; wi < _nlmmt; wi++) {
+      for (int wj = 0; wj < _nrvc; wj++) {
+	input.read(reinterpret_cast<char *>(&rval), sizeof(double));
+	input.read(reinterpret_cast<char *>(&ival), sizeof(double));
+	complex<double> value(rval, ival);
+	instance->set_matrix_element(wi, wj, value);
+      } // wj loop
+    } // wi loop
+  } else {
+    printf("ERROR: could not open input file \"%s\"\n", file_name.c_str());
+  }
   return instance;
 }
 
-TFRFME::double get_param(string param_name) {
+std::complex<double> TFRFME::get_matrix_element(int row, int col) {
+  return wsum[row][col];
+}
+
+double TFRFME::get_param(string param_name) {
   double value;
-  if (param_name.compare("exri") == 0) value = exri;
+  if (param_name.compare("vk") == 0) value = vk;
+  else if (param_name.compare("exri") == 0) value = exri;
   else if (param_name.compare("an") == 0) value = an;
   else if (param_name.compare("ff") == 0) value = ff;
   else if (param_name.compare("tra") == 0) value = tra;
   else if (param_name.compare("spd") == 0) value = spd;
   else if (param_name.compare("frsh") == 0) value = frsh;
   else if (param_name.compare("exril") == 0) value = exril;
-  //TODO: add else clause with exception.
+  else {
+    string message = "Unrecognized parameter name \"" + param_name + "\"";
+    UnrecognizedParameterException ex(message);
+    throw ex;
+  }
   return value;
 }
 
-TFRFME::void write_binary(string file_name, string format) {
-  return;
+void TFRFME::set_matrix_element(int row, int col, complex<double> value) {
+  wsum[row][col] = value;
+}
+
+void TFRFME::set_param(string param_name, double value) {
+  if (param_name.compare("vk") == 0) vk = value;
+  else if (param_name.compare("exri") == 0) exri = value;
+  else if (param_name.compare("an") == 0) an = value;
+  else if (param_name.compare("ff") == 0) ff = value;
+  else if (param_name.compare("tra") == 0) tra = value;
+  else if (param_name.compare("spd") == 0) spd = value;
+  else if (param_name.compare("frsh") == 0) frsh = value;
+  else if (param_name.compare("exril") == 0) exril = value;
+  else {
+    string message = "Unrecognized parameter name \"" + param_name + "\"";
+    UnrecognizedParameterException ex(message);
+    throw ex;
+  }
+}
+
+void TFRFME::write_binary(string file_name, string mode) {
+  if (mode.compare("LEGACY") == 0) {
+    write_legacy(file_name);
+  } else if (mode.compare("HDF5") == 0) {
+    write_hdf5(file_name);
+  } /* else {
+    string message = "Unknown format mode: \"" + mode + "\"";
+    throw UnrecognizedFormatException(message);
+    } */
+}
+
+void TFRFME::write_hdf5(string file_name) {
+  List<string> rec_name_list(1);
+  List<string> rec_type_list(1);
+  List<void *> rec_ptr_list(1);
+  string str_type;
+  rec_name_list.set(0, "LMODE");
+  rec_type_list.set(0, "INT32_(1)");
+  rec_ptr_list.set(0, &lmode);
+  rec_name_list.append("LM");
+  rec_type_list.append("INT32_(1)");
+  rec_ptr_list.append(&lm);
+  rec_name_list.append("NKV");
+  rec_type_list.append("INT32_(1)");
+  rec_ptr_list.append(&nkv);
+  rec_name_list.append("NXV");
+  rec_type_list.append("INT32_(1)");
+  rec_ptr_list.append(&nxv);
+  rec_name_list.append("NYV");
+  rec_type_list.append("INT32_(1)");
+  rec_ptr_list.append(&nyv);
+  rec_name_list.append("NZV");
+  rec_type_list.append("INT32_(1)");
+  rec_ptr_list.append(&nzv);
+  rec_name_list.append("VK");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&lm);
+  rec_name_list.append("EXRI");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&exri);
+  rec_name_list.append("AN");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&an);
+  rec_name_list.append("FF");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&ff);
+  rec_name_list.append("TRA");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&tra);
+  rec_name_list.append("SPD");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&spd);
+  rec_name_list.append("FRSH");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&frsh);
+  rec_name_list.append("EXRIL");
+  rec_type_list.append("FLOAT64_(1)");
+  rec_ptr_list.append(&exril);
+  str_type = "FLOAT64_(" + to_string(nxv) + ")";
+  rec_name_list.append("XVEC");
+  rec_type_list.append(str_type);
+  rec_ptr_list.append(xv);
+  str_type = "FLOAT64_(" + to_string(nyv) + ")";
+  rec_name_list.append("YVEC");
+  rec_type_list.append(str_type);
+  rec_ptr_list.append(yv);
+  str_type = "FLOAT64_(" + to_string(nzv) + ")";
+  rec_name_list.append("ZVEC");
+  rec_type_list.append(str_type);
+  rec_ptr_list.append(zv);
+  rec_name_list.append("WSUM");
+  str_type = "FLOAT64_(" + to_string(nlmmt) + "," + to_string(2 * nrvc) + ")";
+  rec_type_list.append(str_type);
+  // The (N x M) matrix of complex is converted to a (N x 2M) matrix of double,
+  // where REAL(E_i,j) -> E_i,(2 j) and IMAG(E_i,j) -> E_i,(2 j + 1)
+  int num_elements = 2 * nlmmt * nrvc;
+  double * ptr_elements = new double[num_elements]();
+  for (int wi = 0; wi < nlmmt; wi++) {
+    for (int wj = 0; wj < nrvc; wj++) {
+      int index = (2 * nrvc) * wi + 2 * wj;
+      ptr_elements[index] = wsum[wi][wj].real();
+      ptr_elements[index + 1] = wsum[wi][wj].imag();
+    } // wj loop
+  } // wi loop
+  rec_ptr_list.append(ptr_elements);
+
+  string *rec_names = rec_name_list.to_array();
+  string *rec_types = rec_type_list.to_array();
+  void **rec_pointers = rec_ptr_list.to_array();
+  const int rec_num = rec_name_list.length();
+  FileSchema schema(rec_num, rec_types, rec_names);
+  HDFFile *hdf_file = HDFFile::from_schema(schema, file_name, H5F_ACC_TRUNC);
+  for (int ri = 0; ri < rec_num; ri++)
+    hdf_file->write(rec_names[ri], rec_types[ri], rec_pointers[ri]);
+  hdf_file->close();
+
+  delete[] ptr_elements;
+  delete[] rec_names;
+  delete[] rec_types;
+  delete[] rec_pointers;
+  delete hdf_file;
+}
+
+void TFRFME::write_legacy(string file_name) {
+  fstream output;
+  output.open(file_name.c_str(), ios::out | ios::binary);
+  if (output.is_open()) {
+    output.write(reinterpret_cast<char *>(&lmode), sizeof(int));
+    output.write(reinterpret_cast<char *>(&lm), sizeof(int));
+    output.write(reinterpret_cast<char *>(&nkv), sizeof(int));
+    output.write(reinterpret_cast<char *>(&nxv), sizeof(int));
+    output.write(reinterpret_cast<char *>(&nyv), sizeof(int));
+    output.write(reinterpret_cast<char *>(&nzv), sizeof(int));
+    output.write(reinterpret_cast<char *>(&vk), sizeof(double));
+    output.write(reinterpret_cast<char *>(&exri), sizeof(double));
+    output.write(reinterpret_cast<char *>(&an), sizeof(double));
+    output.write(reinterpret_cast<char *>(&ff), sizeof(double));
+    output.write(reinterpret_cast<char *>(&tra), sizeof(double));
+    output.write(reinterpret_cast<char *>(&spd), sizeof(double));
+    output.write(reinterpret_cast<char *>(&frsh), sizeof(double));
+    output.write(reinterpret_cast<char *>(&exril), sizeof(double));
+    for (int xi = 0; xi < nxv; xi++)
+      output.write(reinterpret_cast<char *>(&(xv[xi])), sizeof(double));
+    for (int yi = 0; yi < nyv; yi++)
+      output.write(reinterpret_cast<char *>(&(yv[yi])), sizeof(double));
+    for (int zi = 0; zi < nzv; zi++)
+      output.write(reinterpret_cast<char *>(&(zv[zi])), sizeof(double));
+    for (int wi = 0; wi < nlmmt; wi++) {
+      for (int wj = 0; wj < nrvc; wj++) {
+	double rval = wsum[wi][wj].real();
+	double ival = wsum[wi][wj].imag();
+	output.write(reinterpret_cast<char *>(&rval), sizeof(double));
+	output.write(reinterpret_cast<char *>(&ival), sizeof(double));
+      } // wj loop
+    } // wi loop
+  } else { // Should never occur
+    printf("ERROR: could not open output file \"%s\"\n", file_name.c_str());
+  }
 }
-- 
GitLab