Skip to content
funcs.jl 7.18 KiB
Newer Older

function read_epochs()
    out = DataFrame(filename=String[], date=String[], day=Int[],
                    telescope=String[], instr=String[],
                    scale=Float64[], scale_rel_unc=Float64[],
                    OIII_fwhm=Float64[], OIII_fwhm_unc=Float64[])
    for (root, dirs, files) in walkdir("AT2018zf")
        for file in files
            if file[end-3:end] == ".dat"
                filename = root * "/" * file
                date = filename[27:end-4]
                push!(out, (filename, date, 0, "", "", NaN, NaN, NaN, NaN))
            end
        end
    end
    @assert issorted(out.date)

    dd = Date.(out.date, Ref("yyyymmdd"))
    out[!, :day] .= getproperty.(dd - dd[1], :value)
    @assert issorted(out.day)

    (d, c) = csvread("tabula-2019-Trakhtenbrot-mod.csv", ',', header_exists=true)
    tmp = DataFrame(collect(d), Symbol.(c))
    tmp[!, :Date] .= string.(tmp.Date)

    for i in 1:nrow(out)
        j = findfirst(tmp.Date .== out[i, :date])
        if isnothing(j)
            @warn "Can't find epoch $(out[i, :date]) in tabula-2019-Trakhtenbrot"
            continue
        end
        out[i, :telescope] = tmp[j, :Telescope]
        out[i, :instr] = tmp[j, :Inst]
    end

    out.day .+= 72  # 2018-03-06 corresponds to day 72

    return out
end


# function read_spec(epoch::DataFrameRow; kw...)
#     irow = findfirst(epochs.date .== date)
#     @assert !isnothing(irow)
#     file = epochs[irow, :filename]
# 	spec = Spectrum(Val(:ASCII), file, columns=[1,2]; label="$date: $file", kw...);
#     spec.flux ./= epochs[irow, :scale]
#     spec.err  ./= epochs[irow, :scale]
#     return spec
# end

function read_spec(epoch::DataFrameRow; kw...)
    file = epoch.filename
	spec = Spectrum(Val(:ASCII), file, columns=[1,2]; label="$(epoch.date): $file", kw...);
    spec.flux ./= epoch.scale
    spec.err  ./= epoch.scale
    return spec
end


function boller03_analysis(opt)
    file = opt.path * "/boller.dat"
    if !isfile(file)
        source = QSO{q1927p654}("1ES 1927+654 (Boller+03)", opt.z, ebv=opt.ebv);
        #=
        Resolution: from  Sect 3 it is  6 / 5007 * 3e5 ~ 360 km/s
        however with this value the [OIII] FWHM hits the limit at 100 km/s
        I tried 180, 200 and 250, and the [OIII] luminosity do not change
        =#
	    spec = Spectrum(Val(:ASCII), "boller2003.txt", columns=[1,2]; label="Boller03", resolution=200.)
        source.options[:min_spectral_coverage][:OIII_5007] = 0.35
        add_spec!(source, spec);
        res = qsfit(source);
        viewer(res, showcomps=[:qso_cont, :galaxy, :balmer],
               filename="$(opt.path)/results_boller.html")
        JLD2.save_object(file, res)
    else
        res = JLD2.load_object(file)
    end
    @assert res.model[:OIII_5007].norm.val == 0.00029042977121756283
    @assert res.model[:OIII_5007].norm.val / res.model[:galaxy].norm.val == 60.07373299362222
    return res
end


function estimate_single_epoch_scale!(opt, epochs)
    file = opt.path * "/" * "SE_scale.dat"
    if !isfile(file)
        # Estimate first guess calibration factor
        for irow in 1:nrow(epochs)
            epochs[irow, :scale] = 1.
            spec = read_spec(epochs[irow, :])
            epochs[irow, :scale] = median(spec.flux)
        end
        epochs[:, :scale] .= median(epochs.scale) / 25_000
        @assert all(epochs[:, :scale] .== 5.7866e-20)

        for irow in 1:nrow(epochs)
            date = epochs[irow, :date]
            source = QSO{q1927p654}("1ES 1927+654 ($(date))", opt.z, ebv=opt.ebv);
            source.options[:min_spectral_coverage][:OIII_5007] = 0.35
            spec = read_spec(epochs[irow, :])
            add_spec!(source, spec);
            res = qsfit(source);
            viewer(res, showcomps=[:qso_cont, :galaxy, :balmer],
                   filename="$(opt.path)/results_$(date).html")

            # Update scale and OIII_fwhm
            epochs[irow, :scale] *= res.model[:OIII_5007].norm.val
            epochs[irow, :scale_rel_unc] = res.model[:OIII_5007].norm.unc / res.model[:OIII_5007].norm.val
            epochs[irow, :OIII_fwhm] = res.model[:OIII_5007].fwhm.val
            epochs[irow, :OIII_fwhm_unc] = res.model[:OIII_5007].fwhm.unc
        end
        @assert all(epochs[.!isfinite.(epochs.OIII_fwhm), :scale] .== NaN)
        JLD2.save_object(file, epochs)
    else
        empty!(epochs)
        append!(epochs, JLD2.load_object(file))
    end
