Skip to content

Commit dbf45a3

Browse files
authored
Merge pull request #27 from CMacM/gsinterface_bug
Fixed installation issues causing Galsim libraies to not be correctly linked.
2 parents 2730d62 + 586ad07 commit dbf45a3

6 files changed

Lines changed: 296 additions & 35 deletions

File tree

README.md

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,25 +10,93 @@ Non-affine transforms can simualate complex shear effects such as intrinsic
1010
alignment, flexion, and optical field distrotion maps. Custom transform
1111
functions can also be passed to Stamp objects.
1212

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

15-
The package is not currently available on PyPI while in early development.
15+
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.
1616

17-
First clone the repository:
18-
```shell
17+
The below instructions will work with pure conda, but we recommend using mamba to make the installation of dependencies much quicker.
18+
19+
First, make sure to clone and switch to the repository root:
20+
21+
```bash
1922
git clone https://github.com/CMacM/BATSim.git
23+
cd BATSim
2024
```
2125

22-
Then install the package
23-
```shell
24-
cd BATSim
25-
conda install --file requirements.txt -c conda-forge
26-
pip install . --user
26+
## Regular Install (not editable)
27+
28+
### 1. Create build environment and activate
29+
```bash
30+
mamba create -n batsim -c conda-forge -c defaults python=3.10 conda-build boa
31+
mamba activate batsim
32+
```
33+
34+
### 2. Build the package
35+
From the repository root:
36+
```bash
37+
# mambabuild is sometimes only recognised as a conda command
38+
conda mambabuild --override-channels -c conda-forge -c defaults conda/recipe
39+
```
40+
41+
This will:
42+
- Create an isolated environment to build and run batsim
43+
- Install all required dependencies
44+
- Compile the BATSim C++ extension and link it to Galsim's C++ API
45+
- Produce a conda package in:
46+
```bash
47+
$CONDA_PREFIX/conda-bld/linux-64/
48+
```
49+
50+
### 3. Install the built package
51+
Install the locally built package into the new environment:
52+
```bash
53+
mamba install -c local batsim
54+
```
55+
56+
If this fails, install directly from the build artifact:
57+
```bash
58+
mamba install $CONDA_PREFIX/conda-bld/linux-64/batsim-*.tar.bz2
2759
```
2860

29-
Potential installation pitfalls:
30-
- 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.
61+
## Development Installation (Editable)
62+
63+
For development you can install BATSim in editable mode so that Python changes
64+
take effect immediately without reinstalling.
65+
66+
### 1. Create a development environment and activate
67+
68+
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.
69+
70+
```bash
71+
mamba create -n batsim-dev -c conda-forge -c defaults \
72+
python=3.10 \
73+
galsim \
74+
eigen \
75+
pybind11 \
76+
numpy \
77+
fitsio \
78+
astropy \
79+
matplotlib
80+
mamba activate batsim-dev
81+
```
82+
83+
### 2. Install BATSim in editable mode
84+
85+
```bash
86+
pip install -e .
87+
```
88+
89+
After following either of the above installation routes, with the conda environment active, verify the installation:
90+
91+
```bash
92+
python -c "import batsim; import batsim._gsinterface; print('BATSim installed successfully')"
93+
```
94+
95+
## Pip only installation
96+
97+
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.
3198

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

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

