Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Publish wheels to PyPI

on:
# allows running workflows manually
workflow_dispatch:

release:
types: [published]

jobs:
main:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
arch: manylinux_x86_64
- os: macos-13
arch: macosx_x86_64
- os: macos-14
arch: macosx_arm64

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Build wheels
uses: pypa/[email protected]
env:
CIBW_BUILD: "cp*-${{ matrix.arch }}"
CIBW_ARCHS_LINUX: "x86_64"
CIBW_MANYLINUX_X86_64_IMAGE: quay.io/pypa/manylinux_2_34_x86_64
CIBW_ARCHS_MACOS: "native"
CIBW_ENVIRONMENT_MACOS: >
MACOSX_DEPLOYMENT_TARGET=${{ matrix.os == 'macos-14' && '14.0' || '13.0' }}
DYLD_LIBRARY_PATH=/usr/local/opt/gcc/lib/gcc/current/:$DYLD_LIBRARY_PATH
FC=gfortran-14
CIBW_BUILD_FRONTEND: "build"
with:
package-dir: .
output-dir: wheelhouse
config-file: "{package}/pyproject.toml"

- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: vmecpp-wheels-${{ matrix.os }}
path: ./wheelhouse/*.whl

publish:
name: Publish wheels to PyPI
if: github.event_name == 'release'
needs: main
runs-on: ubuntu-latest
environment:
name: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4
with:
path: wheelhouse

- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
8 changes: 6 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
/bin
/build
__pycache__
*.egg-info
*.egg
*.pyc
build
dist
523 changes: 306 additions & 217 deletions CMakeLists.txt

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions cmake/GenerateScript.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Function to generate and install a Python script
# This function creates a Python script with a specified name, writes content to it,
# and installs it to a specified location.
#
# Arguments:
# SCRIPT_NAME - The name of the script to be generated (e.g., 'my_script').

function(generate_and_install_python_script SCRIPT_NAME)
if(SKBUILD)
# Strip any leading/trailing whitespace from the script name
string(STRIP "${SCRIPT_NAME}" CLEAN_SCRIPT_NAME)

# Define the output path for the generated script using the cleaned name
set(GENERATED_SCRIPT "${CMAKE_BINARY_DIR}/scripts/${CLEAN_SCRIPT_NAME}")

# Generate the script content directly
file(WRITE "${GENERATED_SCRIPT}" "#!/usr/bin/env python3\n\n")
file(APPEND "${GENERATED_SCRIPT}" "import os\n")
file(APPEND "${GENERATED_SCRIPT}" "import sys\n")
file(APPEND "${GENERATED_SCRIPT}" "import sysconfig\n\n")
file(APPEND "${GENERATED_SCRIPT}" "if __name__ == '__main__':\n")
file(APPEND "${GENERATED_SCRIPT}" " os.execv(\n")
file(APPEND "${GENERATED_SCRIPT}" " os.path.join(sysconfig.get_path('platlib'), '${SKBUILD_PROJECT_NAME}', '${CMAKE_INSTALL_BINDIR}', '${CLEAN_SCRIPT_NAME}'),\n")
file(APPEND "${GENERATED_SCRIPT}" " sys.argv,\n")
file(APPEND "${GENERATED_SCRIPT}" " )\n")

# Install the generated script
install(
PROGRAMS "${GENERATED_SCRIPT}"
DESTINATION "${SKBUILD_SCRIPTS_DIR}"
)
endif()
endfunction()
7 changes: 7 additions & 0 deletions njoy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from importlib.metadata import version, PackageNotFoundError
from .paths import *

try:
__version__ = version('njoy')
except PackageNotFoundError:
__version__ = "unknown"
61 changes: 61 additions & 0 deletions njoy/paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import os
import sys
import glob
import warnings
from . import __path__

CORE_BASE_PATH = os.path.join(__path__[0], "core")

if not os.path.exists(CORE_BASE_PATH):
import sysconfig
CORE_BASE_PATH = os.path.join(sysconfig.get_path("platlib"), "njoy", "core")
if not os.path.exists(CORE_BASE_PATH):
raise ImportError("NJOY is not installed. Please run 'pip install njoy'.")
if os.environ.get("NJOY_DEV_MODE", "0") != "1":
warnings.warn(
"It seems that NJOY is being run from its source directory. "
"This setup is not recommended as it may lead to unexpected behavior, "
"such as conflicts between source and installed versions. "
"Please run your script from outside the NJOY source tree.",
RuntimeWarning
)

def get_paths(subdir, pattern="*", recursive=False):
"""
Helper function to return paths that match a given pattern within a subdirectory.

Args:
subdir (str): The subdirectory within the 'core' directory.
pattern (str): The pattern to match files or directories.
recursive (bool): Whether to search recursively in subdirectories.

Returns:
list: A list of matched paths.
"""
search_pattern = os.path.join(CORE_BASE_PATH, subdir, "**", pattern) if recursive else os.path.join(CORE_BASE_PATH, subdir, pattern)
return glob.glob(search_pattern, recursive=recursive)

def get_include_path():
"""Return includes and include path for NJOY headers."""
include = get_paths("include", "*", recursive=True)
include_path = get_paths("include", "", recursive=False)
return include, include_path

def get_core_libraries():
"""Return libraries and library paths for NJOY."""
lib = [lib_file for lib in ["lib", "lib64"] for lib_file in get_paths(lib, "libNJOY*", recursive=True)]
lib_path = [lib_file for lib in ["lib", "lib64"] for lib_file in get_paths(lib, "", recursive=False)]
return lib, lib_path

def get_extra_libraries():
"""Return the extra libraries installed by auditwheel or delocate."""
libs_path = os.path.join(__path__[0], ".dylibs") if sys.platform == "darwin" else os.path.normpath(os.path.join(__path__[0], "..", "njoy.libs"))
return (glob.glob(os.path.join(libs_path, "*")), libs_path) if os.path.exists(libs_path) else ([], [])

# Setup variables
include, include_path = get_include_path()
lib, lib_path = get_core_libraries()
extra_lib, extra_lib_path = get_extra_libraries()

# Export variables for easy access
__all__ = ["include", "include_path", "lib", "lib_path", "extra_lib", "extra_lib_path"]
48 changes: 48 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[build-system]
requires = ["scikit-build-core", "setuptools-scm"]
build-backend = "scikit_build_core.build"

[project]
name = "njoy"
description = "NJOY is a modular code that processes ENDF nuclear data into libraries for applications, with independent modules communicating via files."
authors = [{ name = "The NJOY Development Team", email = "[email protected]" }]
license = { file = "LICENSE" }
readme = "README.md"
requires-python = ">=3.8"
dynamic = ["version"]

keywords = [
"Nuclear Data",
"ENDF",
"Nuclear Engineering",
"Nuclear Physics",
"Monte Carlo",
"Neutron Transport",
]

classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Intended Audience :: End Users/Desktop",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: BSD 3-Clause License",
"Natural Language :: English",
"Topic :: Scientific/Engineering",
"Programming Language :: Fortran",
"Programming Language :: Python :: 3",
]

[project.urls]
Homepage = "https://www.njoy21.io/NJOY2016/"
Documentation = "https://www.njoy21.io/NJOY2016/"
Repository = "https://github.com/njoy/NJOY2016"
Issues = "https://github.com/njoy/NJOY2016/issues"

[tool.scikit-build]
build.verbose = true
logging.level = "INFO"
wheel.packages = ["njoy"]
wheel.install-dir = "njoy"
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"

[tool.setuptools_scm]