From 95d344a79c1d9fd9d706b4af93316daf636c31f8 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sun, 11 May 2025 14:58:00 -0400 Subject: [PATCH 01/12] refactor(EosIO): renamed EosIO -> EOSio --- src/eos/private/{eosIO.cpp => EOSio.cpp} | 17 +-- src/eos/public/{eosIO.h => EOSio.h} | 17 ++- src/eos/public/helm.h | 117 +----------------- src/resource/private/resourceManagerTypes.cpp | 4 +- src/resource/public/resourceManagerTypes.h | 8 +- tests/eos/eosTest.cpp | 4 +- tests/resource/resourceManagerTest.cpp | 4 +- 7 files changed, 27 insertions(+), 144 deletions(-) rename src/eos/private/{eosIO.cpp => EOSio.cpp} (52%) rename src/eos/public/{eosIO.h => EOSio.h} (85%) diff --git a/src/eos/private/eosIO.cpp b/src/eos/private/EOSio.cpp similarity index 52% rename from src/eos/private/eosIO.cpp rename to src/eos/private/EOSio.cpp index ba3e471..23a4adf 100644 --- a/src/eos/private/eosIO.cpp +++ b/src/eos/private/EOSio.cpp @@ -1,23 +1,24 @@ #include +#include -#include "eosIO.h" +#include "EOSio.h" #include "helm.h" #include "debug.h" -EosIO::EosIO(const std::string filename) : m_filename(filename) { +EOSio::EOSio(std::string filename) : m_filename(std::move(filename)) { load(); } -std::string EosIO::getFormat() const { +std::string EOSio::getFormat() const { return m_format; } -EOSTable& EosIO::getTable() { +EOSTable& EOSio::getTable() { return m_table; } -void EosIO::load() { +void EOSio::load() { // Load the EOS table from the file // For now, just set the format to HELM @@ -27,10 +28,10 @@ void EosIO::load() { } } -void EosIO::loadHelm() { +void EOSio::loadHelm() { // Load the HELM table from the file - auto helmTabptr = helmholtz::read_helm_table(m_filename); - m_table = std::move(helmTabptr); + auto helmTabPtr = helmholtz::read_helm_table(m_filename); + m_table = std::move(helmTabPtr); m_loaded = true; } diff --git a/src/eos/public/eosIO.h b/src/eos/public/EOSio.h similarity index 85% rename from src/eos/public/eosIO.h rename to src/eos/public/EOSio.h index aab0c59..15899f0 100644 --- a/src/eos/public/eosIO.h +++ b/src/eos/public/EOSio.h @@ -1,5 +1,4 @@ -#ifndef EOSIO_H -#define EOSIO_H +#pragma once #include #include @@ -13,20 +12,20 @@ using EOSTable = std::variant< >; /** - * @class EosIO + * @class EOSio * @brief Handles the input/output operations for EOS tables. * - * The EosIO class is responsible for loading and managing EOS tables from files. + * 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"); + * EOSio eosIO("path/to/file"); * std::string format = eosIO.getFormat(); * EOSTable& table = eosIO.getTable(); * @endcode */ -class EosIO { +class EOSio { private: std::string m_filename; ///< The filename of the EOS table. bool m_loaded = false; ///< Flag indicating if the table is loaded. @@ -47,12 +46,12 @@ public: * @brief Constructs an EosIO object with the given filename. * @param filename The filename of the EOS table. */ - EosIO(const std::string filename); + EOSio(std::string filename); /** * @brief Default destructor. */ - ~EosIO() = default; + ~EOSio() = default; /** * @brief Gets the format of the EOS table. @@ -66,5 +65,3 @@ public: */ 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 64e7a8a..dcd7559 100644 --- a/src/eos/public/helm.h +++ b/src/eos/public/helm.h @@ -21,9 +21,7 @@ // this file contains a C++ conversion of Frank Timmes' fortran code // helmholtz.f90, which implements the Helmholtz Free Energy EOS described // by Timmes & Swesty (2000) doi:10.1086/313304 -- Aaron Dotter 2025 - -#ifndef HELM_H -#define HELM_H +#pragma once #define IMAX 541 #define JMAX 201 @@ -141,118 +139,6 @@ 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"; @@ -497,4 +383,3 @@ namespace helmholtz } -#endif // HELM_H diff --git a/src/resource/private/resourceManagerTypes.cpp b/src/resource/private/resourceManagerTypes.cpp index 26d77df..b5f6cc9 100644 --- a/src/resource/private/resourceManagerTypes.cpp +++ b/src/resource/private/resourceManagerTypes.cpp @@ -23,7 +23,7 @@ #include "resourceManagerTypes.h" #include "opatIO.h" #include "meshIO.h" -#include "eosIO.h" +#include "EOSio.h" #include "debug.h" @@ -48,7 +48,7 @@ Resource createResource(const std::string& type, const std::string& path) { std::make_unique(p)); }}, {"eos", [](const std::string& p) { return Resource( - std::make_unique(p)); + std::make_unique(p)); }} // Add more mappings as needed }; diff --git a/src/resource/public/resourceManagerTypes.h b/src/resource/public/resourceManagerTypes.h index 5161fcc..df6ca54 100644 --- a/src/resource/public/resourceManagerTypes.h +++ b/src/resource/public/resourceManagerTypes.h @@ -27,7 +27,7 @@ #include "opatIO.h" #include "helm.h" #include "meshIO.h" -#include "eosIO.h" +#include "EOSio.h" /** * @file resourceManagerTypes.h @@ -42,7 +42,7 @@ * @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. + * an OpatIO, MeshIO, or EOSio object. * * Example usage: * @code @@ -52,7 +52,7 @@ using Resource = std::variant< std::unique_ptr, std::unique_ptr, - std::unique_ptr>; + std::unique_ptr>; /** * @brief Extracts the first segment of a given string. @@ -77,7 +77,7 @@ std::string getFirstSegment(const std::string& input); * 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 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. * diff --git a/tests/eos/eosTest.cpp b/tests/eos/eosTest.cpp index b1a352b..f88d0bc 100644 --- a/tests/eos/eosTest.cpp +++ b/tests/eos/eosTest.cpp @@ -27,7 +27,7 @@ std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/tes TEST_F(eosTest, read_helm_table) { Config::getInstance().loadConfig(TEST_CONFIG); ResourceManager& rm = ResourceManager::getInstance(); - auto& eos = std::get>(rm.getResource("eos:helm")); + auto& eos = std::get>(rm.getResource("eos:helm")); auto& table = eos->getTable(); auto& helmTable = *std::get>(table); std::stringstream ss; @@ -59,7 +59,7 @@ TEST_F(eosTest, get_helm_EOS) { eos1.zbar = eos1.abar*zsum; ResourceManager& rm = ResourceManager::getInstance(); - auto& eos = std::get>(rm.getResource("eos:helm")); + auto& eos = std::get>(rm.getResource("eos:helm")); auto& table = eos->getTable(); auto& helmTable = *std::get>(table); EOS helmEos = get_helm_EOS(eos1, helmTable); diff --git a/tests/resource/resourceManagerTest.cpp b/tests/resource/resourceManagerTest.cpp index d0264d7..a64d079 100644 --- a/tests/resource/resourceManagerTest.cpp +++ b/tests/resource/resourceManagerTest.cpp @@ -1,7 +1,7 @@ #include #include "resourceManager.h" #include "config.h" -#include "eosIO.h" +#include "EOSio.h" #include "helm.h" #include "resourceManagerTypes.h" @@ -47,7 +47,7 @@ TEST_F(resourceManagerTest, getResource) { std::string name = "eos:helm"; const Resource &r = rm.getResource(name); // BREAKPOINT(); - const auto &eos = std::get>(r); + const auto &eos = std::get>(r); EXPECT_EQ("helm", eos->getFormat()); EOSTable &table = eos->getTable(); From d56f66c42882e7ca9e8bd5aeda772fc0145d7a07 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Sun, 11 May 2025 14:58:18 -0400 Subject: [PATCH 02/12] feat(python-eos-interface): began work on python eos interface --- src/python/eos/bindings.cpp | 23 +++++++++++++++++++++++ src/python/eos/bindings.h | 5 +++++ src/python/eos/meson.build | 17 +++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 src/python/eos/bindings.cpp create mode 100644 src/python/eos/bindings.h create mode 100644 src/python/eos/meson.build diff --git a/src/python/eos/bindings.cpp b/src/python/eos/bindings.cpp new file mode 100644 index 0000000..775f567 --- /dev/null +++ b/src/python/eos/bindings.cpp @@ -0,0 +1,23 @@ +#include +#include // Needed for vectors, maps, sets, strings +#include // Needed for binding std::vector, std::map etc if needed directly + +#include +#include "helm.h" +#include "resourceManager.h" +#include "bindings.h" + +namespace py = pybind11; + + +void register_eos_bindings(pybind11::module &eos_submodule) { + py::class_(const_submodule, "EOSio") + .def(py::init(), py::arg("filename")) + .def("load", &EOSio::load) + .def_readonly("getFormat", &EOSio::getFormat) + .def_readonly("getTable", &EOSio::getTable) + + .def("__repr__", [](const EOSio &eos) {) + return ""; + }); +} diff --git a/src/python/eos/bindings.h b/src/python/eos/bindings.h new file mode 100644 index 0000000..3945840 --- /dev/null +++ b/src/python/eos/bindings.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +void register_eos_bindings(pybind11::module &eos_submodule); diff --git a/src/python/eos/meson.build b/src/python/eos/meson.build new file mode 100644 index 0000000..a816819 --- /dev/null +++ b/src/python/eos/meson.build @@ -0,0 +1,17 @@ +# Define the library +bindings_sources = files('bindings.cpp') +bindings_headers = files('bindings.h') + +dependencies = [ + const_dep, + python3_dep, + pybind11_dep, +] + +shared_module('py_const', + bindings_sources, + include_directories: include_directories('.'), + cpp_args: ['-fvisibility=default'], + install : true, + dependencies: dependencies, +) From 36916724de3d2517a0a3b1084992089611f04923 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Tue, 13 May 2025 14:09:25 -0400 Subject: [PATCH 03/12] docs(.gitignore): added hypre --- .gitignore | 3 ++- build-config/mpi/meson.build | 0 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 build-config/mpi/meson.build diff --git a/.gitignore b/.gitignore index 92cc732..6510088 100644 --- a/.gitignore +++ b/.gitignore @@ -62,10 +62,11 @@ subprojects/mfem/ subprojects/tetgen/ subprojects/yaml-cpp/ subprojects/quill/ -subprojects/googletest-1.15.2/ +subprojects/googletest-*/ subprojects/opat-core/ subprojects/pybind11*/ subprojects/packagecache/ +subprojects/hypre/ .vscode/ diff --git a/build-config/mpi/meson.build b/build-config/mpi/meson.build new file mode 100644 index 0000000..e69de29 From a6a86fc0b4892251852cbed4f2fb34bfffc99019 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Tue, 13 May 2025 14:12:48 -0400 Subject: [PATCH 04/12] build(hypre): added hypre and mpi as dependencies hypre is required for parallel MFEM usage and hypre requires MPI. Both of these have been added as dependecies. MPI is added as a dependency in the same manner as boost (i.e. with an install script which checks for system installation and provides a way that the user can initiate a system installation). The MPI install script has been incorporated with the mk script. Hypre has been added as a subproject. --- build-config/hypre/meson.build | 2 + build-config/meson.build | 2 + build-config/mpi/.mpi_installed | 0 build-config/mpi/install.sh | 272 ++++++++++++++++++++++++++++++++ build-config/mpi/meson.build | 1 + 5 files changed, 277 insertions(+) create mode 100644 build-config/hypre/meson.build create mode 100644 build-config/mpi/.mpi_installed create mode 100755 build-config/mpi/install.sh diff --git a/build-config/hypre/meson.build b/build-config/hypre/meson.build new file mode 100644 index 0000000..72951bf --- /dev/null +++ b/build-config/hypre/meson.build @@ -0,0 +1,2 @@ +hypre_sp = cmake.subproject('hypre') +hypre_dep = hypre_sp.dependency('HYPRE') diff --git a/build-config/meson.build b/build-config/meson.build index be2adc5..c4e5068 100644 --- a/build-config/meson.build +++ b/build-config/meson.build @@ -6,6 +6,8 @@ subdir('quill') subdir('boost') subdir('opatIO') subdir('pybind') +subdir('mpi') +subdir('hypre') # Set the config file error handling options configErr = get_option('config_error_handling') diff --git a/build-config/mpi/.mpi_installed b/build-config/mpi/.mpi_installed new file mode 100644 index 0000000..e69de29 diff --git a/build-config/mpi/install.sh b/build-config/mpi/install.sh new file mode 100755 index 0000000..9a02198 --- /dev/null +++ b/build-config/mpi/install.sh @@ -0,0 +1,272 @@ +#!/usr/bin/env bash +# +# install_mpi.sh - Interactive installation script for MPI. +# +# This script checks if MPI is installed (by trying to compile a simple MPI program). +# If not found, it prompts the user to install MPI using the native package manager for: +# - FreeBSD, Ubuntu, Debian, Fedora, Arch, Mint, Manjaro, macOS, OpenSuse, and NixOS. +# +# All output is colorized for clarity and logged to mpi-install-log.txt. + +set -e + +# ANSI color codes. +RED="\033[0;31m" +GREEN="\033[0;32m" +YELLOW="\033[0;33m" +BLUE="\033[0;34m" +NC="\033[0m" # No Color + +# Log file. +LOGFILE="mpi-install-log.txt" +touch "$LOGFILE" # Ensure log file exists + +# Log function: prints to stdout and appends to logfile. +log() { + local message="$1" + # Print the colored message to stdout. + echo -e "$message" + # Strip ANSI escape sequences and append the cleaned message to the log file. + echo -e "$message" | sed -r 's/\x1B\[[0-9;]*[mK]//g' >> "$LOGFILE" +} + +# Function to check if MPI (specifically mpicc and a compilable environment) is installed. +check_mpi_installed() { + log "${BLUE}[Info] Checking for MPI (e.g., Open MPI, MPICH) using mpicc...${NC}" + local tmpDir="mpiTest" # Define tmpDir locally + + # Clean up any previous test directory + if [[ -d "$tmpDir" ]]; then + rm -rf "$tmpDir" + fi + mkdir "$tmpDir" + local sourceFile="$tmpDir/test_mpi.c" + local outputFile="$tmpDir/test_mpi_app" + + # Write a minimal MPI C program + cat > "$sourceFile" < +#include + +int main(int argc, char** argv) { + if (MPI_Init(NULL, NULL) != MPI_SUCCESS) { + // Using fprintf for stderr in case stdout is redirected by build tools + fprintf(stderr, "MPI_Init failed\n"); + return 1; + } + int world_size; + if (MPI_Comm_size(MPI_COMM_WORLD, &world_size) != MPI_SUCCESS) { + fprintf(stderr, "MPI_Comm_size failed\n"); + MPI_Finalize(); // Attempt to finalize even if a step fails + return 1; + } + int world_rank; + if (MPI_Comm_rank(MPI_COMM_WORLD, &world_rank) != MPI_SUCCESS) { + fprintf(stderr, "MPI_Comm_rank failed\n"); + MPI_Finalize(); + return 1; + } + // A simple printf to ensure it links and runs, though not strictly necessary for the check + // printf("MPI Initialized successfully on rank %d of %d\\n", world_rank, world_size); + if (MPI_Finalize() != MPI_SUCCESS) { + fprintf(stderr, "MPI_Finalize failed\n"); + return 1; + } + return 0; +} +EOF + + # Try to find mpicc and compile the test program + if command -v mpicc &> /dev/null; then + # Log the compilation command + log "${BLUE}[Info] Attempting to compile MPI test program with: mpicc \"$sourceFile\" -o \"$outputFile\"${NC}" + # Attempt compilation, redirecting stdout and stderr of mpicc to the log file for details + if mpicc "$sourceFile" -o "$outputFile" >> "$LOGFILE" 2>&1; then + log "${GREEN}[Success] MPI compiler (mpicc) found and test program compiled successfully.${NC}" + # Optional: Could add a mpiexec -n 1 "$outputFile" check here for further validation + rm -rf "$tmpDir" + return 0 # MPI seems to be installed and working + else + log "${RED}[Error] MPI compiler (mpicc) found, but failed to compile the test program. See $LOGFILE for details.${NC}" + fi + else + log "${RED}[Error] MPI compiler (mpicc) not found in PATH.${NC}" + fi + + # Cleanup and return failure + rm -rf "$tmpDir" + return 1 +} + +# Prompt the user for a yes/no answer. +prompt_yes_no() { + local promptMsg="$1" + while true; do + read -r -p "$(echo -e "${YELLOW}$promptMsg${NC} (y/n): ")" answer + case "$answer" in + [Yy]* ) return 0;; + [Nn]* ) return 1;; + * ) echo -e "${RED}Please answer yes (y) or no (n).${NC}";; + esac + done +} + +# Detect OS and Distribution. +OS=$(uname -s) +DISTRO="unknown" +if [ -f /etc/os-release ]; then + # shellcheck disable=SC1091 + . /etc/os-release + DISTRO=$ID +elif [ -f /etc/lsb-release ]; then # Fallback for older systems + # shellcheck disable=SC1091 + . /etc/lsb-release + DISTRO=$DISTRIB_ID +fi +# Normalize some distro names +DISTRO=$(echo "$DISTRO" | tr '[:upper:]' '[:lower:]') + + +if [[ "$OS" == "Darwin" ]]; then + OS="macOS" +fi + +if [[ $OS == "macOS" ]]; then + log "${BLUE}[Info] Detected OS: ${OS}${NC}" +else + log "${BLUE}[Info] Detected OS: ${OS} Distribution: ${DISTRO}${NC}" +fi + +# Main script logic +log "${BLUE}[Info] Starting MPI installation check...${NC}" + +if check_mpi_installed; then + log "${GREEN}[Success] MPI appears to be already installed and configured on your system.${NC}" + log "${GREEN}[Success] Your MPI-dependent application can now proceed.${NC}" + exit 0 +else + log "${YELLOW}[Warning] MPI was not detected or is not properly configured on your system.${NC}" + if prompt_yes_no "[Query] Would you like this script to attempt to install MPI (Open MPI) now?"; then + log "${BLUE}[Info] Attempting to install MPI...${NC}" + case "$OS" in + "macOS") + if ! command -v brew &> /dev/null; then + log "${YELLOW}[Warning] Homebrew is not installed. It is highly recommended for installing MPI on macOS.${NC}" + if prompt_yes_no "[Query] Would you like to install Homebrew now?"; then + log "${BLUE}[Info] Installing Homebrew...${NC}" + # Piping to tee to ensure output is captured in the log file + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 2>&1 | tee -a "$LOGFILE" + else + log "${RED}[Error] Homebrew is required for an easy MPI installation on macOS using this script. Aborting.${NC}" + exit 1 + fi + fi + log "${BLUE}[Info] Running: brew install open-mpi${NC}" + brew install open-mpi 2>&1 | tee -a "$LOGFILE" + ;; + "Linux") + case "$DISTRO" in + "ubuntu"|"debian"|"linuxmint"|"pop"|"raspbian") # Added pop and raspbian + log "${BLUE}[Info] Running: sudo apt-get update && sudo apt-get install -y libopenmpi-dev${NC}" + sudo apt-get update 2>&1 | tee -a "$LOGFILE" + sudo apt-get install -y libopenmpi-dev 2>&1 | tee -a "$LOGFILE" + ;; + "fedora"|"centos"|"rhel") # Added centos and rhel (dnf is common now) + if command -v dnf &> /dev/null; then + log "${BLUE}[Info] Running: sudo dnf install -y openmpi-devel${NC}" + sudo dnf install -y openmpi-devel 2>&1 | tee -a "$LOGFILE" + elif command -v yum &> /dev/null; then # Fallback for older CentOS/RHEL + log "${BLUE}[Info] Running: sudo yum install -y openmpi-devel${NC}" + sudo yum install -y openmpi-devel 2>&1 | tee -a "$LOGFILE" + else + log "${RED}[Error] Neither dnf nor yum found. Cannot install MPI on this Fedora/RHEL-like system automatically.${NC}" + exit 1 + fi + ;; + "arch"|"manjaro"|"endeavouros") # Added endeavouros + log "${BLUE}[Info] Running: sudo pacman -S --noconfirm openmpi${NC}" + sudo pacman -S --noconfirm openmpi 2>&1 | tee -a "$LOGFILE" + ;; + "opensuse"|"opensuse-leap"|"opensuse-tumbleweed") # More specific opensuse IDs + log "${BLUE}[Info] Running: sudo zypper install -y openmpi-devel${NC}" # Or openmpi4-devel, openmpi-devel is often a meta package + sudo zypper install -y openmpi-devel 2>&1 | tee -a "$LOGFILE" + ;; + "nixos") + log "${BLUE}[Info] On NixOS, you typically manage packages declaratively or with nix-shell.${NC}" + log "${BLUE}[Info] To install Open MPI imperatively: nix-env -iA nixpkgs.openmpi${NC}" + log "${YELLOW}[Warning] This script will not run nix-env automatically. Please install manually if desired.${NC}" + log "${YELLOW}[Info] For declarative environment, add 'openmpi' to your configuration.nix or shell.nix.${NC}" + # nix-env -iA nixpkgs.openmpi 2>&1 | tee -a "$LOGFILE" # Decided against auto-running this for NixOS + ;; + *) + log "${RED}[Error] Your Linux distribution ($DISTRO) is not recognized or not explicitly supported for automatic MPI installation by this script.${NC}" + log "${RED}[Info] Please try installing Open MPI or another MPI implementation manually.${NC}" + exit 1 + ;; + esac + ;; + "FreeBSD") + log "${BLUE}[Info] Running: sudo pkg install openmpi${NC}" + sudo pkg install -y openmpi 2>&1 | tee -a "$LOGFILE" + ;; + *) + log "${RED}[Error] Automatic MPI installation is not supported on OS: ${OS} by this script.${NC}" + log "${RED}[Info] Please try installing Open MPI or another MPI implementation manually.${NC}" + exit 1 + ;; + esac + + # Verify MPI installation after attempting to install + log "${BLUE}[Info] Verifying MPI installation attempt...${NC}" + if check_mpi_installed; then + log "${GREEN}[Success] MPI installation appears to have succeeded.${NC}" + else + log "${RED}[Error] MPI installation appears to have failed or is still not correctly configured. Please check $LOGFILE and install MPI manually.${NC}" + exit 1 + fi + + else + log "${YELLOW}[Info] User chose not to install MPI automatically.${NC}" + log "${RED}[Error] MPI is required for the application to proceed.${NC}" + log "${YELLOW}Please install an MPI implementation (e.g., Open MPI, MPICH) manually using the appropriate command for your system:${NC}" + case "$OS" in + "macOS") + log "${YELLOW} Example: brew install open-mpi (Install Homebrew from https://brew.sh if not present)${NC}" + ;; + "Linux") + case "$DISTRO" in + "ubuntu"|"debian"|"linuxmint"|"pop"|"raspbian") + log "${YELLOW} Example: sudo apt-get install libopenmpi-dev${NC}" + ;; + "fedora"|"centos"|"rhel") + log "${YELLOW} Example: sudo dnf install openmpi-devel (or sudo yum install openmpi-devel)${NC}" + ;; + "arch"|"manjaro"|"endeavouros") + log "${YELLOW} Example: sudo pacman -S openmpi${NC}" + ;; + "opensuse"|"opensuse-leap"|"opensuse-tumbleweed") + log "${YELLOW} Example: sudo zypper install openmpi-devel${NC}" + ;; + "nixos") + log "${YELLOW} Example: nix-env -iA nixpkgs.openmpi or add to configuration.nix${NC}" + ;; + *) + log "${YELLOW} Please consult your distribution's documentation for installing an MPI development package (e.g., Open MPI, MPICH).${NC}" + ;; + esac + ;; + "FreeBSD") + log "${YELLOW} Example: sudo pkg install openmpi${NC}" + ;; + *) + log "${YELLOW} Please consult your operating system's documentation for installing an MPI development package.${NC}" + ;; + esac + exit 1 + fi +fi + +log "${GREEN}[SUCCESS] MPI is installed and ready for use!${NC}" +log "${GREEN}[SUCCESS] 4DSSE installation can now proceed.${NC}" +exit 0 + diff --git a/build-config/mpi/meson.build b/build-config/mpi/meson.build index e69de29..775a46d 100644 --- a/build-config/mpi/meson.build +++ b/build-config/mpi/meson.build @@ -0,0 +1 @@ +mpi_dep = dependency('mpi', version: '>=5.0.0', language: 'cpp') \ No newline at end of file From bc36dd459dcae5d4f7be7e4d168a82caffe7b6e4 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Tue, 13 May 2025 14:14:33 -0400 Subject: [PATCH 05/12] build(hypre): added hypre as a subproject --- .gitignore | 1 + mk | 17 +++++++++++++++++ subprojects/hypre.wrap | 7 +++++++ subprojects/packagefiles/hypre/CMakeLists.txt | 6 ++++++ 4 files changed, 31 insertions(+) create mode 100644 subprojects/hypre.wrap create mode 100644 subprojects/packagefiles/hypre/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 6510088..c54117a 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ subprojects/hypre/ .vscode/ *.log +mpi-install-log.txt output/ diff --git a/mk b/mk index 8a8f521..c3745aa 100755 --- a/mk +++ b/mk @@ -339,6 +339,23 @@ else fi fi +## --- Check if MPI is installed --- +log "${BLUE}[Info] Checking MPI status...${NC}" +# if the following script exists with anything other than a 0 status the script will exit +if [[ -f ./build-config/mpi/.mpi_installed ]]; then + log "${MAGENTA}[Succsess] MPI already installed. Skipping...${NC}" +else + log "${BLUE}[Info] Installing MPI...${NC}" + if ! ./build-config/mpi/install.sh; then + log "${RED}[Error] MPI check failed. Exiting...${NC}" + exit 1 + else + touch ./build-config/mpi/.mpi_installed + log "${GREEN}[Succsess] MPI check passed.${NC}" + fi +fi + + #check if the last build flags are the same as the current build flags # if the flags are the same skip the configuration step # if they are different then reconfigure the build directory diff --git a/subprojects/hypre.wrap b/subprojects/hypre.wrap new file mode 100644 index 0000000..6e25e35 --- /dev/null +++ b/subprojects/hypre.wrap @@ -0,0 +1,7 @@ +[wrap-git] +url = https://github.com/hypre-space/hypre.git +revision = v2.33.0 +depth = 1 +patch_directory = hypre + +[cmake] \ No newline at end of file diff --git a/subprojects/packagefiles/hypre/CMakeLists.txt b/subprojects/packagefiles/hypre/CMakeLists.txt new file mode 100644 index 0000000..8521326 --- /dev/null +++ b/subprojects/packagefiles/hypre/CMakeLists.txt @@ -0,0 +1,6 @@ +# subprojects/packagefiles/hypre/CMakeLists.txt +cmake_minimum_required(VERSION 3.10) +project(hypre-wrapper C CXX) +add_subdirectory(src) + +# This file is just used to redirect to the src directory where hypre stores its CMakeLists.txt \ No newline at end of file From b5980ea57a5d369c1e478dad43f89e1e8843cd17 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Tue, 13 May 2025 14:18:38 -0400 Subject: [PATCH 06/12] feat(python-eos): work on python eos module --- build-python/meson.build | 2 + src/eos/meson.build | 4 +- src/eos/private/helm.cpp | 7 ++- src/eos/public/EOSio.h | 4 +- src/python/bindings.cpp | 4 ++ src/python/eos/bindings.cpp | 65 ++++++++++++++++++++++++--- src/python/eos/meson.build | 6 ++- src/python/meson.build | 3 +- src/resource/public/resourceManager.h | 7 +-- 9 files changed, 81 insertions(+), 21 deletions(-) diff --git a/build-python/meson.build b/build-python/meson.build index 7141c6e..4e07504 100644 --- a/build-python/meson.build +++ b/build-python/meson.build @@ -8,12 +8,14 @@ py_mod = py_installation.extension_module( meson.project_source_root() + '/src/python/composition/bindings.cpp', meson.project_source_root() + '/src/python/const/bindings.cpp', meson.project_source_root() + '/src/python/config/bindings.cpp', + meson.project_source_root() + '/src/python/eos/bindings.cpp', ], dependencies : [ pybind11_dep, const_dep, config_dep, composition_dep, + eos_dep, species_weight_dep ], cpp_args : ['-UNDEBUG'], # Example: Ensure assertions are enabled if needed diff --git a/src/eos/meson.build b/src/eos/meson.build index f7abb67..00e285c 100644 --- a/src/eos/meson.build +++ b/src/eos/meson.build @@ -1,12 +1,12 @@ # Define the library eos_sources = files( 'private/helm.cpp', - 'private/eosIO.cpp' + 'private/EOSio.cpp' ) eos_headers = files( 'public/helm.h', - 'public/eosIO.h' + 'public/EOSio.h' ) dependencies = [ diff --git a/src/eos/private/helm.cpp b/src/eos/private/helm.cpp index ed60a3e..e02ee41 100644 --- a/src/eos/private/helm.cpp +++ b/src/eos/private/helm.cpp @@ -43,8 +43,11 @@ using namespace std; -double** heap_allocate_contiguous_2D_memory(int rows, int cols) { - double **array = new double*[rows]; +double** heap_allocate_contiguous_2D_memory(const int rows, const int cols) { + if (rows <= 0 || cols <= 0) { + throw std::invalid_argument("Rows and columns must be positive integers."); + } + auto array = new double*[rows]; array[0] = new double[rows * cols]; for (int i = 1; i < rows; i++) { diff --git a/src/eos/public/EOSio.h b/src/eos/public/EOSio.h index 15899f0..aad9894 100644 --- a/src/eos/public/EOSio.h +++ b/src/eos/public/EOSio.h @@ -46,7 +46,7 @@ public: * @brief Constructs an EosIO object with the given filename. * @param filename The filename of the EOS table. */ - EOSio(std::string filename); + explicit EOSio(std::string filename); /** * @brief Default destructor. @@ -64,4 +64,6 @@ public: * @return A reference to the EOS table. */ EOSTable& getTable(); + + std::string getFilename() const { return m_filename; } }; diff --git a/src/python/bindings.cpp b/src/python/bindings.cpp index 1eb5d01..f6fda2e 100644 --- a/src/python/bindings.cpp +++ b/src/python/bindings.cpp @@ -5,6 +5,7 @@ #include "const/bindings.h" #include "composition/bindings.h" #include "config/bindings.h" +#include "eos/bindings.h" PYBIND11_MODULE(fourdsse_bindings, m) { m.doc() = "Python bindings for the 4DSSE project"; @@ -17,4 +18,7 @@ PYBIND11_MODULE(fourdsse_bindings, m) { auto configMod = m.def_submodule("config", "Configuration-module bindings"); register_config_bindings(configMod); + + auto eosMod = m.def_submodule("eos", "EOS-module bindings"); + register_eos_bindings(eosMod); } \ No newline at end of file diff --git a/src/python/eos/bindings.cpp b/src/python/eos/bindings.cpp index 775f567..18218b3 100644 --- a/src/python/eos/bindings.cpp +++ b/src/python/eos/bindings.cpp @@ -1,23 +1,74 @@ #include #include // Needed for vectors, maps, sets, strings #include // Needed for binding std::vector, std::map etc if needed directly +#include #include #include "helm.h" -#include "resourceManager.h" +// #include "resourceManager.h" #include "bindings.h" +#include "EOSio.h" +#include "../../eos/public/helm.h" namespace py = pybind11; void register_eos_bindings(pybind11::module &eos_submodule) { - py::class_(const_submodule, "EOSio") + py::class_(eos_submodule, "EOSio") .def(py::init(), py::arg("filename")) - .def("load", &EOSio::load) - .def_readonly("getFormat", &EOSio::getFormat) - .def_readonly("getTable", &EOSio::getTable) + // .def("load", &EOSio::load) + .def("getFormat", &EOSio::getFormat, "Get the format of the EOS table.") - .def("__repr__", [](const EOSio &eos) {) + .def("__repr__", [](const EOSio &eos) { return ""; }); -} + + py::class_(eos_submodule, "EOSTable"); + + py::class_(eos_submodule, "HELMTable") + .def_readonly("loaded", &helmholtz::HELMTable::loaded) + .def_readonly("imax", &helmholtz::HELMTable::imax) + .def_readonly("jmax", &helmholtz::HELMTable::jmax) + .def_readonly("t", &helmholtz::HELMTable::t) + .def_readonly("d", &helmholtz::HELMTable::d) + .def("__repr__", [](const helmholtz::HELMTable &table) { + return ""; + }) + .def_property_readonly("f", [](helmholtz::HELMTable &table) -> py::array_t { + // --- Check Preconditions --- + // 1. Check if dimensions are valid + if (table.imax <= 0 || table.jmax <= 0) { + // Return empty array or throw error for invalid dimensions + throw std::runtime_error("HELMTable dimensions (imax, jmax) are non-positive."); + // Alternatively: return py::array_t(); + } + // 2. Check if pointer 'f' and the data block 'f[0]' are non-null + // (Essential check assuming f could be null if not loaded/initialized) + if (!table.f || !table.f[0]) { + throw std::runtime_error("HELMTable data buffer 'f' is null or not initialized."); + // Alternatively: return py::array_t(); + } + + // --- Get necessary info --- + py::ssize_t rows = static_cast(table.imax); + py::ssize_t cols = static_cast(table.jmax); + double* data_ptr = table.f[0]; // Pointer to the start of contiguous data block + + // --- Define NumPy array shape and strides --- + std::vector shape = {rows, cols}; + std::vector strides = {cols * sizeof(double), // Stride to next row + sizeof(double)}; // Stride to next element in row + + // --- Create and return the py::array_t --- + // py::cast(table) creates a py::object that acts as the 'base'. + // This tells NumPy not to manage the memory of 'data_ptr' and + // ensures the 'table' object stays alive as long as the NumPy array view exists. + return py::array_t( + shape, // The dimensions of the array + strides, // How many bytes to step in each dimension + data_ptr, // Pointer to the actual data + py::cast(table) // Owner object (keeps C++ object alive) + ); + }, py::return_value_policy::reference_internal); // Keep parent 'table' alive + } diff --git a/src/python/eos/meson.build b/src/python/eos/meson.build index a816819..e98ace6 100644 --- a/src/python/eos/meson.build +++ b/src/python/eos/meson.build @@ -3,12 +3,14 @@ bindings_sources = files('bindings.cpp') bindings_headers = files('bindings.h') dependencies = [ - const_dep, + eos_dep, + config_dep, + resourceManager_dep, python3_dep, pybind11_dep, ] -shared_module('py_const', +shared_module('py_eos', bindings_sources, include_directories: include_directories('.'), cpp_args: ['-fvisibility=default'], diff --git a/src/python/meson.build b/src/python/meson.build index 4d21619..c865fc2 100644 --- a/src/python/meson.build +++ b/src/python/meson.build @@ -1,3 +1,4 @@ subdir('composition') subdir('const') -subdir('config') \ No newline at end of file +subdir('config') +subdir('eos') \ No newline at end of file diff --git a/src/resource/public/resourceManager.h b/src/resource/public/resourceManager.h index fb03a12..7cd7a22 100644 --- a/src/resource/public/resourceManager.h +++ b/src/resource/public/resourceManager.h @@ -18,18 +18,15 @@ // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // *********************************************************************** */ -#ifndef RESOURCE_MANAGER_H -#define RESOURCE_MANAGER_H +#pragma once #include #include -#include #include #include "resourceManagerTypes.h" #include "config.h" #include "probe.h" -#include "quill/LogMacros.h" /** * @class ResourceManager @@ -131,5 +128,3 @@ public: */ std::unordered_map loadAllResources(); }; - -#endif // RESOURCE_MANAGER_H \ No newline at end of file From 353e2072e7c5b391eea43695406019237f8f1c0f Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 12 Jun 2025 13:46:32 -0400 Subject: [PATCH 07/12] build(python): updated all python bindings to reflect serif name --- build-config/hypre/meson.build | 9 ++++++++- build-config/opatIO/meson.build | 7 ++++++- build-python/meson.build | 3 +-- pyproject.toml | 4 ++-- src/polytrope/coeff/meson.build | 2 +- src/polytrope/solver/meson.build | 2 +- src/polytrope/utils/meson.build | 2 +- src/python/bindings.cpp | 4 ++-- subprojects/opat-core.wrap | 2 +- 9 files changed, 23 insertions(+), 12 deletions(-) diff --git a/build-config/hypre/meson.build b/build-config/hypre/meson.build index 72951bf..9e24347 100644 --- a/build-config/hypre/meson.build +++ b/build-config/hypre/meson.build @@ -1,2 +1,9 @@ -hypre_sp = cmake.subproject('hypre') +hypre_cpp_cmake_options = cmake.subproject_options() +hypre_cpp_cmake_options.add_cmake_defines({ + 'BUILD_SHARED_LIBS': 'ON', +}) +hypre_sp = cmake.subproject( + 'hypre', + options: hypre_cpp_cmake_options, +) hypre_dep = hypre_sp.dependency('HYPRE') diff --git a/build-config/opatIO/meson.build b/build-config/opatIO/meson.build index 01ae467..4a69f2e 100644 --- a/build-config/opatIO/meson.build +++ b/build-config/opatIO/meson.build @@ -1,5 +1,10 @@ # Get the subproject object first -opat_sub = subproject('opat-core') +opat_sub = subproject( + 'opat-core', + default_options: { + 'generate_pc': false + } +) # Get the dependency variable from that subproject opatio_dep = opat_sub.get_variable('opatio_dep') \ No newline at end of file diff --git a/build-python/meson.build b/build-python/meson.build index 7141c6e..6d1e5c7 100644 --- a/build-python/meson.build +++ b/build-python/meson.build @@ -2,7 +2,7 @@ py_installation = import('python').find_installation('python3') py_mod = py_installation.extension_module( - 'fourdsse_bindings', # Name of the generated .so/.pyd file (without extension) + 'serif', # Name of the generated .so/.pyd file (without extension) sources: [ meson.project_source_root() + '/src/python/bindings.cpp', meson.project_source_root() + '/src/python/composition/bindings.cpp', @@ -18,5 +18,4 @@ py_mod = py_installation.extension_module( ], cpp_args : ['-UNDEBUG'], # Example: Ensure assertions are enabled if needed install : true, - subdir: 'fourdstar' # Optional: Install the module inside a 'fourdsse' Python package directory ) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 1ebe206..e3f38b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,8 +7,8 @@ requires = [ build-backend = "mesonpy" [project] -name = "fourdstar" # Choose your Python package name -version = "0.1.0" # Your project's version +name = "serif" # Choose your Python package name +version = "0.0.1a" # Your project's version description = "Python interface for the 4DSSE C++ project" readme = "Readme.md" license = { file = "LICENSE.txt" } # Reference your license file [cite: 2] diff --git a/src/polytrope/coeff/meson.build b/src/polytrope/coeff/meson.build index 4c82cff..4fc9f22 100644 --- a/src/polytrope/coeff/meson.build +++ b/src/polytrope/coeff/meson.build @@ -6,7 +6,7 @@ polyCoeff_headers = files( 'public/polyCoeff.h' ) -libPolyCoeff = static_library('polyCoeff', +libPolyCoeff = shared_library('polyCoeff', polyCoeff_sources, include_directories : include_directories('./public'), cpp_args: ['-fvisibility=default'], diff --git a/src/polytrope/solver/meson.build b/src/polytrope/solver/meson.build index f88a183..3b52c56 100644 --- a/src/polytrope/solver/meson.build +++ b/src/polytrope/solver/meson.build @@ -39,7 +39,7 @@ dependencies = [ types_dep, ] -libPolySolver = static_library('polySolver', +libPolySolver = shared_library('polySolver', polySolver_sources, include_directories : include_directories('./public'), cpp_args: ['-fvisibility=default'], diff --git a/src/polytrope/utils/meson.build b/src/polytrope/utils/meson.build index ec0c03c..1587b3a 100644 --- a/src/polytrope/utils/meson.build +++ b/src/polytrope/utils/meson.build @@ -34,7 +34,7 @@ dependencies = [ mfemanalysis_dep, ] -libpolyutils = library('polyutils', +libpolyutils = shared_library('polyutils', polyutils_sources, include_directories : include_directories('./public'), cpp_args: ['-fvisibility=default'], diff --git a/src/python/bindings.cpp b/src/python/bindings.cpp index 1eb5d01..1a19caf 100644 --- a/src/python/bindings.cpp +++ b/src/python/bindings.cpp @@ -6,8 +6,8 @@ #include "composition/bindings.h" #include "config/bindings.h" -PYBIND11_MODULE(fourdsse_bindings, m) { - m.doc() = "Python bindings for the 4DSSE project"; +PYBIND11_MODULE(serif, m) { + m.doc() = "Python bindings for the SERiF project"; auto compMod = m.def_submodule("composition", "Composition-module bindings"); register_comp_bindings(compMod); diff --git a/subprojects/opat-core.wrap b/subprojects/opat-core.wrap index bb78a4b..3db0c11 100644 --- a/subprojects/opat-core.wrap +++ b/subprojects/opat-core.wrap @@ -1,4 +1,4 @@ [wrap-git] url = https://github.com/4D-STAR/opat-core -revision = 0.3.1a +revision = 0.3.3a depth = 1 From dd28e555c87ef100dfb7ffd2e57c91e0fc1d8799 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 12 Jun 2025 14:21:28 -0400 Subject: [PATCH 08/12] fix(eos-bindings): minor bug fixes to bring eos bindings up to main specifically, renamed EosIO -> EOSio, and updated read_helm_table signature --- build-config/meson.build | 2 -- src/eos/private/EOSio.cpp | 2 +- src/eos/public/EOSio.h | 10 +++++----- src/eos/public/helm.h | 12 ++++++------ src/python/eos/bindings.cpp | 36 +++++++++++++++++++++--------------- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/build-config/meson.build b/build-config/meson.build index 4a1443a..0dab29f 100644 --- a/build-config/meson.build +++ b/build-config/meson.build @@ -8,8 +8,6 @@ subdir('opatIO') subdir('mpi') subdir('hypre') subdir('pybind') -subdir('mpi') -subdir('hypre') # Set the config file error handling options configErr = get_option('config_error_handling') diff --git a/src/eos/private/EOSio.cpp b/src/eos/private/EOSio.cpp index 65f9f99..03a6b43 100644 --- a/src/eos/private/EOSio.cpp +++ b/src/eos/private/EOSio.cpp @@ -28,7 +28,7 @@ #include namespace serif::eos { - EOSio::EOSio(const std::string filename) : m_filename(filename) { + EOSio::EOSio(const std::string &filename) : m_filename(filename) { load(); } diff --git a/src/eos/public/EOSio.h b/src/eos/public/EOSio.h index 5312b67..1882c07 100644 --- a/src/eos/public/EOSio.h +++ b/src/eos/public/EOSio.h @@ -33,7 +33,7 @@ namespace serif::eos { >; /** - * @class EosIO + * @class EOSio * @brief Handles the input/output operations for EOS tables. * * The EosIO class is responsible for loading and managing EOS tables from files. @@ -67,7 +67,7 @@ namespace serif::eos { * @brief Constructs an EosIO object with the given filename. * @param filename The filename of the EOS table. */ - explicit EOSio(const std::string filename); + explicit EOSio(const std::string &filename); /** * @brief Default destructor. @@ -78,15 +78,15 @@ namespace serif::eos { * @brief Gets the format of the EOS table. * @return The format of the EOS table as a string. */ - std::string getFormat() const; + [[nodiscard]] std::string getFormat() const; /** * @brief Gets the EOS table. * @return A reference to the EOS table. */ - EOSTable& getTable(); + [[nodiscard]] EOSTable& getTable(); - std::string getFilename() const { return m_filename; } + [[nodiscard]] std::string getFilename() const { return m_filename; } }; } diff --git a/src/eos/public/helm.h b/src/eos/public/helm.h index e3031e9..2577ea4 100644 --- a/src/eos/public/helm.h +++ b/src/eos/public/helm.h @@ -342,8 +342,8 @@ namespace serif::eos::helmholtz { * @param w1md Weight 1 for density (minus). * @return Result of the polynomial. */ - double h3(double fi[36], double w0t, double w1t, double w0mt, double w1mt, - double w0d, double w1d, double w0md, double w1md); + double h3(const std::array &fi, const double w0t, const double w1t, const double w0mt, const double w1mt, + const double w0d, const double w1d, const double w0md, const double w1md); /** * @brief Interpolating polynomial function h5. @@ -362,16 +362,16 @@ namespace serif::eos::helmholtz { * @param w2md Weight 2 for density (minus). * @return Result of the polynomial. */ - double h5(double fi[36], double w0t, double w1t, double w2t, double w0mt, - double w1mt, double w2mt, double w0d, double w1d, double w2d, - double w0md, double w1md, double w2md); + double h5(const std::array &fi, const double w0t, const double w1t, const double w2t, const double w0mt, + const double w1mt, const double w2mt, const double w0d, const double w1d, const double w2d, + const double w0md, const double w1md, const double w2md); /** * @brief Read the Helmholtz EOS table from a file. * @param filename Path to the file containing the table. * @return HELMTable structure containing the table data. */ - std::unique_ptr 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/python/eos/bindings.cpp b/src/python/eos/bindings.cpp index 18218b3..1e30219 100644 --- a/src/python/eos/bindings.cpp +++ b/src/python/eos/bindings.cpp @@ -8,34 +8,38 @@ // #include "resourceManager.h" #include "bindings.h" #include "EOSio.h" -#include "../../eos/public/helm.h" +#include "helm.h" + +namespace serif::eos { + class EOSio; +} namespace py = pybind11; void register_eos_bindings(pybind11::module &eos_submodule) { - py::class_(eos_submodule, "EOSio") + py::class_(eos_submodule, "EOSio") .def(py::init(), py::arg("filename")) // .def("load", &EOSio::load) - .def("getFormat", &EOSio::getFormat, "Get the format of the EOS table.") + .def("getFormat", &serif::eos::EOSio::getFormat, "Get the format of the EOS table.") - .def("__repr__", [](const EOSio &eos) { + .def("__repr__", [](const serif::eos::EOSio &eos) { return ""; }); - py::class_(eos_submodule, "EOSTable"); + py::class_(eos_submodule, "EOSTable"); - py::class_(eos_submodule, "HELMTable") - .def_readonly("loaded", &helmholtz::HELMTable::loaded) - .def_readonly("imax", &helmholtz::HELMTable::imax) - .def_readonly("jmax", &helmholtz::HELMTable::jmax) - .def_readonly("t", &helmholtz::HELMTable::t) - .def_readonly("d", &helmholtz::HELMTable::d) - .def("__repr__", [](const helmholtz::HELMTable &table) { + py::class_(eos_submodule, "HELMTable") + .def_readonly("loaded", &serif::eos::helmholtz::HELMTable::loaded) + .def_readonly("imax", &serif::eos::helmholtz::HELMTable::imax) + .def_readonly("jmax", &serif::eos::helmholtz::HELMTable::jmax) + .def_readonly("t", &serif::eos::helmholtz::HELMTable::t) + .def_readonly("d", &serif::eos::helmholtz::HELMTable::d) + .def("__repr__", [](const serif::eos::helmholtz::HELMTable &table) { return ""; }) - .def_property_readonly("f", [](helmholtz::HELMTable &table) -> py::array_t { + .def_property_readonly("f", [](serif::eos::helmholtz::HELMTable &table) -> py::array_t { // --- Check Preconditions --- // 1. Check if dimensions are valid if (table.imax <= 0 || table.jmax <= 0) { @@ -57,8 +61,10 @@ void register_eos_bindings(pybind11::module &eos_submodule) { // --- Define NumPy array shape and strides --- std::vector shape = {rows, cols}; - std::vector strides = {cols * sizeof(double), // Stride to next row - sizeof(double)}; // Stride to next element in row + std::vector strides = { + static_cast(cols * sizeof(double)), // Stride to next row + static_cast( sizeof(double)) // Stride to next element in row + }; // --- Create and return the py::array_t --- // py::cast(table) creates a py::object that acts as the 'base'. From 1af6a7cac8aa3c6b090060eb00f425b89b0c59ff Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 12 Jun 2025 14:24:28 -0400 Subject: [PATCH 09/12] refactor(python-bindings): renamed fourdstar -> serif --- src/python/{fourdstar => serif}/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/python/{fourdstar => serif}/__init__.py (100%) diff --git a/src/python/fourdstar/__init__.py b/src/python/serif/__init__.py similarity index 100% rename from src/python/fourdstar/__init__.py rename to src/python/serif/__init__.py From f0e9971fc4fe2a3afbc7ab6073b7099c3c80c556 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Thu, 12 Jun 2025 15:06:42 -0400 Subject: [PATCH 10/12] feat(python/eos): added EOSInput class and getTable method to EOSio --- src/eos/public/helm.h | 4 ++-- src/python/eos/bindings.cpp | 28 +++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/eos/public/helm.h b/src/eos/public/helm.h index 2577ea4..41b2cf5 100644 --- a/src/eos/public/helm.h +++ b/src/eos/public/helm.h @@ -35,8 +35,8 @@ #include "debug.h" namespace serif::eos::helmholtz { - constexpr int IMAX = 541; - constexpr int JMAX = 201; + static constexpr int IMAX = 541; + static constexpr int JMAX = 201; /** * @brief 2D array template alias. * @tparam T Type of the array elements. diff --git a/src/python/eos/bindings.cpp b/src/python/eos/bindings.cpp index 1e30219..6410a2d 100644 --- a/src/python/eos/bindings.cpp +++ b/src/python/eos/bindings.cpp @@ -9,6 +9,8 @@ #include "bindings.h" #include "EOSio.h" #include "helm.h" +#include "../../eos/public/EOSio.h" +#include "../../eos/public/helm.h" namespace serif::eos { class EOSio; @@ -22,7 +24,18 @@ void register_eos_bindings(pybind11::module &eos_submodule) { .def(py::init(), py::arg("filename")) // .def("load", &EOSio::load) .def("getFormat", &serif::eos::EOSio::getFormat, "Get the format of the EOS table.") + .def("getTable", [](serif::eos::EOSio &self) -> serif::eos::helmholtz::HELMTable* { + auto& table_variant = self.getTable(); + // Use std::get_if to safely access the contents of the variant. + // This returns a pointer to the value if the variant holds that type, otherwise nullptr. + if (auto* ptr_to_unique_ptr = std::get_if>(&table_variant)) { + return (*ptr_to_unique_ptr).get(); + } + + return nullptr; + }, py::return_value_policy::reference_internal, // IMPORTANT: Keep this policy! + "Get the EOS table data.") .def("__repr__", [](const serif::eos::EOSio &eos) { return ""; }); @@ -77,4 +90,17 @@ void register_eos_bindings(pybind11::module &eos_submodule) { py::cast(table) // Owner object (keeps C++ object alive) ); }, py::return_value_policy::reference_internal); // Keep parent 'table' alive - } + + py::class_(eos_submodule, "EOSInput") + .def(py::init<>()) + .def_readwrite("T", &serif::eos::helmholtz::EOSInput::T) + .def_readwrite("rho", &serif::eos::helmholtz::EOSInput::rho) + .def_readwrite("abar", &serif::eos::helmholtz::EOSInput::abar) + .def_readwrite("zbar", &serif::eos::helmholtz::EOSInput::zbar) + .def("__repr__", [](const serif::eos::helmholtz::EOSInput &input) { + return ""; + }); +} From a8f91dc12d4d008568897227ac5f72427c5fe62b Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Fri, 13 Jun 2025 07:26:55 -0400 Subject: [PATCH 11/12] refactor(helm.h): made use of tabs consistent --- src/eos/public/helm.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/eos/public/helm.h b/src/eos/public/helm.h index 41b2cf5..501db49 100644 --- a/src/eos/public/helm.h +++ b/src/eos/public/helm.h @@ -218,20 +218,20 @@ namespace serif::eos::helmholtz { os << " Electron Chemical Potential: " << std::format("{0:24.16e}",eos.etaele) << "\n"; os << " Electron Number Density: " << std::format("{0:24.16e}",eos.xnefer) << "\n"; os << " Total Pressure: " << std::format("{0:24.16e}",eos.ptot) << "\n"; - os << " dPres/dRho: " << std::format("{0:24.16e}",eos.dpresdd) << "\n"; - os << " dPres/dT: " << std::format("{0:24.16e}",eos.dpresdt) << "\n"; - os << " Gas Pressure: " << std::format("{0:24.16e}",eos.pgas) << "\n"; + os << " dPres/dRho: " << std::format("{0:24.16e}",eos.dpresdd) << "\n"; + os << " dPres/dT: " << std::format("{0:24.16e}",eos.dpresdt) << "\n"; + os << " Gas Pressure: " << std::format("{0:24.16e}",eos.pgas) << "\n"; os << " Radiation Pressure: " << std::format("{0:24.16e}",eos.prad) << "\n"; os << " Total Energy: " << std::format("{0:24.16e}",eos.etot) << "\n"; - os << " dEner/dRho: " << std::format("{0:24.16e}",eos.denerdd) << "\n"; - os << " dEner/dT: " << std::format("{0:24.16e}",eos.denerdt) << "\n"; + os << " dEner/dRho: " << std::format("{0:24.16e}",eos.denerdd) << "\n"; + os << " dEner/dT: " << std::format("{0:24.16e}",eos.denerdt) << "\n"; os << " Gas Energy: " << std::format("{0:24.16e}",eos.egas) << "\n"; - os << " Radiation Energy: " << std::format("{0:24.16e}",eos.erad) << "\n"; - os << " Total Entropy: " << std::format("{0:24.16e}",eos.stot) << "\n"; - os << " dEntr/dRho: " << std::format("{0:24.16e}",eos.dentrdd) << "\n"; - os << " dEntr/dT: " << std::format("{0:24.16e}",eos.dentrdt) << "\n"; - os << " Gas Entropy: " << std::format("{0:24.16e}",eos.sgas) << "\n"; - os << " Radiation Entropy: " << std::format("{0:24.16e}",eos.srad); + os << " Radiation Energy: " << std::format("{0:24.16e}",eos.erad) << "\n"; + os << " Total Entropy: " << std::format("{0:24.16e}",eos.stot) << "\n"; + os << " dEntr/dRho: " << std::format("{0:24.16e}",eos.dentrdd) << "\n"; + os << " dEntr/dT: " << std::format("{0:24.16e}",eos.dentrdt) << "\n"; + os << " Gas Entropy: " << std::format("{0:24.16e}",eos.sgas) << "\n"; + os << " Radiation Entropy: " << std::format("{0:24.16e}",eos.srad); return os; } }; From 09d5a72de6cd7b198682c6267792c0f42e897e5b Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Fri, 13 Jun 2025 07:27:24 -0400 Subject: [PATCH 12/12] feat(pythonInterface/eos): added working python eos interface confirmed that this produces the same results as the underlying C code would predict --- src/python/eos/bindings.cpp | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/python/eos/bindings.cpp b/src/python/eos/bindings.cpp index 6410a2d..40e31d9 100644 --- a/src/python/eos/bindings.cpp +++ b/src/python/eos/bindings.cpp @@ -91,6 +91,56 @@ void register_eos_bindings(pybind11::module &eos_submodule) { ); }, py::return_value_policy::reference_internal); // Keep parent 'table' alive + py::class_(eos_submodule, "EOS") + .def(py::init<>()) + .def_readonly("ye", &serif::eos::helmholtz::EOS::ye) + .def_readonly("etaele", &serif::eos::helmholtz::EOS::etaele) + .def_readonly("xnefer", &serif::eos::helmholtz::EOS::xnefer) + + .def_readonly("ptot", &serif::eos::helmholtz::EOS::ptot) + .def_readonly("pgas", &serif::eos::helmholtz::EOS::pgas) + .def_readonly("prad", &serif::eos::helmholtz::EOS::prad) + + .def_readonly("etot", &serif::eos::helmholtz::EOS::etot) + .def_readonly("egas", &serif::eos::helmholtz::EOS::egas) + .def_readonly("erad", &serif::eos::helmholtz::EOS::erad) + + .def_readonly("stot", &serif::eos::helmholtz::EOS::stot) + .def_readonly("sgas", &serif::eos::helmholtz::EOS::sgas) + .def_readonly("srad", &serif::eos::helmholtz::EOS::srad) + + .def_readonly("dpresdd", &serif::eos::helmholtz::EOS::dpresdd) + .def_readonly("dpresdt", &serif::eos::helmholtz::EOS::dpresdt) + .def_readonly("dpresda", &serif::eos::helmholtz::EOS::dpresda) + .def_readonly("dpresdz", &serif::eos::helmholtz::EOS::dpresdz) + // TODO: Finish adding all the derivatives to the bound class + .def_readonly("dentrdd", &serif::eos::helmholtz::EOS::dentrdd) + .def_readonly("dentrdt", &serif::eos::helmholtz::EOS::dentrdt) + .def_readonly("dentrda", &serif::eos::helmholtz::EOS::dentrda) + .def_readonly("dentrdz", &serif::eos::helmholtz::EOS::dentrdz) + + .def_readonly("denerdd", &serif::eos::helmholtz::EOS::denerdd) + .def_readonly("denerdt", &serif::eos::helmholtz::EOS::denerdt) + .def_readonly("denerda", &serif::eos::helmholtz::EOS::denerda) + .def_readonly("denerdz", &serif::eos::helmholtz::EOS::denerdz) + + .def_readonly("chiT", &serif::eos::helmholtz::EOS::chiT) + .def_readonly("chiRho", &serif::eos::helmholtz::EOS::chiRho) + .def_readonly("csound", &serif::eos::helmholtz::EOS::csound) + .def_readonly("grad_ad", &serif::eos::helmholtz::EOS::grad_ad) + .def_readonly("gamma1", &serif::eos::helmholtz::EOS::gamma1) + .def_readonly("gamma2", &serif::eos::helmholtz::EOS::gamma2) + .def_readonly("gamma3", &serif::eos::helmholtz::EOS::gamma3) + .def_readonly("cV", &serif::eos::helmholtz::EOS::cV) + .def_readonly("cP", &serif::eos::helmholtz::EOS::cP) + .def_readonly("dse", &serif::eos::helmholtz::EOS::dse) + .def_readonly("dpe", &serif::eos::helmholtz::EOS::dpe) + .def_readonly("dsp", &serif::eos::helmholtz::EOS::dsp) + + .def("__repr__", [](const serif::eos::helmholtz::EOS &eos) { + return ""; + }); + py::class_(eos_submodule, "EOSInput") .def(py::init<>()) .def_readwrite("T", &serif::eos::helmholtz::EOSInput::T) @@ -103,4 +153,10 @@ void register_eos_bindings(pybind11::module &eos_submodule) { ", abar=" + std::to_string(input.abar) + ", zbar=" + std::to_string(input.zbar) + ")>"; }); + + eos_submodule.def("get_helm_eos", + &serif::eos::helmholtz::get_helm_EOS, + py::arg("q"), py::arg("table"), + "Calculate the Helmholtz EOS components based on input parameters and table data."); + }