Photometric error stage demo
author: Tianqing Zhang, John-Franklin Crenshaw
This notebook demonstrate the use of
rail.creation.degraders.photometric_errors, which adds column for
the photometric noise to the catalog based on the package PhotErr
developed by John-Franklin Crenshaw. The RAIL stage PhotoErrorModel
inherit from the Noisifier base classes, and the LSST, Roman, Euclid
child classes inherit from the PhotoErrorModel.
If you’re interested in running this in pipeline mode, see
02_Photometric_Realization_with_Other_Surveys.ipynb
in the pipeline_examples/core_examples/ folder.
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from rail.core.data import PqHandle
from rail.interactive.creation.degraders import photometric_errors
Install FSPS with the following commands:
pip uninstall fsps
git clone --recursive https://github.com/dfm/python-fsps.git
cd python-fsps
python -m pip install .
export SPS_HOME=$(pwd)/src/fsps/libfsps
LEPHAREDIR is being set to the default cache directory:
/home/runner/.cache/lephare/data
More than 1Gb may be written there.
LEPHAREWORK is being set to the default cache directory:
/home/runner/.cache/lephare/work
Default work cache is already linked.
This is linked to the run directory:
/home/runner/.cache/lephare/runs/20260601T134643
A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.2.6 as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.
If you are a user of the module, the easiest solution will be to
downgrade to 'numpy<2' or try to upgrade the affected module.
We expect that some modules will need time to support NumPy 2.
Traceback (most recent call last): File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/runpy.py", line 196, in _run_module_as_main
return _run_code(code, main_globals, None,
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/runpy.py", line 86, in _run_code
exec(code, run_globals)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel_launcher.py", line 18, in <module>
app.launch_new_instance()
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/traitlets/config/application.py", line 1082, in launch_instance
app.start()
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/kernelapp.py", line 758, in start
self.io_loop.start()
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/tornado/platform/asyncio.py", line 211, in start
self.asyncio_loop.run_forever()
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/asyncio/base_events.py", line 603, in run_forever
self._run_once()
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/asyncio/base_events.py", line 1909, in _run_once
handle._run()
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/asyncio/events.py", line 80, in _run
self._context.run(self._callback, *self._args)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/utils.py", line 71, in preserve_context
return await f(*args, **kwargs)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/kernelbase.py", line 621, in shell_main
await self.dispatch_shell(msg, subshell_id=subshell_id)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/kernelbase.py", line 478, in dispatch_shell
await result
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/ipkernel.py", line 372, in execute_request
await super().execute_request(stream, ident, parent)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/kernelbase.py", line 834, in execute_request
reply_content = await reply_content
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/ipkernel.py", line 464, in do_execute
res = shell.run_cell(
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/ipykernel/zmqshell.py", line 663, in run_cell
return super().run_cell(*args, **kwargs)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3077, in run_cell
result = self._run_cell(
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3132, in _run_cell
result = runner(coro)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/IPython/core/async_helpers.py", line 128, in _pseudo_sync_runner
coro.send(None)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3336, in run_cell_async
has_raised = await self.run_ast_nodes(code_ast.body, cell_name,
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3519, in run_ast_nodes
if await self.run_code(code, result, async_=asy):
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/IPython/core/interactiveshell.py", line 3579, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "/tmp/ipykernel_4040/2313627096.py", line 5, in <module>
from rail.interactive.creation.degraders import photometric_errors
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/interactive/__init__.py", line 3, in <module>
from . import calib, creation, estimation, evaluation, tools
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/interactive/calib/__init__.py", line 3, in <module>
from rail.utils.interactive.initialize_utils import _initialize_interactive_module
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/utils/interactive/initialize_utils.py", line 17, in <module>
from rail.utils.interactive.base_utils import (
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/utils/interactive/base_utils.py", line 10, in <module>
rail.stages.import_and_attach_all(silent=True)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/stages/__init__.py", line 74, in import_and_attach_all
RailEnv.import_all_packages(silent=silent)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/core/introspection.py", line 541, in import_all_packages
_imported_module = importlib.import_module(pkg)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/importlib/__init__.py", line 126, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/som/__init__.py", line 1, in <module>
from rail.creation.degraders.specz_som import *
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/rail/creation/degraders/specz_som.py", line 15, in <module>
from somoclu import Somoclu
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/somoclu/__init__.py", line 11, in <module>
from .train import Somoclu
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/somoclu/train.py", line 25, in <module>
from .somoclu_wrap import train as wrap_train
File "/opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/somoclu/somoclu_wrap.py", line 11, in <module>
import _somoclu_wrap
---------------------------------------------------------------------------
ImportError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.10.20/x64/lib/python3.10/site-packages/numpy/core/_multiarray_umath.py:44, in __getattr__(attr_name)
39 # Also print the message (with traceback). This is because old versions
40 # of NumPy unfortunately set up the import to replace (and hide) the
41 # error. The traceback shouldn't be needed, but e.g. pytest plugins
42 # seem to swallow it and we should be failing anyway...
43 sys.stderr.write(msg + tb_msg)
---> 44 raise ImportError(msg)
46 ret = getattr(_multiarray_umath, attr_name, None)
47 if ret is None:
ImportError:
A module that was compiled using NumPy 1.x cannot be run in
NumPy 2.2.6 as it may crash. To support both 1.x and 2.x
versions of NumPy, modules must be compiled with NumPy 2.0.
Some module may need to rebuild instead e.g. with 'pybind11>=2.12'.
If you are a user of the module, the easiest solution will be to
downgrade to 'numpy<2' or try to upgrade the affected module.
We expect that some modules will need time to support NumPy 2.
Warning: the binary library cannot be imported. You cannot train maps, but you can load and analyze ones that you have already saved.
The problem occurs because either compilation failed when you installed Somoclu or a path is missing from the dependencies when you are trying to import it. Please refer to the documentation to see your options.
Create a random catalog with ugrizy+YJHF bands as the the true input
data = np.random.normal(23, 3, size=(1000, 9))
data_df = pd.DataFrame(
data=data, columns=["u", "g", "r", "i", "z", "y", "Y", "J", "H"] # values
)
data_truth = PqHandle("input")
data_truth.set_data(data_df)
data_df
| u | g | r | i | z | y | Y | J | H | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 27.597138 | 23.410746 | 24.332932 | 21.738095 | 25.762221 | 22.901580 | 22.856230 | 24.178991 | 21.208914 |
| 1 | 19.492572 | 24.849699 | 21.856477 | 28.348929 | 24.810721 | 24.516700 | 25.210521 | 25.491282 | 26.925990 |
| 2 | 29.528775 | 24.695894 | 19.489144 | 22.534017 | 22.105033 | 28.391906 | 27.913236 | 23.519688 | 23.033575 |
| 3 | 23.001564 | 25.114046 | 25.460844 | 13.027881 | 29.276571 | 16.347359 | 18.821405 | 25.464906 | 28.687989 |
| 4 | 18.741741 | 19.055497 | 20.419327 | 21.201373 | 19.648462 | 22.229420 | 20.813919 | 24.404762 | 23.080636 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 995 | 20.784500 | 22.244543 | 22.330757 | 23.481757 | 25.612422 | 25.472825 | 23.737939 | 20.384226 | 22.815083 |
| 996 | 24.693647 | 27.168555 | 26.823348 | 23.964394 | 19.816125 | 22.723520 | 25.730922 | 23.142357 | 26.550749 |
| 997 | 21.858594 | 22.220576 | 20.974283 | 20.229863 | 21.850657 | 26.088833 | 11.442345 | 20.401696 | 28.636086 |
| 998 | 21.300828 | 26.489568 | 23.922031 | 26.914451 | 21.419059 | 22.900724 | 20.922308 | 25.153080 | 23.993964 |
| 999 | 21.449172 | 24.369495 | 18.990733 | 24.942895 | 22.184028 | 22.599188 | 22.904663 | 23.185487 | 21.214893 |
1000 rows × 9 columns
The LSST error model adds noise to the optical bands
samples_w_errs = photometric_errors.lsst_error_model(sample=data_truth)
samples_w_errs["output"]
Inserting handle into data store. input: None, LSSTErrorModel
Inserting handle into data store. output: inprogress_output.pq, LSSTErrorModel
| u | u_err | g | g_err | r | r_err | i | i_err | z | z_err | y | y_err | Y | J | H | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 30.102427 | 2.737282 | 23.407993 | 0.010137 | 24.338807 | 0.018368 | 21.726908 | 0.005746 | 25.986121 | 0.235627 | 22.916062 | 0.036208 | 22.856230 | 24.178991 | 21.208914 |
| 1 | 19.503096 | 0.005148 | 24.840171 | 0.032171 | 21.854077 | 0.005376 | 27.906369 | 0.590702 | 24.872526 | 0.090710 | 24.621745 | 0.162039 | 25.210521 | 25.491282 | 26.925990 |
| 2 | 27.652200 | 0.855331 | 24.713740 | 0.028803 | 19.490800 | 0.005012 | 22.535723 | 0.007619 | 22.094043 | 0.008986 | inf | inf | 27.913236 | 23.519688 | 23.033575 |
| 3 | 23.004021 | 0.019162 | 25.043191 | 0.038465 | 25.430378 | 0.047588 | 13.028362 | 0.005000 | 27.492445 | 0.736039 | 16.353453 | 0.005004 | 18.821405 | 25.464906 | 28.687989 |
| 4 | 18.743195 | 0.005062 | 19.063674 | 0.005013 | 20.416982 | 0.005042 | 21.203254 | 0.005320 | 19.656611 | 0.005091 | 22.246399 | 0.020219 | 20.813919 | 24.404762 | 23.080636 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 995 | 20.793837 | 0.005809 | 22.258692 | 0.005972 | 22.324416 | 0.005802 | 23.488054 | 0.014406 | 25.744867 | 0.192635 | 25.422898 | 0.314775 | 23.737939 | 20.384226 | 22.815083 |
| 996 | 24.753537 | 0.087319 | 27.681906 | 0.367843 | 26.867078 | 0.167602 | 23.946306 | 0.021044 | 19.814546 | 0.005116 | 22.737282 | 0.030929 | 25.730922 | 23.142357 | 26.550749 |
| 997 | 21.859575 | 0.008522 | 22.219286 | 0.005915 | 20.977609 | 0.005095 | 20.233290 | 0.005071 | 21.852857 | 0.007828 | 26.458192 | 0.680951 | 11.442345 | 20.401696 | 28.636086 |
| 998 | 21.297655 | 0.006636 | 26.470775 | 0.135045 | 23.907867 | 0.012979 | 27.309042 | 0.378543 | 21.412526 | 0.006457 | 22.878675 | 0.035032 | 20.922308 | 25.153080 | 23.993964 |
| 999 | 21.443869 | 0.007004 | 24.384983 | 0.021691 | 18.989867 | 0.005007 | 24.970121 | 0.051696 | 22.195766 | 0.009585 | 22.572717 | 0.026779 | 22.904663 | 23.185487 | 21.214893 |
1000 rows × 15 columns
fig, ax = plt.subplots(figsize=(5, 4), dpi=100)
for band in "ugrizy":
# pull out the magnitudes and errors
mags = samples_w_errs["output"][band].to_numpy()
errs = samples_w_errs["output"][band + "_err"].to_numpy()
# sort them by magnitude
mags, errs = mags[mags.argsort()], errs[mags.argsort()]
# plot errs vs mags
ax.plot(mags, errs, label=band)
ax.legend()
ax.set(xlabel="Magnitude (AB)", ylabel="Error (mags)")
plt.show()
The Roman error model adds noise to the infrared bands
samples_w_errs_roman = photometric_errors.roman_error_model(
sample=data_truth,
m5={"Y": 27.0, "J": 26.4, "H": 26.4},
theta={"Y": 27.0, "J": 0.106, "H": 0.128},
)
samples_w_errs_roman["output"]
Inserting handle into data store. input: None, RomanErrorModel
Inserting handle into data store. output: inprogress_output.pq, RomanErrorModel
| u | g | r | i | z | y | Y | Y_err | J | J_err | H | H_err | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 27.597138 | 23.410746 | 24.332932 | 21.738095 | 25.762221 | 22.901580 | 22.859942 | 0.006913 | 24.170842 | 0.027951 | 21.215713 | 0.005323 |
| 1 | 19.492572 | 24.849699 | 21.856477 | 28.348929 | 24.810721 | 24.516700 | 25.243721 | 0.042524 | 25.510416 | 0.091837 | 26.652818 | 0.244428 |
| 2 | 29.528775 | 24.695894 | 19.489144 | 22.534017 | 22.105033 | 28.391906 | 27.673376 | 0.343298 | 23.528140 | 0.016092 | 23.053220 | 0.011088 |
| 3 | 23.001564 | 25.114046 | 25.460844 | 13.027881 | 29.276571 | 16.347359 | 18.820148 | 0.005001 | 25.431686 | 0.085678 | 27.705607 | 0.553997 |
| 4 | 18.741741 | 19.055497 | 20.419327 | 21.201373 | 19.648462 | 22.229420 | 20.823225 | 0.005053 | 24.411337 | 0.034579 | 23.087848 | 0.011375 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 995 | 20.784500 | 22.244543 | 22.330757 | 23.481757 | 25.612422 | 25.472825 | 23.718735 | 0.011639 | 20.387234 | 0.005072 | 22.819517 | 0.009422 |
| 996 | 24.693647 | 27.168555 | 26.823348 | 23.964394 | 19.816125 | 22.723520 | 25.839393 | 0.072270 | 23.134508 | 0.011778 | 26.416626 | 0.200789 |
| 997 | 21.858594 | 22.220576 | 20.974283 | 20.229863 | 21.850657 | 26.088833 | 11.443304 | 0.005000 | 20.400411 | 0.005074 | 28.217478 | 0.788172 |
| 998 | 21.300828 | 26.489568 | 23.922031 | 26.914451 | 21.419059 | 22.900724 | 20.925271 | 0.005064 | 25.230300 | 0.071689 | 23.972169 | 0.023486 |
| 999 | 21.449172 | 24.369495 | 18.990733 | 24.942895 | 22.184028 | 22.599188 | 22.900142 | 0.007039 | 23.200419 | 0.012382 | 21.219162 | 0.005325 |
1000 rows × 12 columns
fig, ax = plt.subplots(figsize=(5, 4), dpi=100)
for band in "YJH":
# pull out the magnitudes and errors
mags = samples_w_errs_roman["output"][band].to_numpy()
errs = samples_w_errs_roman["output"][band + "_err"].to_numpy()
# sort them by magnitude
mags, errs = mags[mags.argsort()], errs[mags.argsort()]
# plot errs vs mags
ax.plot(mags, errs, label=band)
ax.legend()
ax.set(xlabel="Magnitude (AB)", ylabel="Error (mags)")
plt.show()
The Euclid error model adds noise to YJH bands
samples_w_errs_Euclid = photometric_errors.euclid_error_model(sample=data_truth)
samples_w_errs_Euclid["output"]
Inserting handle into data store. input: None, EuclidErrorModel
Inserting handle into data store. output: inprogress_output.pq, EuclidErrorModel
| u | g | r | i | z | y | Y | Y_err | J | J_err | H | H_err | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 27.597138 | 23.410746 | 24.332932 | 21.738095 | 25.762221 | 22.901580 | 22.855500 | 0.056147 | 24.108074 | 0.141762 | 21.225567 | 0.012625 |
| 1 | 19.492572 | 24.849699 | 21.856477 | 28.348929 | 24.810721 | 24.516700 | 24.716920 | 0.279554 | 25.484755 | 0.436889 | 26.171335 | 0.764601 |
| 2 | 29.528775 | 24.695894 | 19.489144 | 22.534017 | 22.105033 | 28.391906 | inf | inf | 23.512650 | 0.084249 | 23.200052 | 0.069790 |
| 3 | 23.001564 | 25.114046 | 25.460844 | 13.027881 | 29.276571 | 16.347359 | 18.825782 | 0.005192 | 24.969218 | 0.291646 | inf | inf |
| 4 | 18.741741 | 19.055497 | 20.419327 | 21.201373 | 19.648462 | 22.229420 | 20.820811 | 0.010090 | 24.393330 | 0.180941 | 23.025741 | 0.059771 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 995 | 20.784500 | 22.244543 | 22.330757 | 23.481757 | 25.612422 | 25.472825 | 23.698714 | 0.118230 | 20.381447 | 0.006980 | 22.800650 | 0.048909 |
| 996 | 24.693647 | 27.168555 | 26.823348 | 23.964394 | 19.816125 | 22.723520 | 25.294257 | 0.440046 | 23.145421 | 0.060827 | inf | inf |
| 997 | 21.858594 | 22.220576 | 20.974283 | 20.229863 | 21.850657 | 26.088833 | 11.446687 | 0.005000 | 20.399652 | 0.007037 | 27.027379 | 1.279362 |
| 998 | 21.300828 | 26.489568 | 23.922031 | 26.914451 | 21.419059 | 22.900724 | 20.899159 | 0.010663 | 24.773929 | 0.248716 | 23.755097 | 0.113818 |
| 999 | 21.449172 | 24.369495 | 18.990733 | 24.942895 | 22.184028 | 22.599188 | 22.891534 | 0.057978 | 23.334772 | 0.071974 | 21.228030 | 0.012649 |
1000 rows × 12 columns
fig, ax = plt.subplots(figsize=(5, 4), dpi=100)
for band in "YJH":
# pull out the magnitudes and errors
mags = samples_w_errs_Euclid["output"][band].to_numpy()
errs = samples_w_errs_Euclid["output"][band + "_err"].to_numpy()
# sort them by magnitude
mags, errs = mags[mags.argsort()], errs[mags.argsort()]
# plot errs vs mags
ax.plot(mags, errs, label=band)
ax.legend()
ax.set(xlabel="Magnitude (AB)", ylabel="Error (mags)")
plt.show()