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
90 changes: 79 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,93 @@ Non-affine transforms can simualate complex shear effects such as intrinsic
alignment, flexion, and optical field distrotion maps. Custom transform
functions can also be passed to Stamp objects.

## Installation
## Building and installing BATSim from source (Conda + GalSim C++)

The package is not currently available on PyPI while in early development.
The package is not currently available on PyPI and only installable on Linux while in early development. We suggest using conda/mamba to build the package and install its dependencies. This is because the primary dependency, Galsim, does not ship a pre-built C++ library via pip.

First clone the repository:
```shell
The below instructions will work with pure conda, but we recommend using mamba to make the installation of dependencies much quicker.

First, make sure to clone and switch to the repository root:

```bash
git clone https://github.com/CMacM/BATSim.git
cd BATSim
```

Then install the package
```shell
cd BATSim
conda install --file requirements.txt -c conda-forge
pip install . --user
## Regular Install (not editable)

### 1. Create build environment and activate
```bash
mamba create -n batsim -c conda-forge -c defaults python=3.10 conda-build boa
mamba activate batsim
```

### 2. Build the package
From the repository root:
```bash
# mambabuild is sometimes only recognised as a conda command
conda mambabuild --override-channels -c conda-forge -c defaults conda/recipe
```

This will:
- Create an isolated environment to build and run batsim
- Install all required dependencies
- Compile the BATSim C++ extension and link it to Galsim's C++ API
- Produce a conda package in:
```bash
$CONDA_PREFIX/conda-bld/linux-64/
```

### 3. Install the built package
Install the locally built package into the new environment:
```bash
mamba install -c local batsim
```

If this fails, install directly from the build artifact:
```bash
mamba install $CONDA_PREFIX/conda-bld/linux-64/batsim-*.tar.bz2
```

