Skip to content
Open
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
boost*
S4/lib
S4/include
setup.py
OpenBLAS
doc/build
build/
*.egg-info/
__pycache__/
*.pov
12 changes: 8 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ S4_PROF = 0
# Specify custom compilers if needed
CXX = g++
CC = gcc
PYTHON ?= python3

#CFLAGS += -O3 -fPIC
CFLAGS = -Wall -O3 -m64 -march=native -mtune=native -msse3 -msse2 -msse -fPIC
Expand All @@ -96,7 +97,7 @@ S4r_LIBNAME = $(OBJDIR)/libS4r.a
#### and PREFIX if you want to install boost to a different location

# Specify the paths to the boost include and lib directories
BOOST_-L$(BOOST_PREFIX)/lib/PREFIX=${CURDIR}/S4
BOOST_PREFIX=${CURDIR}/S4
BOOST_INC = -I$(BOOST_PREFIX)/include
BOOST_LIBS = -lboost_serialization
BOOST_URL=https://sourceforge.net/projects/boost/files/boost/1.74.0/boost_1_74_0.tar.gz
Expand Down Expand Up @@ -348,8 +349,11 @@ FunctionSampler2D.so: modules/function_sampler_2d.c modules/function_sampler_2d.
#### Python extension

S4_pyext: objdir $(S4_LIBNAME)
sh gensetup.py.sh $(OBJDIR) $(S4_LIBNAME) "$(LIBS)" $(BOOST_PREFIX)
pip3 install --upgrade --use-pep517 --no-build-isolation ./
S4_OBJDIR="$(OBJDIR)" \
S4_LIBFILE="$(S4_LIBNAME)" \
S4_LINK_FLAGS='$(LIBS)' \
BOOST_PREFIX="$(BOOST_PREFIX)" \
$(PYTHON) -m pip install --upgrade --no-build-isolation .