end


function multi_epoch_analysis!(opt, _epochs, job; Nloop = 6)
    estimate_single_epoch_scale!(opt, _epochs)
    epochs = _epochs[opt.subsets[job], :]

    out = fill(NaN, nrow(epochs))
    for loop in 1:Nloop
        file = "$(opt.path)/results_$(job)_$(loop).dat"
        if !isfile(file)
            source = QSO{q1927p654}("1ES 1927+654", opt.z, ebv=opt.ebv);
            source.options[:min_spectral_coverage][:OIII_5007] = 0.5
            source.options[:wavelength_range] = [3500, 6900]
            @gp :zoom "set grid" :-
            for irow in 1:nrow(epochs)
                spec = read_spec(epochs[irow, :])
                add_spec!(source, spec);
                @gp :- :zoom xr=[4750,5150] spec.λ ./ (1 + source.z) spec.flux "w l t '$(epochs[irow, :date])'"
            end
            res = qsfit_multi(source);
            JLD2.save_object(file, res)
            viewer(res, showcomps=[:qso_cont, :galaxy, :balmer], filename="$(opt.path)/results_$(job)_$(loop).html")
            viewer(res, showcomps=[:qso_cont, :galaxy, :balmer], filename="$(opt.path)/results_$(job)_$(loop)_rebin4.html", rebin=4)
        else
            @info file
            res = JLD2.load_object(file);
            println(loop, "  ", res.fitres.gofstat / res.fitres.dof, "  ", res.fitres.elapsed / 3600.)
        end

        OIII_norm = Float64[]
        for i in 1:nrow(epochs)
            push!(OIII_norm, res.multi[i][:OIII_5007].norm.val)
            epochs[i, :scale] *= ((1 + res.multi[i][:OIII_5007].norm.val) / 2)
        end
        out = hcat(out, OIII_norm)

        f = open("$(opt.path)/scales_$(job)_$(loop).txt", "w")
        show(f, epochs)
        close(f)
    end
    out = out[:,2:end]

    @gp "set grid" "set key outside top horiz" xlab="Iteration" ylab="[OIII]λ5007 norm. (arb. units)" :-
    for i in 1:nrow(epochs)
        @gp :- 1:Nloop out[i, :] "w lp t '$(epochs[i, :date])'" :-
    end
    @gp :- xr=[1,6]
    save(term="pngcairo size 1000,700", output="$(opt.path)/calib_$(job).png")

    @gp :- yr=[0.96, 1.04]
    save(term="pngcairo size 1000,700", output="$(opt.path)/calib_$(job)_zoom.png")
    
    _epochs[opt.subsets[job], :scale] .= epochs.scale
    return out
end


function cont_at(bestfit, λ)
    A1 = bestfit[:qso_cont].norm.val - bestfit[:qso_cont].norm.unc
    A  = bestfit[:qso_cont].norm.val
    A2 = bestfit[:qso_cont].norm.val + bestfit[:qso_cont].norm.unc
    if A1 > A2
        A1, A2 = A2, A1
    end
    @assert A1 <= A2

    B1 = (λ / bestfit[:qso_cont].x0.val)^(bestfit[:qso_cont].alpha.val - bestfit[:qso_cont].alpha.unc)
    B  = (λ / bestfit[:qso_cont].x0.val)^(bestfit[:qso_cont].alpha.val)
    B2 = (λ / bestfit[:qso_cont].x0.val)^(bestfit[:qso_cont].alpha.val + bestfit[:qso_cont].alpha.unc)
    if B1 > B2
        B1, B2 = B2, B1
    end
    @assert B1 <= B2

    dd = extrema([A1*B1, A1*B2, A2*B1, A2*B2])

    return λ * A * B, λ * (dd[2]-dd[1])
end