diff --git a/assets/eos/helm_table.dat b/assets/dynamic/eos/helm_table.dat similarity index 100% rename from assets/eos/helm_table.dat rename to assets/dynamic/eos/helm_table.dat diff --git a/assets/dynamic/index.yaml b/assets/dynamic/index.yaml new file mode 100644 index 0000000..a37aa49 --- /dev/null +++ b/assets/dynamic/index.yaml @@ -0,0 +1,4 @@ +eos: + helm: "eos/helm_table.dat" +mesh: + sphere: "mesh/sphere.msh" \ No newline at end of file diff --git a/src/resources/mesh/sphere.msh b/assets/dynamic/mesh/sphere.msh similarity index 100% rename from src/resources/mesh/sphere.msh rename to assets/dynamic/mesh/sphere.msh diff --git a/src/resources/const/const.dat b/assets/static/const/const.dat similarity index 100% rename from src/resources/const/const.dat rename to assets/static/const/const.dat diff --git a/src/resources/const/format.sh b/assets/static/const/format.sh similarity index 100% rename from src/resources/const/format.sh rename to assets/static/const/format.sh diff --git a/src/resources/const/meson.build b/assets/static/const/meson.build similarity index 100% rename from src/resources/const/meson.build rename to assets/static/const/meson.build diff --git a/src/resources/meson.build b/assets/static/meson.build similarity index 100% rename from src/resources/meson.build rename to assets/static/meson.build diff --git a/meson.build b/meson.build index f5476a9..bd29da1 100644 --- a/meson.build +++ b/meson.build @@ -2,11 +2,28 @@ project('4DSSE', 'cpp', version: '0.0.1a', default_options: ['cpp_std=c++23'], m # Add default visibility for all C++ targets add_project_arguments('-fvisibility=default', language: 'cpp') +# Determine the mode +mode = 1 +if get_option('user_mode') + mode = 0 +endif + +# Define DATA_DIR based on mode +if mode == 1 + data_dir = meson.project_source_root() + '/assets/dynamic' +else + data_dir = get_option('prefix') + '/' + get_option('datadir') + '/4DSSE' +endif + +# Pass the DATA_DIR definition to the compiler +add_project_arguments('-DDATA_DIR=' + data_dir, language : 'cpp') # Build external dependencies first so that all the embedded resources are available to the other targets subdir('build-config') subdir('subprojects/PicoSHA2') +subdir('assets/static') + # Build the main project subdir('src') if get_option('build_tests') diff --git a/meson_options.txt b/meson_options.txt index c3b5afd..56baeee 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('build_tests', type: 'boolean', value: true, description: 'Build tests') +option('user_mode', type: 'boolean', value: false, description: 'Enable user mode (set mode = 0)') diff --git a/src/config/private/config.cpp b/src/config/private/config.cpp index ffce7a3..76e7d43 100644 --- a/src/config/private/config.cpp +++ b/src/config/private/config.cpp @@ -46,6 +46,7 @@ bool Config::loadConfig(const std::string& configFile) { std::cerr << "Error: " << e.what() << std::endl; return false; } + m_loaded = true; return true; } @@ -60,3 +61,45 @@ void Config::addToCache(const std::string &key, const YAML::Node &node) { void Config::registerUnknownKey(const std::string &key) { unknownKeys.push_back(key); } + +bool Config::has(const std::string &key) { + if (!m_loaded) { + throw std::runtime_error("Error! Config file not loaded"); + } + if (isKeyInCache(key)) { return true; } + + YAML::Node node = YAML::Clone(yamlRoot); + std::istringstream keyStream(key); + std::string subKey; + while (std::getline(keyStream, subKey, ':')) { + if (!node[subKey]) { + registerUnknownKey(key); + return false; + } + node = node[subKey]; // go deeper + } + + // Key exists and is of the requested type + addToCache(key, node); + return true; +} + +void recurse_keys(const YAML::Node& node, std::vector& keyList, const std::string& path = "") { + if (node.IsMap()) { + for (const auto& it : node) { + std::string key = it.first.as(); + std::string new_path = path.empty() ? key : path + ":" + key; + recurse_keys(it.second, keyList, new_path); + } + } else { + keyList.push_back(path); + } + +} + +std::vector Config::keys() const { + std::vector keyList; + YAML::Node node = YAML::Clone(yamlRoot); + recurse_keys(node, keyList); + return keyList; +} \ No newline at end of file diff --git a/src/config/public/config.h b/src/config/public/config.h index e8b96a0..be33163 100644 --- a/src/config/public/config.h +++ b/src/config/public/config.h @@ -23,14 +23,18 @@ #include #include -#include #include #include #include #include +#include +// Required for YAML parsing #include "yaml-cpp/yaml.h" +// -- Forward Def of Resource manager to let it act as a friend of Config -- +class ResourceManager; + /** * @class Config * @brief Singleton class to manage configuration settings loaded from a YAML file. @@ -94,6 +98,21 @@ private: */ void registerUnknownKey(const std::string &key); + bool m_loaded = false; + + // Only friends can access get without a default value + template + T get(const std::string &key) { + if (!m_loaded) { + throw std::runtime_error("Error! Config file not loaded"); + } + if (has(key)) { + return getFromCache(key, T()); + } else { + throw std::runtime_error("Error! Key not found in config file"); + } + } + public: /** * @brief Get the singleton instance of the Config class. @@ -136,6 +155,9 @@ public: */ template T get(const std::string &key, T defaultValue) { + if (!m_loaded) { + throw std::runtime_error("Error! Config file not loaded"); + } // --- Check if the key has already been checked for existence if (std::find(unknownKeys.begin(), unknownKeys.end(), key) != unknownKeys.end()) { return defaultValue; // If the key has already been added to the unknown cache do not traverse the YAML tree or hit the cache @@ -171,6 +193,19 @@ public: } } + /** + * @brief Check if the key exists in the given config file + * @param key Key to check; + * @return boolean true or false + */ + bool has(const std::string &key); + + /** + * @brief Get all keys defined in the configuration file. + * @return Vector of all keys in the configuration file. + */ + std::vector keys() const; + /** * @brief Print the configuration file path and the YAML root node. * @param os Output stream. @@ -178,6 +213,10 @@ public: * @return Output stream. */ friend std::ostream& operator<<(std::ostream& os, const Config& config) { + if (!config.m_loaded) { + os << "Config file not loaded" << std::endl; + return os; + } if (!config.debug) { os << "Config file: " << config.configFilePath << std::endl; } else{ @@ -190,6 +229,8 @@ public: // Setup gTest class as a friend friend class configTestPrivateAccessor; + // -- Resource Manager is a friend of config so it can create a seperate instance + friend class ResourceManager; }; #endif \ No newline at end of file diff --git a/src/eos/meson.build b/src/eos/meson.build index ca3e18f..f9979b4 100644 --- a/src/eos/meson.build +++ b/src/eos/meson.build @@ -1,23 +1,34 @@ # Define the library eos_sources = files( 'private/helm.cpp', + 'private/eosIO.cpp' ) eos_headers = files( - 'public/helm.h' + 'public/helm.h', + 'public/eosIO.h' ) +dependencies = [ + const_dep, + quill_dep, + probe_dep, + config_dep, + mfem_dep, + macros_dep, +] # Define the libconst library so it can be linked against by other parts of the build system libeos = static_library('eos', eos_sources, include_directories: include_directories('public'), cpp_args: ['-fvisibility=default'], - dependencies: [const_dep, quill_dep, probe_dep, config_dep, mfem_dep], + dependencies: dependencies, install : true) eos_dep = declare_dependency( include_directories: include_directories('public'), link_with: libeos, + dependencies: dependencies ) # Make headers accessible install_headers(eos_headers, subdir : '4DSSE/eos') \ No newline at end of file diff --git a/src/eos/private/eosIO.cpp b/src/eos/private/eosIO.cpp new file mode 100644 index 0000000..ba3e471 --- /dev/null +++ b/src/eos/private/eosIO.cpp @@ -0,0 +1,36 @@ +#include + +#include "eosIO.h" +#include "helm.h" +#include "debug.h" + +EosIO::EosIO(const std::string filename) : m_filename(filename) { + load(); +} + +std::string EosIO::getFormat() const { + return m_format; +} + + +EOSTable& EosIO::getTable() { + return m_table; +} + +void EosIO::load() { + // Load the EOS table from the file + // For now, just set the format to HELM + + m_format = "helm"; + if (m_format == "helm") { + loadHelm(); + } +} + +void EosIO::loadHelm() { + // Load the HELM table from the file + auto helmTabptr = helmholtz::read_helm_table(m_filename); + m_table = std::move(helmTabptr); + m_loaded = true; +} + diff --git a/src/eos/private/helm.cpp b/src/eos/private/helm.cpp index 90c482e..ed60a3e 100644 --- a/src/eos/private/helm.cpp +++ b/src/eos/private/helm.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -121,21 +122,22 @@ namespace helmholtz { } // this function reads in the HELM table and stores in the above arrays - HELMTable read_helm_table(const std::string filename) { + std::unique_ptr read_helm_table(const std::string filename) { Config& config = Config::getInstance(); std::string logFile = config.get("EOS:Helm:LogFile", "log"); Probe::LogManager& logManager = Probe::LogManager::getInstance(); quill::Logger* logger = logManager.getLogger(logFile); LOG_INFO(logger, "read_helm_table : Reading HELM table from file {}", filename); - HELMTable table; + // Make a unique pointer to the HELMTable + std::unique_ptr table = std::make_unique(); string data; int i, j; //set T and Rho (d) arrays - for (j=0; jjmax; j++) { table->t[j] = pow(10, tlo + tstp*j); } - for (i=0; iimax; i++) { table->d[i] = pow(10, dlo + dstp*i); } ifstream helm_table(filename); if (!helm_table) { @@ -144,83 +146,83 @@ namespace helmholtz { throw std::runtime_error("Error (" + std::to_string(errorCode) + ") opening file " + filename); } //read the Helmholtz free energy and its derivatives - for (j=0; jjmax; j++) { + for (i=0; iimax; i++){ getline(helm_table, data); stringstream id(data); - id >> table.f[i][j]; - id >> table.fd[i][j]; - id >> table.ft[i][j]; - id >> table.fdd[i][j]; - id >> table.ftt[i][j]; - id >> table.fdt[i][j]; - id >> table.fddt[i][j]; - id >> table.fdtt[i][j]; - id >> table.fddtt[i][j]; + id >> table->f[i][j]; + id >> table->fd[i][j]; + id >> table->ft[i][j]; + id >> table->fdd[i][j]; + id >> table->ftt[i][j]; + id >> table->fdt[i][j]; + id >> table->fddt[i][j]; + id >> table->fdtt[i][j]; + id >> table->fddtt[i][j]; } } //read the pressure derivative with density - for (j=0; jjmax; j++) { + for (i=0; iimax; i++){ getline(helm_table, data); stringstream id(data); - id >> table.dpdf[i][j]; - id >> table.dpdfd[i][j]; - id >> table.dpdft[i][j]; - id >> table.dpdfdt[i][j]; + id >> table->dpdf[i][j]; + id >> table->dpdfd[i][j]; + id >> table->dpdft[i][j]; + id >> table->dpdfdt[i][j]; } } //read the electron chemical potential - for (j=0; jjmax; j++) { + for (i=0; iimax; i++){ getline(helm_table, data); stringstream id(data); - id >> table.ef[i][j]; - id >> table.efd[i][j]; - id >> table.eft[i][j]; - id >> table.efdt[i][j]; + id >> table->ef[i][j]; + id >> table->efd[i][j]; + id >> table->eft[i][j]; + id >> table->efdt[i][j]; } } //read the number density - for (j=0; jjmax; j++) { + for (i=0; iimax; i++){ getline(helm_table, data); stringstream id(data); - id >> table.xf[i][j]; - id >> table.xfd[i][j]; - id >> table.xft[i][j]; - id >> table.xfdt[i][j]; + id >> table->xf[i][j]; + id >> table->xfd[i][j]; + id >> table->xft[i][j]; + id >> table->xfdt[i][j]; } } helm_table.close(); //done reading // construct the temperature and density deltas and their inverses - for (j=0; jjmax; j++) { + double dth = table->t[j+1] - table->t[j]; double dt2 = dth * dth; double dti = 1.0/dth; double dt2i = 1.0/dt2; - table.dt_sav[j] = dth; - table.dt2_sav[j] = dt2; - table.dti_sav[j] = dti; - table.dt2i_sav[j] = dt2i; + table->dt_sav[j] = dth; + table->dt2_sav[j] = dt2; + table->dti_sav[j] = dti; + table->dt2i_sav[j] = dt2i; } - for (i=0; iimax; i++) { + double dd = table->d[i+1] - table->d[i]; double dd2 = dd * dd; double ddi = 1.0/dd; - table.dd_sav[i] = dd; - table.dd2_sav[i] = dd2; - table.ddi_sav[i] = ddi; + table->dd_sav[i] = dd; + table->dd2_sav[i] = dd2; + table->ddi_sav[i] = ddi; } - table.loaded = true; + table->loaded = true; return table; } diff --git a/src/eos/public/eosIO.h b/src/eos/public/eosIO.h new file mode 100644 index 0000000..aab0c59 --- /dev/null +++ b/src/eos/public/eosIO.h @@ -0,0 +1,70 @@ +#ifndef EOSIO_H +#define EOSIO_H + +#include +#include +#include + +// EOS table format includes +#include "helm.h" + +using EOSTable = std::variant< + std::unique_ptr + >; + +/** + * @class EosIO + * @brief Handles the input/output operations for EOS tables. + * + * The EosIO class is responsible for loading and managing EOS tables from files. + * It supports different formats, currently only HELM format. + * + * Example usage: + * @code + * EosIO eosIO("path/to/file"); + * std::string format = eosIO.getFormat(); + * EOSTable& table = eosIO.getTable(); + * @endcode + */ +class EosIO { +private: + std::string m_filename; ///< The filename of the EOS table. + bool m_loaded = false; ///< Flag indicating if the table is loaded. + std::string m_format; ///< The format of the EOS table. + EOSTable m_table; ///< The EOS table data. + + /** + * @brief Loads the EOS table from the file. + */ + void load(); + + /** + * @brief Loads the HELM format EOS table. + */ + void loadHelm(); +public: + /** + * @brief Constructs an EosIO object with the given filename. + * @param filename The filename of the EOS table. + */ + EosIO(const std::string filename); + + /** + * @brief Default destructor. + */ + ~EosIO() = default; + + /** + * @brief Gets the format of the EOS table. + * @return The format of the EOS table as a string. + */ + std::string getFormat() const; + + /** + * @brief Gets the EOS table. + * @return A reference to the EOS table. + */ + EOSTable& getTable(); +}; + +#endif // EOSIO_H \ No newline at end of file diff --git a/src/eos/public/helm.h b/src/eos/public/helm.h index bdbfdad..64e7a8a 100644 --- a/src/eos/public/helm.h +++ b/src/eos/public/helm.h @@ -35,6 +35,8 @@ #include #include +#include "debug.h" + /** * @brief 2D array template alias. * @tparam T Type of the array elements. @@ -139,6 +141,118 @@ namespace helmholtz heap_deallocate_contiguous_2D_memory(xfdt); } + // // Delete copy constructor and copy assignment operator to prevent accidental shallow copies + // HELMTable(const HELMTable&) = delete; + // HELMTable& operator=(const HELMTable&) = delete; + + // // Move constructor + // HELMTable(HELMTable&& other) noexcept + // : loaded(other.loaded), + // f(other.f), fd(other.fd), ft(other.ft), fdd(other.fdd), ftt(other.ftt), fdt(other.fdt), + // fddt(other.fddt), fdtt(other.fdtt), fddtt(other.fddtt), + // dpdf(other.dpdf), dpdfd(other.dpdfd), dpdft(other.dpdft), dpdfdt(other.dpdfdt), + // ef(other.ef), efd(other.efd), eft(other.eft), efdt(other.efdt), + // xf(other.xf), xfd(other.xfd), xft(other.xft), xfdt(other.xfdt) + // { + // other.f = nullptr; + // other.fd = nullptr; + // other.ft = nullptr; + // other.fdd = nullptr; + // other.ftt = nullptr; + // other.fdt = nullptr; + // other.fddt = nullptr; + // other.fdtt = nullptr; + // other.fddtt = nullptr; + // other.dpdf = nullptr; + // other.dpdfd = nullptr; + // other.dpdft = nullptr; + // other.dpdfdt = nullptr; + // other.ef = nullptr; + // other.efd = nullptr; + // other.eft = nullptr; + // other.efdt = nullptr; + // other.xf = nullptr; + // other.xfd = nullptr; + // other.xft = nullptr; + // other.xfdt = nullptr; + // } + + // // Move assignment operator + // HELMTable& operator=(HELMTable&& other) noexcept { + // if (this != &other) { + // // Deallocate current memory + // heap_deallocate_contiguous_2D_memory(f); + // heap_deallocate_contiguous_2D_memory(fd); + // heap_deallocate_contiguous_2D_memory(ft); + // heap_deallocate_contiguous_2D_memory(fdd); + // heap_deallocate_contiguous_2D_memory(ftt); + // heap_deallocate_contiguous_2D_memory(fdt); + // heap_deallocate_contiguous_2D_memory(fddt); + // heap_deallocate_contiguous_2D_memory(fdtt); + // heap_deallocate_contiguous_2D_memory(fddtt); + // heap_deallocate_contiguous_2D_memory(dpdf); + // heap_deallocate_contiguous_2D_memory(dpdfd); + // heap_deallocate_contiguous_2D_memory(dpdft); + // heap_deallocate_contiguous_2D_memory(dpdfdt); + // heap_deallocate_contiguous_2D_memory(ef); + // heap_deallocate_contiguous_2D_memory(efd); + // heap_deallocate_contiguous_2D_memory(eft); + // heap_deallocate_contiguous_2D_memory(efdt); + // heap_deallocate_contiguous_2D_memory(xf); + // heap_deallocate_contiguous_2D_memory(xfd); + // heap_deallocate_contiguous_2D_memory(xft); + // heap_deallocate_contiguous_2D_memory(xfdt); + + // // Transfer ownership of resources + // loaded = other.loaded; + // f = other.f; + // fd = other.fd; + // ft = other.ft; + // fdd = other.fdd; + // ftt = other.ftt; + // fdt = other.fdt; + // fddt = other.fddt; + // fdtt = other.fdtt; + // fddtt = other.fddtt; + // dpdf = other.dpdf; + // dpdfd = other.dpdfd; + // dpdft = other.dpdft; + // dpdfdt = other.dpdfdt; + // ef = other.ef; + // efd = other.efd; + // eft = other.eft; + // efdt = other.efdt; + // xf = other.xf; + // xfd = other.xfd; + // xft = other.xft; + // xfdt = other.xfdt; + + // // Null out the other object's pointers + // other.f = nullptr; + // other.fd = nullptr; + // other.ft = nullptr; + // other.fdd = nullptr; + // other.ftt = nullptr; + // other.fdt = nullptr; + // other.fddt = nullptr; + // other.fdtt = nullptr; + // other.fddtt = nullptr; + // other.dpdf = nullptr; + // other.dpdfd = nullptr; + // other.dpdft = nullptr; + // other.dpdfdt = nullptr; + // other.ef = nullptr; + // other.efd = nullptr; + // other.eft = nullptr; + // other.efdt = nullptr; + // other.xf = nullptr; + // other.xfd = nullptr; + // other.xft = nullptr; + // other.xfdt = nullptr; + // } + // return *this; + // } + friend std::ostream& operator<<(std::ostream& os, const helmholtz::HELMTable& table) { if (!table.loaded) { os << "HELMTable not loaded\n"; @@ -371,7 +485,7 @@ namespace helmholtz * @param filename Path to the file containing the table. * @return HELMTable structure containing the table data. */ - HELMTable read_helm_table(const std::string filename); + std::unique_ptr read_helm_table(const std::string filename); /** * @brief Calculate the Helmholtz EOS components. diff --git a/src/meshIO/meson.build b/src/meshIO/meson.build index 1ea870a..a684138 100644 --- a/src/meshIO/meson.build +++ b/src/meshIO/meson.build @@ -6,14 +6,22 @@ meshIO_sources = files( meshIO_headers = files( 'public/meshIO.h' ) - +dependencies = [ + mfem_dep +] # Define the libmeshIO library so it can be linked against by other parts of the build system libmeshIO = static_library('meshIO', meshIO_sources, include_directories: include_directories('public'), cpp_args: ['-fvisibility=default'], - dependencies: [mfem_dep], + dependencies: dependencies, install : true) +meshio_dep = declare_dependency( + include_directories: include_directories('public'), + link_with: libmeshIO, + dependencies: dependencies +) + # Make headers accessible install_headers(meshIO_headers, subdir : '4DSSE/meshIO') \ No newline at end of file diff --git a/src/meshIO/private/meshIO.cpp b/src/meshIO/private/meshIO.cpp index 01619bb..a894a14 100644 --- a/src/meshIO/private/meshIO.cpp +++ b/src/meshIO/private/meshIO.cpp @@ -26,7 +26,7 @@ #include "meshIO.h" -MeshIO::MeshIO(const std::string &mesh_file) +MeshIO::MeshIO(const std::string mesh_file) { mesh_file_ = mesh_file; std::ifstream mesh_stream(mesh_file); diff --git a/src/meshIO/public/meshIO.h b/src/meshIO/public/meshIO.h index aad8bf7..1a2c4db 100644 --- a/src/meshIO/public/meshIO.h +++ b/src/meshIO/public/meshIO.h @@ -39,7 +39,7 @@ public: * @brief Constructor that initializes the MeshIO object with a mesh file. * @param mesh_file The name of the mesh file. */ - MeshIO(const std::string &mesh_file); + MeshIO(const std::string mesh_file); /** * @brief Destructor for the MeshIO class. diff --git a/src/meson.build b/src/meson.build index 3547118..d2d2399 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,11 +1,18 @@ -# Build resources first so that all the embedded resources are available to the other targets -subdir('resources') +# Build the main source code in the correct order -# Build the main source code -subdir('dobj') -subdir('const') -subdir('opatIO') -subdir('meshIO') +# Utility Libraries +subdir('misc') subdir('config') subdir('probe') -subdir('eos') \ No newline at end of file +subdir('const') +subdir('dobj') + +# Asset Libraries +subdir('eos') +subdir('opatIO') +subdir('meshIO') + +# Resouce Manager Libraries +subdir('resource') + +# Physics Libraries \ No newline at end of file diff --git a/src/misc/macros/debug.h b/src/misc/macros/debug.h new file mode 100644 index 0000000..284268d --- /dev/null +++ b/src/misc/macros/debug.h @@ -0,0 +1,36 @@ +/** + * @file debug.h + * @brief Defines a macro for triggering a breakpoint in different compilers and platforms. + * + * This file provides a macro `BREAKPOINT()` that triggers a breakpoint + * in the debugger, depending on the compiler and platform being used. + * + * Usage: + * @code + * BREAKPOINT(); // Triggers a breakpoint in the debugger + * @endcode + */ + +#ifdef __GNUC__ // GCC and Clang + /** + * @brief Triggers a breakpoint in GCC and Clang. + */ + #define BREAKPOINT() __builtin_debugtrap() +#elif defined(_MSC_VER) // MSVC + /** + * @brief Triggers a breakpoint in MSVC. + */ + #define BREAKPOINT() __debugbreak() +#elif defined(__APPLE__) && defined(__MACH__) // macOS with Clang and LLDB + #include + /** + * @brief Triggers a breakpoint in macOS with Clang and LLDB. + */ + #define BREAKPOINT() raise(SIGTRAP) +#else + #include + /** + * @brief Triggers a breakpoint in other platforms. + */ + #define BREAKPOINT() std::raise(SIGTRAP) +#endif diff --git a/src/misc/macros/meson.build b/src/misc/macros/meson.build new file mode 100644 index 0000000..f91a3b9 --- /dev/null +++ b/src/misc/macros/meson.build @@ -0,0 +1,21 @@ +# *********************************************************************** +# +# Copyright (C) 2025 -- The 4D-STAR Collaboration +# File Author: Emily Boudreaux +# Last Modified: March 19, 2025 +# +# 4DSSE is free software; you can use it and/or modify +# it under the terms and restrictions the GNU General Library Public +# License version 3 (GPLv3) as published by the Free Software Foundation. +# +# 4DSSE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this software; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# *********************************************************************** # +macros_dep = declare_dependency(include_directories: include_directories('.')) diff --git a/src/misc/macros/warning_control.h b/src/misc/macros/warning_control.h new file mode 100644 index 0000000..23bf5a8 --- /dev/null +++ b/src/misc/macros/warning_control.h @@ -0,0 +1,16 @@ +#ifndef WARNING_CONTROL_H +#define WARNING_CONTROL_H + +#if defined(__GNUC__) || defined(__clang__) + #define DEPRECATION_WARNING_OFF _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #define DEPRECATION_WARNING_ON _Pragma("GCC diagnostic pop") +#elif defined(_MSC_VER) + #define DEPRECATION_WARNING_OFF __pragma(warning(push)) __pragma(warning(disable: 4996)) + #define DEPRECATION_WARNING_ON __pragma(warning(pop)) +#else + #define DEPRECATION_WARNING_OFF + #define DEPRECATION_WARNING_ON +#endif + +#endif // WARNING_CONTROL_H diff --git a/src/misc/meson.build b/src/misc/meson.build new file mode 100644 index 0000000..196a8e4 --- /dev/null +++ b/src/misc/meson.build @@ -0,0 +1,2 @@ +# IMPORTANT: DO NOT MAKE MISC DEPEND ON ANY OTHER MODULE AS IT IS THE FIRST MODULE TO BE BUILT +subdir('macros') diff --git a/src/opatIO/meson.build b/src/opatIO/meson.build index c2229bc..238ee6f 100644 --- a/src/opatIO/meson.build +++ b/src/opatIO/meson.build @@ -16,5 +16,9 @@ libopatIO = library('opatIO', install : true, ) +opatio_dep = declare_dependency( + include_directories: include_directories('public'), + link_with: libopatIO, +) # Make headers accessible install_headers(opatIO_headers, subdir : '4DSSE/opatIO') \ No newline at end of file diff --git a/src/opatIO/private/opatIO.cpp b/src/opatIO/private/opatIO.cpp index 43960f5..bdbf45b 100644 --- a/src/opatIO/private/opatIO.cpp +++ b/src/opatIO/private/opatIO.cpp @@ -56,7 +56,7 @@ T swap_bytes(T value) { // Constructor OpatIO::OpatIO() {} -OpatIO::OpatIO(std::string filename) : filename(filename) { +OpatIO::OpatIO(const std::string filename) : filename(filename) { load(); } diff --git a/src/opatIO/public/opatIO.h b/src/opatIO/public/opatIO.h index f822420..49b6bcf 100644 --- a/src/opatIO/public/opatIO.h +++ b/src/opatIO/public/opatIO.h @@ -21,7 +21,6 @@ #ifndef OPATIO_H #define OPATIO_H -#include #include #include #include diff --git a/src/resource/meson.build b/src/resource/meson.build new file mode 100644 index 0000000..bc32862 --- /dev/null +++ b/src/resource/meson.build @@ -0,0 +1,40 @@ +# Define the library +resourceManager_sources = files( + 'private/resourceManager.cpp', + 'private/resourceManagerTypes.cpp' +) + +resourceManager_headers = files( + 'public/resourceManager.h', + 'public/resourceManagerTypes.h' +) + +dependencies = [ + yaml_cpp_dep, + opatio_dep, + eos_dep, + quill_dep, + config_dep, + probe_dep, + mfem_dep, + macros_dep, + meshio_dep +] + +libResourceHeader_dep = declare_dependency(include_directories: include_directories('public')) +# Define the libresourceManager library so it can be linked against by other parts of the build system +libresourceManager = static_library('resourceManager', + resourceManager_sources, + include_directories: include_directories('public'), + cpp_args: ['-fvisibility=default'], + dependencies: dependencies, + install : true) + +resourceManager_dep = declare_dependency( + include_directories: include_directories('public'), + link_with: libresourceManager, + dependencies: dependencies +) + +# Make headers accessible +install_headers(resourceManager_headers, subdir : '4DSSE/resource') \ No newline at end of file diff --git a/src/resource/private/resourceManager.cpp b/src/resource/private/resourceManager.cpp new file mode 100644 index 0000000..ebbe62d --- /dev/null +++ b/src/resource/private/resourceManager.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include "quill/LogMacros.h" + +#include "resourceManager.h" +#include "resourceManagerTypes.h" +#include "debug.h" + +#include "config.h" + +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +ResourceManager::ResourceManager() { + std::string defaultDataDir = TOSTRING(DATA_DIR); + m_dataDir = m_config.get("Data:Dir", defaultDataDir); + // -- Get the index file path using filesytem to make it a system safe path + std::string indexFilePath = m_dataDir + "/index.yaml"; + std::filesystem::path indexFile(indexFilePath); + + m_resourceConfig.loadConfig(indexFile.string()); + std::vector assets = m_resourceConfig.keys(); + for (auto key : assets ) { + load(key); + } +} + + +std::vector ResourceManager::getAvaliableResources() { + std::vector resources; + resources = m_resourceConfig.keys(); + return resources; +} + +const Resource& ResourceManager::getResource(const std::string &name) const { + auto it = m_resources.find(name); + if (it != m_resources.end()) { + return it->second; + } + throw std::runtime_error("Resource " + name + " not found"); +} + + +bool ResourceManager::loadResource(std::string& name) { + return load(name); +} + +bool ResourceManager::load(const std::string& name) { + const std::string resourcePath = m_dataDir + "/" + m_resourceConfig.get(name); + std::filesystem::path resourceFile(resourcePath); + if (!std::filesystem::exists(resourceFile)) { + LOG_ERROR(m_logger, "Resource file not found: {}", resourceFile.string()); + return false; + } + LOG_INFO(m_logger, "Loading resource: {}", resourceFile.string()); + if (m_resources.find(name) != m_resources.end()) { + LOG_INFO(m_logger, "Resource already loaded: {}", name); + return true; + } + Resource resource = createResource(name, resourcePath); + m_resources[name] = std::move(resource); + // -- Check if the resource is already in the map + return true; +} + diff --git a/src/resource/private/resourceManagerTypes.cpp b/src/resource/private/resourceManagerTypes.cpp new file mode 100644 index 0000000..98593bf --- /dev/null +++ b/src/resource/private/resourceManagerTypes.cpp @@ -0,0 +1,41 @@ +#include + +#include "resourceManagerTypes.h" +#include "opatIO.h" +#include "meshIO.h" +#include "eosIO.h" + +#include "debug.h" + +std::string getFirstSegment(const std::string& input) { + size_t pos = input.find(':'); + if (pos == std::string::npos) { + // No colon found, return the entire string + return input; + } else { + // Return substring from start to the position of the first colon + return input.substr(0, pos); + } +} + + +Resource createResource(const std::string& type, const std::string& path) { + static const std::unordered_map> factoryMap = { + {"opac", [](const std::string& p) { return Resource( + std::make_unique(p)); + }}, + {"mesh", [](const std::string& p) { return Resource( + std::make_unique(p)); + }}, + {"eos", [](const std::string& p) { return Resource( + std::make_unique(p)); + }} + // Add more mappings as needed + }; + auto it = factoryMap.find(getFirstSegment(type)); + if (it != factoryMap.end()) { + return it->second(path); + } else { + throw std::invalid_argument("Unknown resource type: " + type); + } +} \ No newline at end of file diff --git a/src/resource/public/resourceManager.h b/src/resource/public/resourceManager.h new file mode 100644 index 0000000..6c34635 --- /dev/null +++ b/src/resource/public/resourceManager.h @@ -0,0 +1,115 @@ +#ifndef RESOURCE_MANAGER_H +#define RESOURCE_MANAGER_H + +#include +#include +#include +#include + +#include "resourceManagerTypes.h" +#include "config.h" +#include "probe.h" +#include "quill/LogMacros.h" + +/** + * @class ResourceManager + * @brief Manages resources within the application. + * + * The ResourceManager class is responsible for loading, storing, and providing access to resources. + * It follows the Singleton design pattern to ensure only one instance of the manager exists. + */ +class ResourceManager { +private: + /** + * @brief Private constructor to prevent instantiation. + */ + ResourceManager(); + + /** + * @brief Deleted copy constructor to prevent copying. + */ + ResourceManager(const ResourceManager&) = delete; + + /** + * @brief Deleted assignment operator to prevent assignment. + */ + ResourceManager& operator=(const ResourceManager&) = delete; + + Config& m_config = Config::getInstance(); + Probe::LogManager& m_logManager = Probe::LogManager::getInstance(); + quill::Logger* m_logger = m_logManager.getLogger("log"); + + Config m_resourceConfig; + std::string m_dataDir; + std::unordered_map m_resources; + + /** + * @brief Loads a resource by name. + * @param name The name of the resource to load. + * @return True if the resource was loaded successfully, false otherwise. + */ + bool load(const std::string& name); + +public: + /** + * @brief Gets the singleton instance of the ResourceManager. + * @return The singleton instance of the ResourceManager. + */ + static ResourceManager& getInstance() { + static ResourceManager instance; + return instance; + } + + /** + * @brief Gets a list of available resources. + * @return A vector of strings containing the names of available resources. + * + * Example usage: + * @code + * ResourceManager& manager = ResourceManager::getInstance(); + * std::vector resources = manager.getAvaliableResources(); + * @endcode + */ + std::vector getAvaliableResources(); + + /** + * @brief Gets a resource by name. + * @param name The name of the resource to retrieve. + * @return A constant reference to the requested resource. + * @throws std::runtime_error if the resource is not found. + * + * Example usage: + * @code + * ResourceManager& manager = ResourceManager::getInstance(); + * const Resource& resource = manager.getResource("exampleResource"); + * @endcode + */ + const Resource& getResource(const std::string &name) const; + + /** + * @brief Loads a resource by name. + * @param name The name of the resource to load. + * @return True if the resource was loaded successfully, false otherwise. + * + * Example usage: + * @code + * ResourceManager& manager = ResourceManager::getInstance(); + * bool success = manager.loadResource("exampleResource"); + * @endcode + */ + bool loadResource(std::string& name); + + /** + * @brief Loads all resources. + * @return An unordered map with resource names as keys and load success as values. + * + * Example usage: + * @code + * ResourceManager& manager = ResourceManager::getInstance(); + * std::unordered_map results = manager.loadAllResources(); + * @endcode + */ + std::unordered_map loadAllResources(); +}; + +#endif // RESOURCE_MANAGER_H \ No newline at end of file diff --git a/src/resource/public/resourceManagerTypes.h b/src/resource/public/resourceManagerTypes.h new file mode 100644 index 0000000..7700e03 --- /dev/null +++ b/src/resource/public/resourceManagerTypes.h @@ -0,0 +1,71 @@ +#ifndef RESOURCE_MANAGER_TYPES_H +#define RESOURCE_MANAGER_TYPES_H + +#include +#include + +#include "opatIO.h" +#include "helm.h" +#include "meshIO.h" +#include "eosIO.h" + +/** + * @file resourceManagerTypes.h + * @brief Defines types and functions for managing resources. + * + * This file provides type definitions and functions for handling different + * types of resources in a unified manner. + */ + +// -- Valid resource types +/** + * @brief A variant type that can hold different types of resources. + * + * The Resource type is a std::variant that can hold a unique pointer to + * an OpatIO, MeshIO, or EosIO object. + * + * Example usage: + * @code + * Resource resource = std::make_unique(...); + * @endcode + */ +using Resource = std::variant< + std::unique_ptr, + std::unique_ptr, + std::unique_ptr>; + +/** + * @brief Extracts the first segment of a given string. + * + * This function takes a string input and returns the first segment + * separated by a delimiter (default is '/'). + * + * @param input The input string to be processed. + * @return The first segment of the input string. + * + * Example usage: + * @code + * std::string segment = getFirstSegment("path/to/resource"); + * // segment == "path" + * @endcode + */ +std::string getFirstSegment(const std::string& input); + +/** + * @brief Creates a resource based on the specified type and path. + * + * This function creates a resource object based on the provided type + * and initializes it using the given path. + * + * @param type The type of the resource to be created (e.g., "OpatIO", "MeshIO", "EosIO"). + * @param path The path to initialize the resource with. + * @return A Resource object initialized with the specified type and path. + * + * Example usage: + * @code + * Resource resource = createResource("OpatIO", "path/to/opat"); + * @endcode + */ +Resource createResource(const std::string& type, const std::string& path); + +#endif // RESOURCE_MANAGER_TYPES_H \ No newline at end of file diff --git a/tests/eos/eosTest.cpp b/tests/eos/eosTest.cpp index 2ff0be7..b1a352b 100644 --- a/tests/eos/eosTest.cpp +++ b/tests/eos/eosTest.cpp @@ -1,11 +1,12 @@ #include #include +#include #include -#include -#include #include #include "helm.h" +#include "resourceManager.h" +#include "config.h" /** * @file constTest.cpp @@ -17,21 +18,20 @@ */ class eosTest : public ::testing::Test {}; -std::string HELM_FILENAME = std::string(getenv("MESON_SOURCE_ROOT")) + "/assets/eos/helm_table.dat"; +std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/testsConfig.yaml"; + /** * @test Verify default constructor initializes correctly. */ -TEST_F(eosTest, constructor) { - using namespace helmholtz; - EXPECT_NO_THROW(HELMTable table = read_helm_table(HELM_FILENAME)); -} TEST_F(eosTest, read_helm_table) { - using namespace helmholtz; - HELMTable table = read_helm_table(HELM_FILENAME); - // Capture the << operator output as a string + Config::getInstance().loadConfig(TEST_CONFIG); + ResourceManager& rm = ResourceManager::getInstance(); + auto& eos = std::get>(rm.getResource("eos:helm")); + auto& table = eos->getTable(); + auto& helmTable = *std::get>(table); std::stringstream ss; - ss << table; + ss << helmTable; EXPECT_EQ(ss.str(), "HELMTable Data:\n imax: 541, jmax: 201\n Temperature Range: [1000, 1e+13]\n Density Range: [1e-12, 1e+15]\n"); } @@ -58,28 +58,31 @@ TEST_F(eosTest, get_helm_EOS) { eos1.abar = 1.0/asum; eos1.zbar = eos1.abar*zsum; - HELMTable table = read_helm_table(HELM_FILENAME); - EOS eos = get_helm_EOS(eos1, table); - // std::cout << eos << std::endl; + ResourceManager& rm = ResourceManager::getInstance(); + auto& eos = std::get>(rm.getResource("eos:helm")); + auto& table = eos->getTable(); + auto& helmTable = *std::get>(table); + EOS helmEos = get_helm_EOS(eos1, helmTable); + + const double absErr = 1e-12; //Check composition info - EXPECT_DOUBLE_EQ( eos.ye, 8.75e-01); + EXPECT_NEAR( helmEos.ye, 8.75e-01, absErr); //Check E, P, S and derivatives of each wrt Rho and T - EXPECT_DOUBLE_EQ( eos.etaele, 2.3043348231021554e+01); - EXPECT_DOUBLE_EQ( eos.etot, 1.1586558190936826e+17); - EXPECT_DOUBLE_EQ(eos.denerdd, 6.1893000468285858e+10); - EXPECT_DOUBLE_EQ(eos.denerdt, 1.2129708972542575e+08); - EXPECT_DOUBLE_EQ( eos.ptot, 6.9610135220017030e+22); - EXPECT_DOUBLE_EQ(eos.dpresdd, 1.0296440482849070e+17); - EXPECT_DOUBLE_EQ(eos.dpresdt, 7.7171347517311625e+13); - EXPECT_DOUBLE_EQ( eos.stot, 6.0647461970567346e+08); - EXPECT_DOUBLE_EQ(eos.dentrdd,-7.7171347517311645e+01); - EXPECT_DOUBLE_EQ(eos.dentrdt, 1.2129708972542577e+00); + EXPECT_NEAR( helmEos.etaele, 2.3043348231021554e+01, absErr); + EXPECT_NEAR( helmEos.etot, 1.1586558190936826e+17, 1e3); + EXPECT_NEAR(helmEos.denerdd, 6.1893000468285858e+10, 1e-2); + EXPECT_NEAR(helmEos.denerdt, 1.2129708972542575e+08, 1e-7); + EXPECT_NEAR( helmEos.ptot, 6.9610135220017030e+22, 1e10); + EXPECT_NEAR(helmEos.dpresdd, 1.0296440482849070e+17, 1e3); + EXPECT_NEAR(helmEos.dpresdt, 7.7171347517311625e+13, 1.0); + EXPECT_NEAR( helmEos.stot, 6.0647461970567346e+08, 1e-7); + EXPECT_NEAR(helmEos.dentrdd,-7.7171347517311645e+01, absErr); + EXPECT_NEAR(helmEos.dentrdt, 1.2129708972542577e+00, absErr); - const double abs_err = 1.0e-12; // Maxwell relations, should always be zero - EXPECT_NEAR( eos.dse, 0, abs_err); - EXPECT_NEAR( eos.dpe, 0, abs_err); - EXPECT_NEAR( eos.dsp, 0, abs_err); + EXPECT_NEAR( helmEos.dse, 0, absErr); + EXPECT_NEAR( helmEos.dpe, 0, absErr); + EXPECT_NEAR( helmEos.dsp, 0, absErr); } diff --git a/tests/eos/meson.build b/tests/eos/meson.build index 05d2f83..d8154e6 100644 --- a/tests/eos/meson.build +++ b/tests/eos/meson.build @@ -11,7 +11,7 @@ foreach test_file : test_sources test_exe = executable( exe_name, test_file, - dependencies: [gtest_dep, eos_dep, gtest_main], + dependencies: [gtest_dep, eos_dep, gtest_main, resourceManager_dep, config_dep], install_rpath: '@loader_path/../../src' # Ensure runtime library path resolves correctly ) diff --git a/tests/meshIO/meshIOTest.cpp b/tests/meshIO/meshIOTest.cpp index 07eec4f..23f845a 100644 --- a/tests/meshIO/meshIOTest.cpp +++ b/tests/meshIO/meshIOTest.cpp @@ -1,6 +1,5 @@ #include #include "meshIO.h" -#include #include #include "mfem.hpp" diff --git a/tests/meson.build b/tests/meson.build index e744e20..0d89e07 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -11,6 +11,7 @@ subdir('meshIO') subdir('config') subdir('probe') subdir('eos') +subdir('resource') # Subdirectories for sandbox tests subdir('dobj_sandbox') diff --git a/tests/resource/meson.build b/tests/resource/meson.build new file mode 100644 index 0000000..75571d8 --- /dev/null +++ b/tests/resource/meson.build @@ -0,0 +1,24 @@ +# Test files for const +test_sources = [ + 'resourceManagerTest.cpp', +] + +foreach test_file : test_sources + exe_name = test_file.split('.')[0] + message('Building test: ' + exe_name) + + # Create an executable target for each test + test_exe = executable( + exe_name, + test_file, + dependencies: [gtest_dep, resourceManager_dep, gtest_main, macros_dep], + include_directories: include_directories('../../src/resource/public'), + install_rpath: '@loader_path/../../src' # Ensure runtime library path resolves correctly + ) + + # Add the executable as a test + test( + exe_name, + test_exe, + env: ['MESON_SOURCE_ROOT=' + meson.project_source_root(), 'MESON_BUILD_ROOT=' + meson.project_build_root()]) +endforeach diff --git a/tests/resource/resourceManagerTest.cpp b/tests/resource/resourceManagerTest.cpp new file mode 100644 index 0000000..d0264d7 --- /dev/null +++ b/tests/resource/resourceManagerTest.cpp @@ -0,0 +1,61 @@ +#include +#include "resourceManager.h" +#include "config.h" +#include "eosIO.h" +#include "helm.h" +#include "resourceManagerTypes.h" + +#include +#include +#include +#include + +#include "debug.h" + +/** + * @file configTest.cpp + * @brief Unit tests for the resourceManager class. + */ + + +std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/testsConfig.yaml"; +/** + * @brief Test suite for the resourceManager class. + */ +class resourceManagerTest : public ::testing::Test {}; + +/** + * @brief Test the constructor of the resourceManager class. + */ +TEST_F(resourceManagerTest, constructor) { + Config::getInstance().loadConfig(TEST_CONFIG); + EXPECT_NO_THROW(ResourceManager::getInstance()); +} + +TEST_F(resourceManagerTest, getAvaliableResources) { + Config::getInstance().loadConfig(TEST_CONFIG); + ResourceManager& rm = ResourceManager::getInstance(); + std::vector resources = rm.getAvaliableResources(); + std::set expected = {"eos:helm", "mesh:sphere"}; + std::set actual(resources.begin(), resources.end()); + EXPECT_EQ(expected, actual); +} + +TEST_F(resourceManagerTest, getResource) { + Config::getInstance().loadConfig(TEST_CONFIG); + ResourceManager& rm = ResourceManager::getInstance(); + std::string name = "eos:helm"; + const Resource &r = rm.getResource(name); + // BREAKPOINT(); + const auto &eos = std::get>(r); + EXPECT_EQ("helm", eos->getFormat()); + EOSTable &table = eos->getTable(); + + // -- Extract the Helm table from the EOSTable + helmholtz::HELMTable &helmTable = *std::get>(table); + EXPECT_DOUBLE_EQ(helmTable.f[0][0], -1692098915534.8142); + + EXPECT_THROW(rm.getResource("opac:GS98:high:doesNotExist"), std::runtime_error); +} + + diff --git a/tests/testsConfig.yaml b/tests/testsConfig.yaml new file mode 100644 index 0000000..e82bfc2 --- /dev/null +++ b/tests/testsConfig.yaml @@ -0,0 +1,38 @@ +Debug: true + +Probe: + GLVis: + Visualization: true + # Host: "10.8.0.14" + Host: "localhost" + Port: 19916 + DefaultKeyset: "iimmMMc" + GetRaySolution: + MakeDir: true +Poly: + Solver: + ViewInitialGuess: true + GMRES: + MaxIter: 5000 + RelTol: 1.0e-8 + AbsTol: 1.0e-10 + PrintLevel: 0 + Newton: + MaxIter: 200 + RelTol: 1.0e-8 + AbsTol: 1.0e-10 + PrintLevel: 1 + Newton: + Output: + 1D: + Save: true + Path: "output/Poly/1D/poly.csv" + RaySamples: 1000 + RayCoLatitude: 0.0 + RayLongitude: 0.0 + +# THESE ARE ONLY USED BY THE TEST SUITE AND NOT THE MAIN CODE +# ANY OPTIONS NEEDED FOR THE TEST SUITE SHOULD BE PLACED HERE +Tests: + Poly: + Index: 1.1 \ No newline at end of file