Skip to content

Commit

Permalink
Merge pull request #81 from fjebaker/fergus/ogip-flexibility
Browse files Browse the repository at this point in the history
More flexibility with OGIP datasets
  • Loading branch information
fjebaker authored Feb 26, 2024
2 parents e009cce + 14b9dfd commit f5c48b0
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 114 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "SpectralFitting"
uuid = "f2c56810-742e-4b72-8bf4-27af3bb81a12"
authors = ["Fergus Baker <[email protected]>"]
version = "0.5.4"
version = "0.5.5"

[deps]
ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9"
Expand Down
5 changes: 1 addition & 4 deletions src/datasets/datasets.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,5 @@ export make_model_domain,
include("spectrum.jl")
include("response.jl")
include("spectraldata.jl")

# mission specifics
include("ogipdataset.jl")
include("xmm-newton.jl")
include("nustart.jl")
include("mission-specifics.jl")
87 changes: 87 additions & 0 deletions src/datasets/mission-specifics.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@

struct NuStarData{T,H} <: AbstractDataset
data::SpectralData{T}
paths::SpectralDataPaths
observation_id::String
exposure_id::String
object::String
header::H
end

NuStarData(spec_path; rmf_matrix_index = 3, rmf_energy_index = 2, kwargs...) = NuStarData(
load_ogip_dataset(
spec_path;
rmf_matrix_index = rmf_matrix_index,
rmf_energy_index = rmf_energy_index,
kwargs...,
)...,
)

make_label(data::NuStarData) = data.observation_id

@_forward_SpectralData_api NuStarData.data

function Base.show(io::IO, @nospecialize(data::NuStarData{T})) where {T}
print(io, "NuStarData[obs_id=$(data.observation_id)]")
end

function _printinfo(io, data::NuStarData{T}) where {T}
descr = """NuStarData:
. Object : $(data.object)
. Observation ID : $(data.observation_id)
. Exposure ID : $(data.exposure_id)
"""
print(io, descr)
_printinfo(io, data.data)
end


abstract type AbstractXmmNewtonDevice end
struct XmmEPIC <: AbstractXmmNewtonDevice end

struct XmmData{T,H,D} <: AbstractDataset
device::D
data::SpectralData{T}
paths::SpectralDataPaths
observation_id::String
exposure_id::String
object::String
header::H
end

XmmData(
device::AbstractXmmNewtonDevice,
spec_path;
rmf_matrix_index = 2,
rmf_energy_index = 3,
kwargs...,
) = XmmData(
device,
load_ogip_dataset(
spec_path;
rmf_matrix_index = rmf_matrix_index,
rmf_energy_index = rmf_energy_index,
kwargs...,
)...,
)

make_label(data::XmmData) = data.observation_id

@_forward_SpectralData_api XmmData.data

function Base.show(io::IO, @nospecialize(data::XmmData{T})) where {T}
print(io, "XmmData[dev=$(data.device),obs_id=$(data.observation_id)]")
end

function _printinfo(io, data::XmmData{T}) where {T}
descr = """XmmData for $(Base.typename(typeof(data.device)).name):
. Object : $(data.object)
. Observation ID : $(data.observation_id)
. Exposure ID : $(data.exposure_id)
"""
print(io, descr)
_printinfo(io, data.data)
end


export NuStarData, XmmData, XmmEPIC
46 changes: 0 additions & 46 deletions src/datasets/nustart.jl

This file was deleted.

