build(meson): much more robust build system

This commit is contained in:
2026-06-10 14:28:55 -04:00
parent 1c975a873d
commit d6fff3cdbe
33 changed files with 571 additions and 360 deletions

View File

@@ -9,7 +9,7 @@
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"

View File

@@ -11,7 +11,7 @@
#include "fourdst/composition/composition.h" #include "fourdst/composition/composition.h"
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"

View File

@@ -11,7 +11,7 @@
#include "fourdst/composition/composition.h" #include "fourdst/composition/composition.h"
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"

View File

@@ -7,7 +7,7 @@
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"

View File

@@ -7,7 +7,6 @@ cppad_cmake_options.add_cmake_defines({
'include_doc': 'false', 'include_doc': 'false',
'CMAKE_POSITION_INDEPENDENT_CODE': true 'CMAKE_POSITION_INDEPENDENT_CODE': true
}) })
cppad_cmake_options.set_install(false) cppad_cmake_options.set_install(false)
cppad_sp = cmake.subproject( cppad_sp = cmake.subproject(
@@ -31,13 +30,19 @@ libcppad_static = static_library(
) )
cppad_dep = declare_dependency( cppad_dep = declare_dependency(
link_with: libcppad_static,
include_directories: cppad_incs include_directories: cppad_incs
) )
message('Installing CppAD headers to ' + get_option('includedir')) message('Staging vendored CppAD headers for ' + gridfire_vendor_includedir)
install_subdir(
'subprojects/CppAD-20250000.2/include', custom_target(
install_dir: get_option('includedir'), 'vendor_cppad_headers',
strip_directory: true command: copytree_cmd + [
meson.project_source_root() / 'subprojects' / 'CppAD-20250000.2' / 'include' / 'cppad',
'@OUTPUT@',
],
output: 'cppad',
depends: libcppad_static,
install: true,
install_dir: gridfire_vendor_includedir,
) )

View File

@@ -1,5 +1,13 @@
eigen_dep = dependency( eigen_sp = subproject('eigen')
'eigen3', eigen_dep = eigen_sp.get_variable('eigen_dep').as_system()
version : '>=3.3',
fallback : ['eigen', 'eigen_dep'] custom_target(
).as_system() 'vendor_eigen_headers',
command: copytree_cmd + [
meson.project_source_root() / 'subprojects' / 'eigen-3.4.0' / 'Eigen',
'@OUTPUT@',
],
output: 'Eigen',
install: true,
install_dir: gridfire_vendor_includedir,
)

View File

@@ -1,14 +1,11 @@
# bring in all of the fourdst utility repositories
fourdst_build_lib_all = true fourdst_build_lib_all = true
if not get_option('plugin_support') if not get_option('plugin_support')
fourdst_build_lib_all=false fourdst_build_lib_all=false
message('Disabling fourdst plugin support as per user request.') message('Disabling fourdst plugin support as per user request.')
endif endif
fourdst_sp = subproject('fourdst', fourdst_default_options = [
default_options: 'build_tests=' + get_option('build_tests').to_string(),
['build_tests=' + get_option('build_tests').to_string(),
'build_python=' + get_option('build_python').to_string(), 'build_python=' + get_option('build_python').to_string(),
'build_lib_all=' + fourdst_build_lib_all.to_string(), 'build_lib_all=' + fourdst_build_lib_all.to_string(),
'build_lib_comp=true', 'build_lib_comp=true',
@@ -17,7 +14,12 @@ fourdst_sp = subproject('fourdst',
'build_lib_const=true', 'build_lib_const=true',
'pkg_config=' + get_option('pkg_config').to_string(), 'pkg_config=' + get_option('pkg_config').to_string(),
] ]
)
if get_option('build_python')
fourdst_default_options += ['default_library=static']
endif
fourdst_sp = subproject('fourdst', default_options: fourdst_default_options)
composition_dep = fourdst_sp.get_variable('composition_dep') composition_dep = fourdst_sp.get_variable('composition_dep')
log_dep = fourdst_sp.get_variable('log_dep') log_dep = fourdst_sp.get_variable('log_dep')
@@ -36,3 +38,45 @@ if get_option('plugin_support')
warning('Including plugin library from fourdst. Note this will bring in minizip-ng and openssl, which can cause build issues with cross compilation due to their complexity.') warning('Including plugin library from fourdst. Note this will bring in minizip-ng and openssl, which can cause build issues with cross compilation due to their complexity.')
libplugin = fourdst_sp.get_variable('libplugin') libplugin = fourdst_sp.get_variable('libplugin')
endif endif
if get_option('build_python')
sp_root = meson.project_source_root() / 'subprojects'
fourdst_header_trees = [
['config',
sp_root / 'libconfig' / 'src' / 'config' / 'include' / 'fourdst' / 'config',
gridfire_includedir / 'fourdst'],
['composition',
sp_root / 'libcomposition' / 'src' / 'composition' / 'include' / 'fourdst' / 'composition',
gridfire_includedir / 'fourdst'],
['atomic',
sp_root / 'libcomposition' / 'src' / 'composition' / 'include' / 'fourdst' / 'atomic',
gridfire_includedir / 'fourdst'],
['constants',
sp_root / 'libconstants' / 'src' / 'constants' / 'include' / 'fourdst' / 'constants',
gridfire_includedir / 'fourdst'],
['logging',
sp_root / 'liblogging' / 'src' / 'logging' / 'include' / 'fourdst' / 'logging',
gridfire_includedir / 'fourdst'],
['toml++',
sp_root / 'libconfig' / 'build-config' / 'tomlpp' / 'vendor' / 'include' / 'toml++',
gridfire_fourdst_vendor_includedir],
['quill',
sp_root / 'quill' / 'include' / 'quill',
gridfire_fourdst_vendor_includedir],
['CLI',
sp_root / 'CLI11-2.6.1' / 'include' / 'CLI',
gridfire_fourdst_vendor_includedir],
]
foreach t : fourdst_header_trees
custom_target(
'wheel_headers_' + t[0].underscorify(),
command: copytree_cmd + [t[1], '@OUTPUT@'],
output: t[0],
install: true,
install_dir: t[2],
)
endforeach
endif

View File

@@ -1,3 +1,9 @@
json_dep = declare_dependency( json_dep = declare_dependency(
include_directories: include_directories('include') include_directories: include_directories('include')
) )
install_subdir(
'include',
install_dir: gridfire_vendor_includedir,
strip_directory: true,
)

View File

@@ -4,6 +4,41 @@ if get_option('build_python')
subdir('python') subdir('python')
subdir('pybind') subdir('pybind')
endif endif
if get_option('build_python')
gridfire_pkg_dir = py_installation.get_install_dir() / 'gridfire'
gridfire_includedir = gridfire_pkg_dir / 'include'
gridfire_libdir = gridfire_pkg_dir / 'lib'
gridfire_pcdir = gridfire_libdir / 'pkgconfig'
else
gridfire_includedir = get_option('includedir')
gridfire_libdir = get_option('libdir')
gridfire_pcdir = get_option('libdir') / 'pkgconfig'
endif
gridfire_vendor_includedir = gridfire_includedir / 'gridfire' / 'vendor'
gridfire_fourdst_vendor_includedir = gridfire_includedir / 'fourdst' / 'vendor'
vendor_py = import('python').find_installation('python3')
copytree_cmd = [
vendor_py, '-c',
'''import sys, os, stat, shutil
src, dst = sys.argv[1], sys.argv[2]
shutil.rmtree(dst, ignore_errors=True)
shutil.copytree(src, dst)
for root, dirs, files in os.walk(dst):
os.chmod(root, 0o755)
for f in files:
p = os.path.join(root, f)
os.chmod(p, os.stat(p).st_mode | 0o644)
''',
]
copyfiles_cmd = [
vendor_py, '-c',
'import sys, shutil; [shutil.copy2(p, sys.argv[-1]) for p in sys.argv[1:-1]]',
]
subdir('fourdst') subdir('fourdst')
subdir('sundials') subdir('sundials')
@@ -14,7 +49,6 @@ subdir('eigen')
subdir('json') subdir('json')
subdir('CLI11') subdir('CLI11')
subdir('unordered_dense')
if get_option('use_mimalloc') if get_option('use_mimalloc')
subdir('mimalloc') subdir('mimalloc')

View File

@@ -1,8 +1,7 @@
py_installation = import('python').find_installation('python3', pure: false) py_installation = import('python').find_installation('python3', pure: false)
if meson.is_cross_build() and host_machine.system() == 'darwin' if meson.is_cross_build() and host_machine.system() == 'darwin'
py_ver = get_option('python-target-version') py_ver = get_option('python_target_version')
message('Cross build on Darwin, using python version ' + py_ver) message('Cross build on Darwin, using python version ' + py_ver)
py_inc_dir = include_directories('../../cross/python_includes/python-' + py_ver + '/include/python' + py_ver) py_inc_dir = include_directories('../../cross/python_includes/python-' + py_ver + '/include/python' + py_ver)
py_dep = declare_dependency(include_directories: py_inc_dir) py_dep = declare_dependency(include_directories: py_inc_dir)

View File

@@ -1,9 +1,11 @@
cmake = import('cmake') cmake = import('cmake')
cvode_cmake_options = cmake.subproject_options() cvode_cmake_options = cmake.subproject_options()
sundials_hidden_flags = '-Wno-deprecated-declarations -fvisibility=hidden -fvisibility-inlines-hidden'
cvode_cmake_options.add_cmake_defines({ cvode_cmake_options.add_cmake_defines({
'CMAKE_CXX_FLAGS' : '-Wno-deprecated-declarations', 'CMAKE_CXX_FLAGS' : sundials_hidden_flags,
'CMAKE_C_FLAGS' : '-Wno-deprecated-declarations', 'CMAKE_C_FLAGS' : sundials_hidden_flags,
'BUILD_SHARED_LIBS' : 'OFF', 'BUILD_SHARED_LIBS' : 'OFF',
'BUILD_STATIC_LIBS' : 'ON', 'BUILD_STATIC_LIBS' : 'ON',
'EXAMPLES_ENABLE_C' : 'OFF', 'EXAMPLES_ENABLE_C' : 'OFF',
@@ -62,7 +64,6 @@ cvode_includes = [
sundials_sunlinsoldense_includes sundials_sunlinsoldense_includes
] ]
empty_cvode_file = configure_file( empty_cvode_file = configure_file(
output: 'cvode_dummy_ar.cpp', output: 'cvode_dummy_ar.cpp',
command: ['echo'], command: ['echo'],
@@ -70,7 +71,6 @@ empty_cvode_file = configure_file(
) )
libcvode_static = static_library( libcvode_static = static_library(
'cvode-static', 'cvode-static',
empty_cvode_file, empty_cvode_file,
@@ -80,10 +80,6 @@ libcvode_static = static_library(
install: false install: false
) )
cvode_dep = declare_dependency( cvode_dep = declare_dependency(
link_with: libcvode_static,
include_directories: cvode_includes, include_directories: cvode_includes,
) )

View File

@@ -3,8 +3,8 @@ cmake = import('cmake')
kinsol_cmake_options = cmake.subproject_options() kinsol_cmake_options = cmake.subproject_options()
kinsol_cmake_options.add_cmake_defines({ kinsol_cmake_options.add_cmake_defines({
'CMAKE_CXX_FLAGS' : '-Wno-deprecated-declarations', 'CMAKE_CXX_FLAGS' : '-Wno-deprecated-declarations -fvisibility=hidden -fvisibility-inlines-hidden',
'CMAKE_C_FLAGS' : '-Wno-deprecated-declarations', 'CMAKE_C_FLAGS' : '-Wno-deprecated-declarations -fvisibility=hidden -fvisibility-inlines-hidden',
'BUILD_SHARED_LIBS' : 'OFF', 'BUILD_SHARED_LIBS' : 'OFF',
'BUILD_STATIC_LIBS' : 'ON', 'BUILD_STATIC_LIBS' : 'ON',
'EXAMPLES_ENABLE_C' : 'OFF', 'EXAMPLES_ENABLE_C' : 'OFF',
@@ -48,8 +48,5 @@ libkinsol_static = static_library(
kinsol_dep = declare_dependency( kinsol_dep = declare_dependency(
link_with: libkinsol_static,
include_directories: kinsol_includes include_directories: kinsol_includes
) )

View File

@@ -7,3 +7,11 @@ sundials_dep = declare_dependency(
kinsol_dep, kinsol_dep,
], ],
) )
# Vendor the SUNDIALS public headers (see vendor/meson.build). GridFire's
# installed public headers #include <sundials/...> etc., so consumers of
# gridfire.pc must compile against exactly the headers this build used --
# never whatever SUNDIALS happens to be in /usr/local/include (which may be
# a different version or configuration, e.g. an MPI-enabled build whose
# sundials_config.h pulls in <mpi.h>).
subdir('vendor')

View File

@@ -0,0 +1,46 @@
sundials_src_include = meson.project_source_root() / 'subprojects' / 'cvode-7.5.0' / 'include'
kinsol_src_include = meson.project_source_root() / 'subprojects' / 'kinsol-7.5.0' / 'include'
sundials_vendor_dirs = [
'sundials', # core (includes sundials/priv/)
'cvode',
'nvector',
'sunmatrix',
'sunlinsol',
'sunnonlinsol',
'sunmemory',
'sunadaptcontroller',
]
foreach d : sundials_vendor_dirs
custom_target(
'vendor_sundials_' + d,
command: copytree_cmd + [sundials_src_include / d, '@OUTPUT@'],
output: d,
install: true,
install_dir: gridfire_vendor_includedir,
)
endforeach
custom_target(
'vendor_sundials_kinsol',
command: copytree_cmd + [kinsol_src_include / 'kinsol', '@OUTPUT@'],
output: 'kinsol',
install: true,
install_dir: gridfire_vendor_includedir,
)
sundials_cmake_include = meson.global_build_root() / 'subprojects' / 'cvode-7.5.0' / '__CMake_build' / 'include' / 'sundials'
custom_target(
'vendor_sundials_generated',
command: copyfiles_cmd + [
sundials_cmake_include / 'sundials_config.h',
sundials_cmake_include / 'sundials_export.h',
'@OUTDIR@',
],
output: ['sundials_config.h', 'sundials_export.h'],
depends: libcvode_static,
install: true,
install_dir: gridfire_vendor_includedir / 'sundials',
)

View File

@@ -1,4 +1,9 @@
xxhash_dep = declare_dependency( xxhash_dep = declare_dependency(
include_directories: include_directories('include') include_directories: include_directories('include')
) )
install_subdir(
'include',
install_dir: gridfire_vendor_includedir,
strip_directory: true,
)

View File

@@ -0,0 +1,9 @@
prefix=${pcfiledir}/../..
includedir=${prefix}/include
libdir=${prefix}/lib
Name: gridfire
Description: GridFire nuclear reaction network solver (bundled with the gridfire Python wheel)
Version: @VERSION@
Cflags: -I${includedir} -I${includedir}/gridfire/vendor -I${includedir}/fourdst/vendor
Libs: -L${libdir} -lgridfire -Wl,-rpath,${libdir}

View File

@@ -1,22 +1,29 @@
if get_option('pkg_config') if get_option('build_python')
message('Generating wheel-relocatable pkg-config file for GridFire...')
wheel_pc_conf = configuration_data()
wheel_pc_conf.set('VERSION', meson.project_version())
configure_file(
input: 'gridfire-wheel.pc.in',
output: 'gridfire.pc',
configuration: wheel_pc_conf,
install: true,
install_dir: gridfire_pcdir,
)
elif get_option('pkg_config')
message('Generating pkg-config file for GridFire...') message('Generating pkg-config file for GridFire...')
pkg = import('pkgconfig') pkg = import('pkgconfig')
pkg.generate( pkg.generate(
libgridfire,
name: 'gridfire', name: 'gridfire',
description: 'GridFire nuclear reaction network solver', description: 'GridFire nuclear reaction network solver',
version: meson.project_version(),
libraries: [
libgridfire,
libcomposition,
libconst,
liblogging,
libcppad_static,
libcvode_static,
libkinsol_static
],
subdirs: ['gridfire'],
filebase: 'gridfire', filebase: 'gridfire',
install_dir: join_paths(get_option('libdir'), 'pkgconfig') subdirs: ['gridfire'],
requires: [
'fourdst_composition',
'fourdst_config',
'fourdst_constants',
'fourdst_logging',
],
extra_cflags: ['-I${includedir}' / 'gridfire' / 'vendor'],
) )
endif endif

View File

@@ -4,12 +4,15 @@ if get_option('build_python')
gridfire_py_deps = [ gridfire_py_deps = [
py_dep, py_dep,
pybind11_dep, pybind11_dep,
const_dep,
config_dep,
composition_dep,
gridfire_dep gridfire_dep
] ]
if host_machine.system() == 'darwin'
gridfire_ext_rpath = '@loader_path/lib'
else
gridfire_ext_rpath = '$ORIGIN/lib'
endif
py_sources = [ py_sources = [
meson.project_source_root() + '/src/python/bindings.cpp', meson.project_source_root() + '/src/python/bindings.cpp',
meson.project_source_root() + '/src/python/types/bindings.cpp', meson.project_source_root() + '/src/python/types/bindings.cpp',
@@ -41,14 +44,16 @@ if get_option('build_python')
name_prefix: '', name_prefix: '',
name_suffix: 'so', name_suffix: 'so',
install: true, install: true,
install_rpath: gridfire_ext_rpath,
install_dir: py_installation.get_install_dir() + '/gridfire' install_dir: py_installation.get_install_dir() + '/gridfire'
) )
else else
py_mod = py_installation.extension_module( py_mod = py_installation.extension_module(
'_gridfire', # Name of the generated .so/.pyd file (without extension) '_gridfire',
sources: py_sources, sources: py_sources,
dependencies : gridfire_py_deps, dependencies : gridfire_py_deps,
install : true, install : true,
install_rpath: gridfire_ext_rpath,
subdir: 'gridfire', subdir: 'gridfire',
) )
endif endif

View File

@@ -1,4 +1,4 @@
option('log_level', type: 'combo', choices: ['traceL3', 'traceL2', 'traceL1', 'debug', 'info', 'warning', 'error', 'critial'], value: 'info', description: 'Set the log level for the GridFire library') option('log_level', type: 'combo', choices: ['traceL3', 'traceL2', 'traceL1', 'debug', 'info', 'warning', 'error', 'critical'], value: 'info', description: 'Set the log level for the GridFire library')
option('pkg_config', type: 'boolean', value: true, description: 'generate pkg-config file for GridFire (gridfire.pc)') option('pkg_config', type: 'boolean', value: true, description: 'generate pkg-config file for GridFire (gridfire.pc)')
option('build_python', type: 'boolean', value: false, description: 'build the python bindings so you can use GridFire from python') option('build_python', type: 'boolean', value: false, description: 'build the python bindings so you can use GridFire from python')
option('build_tests', type: 'boolean', value: true, description: 'build the test suite') option('build_tests', type: 'boolean', value: true, description: 'build the test suite')

View File

@@ -1,5 +1,5 @@
#!/bin/bash #!/bin/bash
# pip_install_mac.sh - Temporary workaround for meson-python duplicate RPATH bug on macOS # pip_install_mac_patch.sh - Workaround for meson-python duplicate RPATH bug on macOS
set -e set -e
@@ -9,26 +9,30 @@ YELLOW='\033[1;33m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
NC='\033[0m' # No Color NC='\033[0m' # No Color
# Returns 0 if the Mach-O binary at $1 has duplicate LC_RPATH entries.
has_duplicate_rpaths() {
local binary="$1"
local rpaths dup
rpaths=$(otool -l "$binary" | awk '/cmd LC_RPATH/{getline; getline; print $2}')
dup=$(printf '%s\n' "$rpaths" | sort | uniq -d)
[ -n "$dup" ]
}
echo -e "${YELLOW}" echo -e "${YELLOW}"
echo "=========================================================================" echo "========================================================================="
echo " TEMPORARY INSTALLATION WORKAROUND" echo " INSTALLATION + DUPLICATE-RPATH SAFETY NET (macOS)"
echo "=========================================================================" echo "========================================================================="
echo -e "${NC}" echo -e "${NC}"
echo "" echo ""
echo -e "${YELLOW}WARNING:${NC} This script applies a temporary patch to fix a known issue with" echo "This script installs gridfire with pip and then checks the installed"
echo "meson-python that causes duplicate RPATH entries in built Python extensions" echo "extension modules for duplicate LC_RPATH entries (a meson-python bug"
echo "on macOS, preventing module imports." echo "exposed by macOS 26.1, see:"
echo " https://github.com/mesonbuild/meson-python/issues/813 )."
echo "" echo ""
echo "This workaround will:" echo "With the current self-contained wheel layout the bug should not"
echo " 1. Install fourdst using pip" echo "trigger; binaries are only patched if duplicates are actually found."
echo " 2. Locate the installed extension module"
echo " 3. Remove duplicate RPATH entries using install_name_tool"
echo "" echo ""
echo "This is a temporary solution while the meson-python team resolves the" echo -e "${YELLOW}Continue? [y/N]${NC} "
echo "duplicate RPATH bug. For more information, see:"
echo " https://github.com/mesonbuild/meson-python/issues/813"
echo ""
echo -e "${YELLOW}Do you understand and wish to continue? [y/N]${NC} "
read -r response read -r response
if [[ ! "$response" =~ ^[Yy]$ ]]; then if [[ ! "$response" =~ ^[Yy]$ ]]; then
@@ -39,7 +43,6 @@ fi
echo "" echo ""
echo -e "${GREEN}Step 1: Finding current Python environment...${NC}" echo -e "${GREEN}Step 1: Finding current Python environment...${NC}"
# Get the current Python executable
PYTHON_BIN=$(which python3) PYTHON_BIN=$(which python3)
if [ -z "$PYTHON_BIN" ]; then if [ -z "$PYTHON_BIN" ]; then
echo -e "${RED}Error: python3 not found in PATH${NC}" echo -e "${RED}Error: python3 not found in PATH${NC}"
@@ -50,12 +53,11 @@ echo "Using Python: $PYTHON_BIN"
PYTHON_VERSION=$($PYTHON_BIN --version) PYTHON_VERSION=$($PYTHON_BIN --version)
echo "Python version: $PYTHON_VERSION" echo "Python version: $PYTHON_VERSION"
# Get site-packages directory
SITE_PACKAGES=$($PYTHON_BIN -c "import site; print(site.getsitepackages()[0])") SITE_PACKAGES=$($PYTHON_BIN -c "import site; print(site.getsitepackages()[0])")
echo "Site packages: $SITE_PACKAGES" echo "Site packages: $SITE_PACKAGES"
echo "" echo ""
echo -e "${GREEN}Step 2: Installing fourdst with pip...${NC}" echo -e "${GREEN}Step 2: Installing gridfire with pip...${NC}"
$PYTHON_BIN -m pip install . -v --no-build-isolation $PYTHON_BIN -m pip install . -v --no-build-isolation
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
@@ -64,80 +66,43 @@ if [ $? -ne 0 ]; then
fi fi
echo "" echo ""
echo -e "${GREEN}Step 3: Locating installed gridfire extension module...${NC}" FIX_SCRIPT="build-python/fix_rpaths.py"
# Find the .so file check_and_fix() {
SO_FILE=$(find "$SITE_PACKAGES/gridfire" -name "_gridfire.cpython-*-darwin.so" 2>/dev/null | head -n 1) local label="$1" so_file="$2"
if [ -z "$SO_FILE" ]; then if [ -z "$so_file" ]; then
echo -e "${RED}Error: Could not find _gridfire.cpython-*-darwin.so in $SITE_PACKAGES/gridfire${NC}" echo -e "${YELLOW}Skipping ${label}: extension module not found (package may not be installed).${NC}"
echo "Installation may have failed or the file is in an unexpected location." return 0
exit 1
fi fi
echo "Found gridfire extension module: $SO_FILE" echo "Found ${label} extension module: $so_file"
echo "" if has_duplicate_rpaths "$so_file"; then
echo -e "${YELLOW}Duplicate LC_RPATH entries detected in ${label}; applying fix...${NC}"
echo -e "${GREEN}Step 4: Running RPATH fix script for gridfire extension module...${NC}"
# Check if fix_rpath.py exists
FIX_SCRIPT="build-python/fix_rpaths.py"
if [ ! -f "$FIX_SCRIPT" ]; then if [ ! -f "$FIX_SCRIPT" ]; then
echo -e "${RED}Error: $FIX_SCRIPT not found${NC}" echo -e "${RED}Error: $FIX_SCRIPT not found${NC}"
echo "Please ensure you're running this script from the project root directory." echo "Please run this script from the project root directory."
exit 1 exit 1
fi fi
$PYTHON_BIN "$FIX_SCRIPT" "$so_file"
# Run the fix script else
$PYTHON_BIN "$FIX_SCRIPT" "$SO_FILE" echo -e "${GREEN}No duplicate LC_RPATH entries in ${label}; no patch needed.${NC}"
if [ $? -ne 0 ]; then
echo -e "${RED}Error: RPATH fix script failed for gridfire extension module${NC}"
exit 1
fi fi
echo -e "${GREEN}Step 5: Locating installed fourdst extension module...${NC}"
# Find the .so file
SO_FILE=$(find "$SITE_PACKAGES/fourdst" -name "_phys.cpython-*-darwin.so" 2>/dev/null | head -n 1)
if [ -z "$SO_FILE" ]; then
echo -e "${RED}Error: Could not find _phys.cpython-*-darwin.so in $SITE_PACKAGES/fourdst${NC}"
echo "Installation may have failed or the file is in an unexpected location."
exit 1
fi
echo "Found fourdst extension module: $SO_FILE"
echo "" echo ""
}
echo -e "${GREEN}Step 6: Running RPATH fix script for fourdst extension module...${NC}" echo -e "${GREEN}Step 3: Checking installed extension modules...${NC}"
# Check if fix_rpath.py exists GRIDFIRE_SO=$(find "$SITE_PACKAGES/gridfire" -name "_gridfire.cpython-*-darwin.so" 2>/dev/null | head -n 1)
FIX_SCRIPT="build-python/fix_rpaths.py" check_and_fix "gridfire" "$GRIDFIRE_SO"
if [ ! -f "$FIX_SCRIPT" ]; then
echo -e "${RED}Error: $FIX_SCRIPT not found${NC}"
echo "Please ensure you're running this script from the project root directory."
exit 1
fi
# Run the fix script FOURDST_SO=$(find "$SITE_PACKAGES/fourdst" -name "_phys.cpython-*-darwin.so" 2>/dev/null | head -n 1)
$PYTHON_BIN "$FIX_SCRIPT" "$SO_FILE" check_and_fix "fourdst" "$FOURDST_SO"
if [ $? -ne 0 ]; then
echo -e "${RED}Error: RPATH fix script failed for fourdst extension module${NC}"
exit 1
fi
echo ""
echo -e "${GREEN}=========================================================================${NC}" echo -e "${GREEN}=========================================================================${NC}"
echo -e "${GREEN} Installation Complete!${NC}" echo -e "${GREEN} Installation Complete!${NC}"
echo -e "${GREEN}=========================================================================${NC}" echo -e "${GREEN}=========================================================================${NC}"
echo "" echo ""
echo "You can now use fourdst in your Python environment."
echo ""
echo "Test the installation with:" echo "Test the installation with:"
echo " $PYTHON_BIN -c 'import gridfire; print(gridfire.__version__)'" echo " $PYTHON_BIN -c 'import gridfire; print(gridfire.__version__)'"
echo "" echo ""
echo -e "${YELLOW}Note:${NC} If you reinstall or upgrade fourdst, you will need to run this"
echo "script again to apply the RPATH fix."
echo ""

View File

@@ -1,17 +1,17 @@
[build-system] [build-system]
requires = [ requires = [
"meson-python>=0.15.0", # Use a recent version "meson-python>=0.19.0",
"meson==1.9.1", # Specify your Meson version requirement "meson>=1.9.1",
"pybind11>=2.10" # pybind11 headers needed at build time "pybind11>=2.10"
] ]
build-backend = "mesonpy" build-backend = "mesonpy"
[project] [project]
name = "gridfire" # Choose your Python package name name = "gridfire"
version = "v0.7.6rc4.2" # Your project's version version = "v0.7.6rc4.2"
description = "Python interface to the GridFire nuclear network code" description = "Python interface to the GridFire nuclear network code"
readme = "README.md" readme = "README.md"
license = { file = "LICENSE.txt" } # Reference your license file [cite: 2] license = { file = "LICENSE.txt" }
authors = [ authors = [
{name = "Emily M. Boudreaux", email = "emily@boudreauxmail.com"}, {name = "Emily M. Boudreaux", email = "emily@boudreauxmail.com"},
@@ -22,4 +22,20 @@ maintainers = [
] ]
[tool.meson-python.args] [tool.meson-python.args]
setup = ['-Dpkg_config=false', '-Dbuildtype=release', '-Dopenmp_support=true', '-Dasan=false', '-Dlog_level=error', '-Dbuild_tests=false', '-Dbuild_c_api=false', '-Dbuild_examples=false', '-Dbuild_benchmarks=false', '-Dbuild_tools=false', '-Dplugin_support=false', '-Duse_mimalloc=false', '-Dbuild_python=true'] setup = [
'-Ddefault_library=static',
'-Dpkg_config=false',
'-Dbuildtype=release',
'-Dopenmp_support=true',
'-Dasan=false',
'-Dlog_level=error',
'-Dbuild_tests=false',
'-Dbuild_c_api=false',
'-Dbuild_examples=false',
'-Dbuild_benchmarks=false',
'-Dbuild_tools=false',
'-Dplugin_support=false',
'-Duse_mimalloc=false',
'-Dbuild_python=true'
]
install = ['--skip-subprojects']

View File

@@ -16,8 +16,6 @@
#include "gridfire/engine/scratchpads/blob.h" #include "gridfire/engine/scratchpads/blob.h"
#include "ankerl/unordered_dense.h"
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>

View File

@@ -15,6 +15,8 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <tuple> #include <tuple>
#include <span>
#include <optional>
// SUNDIALS/CVODE headers // SUNDIALS/CVODE headers
#include <cvode/cvode.h> #include <cvode/cvode.h>
@@ -80,6 +82,24 @@ namespace gridfire::solver {
[[nodiscard]] std::vector<std::tuple<std::string, std::string>> describe() const override; [[nodiscard]] std::vector<std::tuple<std::string, std::string>> describe() const override;
[[nodiscard]] fourdst::composition::Composition getPhysicalComposition() const; [[nodiscard]] fourdst::composition::Composition getPhysicalComposition() const;
/**
* @brief Zero-copy view of the raw solver state vector.
*
* Layout: [y_0, ..., y_{N-1}, eps] where y_i are molar abundances in
* networkSpecies order and eps is the accumulated specific energy.
* The view is only valid for the duration of the callback.
*/
[[nodiscard]] std::span<const double> rawState() const;
/// Abundance of the species at @p species_index (solver state indexing).
[[nodiscard]] double abundance(size_t species_index) const;
/// Abundance of @p sp, or std::nullopt if it is not in the active network.
[[nodiscard]] std::optional<double> abundance(const fourdst::atomic::Species& sp) const;
/// Accumulated specific energy [erg/g] (the trailing state entry).
[[nodiscard]] double accumulatedSpecificEnergy() const;
}; };
using TimestepCallback = std::function<void(const PointSolverTimestepContext& context)>; ///< Type alias for a timestep callback function. using TimestepCallback = std::function<void(const PointSolverTimestepContext& context)>; ///< Type alias for a timestep callback function.

View File

@@ -26,16 +26,10 @@ message(' Minor: ' + ver_parts[1])
message(' Patch: ' + ver_parts[2]) message(' Patch: ' + ver_parts[2])
message(' Tag: ' + ver_parts[3]) message(' Tag: ' + ver_parts[3])
do_install_version_file = true
if get_option('build_python')
message('Not installing version file since we are building the Python extension. The version information will be included in the Python module instead.')
do_install_version_file = false
endif
configure_file( configure_file(
input : 'config.h.in', input : 'config.h.in',
output : 'config.h', output : 'config.h',
configuration : conf_data, configuration : conf_data,
install : do_install_version_file, install : true,
install_dir : get_option('includedir') / 'gridfire/utils' install_dir : gridfire_includedir / 'gridfire/utils'
) )

View File

@@ -1,7 +1,7 @@
#include "gridfire/engine/procedures/priming.h" #include "gridfire/engine/procedures/priming.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "gridfire/solver/solver.h" #include "gridfire/solver/solver.h"
#include "gridfire/engine/engine_abstract.h" #include "gridfire/engine/engine_abstract.h"

View File

@@ -14,7 +14,7 @@
#include "gridfire/engine/scratchpads/engine_multiscale_scratchpad.h" #include "gridfire/engine/scratchpads/engine_multiscale_scratchpad.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
namespace { namespace {
std::set<fourdst::atomic::Species> initialize_seed_species() { std::set<fourdst::atomic::Species> initialize_seed_species() {

View File

@@ -88,6 +88,28 @@ namespace gridfire::solver {
return engine.collectComposition(state_ctx, base_comp, T9, rho); return engine.collectComposition(state_ctx, base_comp, T9, rho);
} }
static_assert(std::is_same_v<sunrealtype, double>, "PointSolverTimestepContext accessors assume SUNDIALS is built with double precision");
std::span<const double> PointSolverTimestepContext::rawState() const {
return { N_VGetArrayPointer(state),
static_cast<std::size_t>(N_VGetLength(state)) };
}
double PointSolverTimestepContext::abundance(const size_t species_index) const {
return N_VGetArrayPointer(state)[species_index];
}
std::optional<double> PointSolverTimestepContext::abundance(const fourdst::atomic::Species& sp) const {
const auto& species = engine.getNetworkSpecies(state_ctx);
if (std::ranges::find(species, sp) == species.end()) {
return std::nullopt;
}
return abundance(engine.getSpeciesIndex(state_ctx, sp));
}
double PointSolverTimestepContext::accumulatedSpecificEnergy() const {
return N_VGetArrayPointer(state)[networkSpecies.size()];
}
void PointSolverContext::init() { void PointSolverContext::init() {
reset_all(); reset_all();
init_context(); init_context();

View File

@@ -1,5 +1,6 @@
# Define the library subdir('include/gridfire/utils')
subdir('include/gridfire/utils') # Generate the version header file first
cpp = meson.get_compiler('cpp')
gridfire_sources = files( gridfire_sources = files(
'lib/engine/engine_graph.cpp', 'lib/engine/engine_graph.cpp',
@@ -44,7 +45,6 @@ gridfire_build_dependencies = [
eigen_dep, eigen_dep,
sundials_dep, sundials_dep,
json_dep, json_dep,
uod_dep,
] ]
if get_option('use_mimalloc') if get_option('use_mimalloc')
@@ -60,34 +60,63 @@ if get_option('openmp_support')
gridfire_build_dependencies += [openmp_dep] gridfire_build_dependencies += [openmp_dep]
endif endif
# Define the libnetwork library so it can be linked against by other parts of the build system gridfire_link_whole = [libcvode_static, libkinsol_static, libcppad_static]
gridfire_link_args = cpp.get_supported_link_arguments(
'-Wl,--exclude-libs,libcvode-static.a:libkinsol_static.a'
)
if get_option('build_python') if get_option('build_python')
libgridfire = static_library('gridfire', gridfire_link_whole += [libcomposition, libconst, liblogging]
if get_option('plugin_support')
gridfire_link_whole += [libplugin]
endif
libgridfire = shared_library('gridfire',
gridfire_sources, gridfire_sources,
include_directories: include_directories('include'), include_directories: include_directories('include'),
dependencies: gridfire_build_dependencies, dependencies: gridfire_build_dependencies,
cpp_args: gridfire_args, cpp_args: gridfire_args,
objects: [cvode_objs, kinsol_objs], link_whole: gridfire_link_whole,
install : false) link_args: gridfire_link_args,
install: true,
install_dir: gridfire_libdir)
else else
libgridfire = library('gridfire', libgridfire = library('gridfire',
gridfire_sources, gridfire_sources,
include_directories: include_directories('include'), include_directories: include_directories('include'),
dependencies: gridfire_build_dependencies, dependencies: gridfire_build_dependencies,
objects: [cvode_objs, kinsol_objs], link_whole: gridfire_link_whole,
link_args: gridfire_link_args,
cpp_args: gridfire_args, cpp_args: gridfire_args,
install : true) install : true)
endif endif
if get_option('build_python')
gridfire_iface_deps = []
foreach d : gridfire_build_dependencies
gridfire_iface_deps += d.partial_dependency(compile_args: true, includes: true)
endforeach
gridfire_dep = declare_dependency(
include_directories: include_directories('include'),
link_with: libgridfire,
dependencies: gridfire_iface_deps,
compile_args: gridfire_args,
)
else
gridfire_dep = declare_dependency( gridfire_dep = declare_dependency(
include_directories: include_directories('include'), include_directories: include_directories('include'),
link_with: libgridfire, link_with: libgridfire,
sources: gridfire_sources,
dependencies: gridfire_build_dependencies, dependencies: gridfire_build_dependencies,
compile_args: gridfire_args, compile_args: gridfire_args,
) )
endif
install_subdir('include/gridfire', install_dir: get_option('includedir')) meson.override_dependency('gridfire', gridfire_dep)
install_subdir('include/gridfire',
install_dir: gridfire_includedir,
exclude_files: ['utils/config.h.in'],
)
if not get_option('build_c_api') and get_option('build_fortran') if not get_option('build_c_api') and get_option('build_fortran')
@@ -97,3 +126,4 @@ if get_option('build_c_api')
message('Configuring C API...') message('Configuring C API...')
subdir('extern') subdir('extern')
endif endif

View File

@@ -1,4 +1,4 @@
[wrap-git] [wrap-git]
url = https://github.com/4D-STAR/fourdst url = https://github.com/4D-STAR/fourdst
revision = v0.9.22 revision = v0.10.1
depth = 1 depth = 1

View File

@@ -9,7 +9,7 @@
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"

View File

@@ -10,7 +10,7 @@
#include "fourdst/composition/composition.h" #include "fourdst/composition/composition.h"
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"
@@ -156,8 +156,8 @@ void record_abundance_history_callback(const gridfire::solver::PointSolverTimest
std::vector<double> Y; std::vector<double> Y;
for (const auto& species : engine.getNetworkSpecies(ctx.state_ctx)) { for (const auto& species : engine.getNetworkSpecies(ctx.state_ctx)) {
const size_t sid = engine.getSpeciesIndex(ctx.state_ctx, species); const size_t sid = engine.getSpeciesIndex(ctx.state_ctx, species);
double y = N_VGetArrayPointer(ctx.state)[sid]; const double y = ctx.abundance(sid);
Y.push_back(y > 0.0 ? y : 0.0); // Regularize tiny negative abundances to zero Y.push_back(y > 0.0 ? y : 0.0);
} }
const fourdst::composition::Composition comp(engine.getNetworkSpecies(ctx.state_ctx), Y); const fourdst::composition::Composition comp(engine.getNetworkSpecies(ctx.state_ctx), Y);

View File

@@ -11,7 +11,7 @@
#include "fourdst/composition/composition.h" #include "fourdst/composition/composition.h"
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"
@@ -226,14 +226,12 @@ void log_results(const gridfire::NetOut& netOut, const gridfire::NetIn& netIn) {
void record_abundance_history_callback(const gridfire::solver::PointSolverTimestepContext& ctx) { void record_abundance_history_callback(const gridfire::solver::PointSolverTimestepContext& ctx) {
s_wrote_abundance_history = true; s_wrote_abundance_history = true;
const auto& engine = ctx.engine; const auto& engine = ctx.engine;
// std::unordered_map<std::string, std::pair<double, double>> abundances;
std::vector<double> Y; std::vector<double> Y;
for (const auto& species : engine.getNetworkSpecies(ctx.state_ctx)) { for (const auto& species : engine.getNetworkSpecies(ctx.state_ctx)) {
const size_t sid = engine.getSpeciesIndex(ctx.state_ctx, species); const size_t sid = engine.getSpeciesIndex(ctx.state_ctx, species);
double y = N_VGetArrayPointer(ctx.state)[sid]; const double y = ctx.abundance(sid);
Y.push_back(y > 0.0 ? y : 0.0); // Regularize tiny negative abundances to zero Y.push_back(y > 0.0 ? y : 0.0);
} }
fourdst::composition::Composition comp(engine.getNetworkSpecies(ctx.state_ctx), Y); fourdst::composition::Composition comp(engine.getNetworkSpecies(ctx.state_ctx), Y);

View File

@@ -11,7 +11,7 @@
#include "fourdst/composition/composition.h" #include "fourdst/composition/composition.h"
#include "fourdst/logging/logging.h" #include "fourdst/logging/logging.h"
#include "fourdst/atomic/species.h" #include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h" #include "fourdst/composition/utils/utils.h"
#include "quill/Logger.h" #include "quill/Logger.h"
#include "quill/Backend.h" #include "quill/Backend.h"
@@ -157,10 +157,9 @@ void record_abundance_history_callback(const gridfire::solver::PointSolverTimest
std::vector<double> Y; std::vector<double> Y;
for (const auto& species : engine.getNetworkSpecies(ctx.state_ctx)) { for (const auto& species : engine.getNetworkSpecies(ctx.state_ctx)) {
const size_t sid = engine.getSpeciesIndex(ctx.state_ctx, species); const size_t sid = engine.getSpeciesIndex(ctx.state_ctx, species);
double y = N_VGetArrayPointer(ctx.state)[sid]; const double y = ctx.abundance(sid);
Y.push_back(y > 0.0 ? y : 0.0); // Regularize tiny negative abundances to zero Y.push_back(y > 0.0 ? y : 0.0);
} }
const fourdst::composition::Composition comp(engine.getNetworkSpecies(ctx.state_ctx), Y); const fourdst::composition::Composition comp(engine.getNetworkSpecies(ctx.state_ctx), Y);
IntermediateResult stepResult; IntermediateResult stepResult;
stepResult.comp = comp; stepResult.comp = comp;