Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
142 commits
Select commit Hold shift + click to select a range
1a50424
Cell magics are run ina subprocess and the errors are captured, there…
israelmcmc Apr 12, 2025
6559f84
Remove only magic cell. I think magic lines are not problematic
israelmcmc Apr 12, 2025
55d9d7e
Add test for magic cells. Also serves as a negative control
israelmcmc Apr 12, 2025
0e408cc
Change test name
israelmcmc Apr 12, 2025
83d04af
Fail successfully = Green!
israelmcmc Apr 12, 2025
beafef0
Fix file checksum in ts map tutorial
israelmcmc Apr 15, 2025
122d4ad
Catch nb failure exception to allow notebook to still be saved and cl…
israelmcmc Apr 15, 2025
9f7b717
Initial take at new interfaces needed by COSILike
israelmcmc Apr 16, 2025
3101a1e
COSILike implementation using interfaces. WiP
israelmcmc Apr 16, 2025
2eb9a2f
Some more changes
israelmcmc Apr 16, 2025
d6521ca
Toy example working
israelmcmc Apr 17, 2025
f960da5
Remove NullBackground. A None is enough
israelmcmc Apr 17, 2025
1841881
Make all interfaces explicit protocols
israelmcmc Apr 17, 2025
0f55653
Example separate base bkg from 3ml interface
israelmcmc Apr 17, 2025
0ba0ebf
Fix indent
israelmcmc Apr 17, 2025
a4ec731
Add explanations to toy example
israelmcmc Apr 17, 2025
99e84e7
Rename toy example interfaces
israelmcmc Apr 17, 2025
f24c19d
Fix import warnings
israelmcmc Apr 17, 2025
d358b73
Null background no longer exists
israelmcmc Apr 17, 2025
6fc062e
Fix type hints for kwargs
israelmcmc Apr 17, 2025
35dc2f3
Divide 3ML model response from 3ML source response. The former can us…
israelmcmc Apr 17, 2025
d5d1dec
Save progress. Working on: Moving PSR calculation from COSILIKE > get…
israelmcmc Apr 23, 2025
b4a962a
Refactor SpacecraftFile. Unit tests WiP
israelmcmc May 1, 2025
62d4541
All SpacecraftFile tests passing
israelmcmc May 1, 2025
e70cae7
All rsp to arf&rmf converter tests are passing
israelmcmc May 1, 2025
c276bfb
Rename SC file open. Eventually this function should be able to eithe…
israelmcmc May 1, 2025
dea56c1
Separate .ori parsing from future formats
israelmcmc May 1, 2025
2e3b28c
Fix bug on SpacecraftFile select_interval for edge cases
israelmcmc May 2, 2025
795bd48
Using pandas instead of np.loadtxt since it's way faster
israelmcmc May 2, 2025
eefb6f3
All elements are ready for a basic GRB fit. Still testing.
israelmcmc May 2, 2025
1441cf5
Cut SC file from the beginning to make it faster
israelmcmc May 3, 2025
3b81c91
Slightly faster this way
israelmcmc May 3, 2025
6719cb9
Working version. Runs but some results are nan. Still checking.
israelmcmc May 4, 2025
a7b1d4f
Move 3ML plugin in preparation for generic plugin
israelmcmc May 4, 2025
b604db0
Forgot to commit this one on 6719cb9108582880135b7f492029775bee455c75
israelmcmc May 4, 2025
b526255
Finish moving to generic plugin. Work as well as before. Still some n…
israelmcmc May 4, 2025
8229d96
Automatically handle bare background to 3ML Parameter conversion. Fix…
israelmcmc May 4, 2025
627ca84
Fix some issues. Change defaults of free norm parameters. Runs and co…
israelmcmc May 4, 2025
7955cf2
Allows to set full new parameter
israelmcmc May 4, 2025
4eed795
Reproduce the GRB tutorial fit results.
israelmcmc May 4, 2025
db1badf
Change name of normalized bkg distributions
israelmcmc May 4, 2025
c2ed9c6
Take fudge value out of the generic likelihood calculation, and appli…
israelmcmc May 4, 2025
2647602
Rename example. more generic than cosi
israelmcmc May 4, 2025
c43d40d
Cleanup a little. Change bkg default label.
israelmcmc May 4, 2025
4947be0
Rename SpacecraftFile to SpacecraftHistory, since it no longer needs …
israelmcmc May 5, 2025
5bc9423
Move PolarizationASAD to different module to avoid circular import
israelmcmc May 6, 2025
c39d23d
Move RspArfRmfConverter to different module to avoid circular import
israelmcmc May 6, 2025
b9b34f6
Histpy polarization axis (draft)
israelmcmc May 28, 2025
a5b67bf
Generate PSR from a generic BinnedInstrumentResponseInterface.
israelmcmc Jun 3, 2025
87975c8
Small type annotation
israelmcmc Jun 27, 2025
3c0eb88
Axes check
israelmcmc Jun 27, 2025
c7ec659
Remove BinnedThreeMLPointSourceResponseLocal in favor of a general on…
israelmcmc Jun 30, 2025
214f2ae
Change name from model response to model folding.
israelmcmc Jun 30, 2025
860627b
BinnedInstrumentResponse can now handle data in inertial coordinates.
israelmcmc Jul 14, 2025
e4a8d9c
Debugging changes. Still wip.
israelmcmc Jul 15, 2025
9f3ef28
Make copy param optional
israelmcmc Jul 16, 2025
53bf6ad
Fix exception name
israelmcmc Jul 17, 2025
d4b2264
Fix transformation of the orbit location as specified in an .ori file…
israelmcmc Jul 18, 2025
3a0e9ba
Fix bug with gcrs in cartesian
israelmcmc Jul 18, 2025
ac7f843
Rever to being an example of the GRB fit only, and not Crab as well
israelmcmc Jul 18, 2025
00c5c43
Add working example of Crab fit using new interfaces.
israelmcmc Jul 18, 2025
5c1013d
Also move the toy example
israelmcmc Jul 18, 2025
f50f7bf
Pass around the BinnedDataInterface implementation instead of the bar…
israelmcmc Jul 21, 2025
a983e47
Remove the need for a full direction axis for a POINTSourceResponse
israelmcmc Jul 21, 2025
dca817d
Unbinned data interface
israelmcmc Jul 24, 2025
fcdca5f
Machinery for unbinned toy example is now working
israelmcmc Jul 25, 2025
35b52d4
Update dwell time map analysis to new signatures developed for the sc…
israelmcmc Jul 25, 2025
d177485
Use the more general iterables instead of numpy arrays for interfaces
israelmcmc Aug 7, 2025
217979b
Use set_data() instead of expectations(data).
israelmcmc Sep 25, 2025
954bd15
Add tstart/tstop for all data types
israelmcmc Sep 26, 2025
e7a100b
Change meaning of nevents to mean events after selection
israelmcmc Sep 29, 2025
60d9028
Rework unbinned data interfaces
israelmcmc Sep 29, 2025
d18f0d3
Simplify interfaces. Remove set_data and other methods not strictly n…
israelmcmc Oct 1, 2025
ee95e39
Attempt to make crab example reproducible.
israelmcmc Oct 1, 2025
98eeb13
Keep the simplification by removing set_data/response/bkg from likeli…
israelmcmc Oct 1, 2025
edf1157
Improve event selector and event metadata interface
israelmcmc Oct 1, 2025
443571c
work out toy example time selection
israelmcmc Oct 2, 2025
d6d3974
Use main() in toy
israelmcmc Oct 2, 2025
63a948d
Optimize the selection of toy example
israelmcmc Oct 2, 2025
b65695a
Rework of events stream to make it possible to cache and add DC3 data…
israelmcmc Oct 3, 2025
f85069f
Add time selection to crab unbinned example
israelmcmc Oct 3, 2025
ce44166
Merge branch 'refs/heads/develop' into interfaces
israelmcmc Oct 4, 2025
1921e6e
Changes to make interfaces work with latest develop branch
israelmcmc Oct 4, 2025
5facc13
PSR interp trapz wip. Need to fix a few stuff still, but existing exa…
israelmcmc Oct 7, 2025
90c09e1
Handle measurement phase-space
israelmcmc Oct 8, 2025
9ec0c41
unbinned model folding. bkg wip
israelmcmc Oct 8, 2025
dabc8a7
unbinned background. both expectation dentisty and event probability.…
israelmcmc Oct 10, 2025
ba8511e
Fix unbinned toy model and signal-only crab unbinned fit
israelmcmc Oct 17, 2025
080c86e
Fix crab unbinned. 50% error, I think due to inputs
israelmcmc Oct 18, 2025
bacdb91
All interfaces examples working
israelmcmc Oct 18, 2025
306bcf4
clean up toy example a little
israelmcmc Oct 18, 2025
5912a90
change ncounts to expected counts and remote start/stop from expectat…
israelmcmc Oct 19, 2025
2eeba64
fit one full orbit
israelmcmc Oct 20, 2025
1930985
Merge branch 'gti' into gti_interface
hiyoneda Oct 20, 2025
62079b3
Modifed GTI class
hiyoneda Oct 20, 2025
5750ba7
Added EventSelectorGTI
hiyoneda Oct 20, 2025
d3251eb
Add MutliTimeSelector, removed GTISelector
hiyoneda Oct 21, 2025
f64cd6a
Modified GoodTimeInterval
hiyoneda Oct 21, 2025
8716aed
Removed unnecessary functions for GoodTimeIntervals for this PR
hiyoneda Oct 21, 2025
a1b0e8a
Fix issue with sum of expectations densities. Caught by Pascal.
israelmcmc Oct 22, 2025
b68d242
commit
GallegoSav Oct 22, 2025
c9281dc
Update example_crab_fit_threeml_plugin_interfaces.py
GallegoSav Oct 22, 2025
a115f5b
Update example_crab_fit_threeml_plugin_interfaces.py
GallegoSav Oct 22, 2025
063f725
Merge pull request #4 from hiyoneda/gti_interface
israelmcmc Oct 23, 2025
6af64ce
Fix bug preventing arbitrary name for single component bkg
israelmcmc Oct 23, 2025
0be411f
Single or multiple bkg
israelmcmc Oct 23, 2025
c0a3b81
Merge branch 'multiplebck_savitri' into interfaces
israelmcmc Oct 23, 2025
9dfacba
update histpy version to the one with timeaxis
israelmcmc Oct 23, 2025
007eb44
sort -> _sort (GTI)
hiyoneda Oct 24, 2025
4836366
Replace an original TimeSelector with MultiTimeSelector;MultiTimeSele…
hiyoneda Oct 24, 2025
1ed6dca
Fix bug. SkyCoord takes latitude, not colatitude. Found by Pascal.
nlopez-code Oct 24, 2025
5f1892d
Decouple conventions from astropy
israelmcmc Oct 26, 2025
27917c8
doc random event. wip
israelmcmc Oct 26, 2025
e574ad8
Add photons with polarization
israelmcmc Oct 26, 2025
1994b0c
WIP. Most distributions ready for the ideal response.
israelmcmc Oct 26, 2025
be356d1
Revert. The number of detected events is handled the effective area. …
israelmcmc Oct 27, 2025
baf805b
Added the method apply_gti to SpacecraftHistory
hiyoneda Oct 28, 2025
99c230e
Relative coordinates
israelmcmc Oct 28, 2025
a061b98
Faster conversion by avoiding intermediate perpendicular vector step.…
israelmcmc Oct 29, 2025
a5780ff
ideal response random event working!
israelmcmc Oct 29, 2025
09cbaf2
ideal response pdf working!
israelmcmc Oct 30, 2025
b16324f
Polarized and unpolarized version
israelmcmc Oct 31, 2025
fd5495a
Merge pull request #7 from hiyoneda/spacecrafthistory_with_gti
israelmcmc Oct 31, 2025
b44f36b
Revert "Added the method apply_gti to SpacecraftHistory"
israelmcmc Oct 31, 2025
c5d8e82
Merge pull request #8 from israelmcmc/revert-7-spacecrafthistory_with…
israelmcmc Oct 31, 2025
f0e1519
Merge pull request #6 from hiyoneda/gti_interface
israelmcmc Oct 31, 2025
db9d000
Protect unbinned likelihood from ill defined expectation
israelmcmc Nov 3, 2025
8f4918e
Add missing event type
israelmcmc Nov 3, 2025
5d63189
Working example of ideal irf
israelmcmc Nov 3, 2025
d8f1547
convenience function to get array of all values
israelmcmc Nov 3, 2025
fc913af
Ideal response analysis. All working.
israelmcmc Nov 3, 2025
0dcbddb
Match FWHM 3deg
israelmcmc Nov 4, 2025
15ddd5e
Clean example a little bit
israelmcmc Nov 4, 2025
40d0de0
get nevents
israelmcmc Nov 4, 2025
dce3ee7
Add tests for SCHistory attitude and location interpolation
israelmcmc Jan 14, 2026
907f3ee
Bring back improvements to SpacecraftHistory interpolations (back whe…
israelmcmc Jan 14, 2026
6c7c53e
Merge branch 'develop' into interfaces_dev_merge
israelmcmc Jan 14, 2026
363b256
Make interpolation work with multiple inputs
israelmcmc Jan 14, 2026
317ab82
Make crab binned fit work with latest develop
israelmcmc Jan 26, 2026
7e9e987
Merge branch 'develop' into interfaces_devmerge
israelmcmc Jan 28, 2026
4dbfbee
Merge develop nd fix some unit test (not everyone yet)
israelmcmc Jan 30, 2026
e019541
Fix bug from develop-interfaces merge. GCRS only has "distance" in sp…
israelmcmc Feb 2, 2026
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
5 changes: 3 additions & 2 deletions cosipy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

from .response import DetectorResponse

from .spacecraftfile import *

from .data_io import DataIO
from .data_io import UnBinnedData
from .data_io import BinnedData
from .data_io import ReadTraTest

from .threeml import COSILike
from .threeml import Band_Eflux

from .spacecraftfile import SpacecraftFile
from .spacecraftfile import SpacecraftHistory

from .ts_map import FastTSMap, MOCTSMap

Expand Down
2 changes: 1 addition & 1 deletion cosipy/background_estimation/ContinuumEstimation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def calc_psr(self, sc_orientation, detector_response, coord, nside=16):
----------
ori_file : str
Full path to orienation file.
sc_orientation : cosipy.spacecraftfile.SpacecraftFile
sc_orientation : cosipy.spacecraftfile.SpacecraftHistory
Spacecraft orientation object.
detector_response : str
Full path to detector response file.
Expand Down
1 change: 1 addition & 0 deletions cosipy/background_estimation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .LineBackgroundEstimation import LineBackgroundEstimation
from .ContinuumEstimation import ContinuumEstimation
from .free_norm_threeml_binned_bkg import *
307 changes: 307 additions & 0 deletions cosipy/background_estimation/free_norm_threeml_binned_bkg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
import itertools
from typing import Dict, Tuple, Union, Any, Type, Optional, Iterable

import numpy as np
from astromodels import Parameter
from astropy.coordinates import SkyCoord, CartesianRepresentation, UnitSphericalRepresentation
from astropy.time import Time
from histpy import Histogram
from histpy import Axes

from astropy import units as u
from scoords import SpacecraftFrame

from cosipy import SpacecraftHistory
from cosipy.data_io.EmCDSUnbinnedData import TimeTagEmCDSEventInSCFrame
from cosipy.interfaces import BinnedBackgroundInterface, BinnedDataInterface, DataInterface, BackgroundDensityInterface, \
BackgroundInterface, EventInterface

__all__ = ["FreeNormBinnedBackground"]

from cosipy.interfaces.data_interface import TimeTagEmCDSEventDataInSCFrameInterface

from cosipy.interfaces.event import TimeTagEmCDSEventInSCFrameInterface
from cosipy.util.iterables import itertools_batched

class FreeNormBackground(BackgroundInterface):
"""
This must translate to/from regular parameters
with arbitrary type from/to 3ML parameters

Default to "bkg_norm" is there was a single unlabeled component
"""

_default_label = 'bkg_norm'

def __init__(self,
distribution:Union[Histogram, Dict[str, Histogram]],
sc_history:SpacecraftHistory,
copy = True):
"""

Parameters
----------
distribution
sc_history
copy: copy hist distribution
"""

if isinstance(distribution, Histogram):
# Single component
self._distributions = {self._default_label: distribution}
self._norms = np.ones(1) # Hz. Each component
self._norm = 1 # Hz. Total
self._single_component = True
else:
# Multiple label components.
self._distributions = distribution
self._norms = np.ones(self.ncomponents) # Hz Each component
self._norm = np.sum(self._norms) # Hz. Total
self._single_component = False

self._labels = tuple(self._distributions.keys())

# Normalize
# Unit: second
self._livetime = sc_history.cumulative_livetime().to_value(u.s)
for label,dist in self._distributions.items():
dist_norm = np.sum(dist)
if copy:
self._distributions[label] = dist/dist_norm
else:
dist /= dist_norm

# These will be densify anyway since _expectation is dense
# And histpy doesn't yet handle this operation efficiently
# See Histogram._inplace_operation_handle_sparse()
# Do it once and for all
for label, bkg in self._distributions.items():
if bkg.is_sparse:
self._distributions[label] = bkg.to_dense()

if self.ncomponents == 0:
raise ValueError("You need to input at least one components")

self._axes = None
for bkg in self._distributions.values():
if self._axes is None:
self._axes = bkg.axes
else:
if self._axes != bkg.axes:
raise ValueError("All background components mus have the same axes")

@property
def norm(self):
"""
Sum of all rates
"""

return u.Quantity(self._norm, u.Hz)

@property
def norms(self):
if self._single_component:
return {self._default_label: u.Quantity(self._norms[0], u.Hz)}
else:
return {l:u.Quantity(n, u.Hz, copy = False) for l,n in zip(self.labels,self._norms)}

@property
def ncomponents(self):
return len(self._distributions)

@property
def meausured_axes(self):
return self._axes

@property
def labels(self):
return self._labels

def set_norm(self, norm: Union[u.Quantity, Dict[str, u.Quantity]]):

if self._single_component:
if isinstance(norm, dict):
self._norms[0] = norm[self._default_label].to_value(u.Hz)
else:
self._norms[0] = norm.to_value(u.Hz)
else:
# Multiple
if not isinstance(norm, dict):
raise TypeError("This a multi-component background. Provide labeled norm values in a dictionary")

for label,norm_i in norm.items():
if label not in self.labels:
raise ValueError(f"Norm {label} not in {self.labels}")

self._norms[self.labels.index(label)] = norm_i.to_value(u.Hz)

self._norm = sum(n for n in self._norms)

def set_parameters(self, **parameters:Dict[str, u.Quantity]) -> None:
"""
Same keys as background components
"""

self.set_norm(parameters)

@property
def parameters(self) -> Dict[str, u.Quantity]:
return self.norms

class FreeNormBinnedBackground(FreeNormBackground, BinnedBackgroundInterface):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Cache
self._expectation = None
self._last_norm_values = None

def expectation(self, axes:Axes, copy:bool = True)->Histogram:
"""

Parameters
----------
axes
copy:
If True, it will return an array that the user if free to modify.
Otherwise, it will result a reference, possible to the cache, that
the user should not modify

Returns
-------

"""

if axes != self.meausured_axes:
raise ValueError("Requested axes do not match the background component axes")

# Check if we can use the cache
if self._expectation is None:
# First call. Initialize
self._expectation = Histogram(self.meausured_axes)

elif self.norms == self._last_norm_values:
# No changes. Use cache
if copy:
return self._expectation.copy()
else:
return self._expectation

else:
# First call or norms have change. Recalculate
self._expectation.clear()

# Compute expectation
for label,bkg in self._distributions.items():
norm = self._norms[self.labels.index(label)]
self._expectation += bkg * norm * self._livetime

# Cache. Regular copy is enough since norm values are float en not mutable
self._last_norm_values = self.norms.copy()

if copy:
return self._expectation.copy()
else:
return self._expectation


class FreeNormBackgroundInterpolatedDensityTimeTagEmCDS(FreeNormBackground, BackgroundDensityInterface):

@property
def event_type(self) -> Type[EventInterface]:
return TimeTagEmCDSEventInSCFrameInterface

def __init__(self,
data:TimeTagEmCDSEventDataInSCFrameInterface,
distribution:Union[Histogram, Dict[str, Histogram]],
sc_history:SpacecraftHistory,
copy=True,
batch_size = 100000,
*args,
**kwargs):

super().__init__(distribution, sc_history,
copy=copy, *args, **kwargs)

# We need the density per phase space for the specific measurement units TimeTagEmCDSEventInSCFrameInterface
# Energy: keV
# Phi: rad
# PsiChi: sr (for the phase space. The axis is a HealpixAxis)
# Time: seconds (taken into account by the norm (a rate) unit)

psichi_frame = None

for label,dist in self._distributions.items():

dist = self._distributions[label] = dist.project('Em', 'Phi', 'PsiChi')

dist.axes['Em'] = dist.axes['Em'].to(u.keV).to(None, copy=False, update=False)
dist.axes['Phi'] = dist.axes['Phi'].to(u.rad).to(None, copy=False, update=False)

energy_phase_space = dist.axes['Em'].widths
phi_phase_space = dist.axes['Phi'].widths
psichi_phase_space = dist.axes['PsiChi'].pixarea().to_value(u.sr)

if psichi_frame is None:
psichi_frame = dist.axes['PsiChi'].coordsys
else:
if psichi_frame != dist.axes['PsiChi'].coordsys:
raise ValueError("All PsiChi axes must be in the same frame")

dist /= dist.axes.expand_dims(energy_phase_space, 'Em')
dist /= dist.axes.expand_dims(phi_phase_space, 'Phi')
dist /= psichi_phase_space

# Compute the probabilities once and for all
# TODO: account for livetime
self._prob = [[] for _ in range(self.ncomponents)]

for events_chunk in itertools_batched(data, batch_size):

jd1, jd2, energy,phi, psichi_lon, psichi_lat = np.asarray([[
event.jd1,
event.jd2,
event.energy_keV,
event.scattering_angle_rad,
event.scattered_lon_rad_sc,
event.scattered_lat_rad_sc]
for event in events_chunk], dtype=float).transpose()

times = Time(jd1, jd2, format = 'jd')

# Transform local to inertial
sc_psichi_coord = SkyCoord(psichi_lon, psichi_lat, unit=u.rad, frame=SpacecraftFrame())
sc_psichi_vec = sc_psichi_coord.cartesian.xyz.value
attitudes = sc_history.interp_attitude(times).transform_to(psichi_frame)
inertial_psichi_vec = attitudes.rot.apply(sc_psichi_vec.transpose())
inertial_psichi_sph = UnitSphericalRepresentation.from_cartesian(CartesianRepresentation(*inertial_psichi_vec.transpose()))
inertial_psichi_coord = SkyCoord(inertial_psichi_sph, frame = psichi_frame)

for label,dist in self._distributions.items():
prob = dist.interp(energy, phi, inertial_psichi_coord)
self._prob[self.labels.index(label)].extend(prob)

self._prob = np.asarray(self._prob)

def expected_counts(self) -> float:
"""
Total expected counts
"""
return self._livetime * self._norm

def expectation_density(self) -> Iterable[float]:
"""
Return the expected number of counts density from the start-th event
to the stop-th event. This equals the event probabiliy times the number of events
"""

# Multiply each probability by the norm, and then sum
return np.tensordot(self._prob, self._norms, axes = (0,0))







42 changes: 42 additions & 0 deletions cosipy/data_io/BinnedData.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
from cosipy.data_io import UnBinnedData

import logging
import astropy.units as u
from astropy.coordinates import SkyCoord

from cosipy.interfaces import BinnedDataInterface

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -496,3 +501,40 @@ def get_raw_lightcurve(self, binned_data=None, output_name=None, show_plots=Fals
d = {"Time[UTC]":self.time_bin_centers,"Rate[ct/s]":self.time_hist/self.time_bin_widths}
df = pd.DataFrame(data=d)
df.to_csv(f"{output_name}.dat",index=False,sep="\t",columns=["Time[UTC]","Rate[ct/s]"])

def get_em_cds(self):
return EmCDSBinnedData(self.binned_data.project('Em', 'Phi', 'PsiChi'))

class EmCDSBinnedData(BinnedDataInterface):
"""
Measured energy (Em), Compton polar scattering angle (Phi), and the scattering direction (PsiChi).
Phi and PsiChi are the Compton Data Space (CDS). No time dependence
"""
def __init__(self, data:Histogram):

# Checks
if set(data.axes.labels) != {'Em', 'Phi', 'PsiChi'}:
raise ValueError(f"Wrong axes. 'Em', 'Psi', 'PsiChi' expected.")

if not data.axes['Em'].unit.is_equivalent(u.keV):
raise ValueError(f"Em axis should have units of energy")

if not data.axes['Phi'].unit.is_equivalent(u.deg):
raise ValueError(f"Psi axis should have angle units")

if not isinstance(data.axes['PsiChi'],HealpixAxis):
raise ValueError(f"PsiChi must be of type {HealpixAxis}.")

if data.axes['PsiChi'].coordsys is None:
raise ValueError(f"PsiChi axes must have a coordinate system.")

self._data = data

@property
def data(self) -> Histogram:
return self._data
@property
def axes(self) -> Axes:
return self._data.axes


Loading
Loading