Potential installation pitfalls:
- The above will install the python package of the dependence GalSim. However, you may need to install the C++ shared library of GalSim, not installed by default with the above installation. Find details on how to do this [here][https://galsim-developers.github.io/GalSim/_build/html/install_pip.html]. You may then need to update your LD\_LIBRARY\_PATH, LIBRARY\_PATH, and CPLUS\_INCLUDE\_PATH to point to build and include folders for the GalSim C++ shared library.
## Development Installation (Editable)

For development you can install BATSim in editable mode so that Python changes
take effect immediately without reinstalling.

### 1. Create a development environment and activate

Note: You may encounter issues if some of these packages are already installed locally and have different builds. To fix, remove them and ensure they are installed through conda-forge.

```bash
mamba create -n batsim-dev -c conda-forge -c defaults \
python=3.10 \
galsim \
eigen \
pybind11 \
numpy \
fitsio \
astropy \
matplotlib
mamba activate batsim-dev
```

### 2. Install BATSim in editable mode

```bash
pip install -e .
```

After following either of the above installation routes, with the conda environment active, verify the installation:

```bash
python -c "import batsim; import batsim._gsinterface; print('BATSim installed successfully')"
```

## Pip only installation

We currently do not provide a pip only installation route as Galsim requires the shared C++ library to be built and linked manually when installed via pip. If you wish to build via pip only, you will need to install all dependencies and build and link the Galsim C++ headers. You can find details on how to do this [here][https://galsim-developers.github.io/GalSim/_build/html/install_pip.html]. You may then need to update your LD\_LIBRARY\_PATH, LIBRARY\_PATH, and CPLUS\_INCLUDE\_PATH to point to build and include folders for the GalSim C++ shared library.

We plan to add a pure pip installation route in future.

![BATSim Logo](./image/batsim_logo.png)

Expand Down
49 changes: 49 additions & 0 deletions conda/recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{% set name = "batsim" %}
{% set version = "0.0.0" %}

package:
name: {{ name|lower }}
version: {{ version }}

source:
path: ../..

build:
number: 0
script: "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation"
# skip: true # [not linux] # uncomment if you want linux-only builds

requirements:
# Do not request conda-forge compiler toolchain packages on this cluster
# (they are unavailable, and the solve fails). Use system gcc/g++ instead.
build: []
host:
- python
- pip
- setuptools
- wheel
- numpy
- pybind11
- galsim
- eigen
- fitsio
run:
- python
- numpy
- pybind11
- galsim
- eigen
- fitsio
- matplotlib
- astropy

test:
commands:
- test -d "$PREFIX/include/eigen3/Eigen"
imports:
- batsim
- batsim._gsinterface

about:
license: MIT
summary: "BATSim package (links against GalSim C++ library)"
101 changes: 101 additions & 0 deletions notebooks/dev/debug_getflucvec.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "5da4e60a",
"metadata": {},
"outputs": [],
"source": [
"import batsim\n",
"import galsim\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "49b826d8",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<galsim._galsim.SBSersic object at 0x7fab3ae8b570>\n"
]
},
{
"data": {
"text/plain": [
"array([[0.0285582]])"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sb_obj = galsim.Sersic(n=4, half_light_radius=0.5)\n",
"\n",
"print(sb_obj._sbp)\n",
"\n",
"batsim._gsinterface.getFluxVec(scale=0.2, gsobj=sb_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]]))"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "4abe2a44",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<galsim._galsim.SBTransform object at 0x7facfc0a0130>\n"
]
},
{
"data": {
"text/plain": [
"array([[0.0285582]])"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"trans_obj = galsim.Sersic(n=4, half_light_radius=0.5).shear(g1=0.1, g2=0.2).shift(0.5, 0.5)\n",
"\n",
"print(trans_obj._sbp)\n",
"\n",
"batsim._gsinterface.getFluxVec(scale=0.2, gsobj=sb_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]]))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "batsim-dev",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.19"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
13 changes: 6 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
[build-system]
requires = [
"setuptools>=38", # Used to build and package the Python project
"pybind11>=2.2", # Builds python - cpp interface for direct calls to galsim cpp layer
"wheel"
"setuptools>=61",
"wheel",
"pybind11>=2.2",
"numpy"
]
build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
testpaths = [
"tests",
]
testpaths = ["tests"]
addopts = "-vv -s"

[tool.black]
Expand All @@ -18,4 +17,4 @@ target-version = ["py38"]

[tool.isort]
profile = "black"
line_length = 110
line_length = 110
53 changes: 36 additions & 17 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,45 @@ def run(self):
super().run()

def find_galsim_paths(self):
# Implement logic to locate GalSim's include and library directories
# This is a placeholder implementation; adjust based on your setup

# Example: Assuming GalSim is installed in a conda environment
conda_prefix = os.environ.get("CONDA_PREFIX")
include_dirs = []
lib_dirs = []
if conda_prefix:
include_dirs.append(os.path.join(conda_prefix, "include"))
include_dirs.append(os.path.join(conda_prefix, "include/galsim"))
include_dirs.append(os.path.join(conda_prefix, "include/eigen3/"))
lib_dirs.append(os.path.join(conda_prefix, "lib"))
else:
# Fallback or other logic to locate GalSim
include_dirs.append("/usr/local/include")
include_dirs.append("/usr/local/include/galsim")
include_dirs.append("/usr/local/include/eigen3")
lib_dirs.append("/usr/local/lib")

return include_dirs, lib_dirs
# Prefer GalSim's own include directory if import works
try:
import galsim
inc = galsim.include_dir # .../site-packages/galsim/include
include_dirs.append(inc)
include_dirs.append(os.path.join(inc, "galsim")) # <-- add this
except Exception:
print("Error: Could not import GalSim to find include directory.")

# Conda-build uses PREFIX for the host env (headers live here)
prefixes = [
os.environ.get("PREFIX"), # conda-build host env
os.environ.get("CONDA_PREFIX"), # active env fallback
]

for p in prefixes:
if not p:
continue
include_dirs.append(os.path.join(p, "include"))
include_dirs.append(os.path.join(p, "include", "galsim"))
include_dirs.append(os.path.join(p, "include", "eigen3"))
lib_dirs.append(os.path.join(p, "lib"))

# 3) Last-resort system paths
include_dirs += ["/usr/local/include", "/usr/include", "/usr/include/eigen3"]
lib_dirs += ["/usr/local/lib", "/usr/lib"]

# De-duplicate preserving order
def uniq(xs):
out = []
for x in xs:
if x and x not in out:
out.append(x)
return out

return uniq(include_dirs), uniq(lib_dirs)


# Define your extension module
Expand Down
25 changes: 25 additions & 0 deletions tests/test_c_layer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import batsim
import galsim
import numpy as np

def test_get_flux_vec():
sb_obj = galsim.Sersic(n=4, half_light_radius=0.5)
trans_obj = sb_obj.shear(g1=0.1, g2=0.2).shift(0.5, 0.5)

try:
sb_flux = batsim._gsinterface.getFluxVec(
scale=0.2, gsobj=sb_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]])
)
except Exception as e:
print("Error in getFluxVec for Sersic profile:", e)

try:
trans_flux = batsim._gsinterface.getFluxVec(
scale=0.2, gsobj=trans_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]])
)
except Exception as e:
print("Error in getFluxVec for Transform profile:", e)

if __name__ == "__main__":
test_get_flux_vec()

Loading