{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 02 - Configuration and Coordinates\n", "\n", "## Purpose\n", "\n", "This tutorial explains two key components within SMPy:\n", "\n", "1. **Configuration** controls reproducibility.\n", "2. **Coordinate system choice** controls physical interpretation.\n", "\n", "The notebook is intentionally short and conceptual." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1) Configuration Anatomy\n", "\n", "Think of the config as a scientific contract:\n", "\n", "- `general`: data source, coordinate system, output behavior\n", "- `methods`: algorithm-specific parameters\n", "- `plotting`: map rendering choices\n", "- `snr`: null-realization controls\n", "\n", "Every single tunable parameter in the model is defined in the configuration yaml file." ] }, { "cell_type": "code", "execution_count": 22, "id": "83de2952", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Python package version: 0.5.0\n", "Random seed: 42\n", "Data file: /Users/vassig/research/SMPy/examples/data/forecast_lum_annular.fits\n", "Artifacts: /Users/vassig/research/SMPy/examples/outputs/tutorials\n" ] } ], "source": [ "from pathlib import Path\n", "import random\n", "import numpy as np\n", "import pandas as pd\n", "import yaml\n", "\n", "import smpy\n", "from IPython.display import Image, display\n", "from smpy.config import Config\n", "from smpy.run import run\n", "\n", "SEED = 42\n", "random.seed(SEED)\n", "np.random.seed(SEED)\n", "\n", "\n", "def find_repo_root(start: Path) -> Path:\n", " for candidate in [start, *start.parents]:\n", " if (candidate / \"setup.py\").exists() and (candidate / \"smpy\").is_dir():\n", " return candidate\n", " raise RuntimeError(\"Could not locate SMPy repository root.\")\n", "\n", "\n", "repo_root = find_repo_root(Path.cwd().resolve())\n", "data_file = repo_root / \"examples\" / \"data\" / \"forecast_lum_annular.fits\"\n", "artifacts_dir = repo_root / \"examples\" / \"outputs\" / \"tutorials\"\n", "artifacts_dir.mkdir(parents=True, exist_ok=True)\n", "\n", "print(f\"Python package version: {smpy.__version__}\")\n", "print(f\"Random seed: {SEED}\")\n", "print(f\"Data file: {data_file}\")\n", "print(f\"Artifacts: {artifacts_dir}\")" ] }, { "cell_type": "code", "execution_count": 23, "id": "ade0c64a", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validated config sections: ['general', 'methods', 'plotting', 'snr']\n", "Coordinate system: radec\n", "Method: kaiser_squires\n", "\n", "Configuration:\n", "\n", "General:\n", " input_path: /Users/vassig/research/SMPy/examples/data/forecast_lum_annular.fits\n", " input_hdu: 1\n", " output_directory: /Users/vassig/research/SMPy/examples/outputs/tutorials\n", " output_base_name: tutorial02_minimal\n", " coordinate_system: radec\n", " radec: {'resolution': 0.4, 'coord1': 'ra', 'coord2': 'dec'}\n", " pixel: {'downsample_factor': 1, 'coord1': 'X_IMAGE', 'coord2': 'Y_IMAGE', 'pixel_axis_reference': 'catalog'}\n", " g1_col: g1_Rinv\n", " g2_col: g2_Rinv\n", " weight_col: weight\n", " method: kaiser_squires\n", " create_snr: False\n", " create_counts_map: False\n", " overlay_counts_map: False\n", " mode: ['E']\n", " print_timing: False\n", " save_fits: False\n", " save_plots: False\n", " _coord_system_set_by_user: True\n", " _pixel_scale_set_by_user: True\n", "\n", "Methods:\n", " kaiser_squires: {'smoothing': {'type': 'gaussian', 'sigma': 2.0}}\n", " aperture_mass: {'filter': {'type': 'schirmer', 'scale': 60, 'truncation': 1.0, 'l': 3}}\n", " ks_plus: {'inpainting_iterations': 100, 'reduced_shear_iterations': 3, 'nscales': None, 'extension_size': 'double', 'use_wavelet_constraints': True, 'constrain_B': False, 'threshold_schedule': 'exp', 'threshold_tau': None, 'smoothing': {'type': 'gaussian', 'sigma': 2.0}}\n", "\n", "Plotting:\n", " figsize: [12, 8]\n", " fontsize: 15\n", " cmap: viridis\n", " xlabel: auto\n", " ylabel: auto\n", " plot_title: Mass Map\n", " gridlines: True\n", " vmax: None\n", " vmin: None\n", " threshold: None\n", " verbose: None\n", " cluster_center: None\n", " scaling: {'type': 'linear', 'gamma': 2, 'percentile': None, 'convergence': {'linthresh': 0.1, 'linscale': 1.0}, 'snr': {'linthresh': 5, 'linscale': 0.5}}\n", "\n", "Snr:\n", " shuffle_type: spatial\n", " num_shuffles: 100\n", " seed: 0\n", " smoothing: {'type': 'gaussian', 'sigma': 2.0}\n", " plot_title: Signal-to-Noise Map\n" ] } ], "source": [ "config = Config.from_defaults(\"kaiser_squires\")\n", "config.update_from_kwargs(\n", " data=str(data_file),\n", " coord_system=\"radec\",\n", " pixel_scale=0.4,\n", " g1_col=\"g1_Rinv\",\n", " g2_col=\"g2_Rinv\",\n", " weight_col=\"weight\",\n", " mode=[\"E\"],\n", " save_plots=False,\n", " save_fits=False,\n", " output_dir=str(artifacts_dir),\n", " output_base_name=\"tutorial02_minimal\",\n", ")\n", "config.validate()\n", "\n", "cfg = config.to_dict()\n", "print(\"Validated config sections:\", list(cfg.keys()))\n", "print(\"Coordinate system:\", cfg[\"general\"][\"coordinate_system\"])\n", "print(\"Method:\", cfg[\"general\"][\"method\"])\n", "\n", "# Print the config in a readable format\n", "print(\"\\nConfiguration:\")\n", "for section, options in cfg.items():\n", " print(f\"\\n{section.capitalize()}:\")\n", " for key, value in options.items():\n", " print(f\" {key}: {value}\")\n" ] }, { "cell_type": "markdown", "id": "4c69ef9f", "metadata": {}, "source": [ "## 2) Reproducibility Essentials\n", "\n", "Good baseline practice:\n", "\n", "- Save the exact config used for a run.\n", "- Keep output names stable and explicit.\n", "- Log package versions and seed." ] }, { "cell_type": "code", "execution_count": 24, "id": "a31590f0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Configuration saved to: /Users/vassig/research/SMPy/examples/outputs/tutorials/config_minimal.yaml\n", "Saved and reloaded: /Users/vassig/research/SMPy/examples/outputs/tutorials/config_minimal.yaml\n", "Reloaded coordinate system: radec\n" ] } ], "source": [ "minimal_config_path = artifacts_dir / \"config_minimal.yaml\"\n", "config.save_config(minimal_config_path)\n", "\n", "reloaded = Config.from_file(minimal_config_path)\n", "reloaded.validate()\n", "\n", "print(f\"Saved and reloaded: {minimal_config_path}\")\n", "print(\"Reloaded coordinate system:\", reloaded.to_dict()[\"general\"][\"coordinate_system\"])" ] }, { "cell_type": "markdown", "id": "e78a94c4", "metadata": {}, "source": [ "## 3) Coordinate Systems: How to Choose\n", "\n", "Use this quick rule:\n", "\n", "| Data columns look like | Use | Main scale knob |\n", "|---|---|---|\n", "| `ra`, `dec` in sky coordinates | `radec` | `pixel_scale` (arcmin/pixel) |\n", "| detector/image coordinates (`X_*`, `Y_*`) | `pixel` | `downsample_factor` |\n", "\n", "Important distinction:\n", "\n", "- `pixel_scale` sets angular map resolution in sky coordinates.\n", "- `downsample_factor` coarsens a native pixel-coordinate grid." ] }, { "cell_type": "code", "execution_count": 25, "id": "72a23844", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Configuration saved to: /Users/vassig/research/SMPy/examples/outputs/tutorials/config_radec.yaml\n", "Convergence map saved as PNG file: /Users/vassig/research/SMPy/examples/outputs/tutorials/kaiser_squires/tutorial02_radec_kaiser_squires_e_mode.png\n", "Configuration saved to: /Users/vassig/research/SMPy/examples/outputs/tutorials/config_pixel.yaml\n", "Convergence map saved as PNG file: /Users/vassig/research/SMPy/examples/outputs/tutorials/kaiser_squires/tutorial02_pixel_kaiser_squires_e_mode.png\n", "RA/Dec map shape: (38, 57)\n", "Pixel map shape: (38, 57)\n" ] } ], "source": [ "def make_base_config(output_base_name: str) -> Config:\n", " c = Config.from_defaults(\"kaiser_squires\")\n", " c.update_from_kwargs(\n", " data=str(data_file),\n", " g1_col=\"g1_Rinv\",\n", " g2_col=\"g2_Rinv\",\n", " weight_col=\"weight\",\n", " mode=[\"E\"],\n", " save_plots=True,\n", " save_fits=False,\n", " output_dir=str(artifacts_dir),\n", " output_base_name=output_base_name,\n", " )\n", " return c\n", "\n", "\n", "# RA/Dec configuration\n", "radec_config = make_base_config(\"tutorial02_radec\")\n", "radec_config.update_from_kwargs(coord_system=\"radec\", pixel_scale=0.4)\n", "radec_config_path = artifacts_dir / \"config_radec.yaml\"\n", "radec_config.save_config(radec_config_path)\n", "radec_result = run(radec_config)\n", "\n", "# Pixel configuration (explicitly map coordinate column names)\n", "pixel_config = make_base_config(\"tutorial02_pixel\")\n", "pixel_config.update_from_kwargs(coord_system=\"pixel\", downsample_factor=170)\n", "pixel_dict = pixel_config.to_dict()\n", "pixel_dict[\"general\"][\"pixel\"][\"coord1\"] = \"X_IMAGE_se\"\n", "pixel_dict[\"general\"][\"pixel\"][\"coord2\"] = \"Y_IMAGE_se\"\n", "pixel_config = Config(pixel_dict)\n", "pixel_config.validate()\n", "pixel_config_path = artifacts_dir / \"config_pixel.yaml\"\n", "pixel_config.save_config(pixel_config_path)\n", "pixel_result = run(pixel_config)\n", "\n", "print(\"RA/Dec map shape:\", radec_result[\"maps\"][\"E\"].shape)\n", "print(\"Pixel map shape:\", pixel_result[\"maps\"][\"E\"].shape)" ] }, { "cell_type": "code", "execution_count": 26, "id": "45f87f78", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| \n", " | coordinate_system | \n", "map_shape | \n", "pixel_scale_arcmin | \n", "mean_E | \n", "std_E | \n", "min_E | \n", "max_E | \n", "downsample_factor | \n", "
|---|---|---|---|---|---|---|---|---|
| 0 | \n", "radec | \n", "38x57 | \n", "0.4 | \n", "2.460328e-18 | \n", "0.042876 | \n", "-0.104254 | \n", "0.165443 | \n", "NaN | \n", "
| 1 | \n", "pixel | \n", "38x57 | \n", "NaN | \n", "-4.100547e-19 | \n", "0.043971 | \n", "-0.108428 | \n", "0.165440 | \n", "170.0 | \n", "