clean:
rm -rf $(OBJDIR)
rm -rf $(OBJDIR)
49 changes: 37 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ If you are looking for a more user-friendly interface to S4 (wavelength-dependen
## Python prerequisites

```
pip install numpy wheel setuptools
pip install numpy wheel setuptools setuptools-scm
```

## Key steps:
Expand All @@ -23,6 +23,42 @@ using Homebrew (see below). If you want to use Boost libraries in a different lo
**Note for users of new (late 2020 onwards) Apple machines with M1/Apple silicon/ARM chips: you will need to use Makefile.m1
to compile successfully, see notes below on how to do this.**

## Python Package Information

- **Distribution name**: `s4-fmm`
- **Python import**: `import S4`
- **Supported Python versions**: Python >=3.10,<3.14
- **Supported NumPy versions**: NumPy >=1.26,<3 at runtime, NumPy >=2,<3 when building wheels

These are initial maintained support ranges pending automated compatibility testing.

## Conda Development Environment

For development, you can use the provided conda environment:

```bash
conda env create -f environment.yml
conda activate s4-dev
make S4_pyext
```

Note that native libraries (BLAS, LAPACK, FFTW3, SuiteSparse, Boost) still need to be installed separately according to the platform-specific instructions below.

## Versioning

S4 now uses Git tags for versioning via setuptools-scm. To create a release:

```bash
git tag -a v1.1.0 -m "S4 Python package version 1.1.0"
git push origin v1.1.0
```

The installed version can be checked with:

```bash
python -c "from importlib.metadata import version; print(version('s4-fmm'))"
```

## Installing relevant libraries etc.:

**On Ubuntu (with a working version of Python3):**
Expand All @@ -47,17 +83,6 @@ brew install fftw suite-sparse openblas lapack boost

You can get the make and git commands from homebrew, or through Apple Developer Tools. If the packages are installed/symlinked by Homebrew to the default location (/usr/local/include) you should not have to modify the Makefile, and you should be able to use the same Makefile as Ubuntu/Linux (i.e. no need to use Makefile.osx).

*If you have multiple Python versions, you may need to modify the S4_pyext part of the Makefile:*

````
pip3 install --upgrade ./
````

to e.g.:
```
[path of target python or virtual environment] setup.py install
```

You can install S4 into a virtual environment automatically by just activating that environment in your terminal before running `make S4_pyext`.

See [here](https://rayflare.readthedocs.io/en/latest/Installation/installation.html) for more extensive instructions.
Expand Down
4 changes: 2 additions & 2 deletions S4/main_python.c
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,7 @@ static PyObject *S4Sim_GetFieldsOnGridNumpy(S4Sim *self, PyObject *args, PyObjec
/* strides[0] = strides[1]; */
/* strides[1] = temp; */
/* PyArray_UpdateFlags(Earr, NPY_ARRAY_UPDATE_ALL); */
PyArray_ENABLEFLAGS(Earr, NPY_ARRAY_OWNDATA);
PyArray_ENABLEFLAGS((PyArrayObject*)Earr, NPY_ARRAY_OWNDATA);
/* PyArray_ENABLEFLAGS(Earr, NPY_ARRAY_F_CONTIGUOUS); */
PyObject *Harr;
Harr = PyArray_SimpleNewFromData(3, dims, NPY_COMPLEX128, Hfields);
Expand All @@ -1499,7 +1499,7 @@ static PyObject *S4Sim_GetFieldsOnGridNumpy(S4Sim *self, PyObject *args, PyObjec
/* strides[1] = temp; */

/* PyArray_UpdateFlags(Harr, NPY_ARRAY_UPDATE_ALL); */
PyArray_ENABLEFLAGS(Harr, NPY_ARRAY_OWNDATA);
PyArray_ENABLEFLAGS((PyArrayObject*)Harr, NPY_ARRAY_OWNDATA);
/* Harr->flags |= NPY_OWNDATA */

/* PyArray_ENABLEFLAGS(Harr, NPY_ARRAY_F_CONTIGUOUS); */
Expand Down
20 changes: 20 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Conda development environment for S4 Python packaging
#
# This file establishes Python-level development constraints only.
# Native S4 build dependencies (BLAS, LAPACK, FFTW3, SuiteSparse, Boost)
# are still installed separately according to existing platform instructions.
# Full conda packaging of native dependencies will be addressed in a later task.

name: s4-dev

channels:
- conda-forge

dependencies:
- python=3.12
- numpy>=2,<3
- pip
- setuptools>=80
- setuptools-scm>=9.2
- python-build>=1.2
- matplotlib>=3.8
39 changes: 0 additions & 39 deletions gensetup.py.sh

This file was deleted.

35 changes: 35 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[build-system]
requires = [
"setuptools>=80",
"setuptools-scm>=9.2",
"numpy>=2,<3",
]
build-backend = "setuptools.build_meta"

[project]
name = "s4-fmm"
dynamic = ["version"]
description = "Stanford Stratified Structure Solver (S4): Fourier Modal Method"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "GPL" }
dependencies = [
"numpy>=1.26,<3",
]

[project.optional-dependencies]
examples = [
"matplotlib>=3.8",
]
dev = [
"build>=1.2",
"matplotlib>=3.8",
]

[tool.setuptools]
# S4 is a C extension, not a Python package directory structure
# We only build the S4 extension module, no package discovery needed
py-modules = []

[tool.setuptools_scm]
fallback_version = "1.1.0"
77 changes: 77 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""
Setuptools-based build configuration for S4 Python extension.

This is a transitional wrapper around the existing Make-based native build.
The native S4 library (libS4.a) must be built first using the Makefile.
"""

from pathlib import Path
import os
import shlex
from setuptools import Extension, setup
import numpy as np


def parse_linker_flags(flags_str: str):
"""
Parse linker flags from Makefile LIBS variable.

Converts flags like '-lfoo -L/path' into appropriate setuptools parameters.
Preserves order for linker-sensitive flags.
"""
libraries = []
library_dirs = []
extra_link_args = []

for token in shlex.split(flags_str):
if token.startswith('-l'):
libraries.append(token[2:])
elif token.startswith('-L'):
library_dirs.append(token[2:])
else:
extra_link_args.append(token)

return libraries, library_dirs, extra_link_args


def main():
# Get build configuration from environment variables (set by Makefile)
objdir = os.environ.get('S4_OBJDIR', './build')
libfile = os.environ.get('S4_LIBFILE', './build/libS4.a')
link_flags = os.environ.get('S4_LINK_FLAGS', '')
boost_prefix = os.environ.get('BOOST_PREFIX', f'{Path(__file__).parent.resolve()}/S4')

# Parse linker flags
libs, lib_dirs, extra_link_args = parse_linker_flags(link_flags)

# Base libraries required for S4 extension
libs.extend(['S4', 'stdc++'])
lib_dirs.extend([objdir, f'{boost_prefix}/lib'])

# Include directories
include_dirs = [
f'{boost_prefix}/include',
np.get_include(),
]

# Extension configuration
S4module = Extension(
'S4',
sources=['S4/main_python.c'],
libraries=libs,
library_dirs=lib_dirs,
include_dirs=include_dirs,
extra_objects=[libfile],
runtime_library_dirs=[f'{boost_prefix}/lib'],
extra_compile_args=['-std=gnu99'],
)

# Setup configuration (metadata comes from pyproject.toml)
setup(
ext_modules=[S4module],
)


if __name__ == '__main__':
main()