conda/recipe/meta.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{% set name = "batsim" %}
2+
{% set version = "0.0.0" %}
3+
4+
package:
5+
name: {{ name|lower }}
6+
version: {{ version }}
7+
8+
source:
9+
path: ../..
10+
11+
build:
12+
number: 0
13+
script: "{{ PYTHON }} -m pip install . -vv --no-deps --no-build-isolation"
14+
# skip: true # [not linux] # uncomment if you want linux-only builds
15+
16+
requirements:
17+
# Do not request conda-forge compiler toolchain packages on this cluster
18+
# (they are unavailable, and the solve fails). Use system gcc/g++ instead.
19+
build: []
20+
host:
21+
- python
22+
- pip
23+
- setuptools
24+
- wheel
25+
- numpy
26+
- pybind11
27+
- galsim
28+
- eigen
29+
- fitsio
30+
run:
31+
- python
32+
- numpy
33+
- pybind11
34+
- galsim
35+
- eigen
36+
- fitsio
37+
- matplotlib
38+
- astropy
39+
40+
test:
41+
commands:
42+
- test -d "$PREFIX/include/eigen3/Eigen"
43+
imports:
44+
- batsim
45+
- batsim._gsinterface
46+
47+
about:
48+
license: MIT
49+
summary: "BATSim package (links against GalSim C++ library)"
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 1,
6+
"id": "5da4e60a",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"import batsim\n",
11+
"import galsim\n",
12+
"import numpy as np"
13+
]
14+
},
15+
{
16+
"cell_type": "code",
17+
"execution_count": 4,
18+
"id": "49b826d8",
19+
"metadata": {},
20+
"outputs": [
21+
{
22+
"name": "stdout",
23+
"output_type": "stream",
24+
"text": [
25+
"<galsim._galsim.SBSersic object at 0x7fab3ae8b570>\n"
26+
]
27+
},
28+
{
29+
"data": {
30+
"text/plain": [
31+
"array([[0.0285582]])"
32+
]
33+
},
34+
"execution_count": 4,
35+
"metadata": {},
36+
"output_type": "execute_result"
37+
}
38+
],
39+
"source": [
40+
"sb_obj = galsim.Sersic(n=4, half_light_radius=0.5)\n",
41+
"\n",
42+
"print(sb_obj._sbp)\n",
43+
"\n",
44+
"batsim._gsinterface.getFluxVec(scale=0.2, gsobj=sb_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]]))"
45+
]
46+
},
47+
{
48+
"cell_type": "code",
49+
"execution_count": 5,
50+
"id": "4abe2a44",
51+
"metadata": {},
52+
"outputs": [
53+
{
54+
"name": "stdout",
55+
"output_type": "stream",
56+
"text": [
57+
"<galsim._galsim.SBTransform object at 0x7facfc0a0130>\n"
58+
]
59+
},
60+
{
61+
"data": {
62+
"text/plain": [
63+
"array([[0.0285582]])"
64+
]
65+
},
66+
"execution_count": 5,
67+
"metadata": {},
68+
"output_type": "execute_result"
69+
}
70+
],
71+
"source": [
72+
"trans_obj = galsim.Sersic(n=4, half_light_radius=0.5).shear(g1=0.1, g2=0.2).shift(0.5, 0.5)\n",
73+
"\n",
74+
"print(trans_obj._sbp)\n",
75+
"\n",
76+
"batsim._gsinterface.getFluxVec(scale=0.2, gsobj=sb_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]]))"
77+
]
78+
}
79+
],
80+
"metadata": {
81+
"kernelspec": {
82+
"display_name": "batsim-dev",
83+
"language": "python",
84+
"name": "python3"
85+
},
86+
"language_info": {
87+
"codemirror_mode": {
88+
"name": "ipython",
89+
"version": 3
90+
},
91+
"file_extension": ".py",
92+
"mimetype": "text/x-python",
93+
"name": "python",
94+
"nbconvert_exporter": "python",
95+
"pygments_lexer": "ipython3",
96+
"version": "3.10.19"
97+
}
98+
},
99+
"nbformat": 4,
100+
"nbformat_minor": 5
101+
}

pyproject.toml

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
[build-system]
22
requires = [
3-
"setuptools>=38", # Used to build and package the Python project
4-
"pybind11>=2.2", # Builds python - cpp interface for direct calls to galsim cpp layer
5-
"wheel"
3+
"setuptools>=61",
4+
"wheel",
5+
"pybind11>=2.2",
6+
"numpy"
67
]
78
build-backend = "setuptools.build_meta"
89

