-
Couldn't load subscription status.
- Fork 12
Implement EventList #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,150 @@ | ||||||
| import Base: read, write, join, sort, sort! | ||||||
|
|
||||||
| @with_kw mutable struct EventList{T1<:AbstractVector,T2<:AbstractVector,T3<:AbstractMatrix} | ||||||
| time::T1 | ||||||
| energy::T2=Float64[] | ||||||
| ncounts::Int=0 | ||||||
| mjdref::Float64=0 | ||||||
| dt::Float64=0 | ||||||
| notes::String="" | ||||||
| gti::T3=reshape(Float64[],0,2) | ||||||
| PI::Vector{Int}=Int[] | ||||||
| high_precision::Bool=false | ||||||
| mission::String="" | ||||||
| instr::String="" | ||||||
| header::String="" | ||||||
| detector_id::Vector{String}=String[] | ||||||
| ephem::String="" | ||||||
| timeref::String="" | ||||||
| timesys::String="" | ||||||
| end | ||||||
|
|
||||||
| function read(::Type{EventList},filename::String, format::String) | ||||||
| if format=="fits" | ||||||
| return load_events_from_fits(filename) | ||||||
| else | ||||||
| throw(ArgumentError("File format $(format) not supported still.")) | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| function write(ev::EventList, filename::String, format::String) | ||||||
| if format=="fits" | ||||||
| write_events_to_fits(filename, ev) | ||||||
| else | ||||||
| throw(ArgumentError("File format $(format) not supported still.")) | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| function from_lc(lc::LightCurve) | ||||||
| times = [lc.time[i] for i in 1:length(lc.time) for _ in 1:lc.counts[i]] | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's preferable to iterate over the actual indices of an array with
Suggested change
In this way you also avoid the assumption that arrays are 1-based indexed, which is not necessarily the case. |
||||||
| return EventList(time=times,gti=lc.gti) | ||||||
| end | ||||||
|
|
||||||
| function to_lc(ev::EventList, dt) | ||||||
| if isempty(ev.gti) | ||||||
| tstart=0 | ||||||
| tseg=0 | ||||||
|
Comment on lines
+45
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Line 10 in cdeaf8e
zero(eltype(ev.gti)).
|
||||||
| else | ||||||
| tstart = ev.gti[begin][begin] | ||||||
| tseg = ev.gti[end][end]-tstart | ||||||
|
Comment on lines
+48
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that I look closer to this, I'm a bit confused by the |
||||||
| end | ||||||
| return to_lc(ev, dt, tstart, tseg) | ||||||
| end | ||||||
|
|
||||||
| to_lc(ev::EventList, dt::Real, | ||||||
| tstart::Real, tseg::Real) = make_lightcurves(ev.time, dt, ev.gti; | ||||||
| tstart=tstart, tseg=tseg, | ||||||
| mjdref=ev.mjdref) | ||||||
|
|
||||||
| function join(ev1::EventList, ev2::EventList) | ||||||
| new_ev = EventList(time=vcat(ev1.time, ev2.time)) | ||||||
| if ev1.dt!=ev2.dt | ||||||
| new_ev.dt = max(ev1.dt,ev2.dt); | ||||||
| end | ||||||
|
|
||||||
| # Tolerance for MJDREF:1 microsecond | ||||||
| if !isapprox(ev1.mjdref, ev2.mjdref, atol=1e-6 / 86400) | ||||||
| ev2.mjdref = ev1.mjdref | ||||||
| end | ||||||
|
|
||||||
| order = sortperm(new_ev.time) | ||||||
| new_ev.time = new_ev.time[order] | ||||||
|
|
||||||
| if !isempty(ev1.PI) || !isempty(ev2.PI) | ||||||
| if isempty(ev1.PI) | ||||||
| ev1.PI = zero(ev1.time) | ||||||
| end | ||||||
| if isempty(ev2.PI) | ||||||
| ev2.PI = zero(ev2.time) | ||||||
| end | ||||||
| new_ev.PI = vcat(ev1.PI, ev2.PI) | ||||||
| new_ev.PI = new_ev.PI[order] | ||||||
| else | ||||||
| new_ev.PI = Float64[] | ||||||
| end | ||||||
|
|
||||||
| if !isempty(ev1.energy) || !isempty(ev2.energy) | ||||||
| if isempty(ev1.energy) | ||||||
| ev1.energy = zero(ev1.time) | ||||||
| end | ||||||
| if isempty(ev2.energy) | ||||||
| ev2.energy = zero(ev2.time) | ||||||
| end | ||||||
| new_ev.energy = vcat(ev1.energy, ev2.energy) | ||||||
| new_ev.energy = new_ev.energy[order] | ||||||
| else | ||||||
| new_ev.energy = Float64[] | ||||||
| end | ||||||
|
|
||||||
| if !isempty(ev1.gti) || !isempty(ev2.gti) | ||||||
| if isempty(ev1.gti) | ||||||
| ev1.gti = [ev1.time[begin] - ev1.dt/2 ev1.time[end] + ev1.dt/2] | ||||||
| end | ||||||
| if isempty(ev2.gti) | ||||||
| ev2.gti = [ev2.time[begin] - ev2.dt/2 ev2.time[end] + ev2.dt/2] | ||||||
| end | ||||||
| new_ev.gti = operations_on_gtis([ev1.gti,ev2.gti], intersect) | ||||||
| if isempty(new_ev.gti) | ||||||
| @warn """ | ||||||
| GTIs in these two event lists do not overlap at all. | ||||||
| Merging instead of returning an overlap. | ||||||
| """ | ||||||
| new_ev.gti = operations_on_gtis([ev1.gti,ev2.gti], union) | ||||||
| end | ||||||
| else | ||||||
| new_ev.gti = reshape(Float64[],0,2) | ||||||
| end | ||||||
|
|
||||||
| for attr in (:mission, :instr) | ||||||
| if getfield(ev1, attr) != getfield(ev2, attr) | ||||||
| setfield!(new_ev, attr, string(getfield(ev1, attr),',',getfield(ev2, attr))) | ||||||
| else | ||||||
| setfield!(new_ev, attr, getfield(ev1, attr)) | ||||||
| end | ||||||
| end | ||||||
|
|
||||||
| new_ev.mjdref = ev1.mjdref | ||||||
|
|
||||||
| return new_ev | ||||||
| end | ||||||
|
|
||||||
| function sort(ev::EventList) | ||||||
| order = sortperm(ev.time) | ||||||
| new_ev = deepcopy(ev) | ||||||
| apply_mask(new_ev, order) | ||||||
| return new_ev | ||||||
| end | ||||||
|
|
||||||
| function sort!(ev::EventList) | ||||||
| order = sortperm(ev.time) | ||||||
| apply_mask(ev, order) | ||||||
| end | ||||||
|
|
||||||
| function apply_mask(ev::EventList, mask::AbstractVector{T};) where {T<:Union{Bool,Int}} | ||||||
| for field in fieldnames(EventList) | ||||||
| fval = getfield(ev, field) | ||||||
| if fval isa AbstractVector && !isempty(fval) | ||||||
| setfield!(ev, field, deepcopy(fval)[mask]) | ||||||
| end | ||||||
| end | ||||||
| end | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| function load_events_from_fits(filename::String) | ||
| FITS(filename) do hduList | ||
| eventHDU = hduList["EVENTS"] | ||
| cols = FITSIO.colnames(eventHDU) | ||
| df = DataFrame(eventHDU) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why going through a dataframe? That looks unnecessary? For example, you can get the |
||
| if "TIME" in cols | ||
| time = df[!,"TIME"] | ||
| else | ||
| throw(KeyError("Time stamp data not provided or column names not appropriate in the file")) | ||
| end | ||
| ev = EventList(time=time) | ||
| if "PI" in cols | ||
| PI = df[!,"PI"] | ||
| ev.PI = PI | ||
| end | ||
| if "ENERGY" in cols | ||
| energy = df[!,"ENERGY"] | ||
| ev.energy = energy | ||
| end | ||
| ev.gti = load_gtis(filename) | ||
| header = FITSIO.read_header(eventHDU) | ||
| if haskey(header,"MJDREFI") | ||
| ev.mjdref += header["MJDREFI"] | ||
| end | ||
| if haskey(header,"MJDREFF") | ||
| ev.mjdref += header["MJDREFF"] | ||
| end | ||
| if haskey(header,"INSTRUME") | ||
| ev.instr = header["INSTRUME"] | ||
| end | ||
| if haskey(header,"TIMEREF") | ||
| ev.timeref = header["TIMEREF"] | ||
| end | ||
| if haskey(header,"TIMESYS") | ||
| ev.timesys = header["TIMESYS"] | ||
| end | ||
| if haskey(header,"PLEPHEM") | ||
| ev.ephem = header["PLEPHEM"] | ||
| end | ||
| ev | ||
| end | ||
| end | ||
|
|
||
| function write_events_to_fits(filename::String, ev::EventList) | ||
| FITS(filename,"w") do hduList | ||
|
|
||
| tkeys = String[] | ||
| values = [] | ||
giordano marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| for field in fieldnames(EventList) | ||
| fval = getfield(ev, field) | ||
| if !(fval isa AbstractVecOrMat) | ||
| push!(tkeys, String(field)) | ||
| push!(values, fval) | ||
| end | ||
| end | ||
|
|
||
| header = FITSHeader(tkeys, values,fill("",length(tkeys))) | ||
|
|
||
| eventData = Dict("TIME"=>ev.time, "ENERGY"=>ev.energy, "PI"=>ev.PI); | ||
| FITSIO.write(hduList, eventData, header = header, name = "EVENTS") | ||
|
|
||
| gtiData = Dict("START"=>ev.gti[:,1], "STOP"=>ev.gti[:,2]) | ||
| FITSIO.write(hduList, gtiData, name = "GTI") | ||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I presume the first argument is to indicate the output type, right? Most
readmethods have it as last argument, not first one