01 - SMPy Quickstart¶
Learning Objectives¶
By the end of this tutorial, you will understand SMPy’s three API styles (aside from CLI runner):
High-level API: One-line functions for quick results
Config API: YAML/dictionary-based for reproducible workflows
Class API: Direct mapper access for maximum control
Prerequisites¶
SMPy installed (
pip install .from the repository root)
API Overview¶
SMPy provides three levels of API access to match different use cases:
High-level functional API (Quick) — Simple one-line functions like
map_mass()and method-specific variants (map_kaiser_squires(),map_aperture_mass(),map_ks_plus()) that take your data path and parameters directly. These functions handle everything internally: loading data, creating grids, running the mass mapping, and saving outputs. Perfect for quick analysis and exploration.Configuration-based API (Power Users) — A more structured approach using
Configobjects and therun()function, which separates all analysis parameters from your code. This enables reproducible workflows through YAML files and is ideal for production pipelines where you need to track exactly what parameters produced which results.Class-based API (Prototyping/Testing) — Direct access to the coordinate systems, grid creation, and mapper classes for users who need fine-grained control. This low-level API is available when you need to customize the data flow, integrate with existing pipelines, or extend SMPy’s functionality. Not necessary for most users.
The full configuration based API is recommended for scientific use to ensure control over full analysis pipeline.
Quick Reference¶
# Simple API (uses defaults)
from smpy import map_mass
result = map_mass(data='catalog.fits', method='kaiser_squires',
coord_system='radec', pixel_scale=0.4)
# Config API (for power users, full configuration)
from smpy.config import Config
from smpy.run import run
config = Config.from_defaults('kaiser_squires')
config.update_from_kwargs(data='catalog.fits', pixel_scale=0.4)
result = run(config)
# Class API (prototyping/testing)
from smpy.mapping_methods import KaiserSquiresMapper
mapper = KaiserSquiresMapper(config_dict)
kappa_e, kappa_b = mapper.create_maps(g1_grid, g2_grid)
[1]:
# Cell 2: Environment setup
import sys
import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path
# SMPy imports - we'll use all API levels
import smpy
from smpy import map_mass, map_kaiser_squires, map_aperture_mass, map_ks_plus
from smpy.config import Config
from smpy.run import run
# Set deterministic seed
SEED = 42
np.random.seed(SEED)
print(f"Python: {sys.version.split()[0]}")
print(f"SMPy: {smpy.__version__}")
print(f"Random seed: {SEED}")
def _find_repo_root(start: Path) -> Path:
"""Find the SMPy repository root by walking upward from a starting directory."""
for candidate in [start, *start.parents]:
if (candidate / 'setup.py').exists() and (candidate / 'smpy').is_dir():
return candidate
raise RuntimeError('Could not find SMPy repository root (expected setup.py and smpy/).')
repo_root = _find_repo_root(Path.cwd().resolve())
data_file = repo_root / 'examples' / 'data' / 'forecast_lum_annular.fits'
assert data_file.exists(), f"Data file not found: {data_file}"
data_file
Python: 3.10.19
SMPy: 0.5.0
Random seed: 42
[1]:
PosixPath('/home/docs/checkouts/readthedocs.org/user_builds/smpy-docs/checkouts/latest/examples/data/forecast_lum_annular.fits')
Part 1: Simple API - Get Results Fast¶
SMPy’s simple API is perfect for interactive analysis and notebooks. Each mass mapping method has its own function with sensible defaults.
[2]:
# Cell 3: Simple API demonstration
# Method 1: The unified map_mass() function - specify method as parameter
result_ks = map_mass(
data=str(data_file),
method='kaiser_squires', # Choose your method
coord_system='radec',
pixel_scale=0.4,
smoothing=2.0,
g1_col='g1_Rinv',
g2_col='g2_Rinv',
weight_col='weight',
save_plots=False,
)
print("✓ Kaiser-Squires complete")
print(f" E-mode shape: {result_ks['maps']['E'].shape}")
print(f" Peak convergence: {result_ks['maps']['E'].max():.3f}")
✓ Kaiser-Squires complete
E-mode shape: (38, 57)
Peak convergence: 0.165
[3]:
# Cell 4: Method-specific functions
# Method 2: Direct method functions for common use cases
result_am = map_aperture_mass(
data=str(data_file),
coord_system='radec',
pixel_scale=0.4,
filter_type='schirmer',
filter_scale=60,
g1_col='g1_Rinv',
g2_col='g2_Rinv',
weight_col='weight',
save_plots=False,
)
print("✓ Aperture Mass complete")
print(f" Filter type: Schirmer")
✓ Aperture Mass complete
Filter type: Schirmer
Part 2: Config API - Reproducible Workflows for Power Users (Recommended)¶
For production work, SMPy’s Config API provides:
YAML files for single-file master configurations
Validation to catch errors before running
[4]:
# Cell 5: Config-based workflow
# Create a configuration programmatically
config = Config.from_defaults('kaiser_squires')
# Update with our specific parameters
config.update_from_kwargs(
data=str(data_file),
coord_system='radec',
pixel_scale=0.4,
g1_col='g1_Rinv',
g2_col='g2_Rinv',
weight_col='weight',
smoothing=2.0,
mode=['E', 'B'],
create_snr=False,
save_plots=False,
)
# Show what we've configured (general section)
print("Configuration preview:")
config.show_config(section='general')
Configuration preview:
general:
input_path: /home/docs/checkouts/readthedocs.org/user_builds/smpy-docs/checkouts/latest/examples/data/forecast_lum_annular.fits
input_hdu: 1
output_directory: .
output_base_name: smpy_output
coordinate_system: radec
radec:
resolution: 0.4
coord1: ra
coord2: dec
pixel:
downsample_factor: 1
coord1: X_IMAGE
coord2: Y_IMAGE
pixel_axis_reference: catalog
g1_col: g1_Rinv
g2_col: g2_Rinv
weight_col: weight
method: kaiser_squires
create_snr: false
create_counts_map: false
overlay_counts_map: false
mode:
- E
- B
print_timing: false
save_fits: false
save_plots: false
_coord_system_set_by_user: true
_pixel_scale_set_by_user: true
Configurations can be saved as YAML files to serve as both a record of the parameters used to create the map and a reusable configuration file for future runs.
[5]:
# Cell 6: Run with config
# Execute using the configuration
result_config = run(config)
print("✓ Config-based run complete")
✓ Config-based run complete
Part 3 (Optional/Prototyping): Class API - Maximum Control¶
For advanced users who need fine-grained control, SMPy exposes the mapper classes directly.
[6]:
# Cell 7: Direct mapper class usage
from smpy.mapping_methods import KaiserSquiresMapper, KSPlusMapper
from smpy.utils import load_shear_data
from smpy.coordinates import get_coordinate_system
# Load and prepare data manually
shear_df = load_shear_data(
str(data_file),
coord1_col='ra',
coord2_col='dec',
g1_col='g1_Rinv',
g2_col='g2_Rinv',
weight_col='weight',
hdu=1
)
# Set up coordinate system
coord_system = get_coordinate_system('radec')
shear_df = coord_system.transform_coordinates(shear_df)
scaled_bounds, true_bounds = coord_system.calculate_boundaries(
shear_df['coord1'].values,
shear_df['coord2'].values
)
# Create shear grid
g1_grid, g2_grid = coord_system.create_grid(
shear_df,
scaled_bounds,
{'general': {'radec': {'resolution': 0.4}}}
)
print(f"✓ Manual data preparation complete")
print(f" Grid shape: {g1_grid.shape}")
print(f" Number of galaxies: {len(shear_df)}")
✓ Manual data preparation complete
Grid shape: (38, 57)
Number of galaxies: 8767
[7]:
# Cell 8: Use mapper class directly
# Create mapper with custom config
mapper_config = {
'general': {'method': 'kaiser_squires', 'mode': ['E']},
'methods': {
'kaiser_squires': {
'smoothing': {'type': 'gaussian', 'sigma': 2.0}
}
},
'plotting': {}
}
# Initialize and run mapper
mapper = KaiserSquiresMapper(mapper_config)
kappa_e, kappa_b = mapper.create_maps(g1_grid, -g2_grid)
# Note: -g2 for RA/Dec
# high-level API handles this automatically, but the low-level API expects the user to manage it
print("✓ Direct mapper complete")
print(f" Convergence range: [{kappa_e.min():.3f}, {kappa_e.max():.3f}]")
✓ Direct mapper complete
Convergence range: [-0.104, 0.165]
Comparing the Results¶
Let’s visualize all three methods to confirm they produce equivalent results:
[8]:
# Cell 9: Comparison visualization
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# Simple API results
axes[0, 0].imshow(result_ks['maps']['E'], cmap='magma')
axes[0, 0].set_title('Simple API: map_mass()')
axes[0, 1].imshow(result_am['maps']['E'], cmap='magma')
axes[0, 1].set_title('Simple API: map_aperture_mass()')
# Config API result
axes[1, 0].imshow(result_config['maps']['E'], cmap='magma')
axes[1, 0].set_title('Config API: run(config)')
# Class API result
axes[1, 1].imshow(kappa_e, cmap='magma')
axes[1, 1].set_title('Class API: KaiserSquiresMapper')
plt.tight_layout()
plt.show()
Full Configuration File Example¶
[9]:
config.show_config()
general:
input_path: /home/docs/checkouts/readthedocs.org/user_builds/smpy-docs/checkouts/latest/examples/data/forecast_lum_annular.fits
input_hdu: 1
output_directory: .
output_base_name: smpy_output
coordinate_system: radec
radec:
resolution: 0.4
coord1: ra
coord2: dec
pixel:
downsample_factor: 1
coord1: X_IMAGE
coord2: Y_IMAGE
pixel_axis_reference: catalog
g1_col: g1_Rinv
g2_col: g2_Rinv
weight_col: weight
method: kaiser_squires
create_snr: false
create_counts_map: false
overlay_counts_map: false
mode:
- E
- B
print_timing: false
save_fits: false
save_plots: false
_coord_system_set_by_user: true
_pixel_scale_set_by_user: true
methods:
kaiser_squires:
smoothing:
type: gaussian
sigma: 2.0
aperture_mass:
filter:
type: schirmer
scale: 60
truncation: 1.0
l: 3
ks_plus:
inpainting_iterations: 100
reduced_shear_iterations: 3
nscales: null
extension_size: double
use_wavelet_constraints: true
constrain_B: false
threshold_schedule: exp
threshold_tau: null
smoothing:
type: gaussian
sigma: 2.0
plotting:
figsize:
- 12
- 8
fontsize: 15
cmap: viridis
xlabel: auto
ylabel: auto
plot_title: Mass Map
gridlines: true
vmax: null
vmin: null
threshold: null
verbose: null
cluster_center: null
xray_contours:
ctr_file: null
show_on_convergence: false
show_on_snr: false
color: cyan
linewidth: 0.8
alpha: 0.7
scaling:
type: linear
gamma: 2
percentile: null
convergence:
linthresh: 0.1
linscale: 1.0
snr:
linthresh: 5
linscale: 0.5
snr:
shuffle_type: spatial
num_shuffles: 100
seed: 0
smoothing:
type: gaussian
sigma: 2.0
plot_title: Signal-to-Noise Map
[ ]: