Skip to content
Merged
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: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.git
.DS_Store
build
docs/_build
quickstart
19 changes: 11 additions & 8 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,28 @@ on: push
jobs:
test:
name: Run Unit Tests
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
defaults:
run:
# pinning the shell name helps to properly set conda
shell: bash -l {0}
strategy:
matrix:
python-version: [ 3.7, 3.8, 3.9]
python-version: [ 3.7, 3.8, 3.9, '3.12', '3.13', '3.14' ]
env:
OMP_NUM_THREADS: 2
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v6
- name: Setup conda for Python ${{ matrix.python-version }}
uses: conda-incubator/setup-miniconda@v2
uses: conda-incubator/setup-miniconda@v4
with:
activate-environment: test
python-version: ${{ matrix.python-version }}
channels: conda-forge
allow-softlinks: true
channel-priority: flexible
show-channel-urls: true
use-only-tar-bz2: true
- name: Print conda config
run: |
conda info
Expand All @@ -45,17 +44,21 @@ jobs:
- name: Build and install galario, build docs
run: |
conda activate test
conda install astropy cython nomkl numpy pytest scipy sphinx
conda install cython nomkl numpy pytest scipy sphinx
pip install coverage codecov pytest-cov
mkdir build && cd build
cmake -DCMAKE_INSTALL_PREFIX=/tmp -DCMAKE_PREFIX_PATH=${CONDA_PREFIX} ..
make
make install
- name: Run unit tests
run: python/py.test.sh -sv --cov=./ python/test_galario.py
run: python/py.test.sh -sv --cov=./ --cov-report=xml:coverage.xml python/test_galario.py
working-directory: build
- name: Upload code coverage report
run: bash <(curl -s https://codecov.io/bash) || echo 'Codecov failed to upload'
uses: codecov/codecov-action@v5
with:
files: ./build/coverage.xml
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
- name: build docs
run: |
conda activate test
Expand Down
4 changes: 3 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
# For documentation see https://mtazzari.github.io/galario/ #
###############################################################################

cmake_minimum_required(VERSION 3.0)
# CMake 4 no longer provides compatibility for projects declaring a version
# older than 3.5. 3.10 still supports the legacy FindCUDA-based GPU build.
cmake_minimum_required(VERSION 3.10)
project(galario)
set(PACKAGE_VERSION "'1.2.2'")

Expand Down
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Reproduce the Linux CPU installation with pip-managed Python dependencies.
# CUDA is intentionally disabled here: a CUDA build requires an NVIDIA toolkit
# and is covered by the same CMake path when GALARIO_CHECK_CUDA is enabled.
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive \
OMP_NUM_THREADS=2 \
PYTHONPATH=/opt/galario/lib/python3.10/site-packages \
PYTHONDONTWRITEBYTECODE=1

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
libfftw3-dev \
python3 \
python3-dev \
python3-pip \
&& rm -rf /var/lib/apt/lists/*

RUN python3 -m pip install --no-cache-dir \
'Cython>=3' \
numpy \
pytest \
pytest-cov \
scipy

WORKDIR /src
COPY . .

RUN cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/opt/galario \
-DGALARIO_CHECK_CUDA=OFF \
&& cmake --build build --parallel \
&& cmake --install build \
&& python3 -c 'import galario; print(galario.__file__)' \
&& ctest --test-dir build --output-on-failure
37 changes: 37 additions & 0 deletions Dockerfile.conda
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Reproduce the documented and GitHub Actions Linux CPU build in a Conda
# environment. CUDA is intentionally disabled because this image has neither
# an NVIDIA toolkit nor GPU access.
FROM continuumio/miniconda3:latest

ENV DEBIAN_FRONTEND=noninteractive \
OMP_NUM_THREADS=2 \
PYTHONDONTWRITEBYTECODE=1

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
libfftw3-dev \
&& rm -rf /var/lib/apt/lists/*

RUN conda create -y --name galario --channel conda-forge \
python=3.12 \
cython \
numpy \
pytest \
pytest-cov \
scipy \
&& conda clean --all --yes

WORKDIR /src
COPY . .

RUN conda run --no-capture-output --name galario \
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DGALARIO_CHECK_CUDA=OFF \
&& conda run --no-capture-output --name galario cmake --build build --parallel \
&& conda run --no-capture-output --name galario cmake --install build \
&& conda run --no-capture-output --name galario \
python -c 'import galario; print(galario.__file__)' \
&& conda run --no-capture-output --name galario \
ctest --test-dir build --output-on-failure
4 changes: 2 additions & 2 deletions docs/conf.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ release = str(galario.__version__)
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
language = 'en'

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
Expand Down Expand Up @@ -307,4 +307,4 @@ texinfo_documents = [

# override CSS file
def setup(app):
app.add_stylesheet('css/custom.css')
app.add_css_file('css/custom.css')
24 changes: 24 additions & 0 deletions docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,30 @@ To create a conda environment for |galario|, see Section 1.4, step 2.
Due to technical limitations, the conda package does not support GPUs at the
moment. If you want to use a GPU, read on as you have to build |galario| by hand.

Linux installation test containers
----------------------------------

The repository includes two Linux CPU test containers. The Conda container
mirrors the manual and GitHub Actions installation path, using system FFTW and
a Conda environment for Python and its build dependencies:

.. code-block:: bash

./scripts/build-linux-conda-container.sh

The pip container provides complementary coverage for a future PyPI release:

.. code-block:: bash

./scripts/build-linux-container.sh

It builds an ``linux/amd64`` image by default, matching the usual Linux
research-server target. Set ``DOCKER_PLATFORM`` to build another platform.
The image deliberately configures ``-DGALARIO_CHECK_CUDA=OFF`` because Docker
Desktop on a non-NVIDIA host cannot compile or run CUDA. The production CUDA
configuration remains available by enabling ``GALARIO_CHECK_CUDA`` on a host
with a compatible NVIDIA toolkit.

Build requirements
------------------

Expand Down
34 changes: 23 additions & 11 deletions python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,19 @@ endif()
###
# installation
###
# to set ${PYTHON_PKG_DIR}. It is prepended by ${CMAKE_INSTALL_PREFIX} if set
include(PythonInstall)
# PythonInstall from GreatCMakeCookOff imports distutils, which was removed in
# Python 3.12. Use the conventional installation layout relative to
# CMAKE_INSTALL_PREFIX instead.
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"import sys; print('lib/python{}.{}{}'.format(sys.version_info[0], sys.version_info[1], '/site-packages'))"
OUTPUT_VARIABLE PYTHON_PKG_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(PYTHON_PKG_DIR MATCHES "^\\.\\.")
message(FATAL_ERROR "Could not determine a Python package directory relative to ${PYTHON_EXECUTABLE}")
endif()
message(STATUS "Python install path (PYTHON_PKG_DIR): ${PYTHON_PKG_DIR}")

# I guess variable has to be in cache to survive reinvocations of cmake that happen during building
set(GALARIO_PYTHON_PKG_DIR "${PYTHON_PKG_DIR}" CACHE PATH "Current python install path")
Expand Down Expand Up @@ -77,13 +88,14 @@ configure_file("${galario_test}" "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
add_test(NAME CanImport COMMAND ${PYTHON_EXECUTABLE} -c "import ${PROJECT_NAME}")

# https://github.com/UCL/GreatCMakeCookOff/wiki/Adding-py.test-to-ctest
include(AddPyTest)

# I don't understand the first arg but it seems to work if py.test already installed
setup_pytest("${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/py.test.sh")
# Keep a small compatibility wrapper for documented/manual test commands.
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/py.test.sh.in"
"${CMAKE_CURRENT_BINARY_DIR}/py.test.sh" @ONLY)
if(UNIX)
execute_process(COMMAND chmod +x "${CMAKE_CURRENT_BINARY_DIR}/py.test.sh")
endif()

# the source name has to match test_*.py
# https://github.com/UCL/GreatCMakeCookOff/issues/56
#
# need to run in binary_dir so import of pygalario works
add_pytest("${CMAKE_CURRENT_BINARY_DIR}/${galario_test}" NOINSTALL CMDLINE --cov=./)
# Run in the binary directory so the generated galario package is importable.
add_test(NAME galario
COMMAND "${PYTHON_EXECUTABLE}" -m pytest "${CMAKE_CURRENT_BINARY_DIR}/${galario_test}" --cov=./)
set_tests_properties(galario PROPERTIES WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
8 changes: 5 additions & 3 deletions python/libcommon.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
###############################################################################

cimport numpy as np
from cpython cimport PyObject, Py_INCREF
from cpython cimport Py_INCREF

# Numpy must be initialized. When using numpy from C or Cython you must
# _always_ do that, or you will have segfaults
Expand Down Expand Up @@ -82,10 +82,12 @@ cdef class ArrayWrapper:

# Create a 2D array, of length `nx*ny/2+1`
ndarray = np.PyArray_SimpleNewFromData(2, shape, complex_typenum, self.data_ptr)
ndarray.base = <PyObject*> self

# without this, data would be cleaned up right away
# NumPy owns a reference to its base object. Assigning ``.base`` was
# accepted by older Cython releases, but is a read-only property in
# Cython 3. PyArray_SetBaseObject steals the reference passed to it.
Py_INCREF(self)
np.PyArray_SetBaseObject(ndarray, self)
return ndarray

def __dealloc__(self):
Expand Down
2 changes: 2 additions & 0 deletions python/py.test.sh.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
exec "@PYTHON_EXECUTABLE@" -m pytest "$@"
13 changes: 6 additions & 7 deletions python/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import numpy as np

from scipy.interpolate import interp1d, RectBivariateSpline
from scipy.integrate import trapz, quadrature

__all__ = ["py_sampleImage", "py_sampleProfile", "py_chi2Profile", "py_chi2Image",
"radial_profile", "g_sweep_prototype", "sweep_ref",
Expand Down Expand Up @@ -217,7 +216,7 @@ def g_sweep_prototype(I, Rmin, dR, nrow, ncol, dxy, inc, dtype_image='float64'):
inc_cos = np.cos(inc)

# radial extent in number of image pixels covered by the profile
rmax = min(np.int(np.ceil((Rmin+nrad*dR)/dxy)), irow_center)
rmax = min(int(np.ceil((Rmin+nrad*dR)/dxy)), irow_center)
row_offset = irow_center-rmax
col_offset = icol_center-rmax
for irow in range(rmax*2):
Expand All @@ -227,7 +226,7 @@ def g_sweep_prototype(I, Rmin, dR, nrow, ncol, dxy, inc, dtype_image='float64'):
rr = np.sqrt((x/inc_cos)**2. + (y)**2.)

# interpolate 1D
iR = np.int(np.floor((rr-Rmin) / dR))
iR = int(np.floor((rr-Rmin) / dR))
if iR >= nrad-1:
image[irow+row_offset, jcol+col_offset] = 0.
else:
Expand Down Expand Up @@ -394,10 +393,10 @@ def int_bilin_MT(f, x, y):
for i in range(len(x)):
t = y[i] - np.floor(y[i])
u = x[i] - np.floor(x[i])
y0 = f[np.int(np.floor(y[i])), np.int(np.floor(x[i]))]
y1 = f[np.int(np.floor(y[i])) + 1, np.int(np.floor(x[i]))]
y2 = f[np.int(np.floor(y[i])) + 1, np.int(np.floor(x[i])) + 1]
y3 = f[np.int(np.floor(y[i])), np.int(np.floor(x[i])) + 1]
y0 = f[int(np.floor(y[i])), int(np.floor(x[i]))]
y1 = f[int(np.floor(y[i])) + 1, int(np.floor(x[i]))]
y2 = f[int(np.floor(y[i])) + 1, int(np.floor(x[i])) + 1]
y3 = f[int(np.floor(y[i])), int(np.floor(x[i])) + 1]

vis_int[i] = t * u * (y0 - y1 + y2 - y3)
vis_int[i] += t * (y1 - y0)
Expand Down
7 changes: 7 additions & 0 deletions scripts/build-linux-conda-container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env sh
# Build the Linux/amd64 Conda installation test image from the repository root.
set -eu

image_tag=${1:-galario-linux-conda-test:local}
platform=${DOCKER_PLATFORM:-linux/amd64}
exec docker build --quiet --platform "$platform" --file Dockerfile.conda --tag "$image_tag" .
7 changes: 7 additions & 0 deletions scripts/build-linux-container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env sh
# Build the Linux CPU installation test image from the repository root.
set -eu

image_tag=${1:-galario-linux-test:local}
platform=${DOCKER_PLATFORM:-linux/amd64}
exec docker build --quiet --platform "$platform" --tag "$image_tag" .
Loading