/* *********************************************************************** // // Copyright (C) 2025 -- The 4D-STAR Collaboration // File Authors: Aaron Dotter, Emily Boudreaux // Last Modified: March 06, 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 // // *********************************************************************** */ // 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 #define IMAX 541 #define JMAX 201 #include #include #include #include #include #include /** * @brief 2D array template alias. * @tparam T Type of the array elements. * @tparam J Number of columns. * @tparam I Number of rows. */ template using array2D = std::array, I>; double** heap_allocate_contiguous_2D_memory(int rows, int cols); void heap_deallocate_contiguous_2D_memory(double **array); namespace helmholtz { const double tlo = 3.0, thi = 13.0; const double dlo = -12.0, dhi = 15.0; const double tstp = (thi - tlo) / (JMAX - 1), tstpi = 1 / tstp; const double dstp = (dhi - dlo) / (IMAX - 1), dstpi = 1 / dstp; /** * @struct HELMTable * @brief Structure to hold the Helmholtz EOS table data. */ struct HELMTable { bool loaded = false; const int imax = IMAX, jmax = JMAX; double t[JMAX], d[IMAX]; double** f; double** fd; double** ft; double** fdd; double** ftt; double** fdt; double** fddt; double** fdtt; double** fddtt; double** dpdf; double** dpdfd; double** dpdft; double** dpdfdt; double** ef; double** efd; double** eft; double** efdt; double** xf; double** xfd; double** xft; double** xfdt; double dt_sav[JMAX], dt2_sav[JMAX], dti_sav[JMAX], dt2i_sav[JMAX]; double dd_sav[IMAX], dd2_sav[IMAX], ddi_sav[IMAX]; // Constructor HELMTable() { f = heap_allocate_contiguous_2D_memory(IMAX, JMAX); fd = heap_allocate_contiguous_2D_memory(IMAX, JMAX); ft = heap_allocate_contiguous_2D_memory(IMAX, JMAX); fdd = heap_allocate_contiguous_2D_memory(IMAX, JMAX); ftt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); fdt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); fddt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); fdtt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); fddtt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); dpdf = heap_allocate_contiguous_2D_memory(IMAX, JMAX); dpdfd = heap_allocate_contiguous_2D_memory(IMAX, JMAX); dpdft = heap_allocate_contiguous_2D_memory(IMAX, JMAX); dpdfdt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); ef = heap_allocate_contiguous_2D_memory(IMAX, JMAX); efd = heap_allocate_contiguous_2D_memory(IMAX, JMAX); eft = heap_allocate_contiguous_2D_memory(IMAX, JMAX); efdt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); xf = heap_allocate_contiguous_2D_memory(IMAX, JMAX); xfd = heap_allocate_contiguous_2D_memory(IMAX, JMAX); xft = heap_allocate_contiguous_2D_memory(IMAX, JMAX); xfdt = heap_allocate_contiguous_2D_memory(IMAX, JMAX); } // Destructor ~HELMTable() { 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); } friend std::ostream& operator<<(std::ostream& os, const helmholtz::HELMTable& table) { if (!table.loaded) { os << "HELMTable not loaded\n"; return os; } double tMin = std::numeric_limits::max(), tMax = std::numeric_limits::lowest(); double dMin = std::numeric_limits::max(), dMax = std::numeric_limits::lowest(); for (const auto& temp : table.t) { if (temp < tMin) tMin = temp; if (temp > tMax) tMax = temp; } for (const auto& dens : table.d) { if (dens < dMin) dMin = dens; if (dens > dMax) dMax = dens; } os << "HELMTable Data:\n"; os << " imax: " << table.imax << ", jmax: " << table.jmax << "\n"; os << " Temperature Range: [" << tMin << ", " << tMax << "]\n"; os << " Density Range: [" << dMin << ", " << dMax << "]\n"; return os; } }; /** * @struct EOSInput * @brief Structure to hold the input parameters for the EOS calculation. */ struct EOSInput { double T; ///< Temperature. double rho; ///< Density. double abar; ///< Mean atomic mass. double zbar; ///< Mean atomic number. friend std::ostream& operator<<(std::ostream& os, const helmholtz::EOSInput& eosInput) { os << "EOSInput Data:\n"; os << " Temperature: " << eosInput.T << "\n"; os << " Density: " << eosInput.rho << "\n"; os << " Mean Atomic Mass: " << eosInput.abar << "\n"; os << " Mean Atomic Number: " << eosInput.zbar << "\n"; return os; } }; /** * @struct EOS * @brief Structure to hold the output parameters and derivatives of the EOS calculation. */ struct EOS { // output double ye, etaele, xnefer; // double ptot, pgas, prad; // pressure double etot, egas, erad; // energy double stot, sgas, srad; // entropy // derivatives double dpresdd, dpresdt, dpresda, dpresdz; double dentrdd, dentrdt, dentrda, dentrdz; double denerdd, denerdt, denerda, denerdz; // these could become functions: double chiT, chiRho, csound, grad_ad; // derived quantities double gamma1, gamma2, gamma3, cV, cP; // derived quantities double dse, dpe, dsp; // Maxwell relations friend std::ostream& operator<<(std::ostream& os, const helmholtz::EOS& eos) { os << "EOS Data:\n" << std::setw(20) << std::left; os << " Electron Fraction: " << std::format("{0:24.16e}",eos.ye) << "\n"; 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 << " 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 << " 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); return os; } }; // interpolating polynomial function definitions /** * @brief Interpolating polynomial function psi0. * @param z Input value. * @return Result of the polynomial. */ double psi0(double z); /** * @brief Derivative of the interpolating polynomial function psi0. * @param z Input value. * @return Derivative of the polynomial. */ double dpsi0(double z); /** * @brief Second derivative of the interpolating polynomial function psi0. * @param z Input value. * @return Second derivative of the polynomial. */ double ddpsi0(double z); /** * @brief Interpolating polynomial function psi1. * @param z Input value. * @return Result of the polynomial. */ double psi1(double z); /** * @brief Derivative of the interpolating polynomial function psi1. * @param z Input value. * @return Derivative of the polynomial. */ double dpsi1(double z); /** * @brief Second derivative of the interpolating polynomial function psi1. * @param z Input value. * @return Second derivative of the polynomial. */ double ddpsi1(double z); /** * @brief Interpolating polynomial function psi2. * @param z Input value. * @return Result of the polynomial. */ double psi2(double z); /** * @brief Derivative of the interpolating polynomial function psi2. * @param z Input value. * @return Derivative of the polynomial. */ double dpsi2(double z); /** * @brief Second derivative of the interpolating polynomial function psi2. * @param z Input value. * @return Second derivative of the polynomial. */ double ddpsi2(double z); /** * @brief Interpolating polynomial function xpsi0. * @param z Input value. * @return Result of the polynomial. */ double xpsi0(double z); /** * @brief Derivative of the interpolating polynomial function xpsi0. * @param z Input value. * @return Derivative of the polynomial. */ double xdpsi0(double z); /** * @brief Interpolating polynomial function xpsi1. * @param z Input value. * @return Result of the polynomial. */ double xpsi1(double z); /** * @brief Derivative of the interpolating polynomial function xpsi1. * @param z Input value. * @return Derivative of the polynomial. */ double xdpsi1(double z); /** * @brief Interpolating polynomial function h3. * @param fi Array of coefficients. * @param w0t Weight 0 for temperature. * @param w1t Weight 1 for temperature. * @param w0mt Weight 0 for temperature (minus). * @param w1mt Weight 1 for temperature (minus). * @param w0d Weight 0 for density. * @param w1d Weight 1 for density. * @param w0md Weight 0 for density (minus). * @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); /** * @brief Interpolating polynomial function h5. * @param fi Array of coefficients. * @param w0t Weight 0 for temperature. * @param w1t Weight 1 for temperature. * @param w2t Weight 2 for temperature. * @param w0mt Weight 0 for temperature (minus). * @param w1mt Weight 1 for temperature (minus). * @param w2mt Weight 2 for temperature (minus). * @param w0d Weight 0 for density. * @param w1d Weight 1 for density. * @param w2d Weight 2 for density. * @param w0md Weight 0 for density (minus). * @param w1md Weight 1 for density (minus). * @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); /** * @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. */ HELMTable read_helm_table(const std::string filename); /** * @brief Calculate the Helmholtz EOS components. * @param q EOSInput parameters for the EOS calculation. * @param table HELMTable structure containing the table data. * @return EOS structure containing the calculated quantities. */ EOS get_helm_EOS(EOSInput &q, const HELMTable &table); } #endif // HELM_H