55 changes: 47 additions & 8 deletions src/datasets/ogip.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ rmf_energy_index(c::AbstractOGIPConfig) = error("Not implemented for $(typeof(c)
struct StandardOGIPConfig{T} <: AbstractOGIPConfig{T}
rmf_matrix_index::Int
rmf_energy_index::Int
rmf_matrix_key::String
function StandardOGIPConfig(;
rmf_matrix_index = 3,
rmf_energy_index = 2,
rmf_matrix_key = "SPECRESP",
T::Type = Float64,
)
@assert rmf_matrix_index != rmf_energy_index
new{T}(rmf_matrix_index, rmf_energy_index)
new{T}(rmf_matrix_index, rmf_energy_index, rmf_matrix_key)
end
end
rmf_matrix_index(c::StandardOGIPConfig) = c.rmf_matrix_index
Expand Down Expand Up @@ -60,6 +62,21 @@ function _parse_any(::Type{T}, @nospecialize(value::V))::T where {T,V}
end
end

function _string_boolean(@nospecialize(value::V))::Bool where {V}
if V <: AbstractString
if value == "F"
false
elseif value == "T"
true
else
@warn("Unknown boolean string: $(value)")
false
end
else
value
end
end

# functions
function parse_rmf_header(table::TableHDU)
header = FITSIO.read_header(table)
Expand Down Expand Up @@ -107,6 +124,24 @@ function _chan_to_vectors(chan::Matrix)
end
end

function _translate_channel_array(channel)
if channel isa Matrix
_chan_to_vectors(channel)
elseif eltype(channel) <: AbstractVector
channel
else
map(i -> [i], channel)
end
end

function _adapt_matrix_type(T::Type, mat::M) where {M}
if eltype(M) <: AbstractVector
map(row -> convert.(T, row), mat)
elseif M <: AbstractMatrix
map(row -> convert.(T, row), eachcol(mat))
end
end

function read_rmf_matrix(table::TableHDU, header::RMFHeader, T::Type)
energy_low = convert.(T, read(table, "ENERG_LO"))
energy_high = convert.(T, read(table, "ENERG_HI"))
Expand All @@ -115,17 +150,18 @@ function read_rmf_matrix(table::TableHDU, header::RMFHeader, T::Type)
matrix_raw = read(table, "MATRIX")

# type stable: convert to common vector of vector format
f_chan::Vector{Vector{Int}} =
f_chan_raw isa Matrix ? _chan_to_vectors(f_chan_raw) : f_chan_raw
n_chan::Vector{Vector{Int}} =
n_chan_raw isa Matrix ? _chan_to_vectors(n_chan_raw) : n_chan_raw
f_chan::Vector{Vector{Int}} = _translate_channel_array(f_chan_raw)
n_chan::Vector{Vector{Int}} = _translate_channel_array(n_chan_raw)

@show typeof(matrix_raw)
@show size(f_chan), size(matrix_raw)

RMFMatrix(
f_chan,
n_chan,
energy_low,
energy_high,
map(row -> convert.(T, row), matrix_raw),
_adapt_matrix_type(T, matrix_raw),
header,
)
end
Expand Down Expand Up @@ -159,7 +195,7 @@ end
function read_ancillary_response(path::String, ogip_config::AbstractOGIPConfig{T}) where {T}
fits = FITS(path)
(bins_low, bins_high, effective_area) = _read_fits_and_close(path) do fits
area::Vector{T} = convert.(T, read(fits[2], "SPECRESP"))
area::Vector{T} = convert.(T, read(fits[2], ogip_config.rmf_matrix_key))
lo::Vector{T} = convert.(T, read(fits[2], "ENERG_LO"))
hi::Vector{T} = convert.(T, read(fits[2], "ENERG_HI"))
(lo, hi, area)
Expand Down Expand Up @@ -229,7 +265,7 @@ function read_spectrum(path, config::AbstractOGIPConfig{T}) where {T}
info = _read_fits_and_close(path) do fits
header = read_header(fits[2])
# if not set, assume not poisson errors
is_poisson = get(header, "POISSERR", false)
is_poisson = _string_boolean(get(header, "POISSERR", false))
# read general infos
instrument = header["INSTRUME"]
telescope = header["TELESCOP"]
Expand Down Expand Up @@ -314,6 +350,9 @@ function read_filename(header, entry, parent, exts...)
parent_name = basename(parent)
if haskey(header, entry)
path::String = strip(header[entry])
if path == "NONE"
return missing
end
name = find_file(data_directory, path, parent_name, exts)
if !ismissing(name)
return name
Expand Down
13 changes: 8 additions & 5 deletions src/datasets/ogipdataset.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ struct OGIPDataset{T,H} <: AbstractDataset
header::H
end

function OGIPDataset(
function load_ogip_dataset(
spec_path;
T::Type = Float64,
hdu = 2,
background = missing,
response = missing,
Expand All @@ -22,20 +21,24 @@ function OGIPDataset(
response = response,
ancillary = ancillary,
)
config = StandardOGIPConfig(rmf_matrix_index = 2, rmf_energy_index = 3, T = T)
config = StandardOGIPConfig(; kwargs...)

header = read_fits_header(paths.spectrum; hdu = hdu)

obs_id = haskey(header, "OBS_ID") ? header["OBS_ID"] : "[no observation id]"
exposure_id = haskey(header, "EXP_ID") ? header["EXP_ID"] : "[no exposure id]"
object = haskey(header, "OBJECT") ? header["OBJECT"] : "[no object]"

data = SpectralData(paths, config; kwargs...)
OGIPDataset(data, paths, obs_id, exposure_id, object, header)
data = SpectralData(paths, config)
(data, paths, obs_id, exposure_id, object, header)
end

OGIPDataset(spec_path; kwargs...) = OGIPDataset(load_ogip_dataset(spec_path; kwargs...)...)

@_forward_SpectralData_api OGIPDataset.data

make_label(d::OGIPDataset) = d.observation_id

function _printinfo(io, data::OGIPDataset{T}) where {T}
descr = """OGIPDataset:
. Object : $(data.object)
Expand Down
5 changes: 3 additions & 2 deletions src/datasets/spectraldata.jl
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,9 @@ function _dataset_from_ogip(paths::SpectralDataPaths, config::OGIP.AbstractOGIPC
resp = if !ismissing(paths.response)
OGIP.read_rmf(paths.response, config)
else
@warn "No response file found."
missing
throw(
"No response file found in the header. Response must be specified with the keyword `response=PATH`.",
)
end
ancillary = if !ismissing(paths.ancillary)
OGIP.read_ancillary_response(paths.ancillary, config)
Expand Down
48 changes: 0 additions & 48 deletions src/datasets/xmm-newton.jl

This file was deleted.

0 comments on commit f5c48b0

Please sign in to comment.