910
[tool.pytest.ini_options]
10-
testpaths = [
11-
"tests",
12-
]
11+
testpaths = ["tests"]
1312
addopts = "-vv -s"
1413

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

1918
[tool.isort]
2019
profile = "black"
21-
line_length = 110
20+
line_length = 110

setup.py

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,45 @@ def run(self):
2020
super().run()
2121

2222
def find_galsim_paths(self):
23-
# Implement logic to locate GalSim's include and library directories
24-
# This is a placeholder implementation; adjust based on your setup
25-
26-
# Example: Assuming GalSim is installed in a conda environment
27-
conda_prefix = os.environ.get("CONDA_PREFIX")
2823
include_dirs = []
2924
lib_dirs = []
30-
if conda_prefix:
31-
include_dirs.append(os.path.join(conda_prefix, "include"))
32-
include_dirs.append(os.path.join(conda_prefix, "include/galsim"))
33-
include_dirs.append(os.path.join(conda_prefix, "include/eigen3/"))
34-
lib_dirs.append(os.path.join(conda_prefix, "lib"))
35-
else:
36-
# Fallback or other logic to locate GalSim
37-
include_dirs.append("/usr/local/include")
38-
include_dirs.append("/usr/local/include/galsim")
39-
include_dirs.append("/usr/local/include/eigen3")
40-
lib_dirs.append("/usr/local/lib")
4125

42-
return include_dirs, lib_dirs
26+
# Prefer GalSim's own include directory if import works
27+
try:
28+
import galsim
29+
inc = galsim.include_dir # .../site-packages/galsim/include
30+
include_dirs.append(inc)
31+
include_dirs.append(os.path.join(inc, "galsim")) # <-- add this
32+
except Exception:
33+
print("Error: Could not import GalSim to find include directory.")
34+
35+
# Conda-build uses PREFIX for the host env (headers live here)
36+
prefixes = [
37+
os.environ.get("PREFIX"), # conda-build host env
38+
os.environ.get("CONDA_PREFIX"), # active env fallback
39+
]
40+
41+
for p in prefixes:
42+
if not p:
43+
continue
44+
include_dirs.append(os.path.join(p, "include"))
45+
include_dirs.append(os.path.join(p, "include", "galsim"))
46+
include_dirs.append(os.path.join(p, "include", "eigen3"))
47+
lib_dirs.append(os.path.join(p, "lib"))
48+
49+
# 3) Last-resort system paths
50+
include_dirs += ["/usr/local/include", "/usr/include", "/usr/include/eigen3"]
51+
lib_dirs += ["/usr/local/lib", "/usr/lib"]
52+
53+
# De-duplicate preserving order
54+
def uniq(xs):
55+
out = []
56+
for x in xs:
57+
if x and x not in out:
58+
out.append(x)
59+
return out
60+
61+
return uniq(include_dirs), uniq(lib_dirs)
4362

4463

4564
# Define your extension module

tests/test_c_layer.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import batsim
2+
import galsim
3+
import numpy as np
4+
5+
def test_get_flux_vec():
6+
sb_obj = galsim.Sersic(n=4, half_light_radius=0.5)
7+
trans_obj = sb_obj.shear(g1=0.1, g2=0.2).shift(0.5, 0.5)
8+
9+
try:
10+
sb_flux = batsim._gsinterface.getFluxVec(
11+
scale=0.2, gsobj=sb_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]])
12+
)
13+
except Exception as e:
14+
print("Error in getFluxVec for Sersic profile:", e)
15+
16+
try:
17+
trans_flux = batsim._gsinterface.getFluxVec(
18+
scale=0.2, gsobj=trans_obj._sbp, xy_coords=np.array([[0.1, 0.1], [0.2, 0.2]])
19+
)
20+
except Exception as e:
21+
print("Error in getFluxVec for Transform profile:", e)
22+
23+
if __name__ == "__main__":
24+
test_get_flux_vec()
25+

0 commit comments

Comments
 (0)