diff --git a/.coveragerc b/.coveragerc index 503c19b88..2f74e1948 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,9 +1,12 @@ [run] parallel = true source = simsopt +relative_files = true [paths] sources = - /opt/hostedtoolcache/Python/3.8.17/x64/lib/python3.8/site-packages - /opt/hostedtoolcache/Python/3.9.17/x64/lib/python3.9/site-packages - /opt/hostedtoolcache/Python/3.10.12/x64/lib/python3.10/site-packages + src/ + /**/lib/python3.8/site-packages + /**/lib/python3.9/site-packages + /**/lib/python3.10/site-packages + /**/lib/python3.11/site-packages diff --git a/.github/workflows/conda.yml b/.github/workflows/conda.yml index 35b1ce59f..b178720e0 100644 --- a/.github/workflows/conda.yml +++ b/.github/workflows/conda.yml @@ -16,7 +16,7 @@ jobs: fail-fast: false matrix: platform: [ubuntu-latest, macos-11] - python-version: ["3.8"] + python-version: ["3.9"] runs-on: ${{ matrix.platform }} @@ -41,7 +41,7 @@ jobs: channel-priority: strict - name: Prepare - run: conda install -y conda-build conda-verify setuptools_scm anaconda-client + run: conda install -y conda-build conda-verify setuptools_scm anaconda-client scikit-build-core - name: Build and upload if: github.event_name == 'release' && github.event.action == 'published' diff --git a/.github/workflows/extensive_ci.yml b/.github/workflows/extensive_test.yml similarity index 97% rename from .github/workflows/extensive_ci.yml rename to .github/workflows/extensive_test.yml index bc1bef2c8..43183f13f 100644 --- a/.github/workflows/extensive_ci.yml +++ b/.github/workflows/extensive_test.yml @@ -29,12 +29,12 @@ jobs: matrix: test-type: [unit, integrated] packages: [all, vmec, spec, none] - python-version: [3.8.17, 3.9.17, 3.10.12] # To sync with coveragerc use 3 levels with python version + python-version: [3.8, 3.9, "3.10"] include: - - python-version: 3.9.17 + - python-version: 3.9 test-type: unit packages: none - - python-version: 3.9.17 + - python-version: 3.9 test-type: integrated packages: none @@ -165,7 +165,9 @@ jobs: - name: Install simsopt package if: contains(matrix.packages, 'spec') || contains(matrix.packages, 'all') - run: pip install -v .[MPI,SPEC] + run: | + pip install -v . + pip install mpi4py py_spec pyoculus h5py - name: Install simsopt package if: contains(matrix.packages, 'none') @@ -173,7 +175,9 @@ jobs: - name: Install simsopt package if: contains(matrix.packages, 'vmec') - run: pip install -v .[MPI] + run: | + pip install -v . + pip install mpi4py - name: Run serial examples as part of integrated tests if: contains(matrix.test-type, 'integrated') && (contains(matrix.packages, 'none') || contains(matrix.packages, 'all')) @@ -247,7 +251,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.8.17 + python-version: 3.9 - name: Install coverage run: pip install coverage diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 43e4ac956..ccbd8d01f 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8] + python-version: [3.9] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/non_simd_tests.yml b/.github/workflows/non_simd_tests.yml index f8e06e610..946a91db5 100644 --- a/.github/workflows/non_simd_tests.yml +++ b/.github/workflows/non_simd_tests.yml @@ -27,7 +27,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8.15] # To sync with coveragerc use 3 level + python-version: [3.9] steps: # First print out lots of information. We do this in separate diff --git a/.github/workflows/ci.yml b/.github/workflows/tests.yml similarity index 95% rename from .github/workflows/ci.yml rename to .github/workflows/tests.yml index ea407082a..ebff4fa57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/tests.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: [3.8.17] # To sync with coveragerc use 3 level + python-version: [3.9] test-type: [unit, integrated] steps: @@ -49,6 +49,11 @@ jobs: set -ex ls -l + - name: Print python location + run: | + set -ex + which python + - name: apt-get stuff needed for libstell and vmec run: | sudo apt-get update @@ -88,7 +93,7 @@ jobs: - name: Install python dependencies run: | sudo apt-get install graphviz graphviz-dev - pip install wheel numpy scipy f90nml h5py scikit-build cmake qsc sympy pyevtk matplotlib ninja plotly networkx pygraphviz + pip install numpy cmake scikit-build f90nml ninja wheel setuptools sympy qsc pyevtk matplotlib plotly networkx pygraphviz mpi4py py_spec pyoculus h5py - name: Install booz_xform run: pip install -v git+https://github.com/hiddenSymmetries/booz_xform @@ -130,8 +135,6 @@ jobs: run: | cd SPEC pip install . - # python setup.py bdist_wheel - # pip install dist/*.whl - name: Try import spec run: python -c "import spec.spec_f90wrapped as spec; print(spec.constants.version)" @@ -159,7 +162,8 @@ jobs: run: python -c "print(dir()); import vmec; print(dir()); print(dir(vmec)); print('package:', vmec.__package__); print('spec:', vmec.__spec__); print('doc:', vmec.__doc__); print('file:', vmec.__file__); print('path:', vmec.__path__)" - name: Install simsopt package - run: env CMAKE_BUILD_TYPE=Debug pip install -v .[MPI,SPEC] + # run: env CMAKE_BUILD_TYPE=Debug pip install -v .[MPI,SPEC] + run: pip install -v ".[MPI,SPEC,VIS]" - name: Verify that importing simsopt does not automatically initialize MPI run: ./tests/verify_MPI_not_initialized.py diff --git a/.github/workflows/wheel.yml b/.github/workflows/wheel.yml index 4b7f6468d..e925d413e 100644 --- a/.github/workflows/wheel.yml +++ b/.github/workflows/wheel.yml @@ -45,7 +45,7 @@ jobs: - uses: actions/setup-python@v4 name: Install Python with: - python-version: '3.8' + python-version: '3.9' - name: Download all submodules run: git submodule update --init diff --git a/CMakeLists.txt b/CMakeLists.txt index 838e8e197..14f8ffc49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,13 @@ cmake_minimum_required(VERSION 3.15) -project(simsoptpp) +project(simsoptpp LANGUAGES CXX) #set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(Python_FIND_STRATEGY LOCATION) -find_package(Python 3 COMPONENTS Interpreter Development.Module NumPy) +find_package(Python REQUIRED COMPONENTS Interpreter Development.Module NumPy) +find_package(pybind11 CONFIG REQUIRED) message(status "Python executable is ${Python_EXECUTABLE}") message(status "Python Development Module found value is ${Python_Development.Module_FOUND}") @@ -40,8 +41,12 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) include(CheckCXXCompilerFlag) IF(DEFINED ENV{CI}) - message(STATUS "CI environment detected. Set compilation flags targetting Westmere microarch.") - set(CMAKE_CXX_FLAGS "-O3 -march=westmere") + if (APPLE) + set(CMAKE_CXX_FLAGS "-O3") + else() + message(STATUS "CI environment detected. Set compilation flags targetting Westmere microarch.") + set(CMAKE_CXX_FLAGS "-O3 -march=westmere") + endif() elseif(DEFINED ENV{CONDA_BUILD}) message(STATUS "conda build environment detected. Let conda set compilation flags accordingly.") # set(CMAKE_CXX_FLAGS "-O3 -march=ivybridge -mfma -ffp-contract=fast") @@ -106,7 +111,7 @@ else() message(STATUS "Boost include dirs are ${Boost_INCLUDE_DIRS}") endif() -add_subdirectory(thirdparty/pybind11) +# add_subdirectory(thirdparty/pybind11) add_subdirectory(thirdparty/fmt EXCLUDE_FROM_ALL) set(XTENSOR_USE_OPENMP 0) set(XTENSOR_USE_TBB 0) @@ -164,4 +169,4 @@ target_link_libraries(profiling PRIVATE fmt::fmt-header-only) #install(TARGETS ${PROJECT_NAME} # #LIBRARY # DESTINATION src/${PROJECT_NAME}) -#install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${PROJECT_NAME}) +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION .) diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index a434fd608..4abd5a27c 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -1,9 +1,6 @@ -{% set data = load_setup_py_data(setup_file='../setup.py', from_recipe_dir=True) %} -{% set version = data.get('version') %} - package: name: simsopt - version: {{ version }} + version: {{ GIT_DESCRIBE_TAG }}+{{ GIT_BUILD_STR }} source: path: .. @@ -24,14 +21,17 @@ requirements: - libgomp # [linux] - "setuptools_scm[toml]" - boost + - pybind11 + - scikit-build-core host: - pip - wheel - - setuptools - numpy {{ numpy }} - "setuptools_scm[toml]" + - scikit-build-core - python {{ python }} + - pybind11 run: - python @@ -49,13 +49,13 @@ requirements: - pyevtk about: - home: {{ data.get('url') }} + home: https://github.com/hiddenSymmetries/simsopt summary: simplified framework for stellarator optimization license: MIT license_file: "LICENSE" description: doc_url: https://simsopt.readthedocs.io/ - dev_url: {{ data.get('url') }} + dev_url: https://github.com/hiddenSymmetries/simsopt extra: recipe-maintainers: diff --git a/pyproject.toml b/pyproject.toml index f877c5587..13f0e9298 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,95 @@ [build-system] -requires = ["setuptools>=45", "wheel", "oldest-supported-numpy", "cmake", "ninja", "setuptools_scm[toml]>=6.0"] -build-backend = "setuptools.build_meta" +requires = [ + "scikit-build-core>=0.3.3", + "pybind11", + "oldest-supported-numpy", + "setuptools_scm>=8.0", + 'tomli; python_version < "3.11"',] +build-backend = "scikit_build_core.build" + +[project] +name = "simsopt" +license = {text = "MIT License"} +description="Framework for optimizing stellarators" +readme = "README.md" +# long_description = file: README.md +# long_description_content_type = text/markdown +requires-python = ">=3.8" +authors = [ + {name = "Matt Landreman", email = "mattland@umd.edu"}, + {name = "Bharat Medasani", email = "mbkumar@gmail.com"}, + {name = "Florian Wechsung", email = "wechsung@nyu.edu"} +] +maintainers = [ + {name = "Bharat Medasani", email = "mbkumar@gmail.com"}, + {name = "Matt Landreman", email = "mattland@umd.edu"}, +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Physics", + "License :: OSI Approved :: MIT License", + "Operating System :: MacOS :: MacOS X", + "Operating System :: POSIX :: Linux", + "Environment :: Console", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10" +] +keywords = [ + "plasma physics", + "plasma", + "magnetohydrodynamics", + "mhd", + "nuclear fusion reactor", + "fusion", + "stellarator", + "vmec", + "spec", + "optimization", + "Biot-Savart", + "magnetostatics" +] +dependencies = [ + "numpy>=1.21", + "jax>=0.2.5", + "jaxlib>=0.1.56", + "scipy>=1.5.4", + "Deprecated>=1.2.10", + "nptyping>=1.3.0", + "monty>=2021.6.10", + "ruamel.yaml", + "sympy", + "f90nml", + "randomgen", + "pyevtk", + "matplotlib" +] +dynamic = ["version"] + +[project.optional-dependencies] +SPEC = ["py_spec>=3.0.1", "pyoculus>=0.1.1", "h5py>=3.1.0"] +MPI = ["mpi4py>=3.0.3"] +VIS = ["vtk >= 8.1.2", "PyQt5", "mayavi", "plotly", "networkx"] +DOCS = ["sphinx", "sphinx-rtd-theme"] + +[project.urls] +Homepage = "https://github.com/hiddenSymmetries/simsopt" +Download = "https://pypi.org/project/simsopt" +Issues = "https://github.com/hiddenSymmetries/simsopt/issues" +Documentation = "https://simsopt.readthedocs.io" +Repository = "https://github.com/hiddenSymmetries/simsopt" + +[tool.setuptools_scm] +write_to = "src/simsopt/_version.py" +local_scheme = "dirty" + +[tool.scikit-build] +metadata.version.provider = "scikit_build_core.metadata.setuptools_scm" +sdist.include = ["src/simsopt/_version.py"] +build-dir = "build/{wheel_tag}" -[tools.setuptools_scm] [tool.ruff] src = ["src/simsopt"] @@ -18,3 +105,14 @@ extend-exclude = ["thirdparty"] "tests/geo/test_curve.py" = ["F401"] "tests/geo/test_surface.py" = ["F401"] "tests/mhd/test_virtual_casing.py" = ["F841"] + +[tool.coverage.run] +parallel = true +relative_files = true +source = ["simsopt"] + +[tool.coverage.paths] +source = [ + "src", + "/**/lib*/python*/site-packages" +] diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 57ac6f15a..000000000 --- a/setup.cfg +++ /dev/null @@ -1,112 +0,0 @@ -[metadata] -name = simsopt -description: Framework for optimizing stellarators -long_description = file: README.md -long_description_content_type = text/markdown -license = MIT -license_files = LICENSE -author = "Matt Landreman, Bharat Medasani, Florian Wechsung" -author_email = mattland@umd.edu, mbkumar@gmail.com, wechsung@nyu.edu -maintainer = "Bharat Medasani" -maintainer_email = mbkumar@gmail.com -url = https://github.com/hiddenSymmetries/simsopt -download_url = https://pypi.org/project/simsopt -project_urls = - Bug Tracker = https://github.com/hiddenSymmetries/simsopt/issues - Documentation = https://simsopt.readthedocs.io - Source Code = https://github.com/hiddenSymmetries/simsopt/archive/master.zip -classifier = - Development Status :: 3 - Alpha - Intended Audience :: Science/Research - Topic :: Scientific/Engineering :: Physics - License :: OSI Approved :: MIT License - Operating System :: MacOS :: MacOS X - Operating System :: POSIX :: Linux - Environment :: Console - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 -keywords = - plasma physics - plasma - magnetohydrodynamics - mhd - nuclear fusion reactor - fusion - stellarator - vmec - spec - optimization - Biot-Savart - magnetostatics - -[options] -install_requires = - numpy >= 1.21 - jax >= 0.2.5 - jaxlib >= 0.1.56 - scipy >= 1.5.4 - Deprecated >= 1.2.10 - nptyping >= 1.3.0 - monty >= 2021.6.10 - ruamel.yaml - sympy - f90nml - randomgen - pyevtk - matplotlib -package_dir = - =src -packages = find: - -[options.extras_require] -SPEC = - py_spec >= 3.0.1 - pyoculus >= 0.1.1 - h5py >= 3.1.0 -MPI = - mpi4py >= 3.0.3 -VIS = - vtk >= 8.1.2 - PyQt5 - mayavi -DOCS = - sphinx - sphinx-rtd-theme - -[options.packages.find] -where = src - -[options.package_data] -* = - input.default - defaults.sp - log_config.yaml - NCSX.dat - HSX.dat - W7-X.dat - GIULIANI_length18_nsurfaces5.ma - GIULIANI_length18_nsurfaces5.curves - GIULIANI_length18_nsurfaces5.currents - GIULIANI_length20_nsurfaces5.ma - GIULIANI_length20_nsurfaces5.curves - GIULIANI_length20_nsurfaces5.currents - GIULIANI_length22_nsurfaces5.ma - GIULIANI_length22_nsurfaces5.curves - GIULIANI_length22_nsurfaces5.currents - GIULIANI_length24_nsurfaces5.ma - GIULIANI_length24_nsurfaces5.curves - GIULIANI_length24_nsurfaces5.currents - GIULIANI_length18_nsurfaces9.ma - GIULIANI_length18_nsurfaces9.curves - GIULIANI_length18_nsurfaces9.currents - GIULIANI_length20_nsurfaces9.ma - GIULIANI_length20_nsurfaces9.curves - GIULIANI_length20_nsurfaces9.currents - GIULIANI_length22_nsurfaces9.ma - GIULIANI_length22_nsurfaces9.curves - GIULIANI_length22_nsurfaces9.currents - GIULIANI_length24_nsurfaces9.ma - GIULIANI_length24_nsurfaces9.curves - GIULIANI_length24_nsurfaces9.currents diff --git a/setup.py b/setup.py deleted file mode 100644 index 97344c6e0..000000000 --- a/setup.py +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env python3 - -import os -import sys -import subprocess -from pathlib import Path -from setuptools import setup, Extension -from setuptools.command.build_ext import build_ext - -import setuptools_scm - -# Convert distutils Windows platform specifiers to CMake -A arguments -PLAT_TO_CMAKE = { - "win32": "Win32", - "win-amd64": "x64", - "win-arm32": "ARM", - "win-arm64": "ARM64", -} - - -# A CMakeExtension needs a sourcedir instead of a file list. -# The name must be the _single_ output extension from the CMake build. -# If you need multiple extensions, see scikit-build. -class CMakeExtension(Extension): - def __init__(self, name, sourcedir=""): - Extension.__init__(self, name, sources=[]) - self.sourcedir = os.path.abspath(sourcedir) - - -class CMakeBuild(build_ext): - # debug: bool = True - def build_extension(self, ext): - extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) - - # required for auto-detection of auxiliary "native" libs - if not extdir.endswith(os.path.sep): - extdir += os.path.sep - - cfg = "Debug" if self.debug else "Release" - cfg = os.getenv("CMAKE_BUILD_TYPE", cfg) - print(f"Choose CMAKE_BUILD_TYPE={cfg}", flush=True) - - # CMake lets you override the generator - we need to check this. - # Can be set with Conda-Build, for example. - cmake_generator = os.environ.get("CMAKE_GENERATOR", "") - - # Set Python_EXECUTABLE instead if you use PYBIND11_FINDPYTHON - # EXAMPLE_VERSION_INFO shows you how to pass a value into the C++ code - # from Python. - - PYTHON_ROOT = str((Path(sys.executable).parent / "..").resolve()) - cmake_args = [ - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY={}".format(extdir), - "-DPYTHON_EXECUTABLE={}".format(sys.executable), - "-DEXAMPLE_VERSION_INFO={}".format(self.distribution.get_version()), - "-DCMAKE_BUILD_TYPE={}".format(cfg), # not used on MSVC, but no harm - "-DPython_ROOT_DIR={}".format(PYTHON_ROOT), - ] - build_args = [] - - if self.compiler.compiler_type != "msvc": - # Using Ninja-build since it a) is available as a wheel and b) - # multithreads automatically. MSVC would require all variables be - # exported for Ninja to pick it up, which is a little tricky to do. - # Users can override the generator with CMAKE_GENERATOR in CMake - # 3.15+. - if not cmake_generator: - cmake_args += ["-GNinja"] - - else: - - # Single config generators are handled "normally" - single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) - - # CMake allows an arch-in-generator style for backward compatibility - contains_arch = any(x in cmake_generator for x in {"ARM", "Win64"}) - - # Specify the arch if using MSVC generator, but only if it doesn't - # contain a backward-compatibility arch spec already in the - # generator name. - if not single_config and not contains_arch: - cmake_args += ["-A", PLAT_TO_CMAKE[self.plat_name]] - - # Multi-config generators have a different way to specify configs - if not single_config: - cmake_args += [ - "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY_{}={}".format(cfg.upper(), extdir) - ] - build_args += ["--config", cfg] - - # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level - # across all generators. - if "CMAKE_BUILD_PARALLEL_LEVEL" not in os.environ: - # self.parallel is a Python 3 only way to set parallel jobs by hand - # using -j in the build_ext call, not supported by pip or PyPA-build. - if hasattr(self, "parallel") and self.parallel: - # CMake 3.12+ only. - build_args += ["-j{}".format(self.parallel)] - - if not os.path.exists(self.build_temp): - os.makedirs(self.build_temp) - - subprocess.check_call( - ["cmake", ext.sourcedir] + cmake_args, cwd=self.build_temp - ) - subprocess.check_call( - ["cmake", "--build", ".", "--target", "simsoptpp"] + build_args, cwd=self.build_temp - ) - - -def my_local_scheme(version: setuptools_scm.version.ScmVersion) -> str: - """My local node and date version.""" - node_and_date = setuptools_scm.version.get_local_node_and_date(version) - dirty = ".dirty" if version.dirty else "" - return str(node_and_date) + dirty - -version = setuptools_scm.get_version( - write_to=Path(".") / "src" / "simsopt" / "_version.py", - version_scheme="post-release", - local_scheme=my_local_scheme, -) - - -setup( - # use_scm_version=True, - version=version, - setup_requires=["setuptools_scm"], - ext_modules=[CMakeExtension("simsoptpp")], - cmdclass={"build_ext": CMakeBuild} -)