|
| 1 | +verbose = true |
| 2 | + using Dates |
| 3 | + if verbose println(now()) end |
| 4 | + if verbose && !isdefined(Main,:RvSpectML) println("# Loading RvSpecML") end |
| 5 | + using RvSpectMLBase |
| 6 | + using EchelleCCFs |
| 7 | + using EchelleInstruments#, EchelleInstruments.NEID |
| 8 | + using RvSpectML |
| 9 | + if verbose println("# Loading NeidSolarScripts") end |
| 10 | + using SunAsAStar |
| 11 | + using NeidSolarScripts |
| 12 | + println("# Loading other packages 1/2") |
| 13 | + using ArgParse |
| 14 | + using Markdown |
| 15 | + |
| 16 | +println("# Parsing arguments...") |
| 17 | + #lsf_width = 3.0e3 |
| 18 | + function parse_commandline() |
| 19 | + s = ArgParseSettings( description = "Calculate RVs from JLD2 file with NEID's daily order CCFs.") |
| 20 | + #import_settings!(s, s_files_only, args_only=false) |
| 21 | + add_arg_group!(s, "Files", :argg_files) |
| 22 | + @add_arg_table! s begin |
| 23 | + "input" |
| 24 | + help = "Filename for input with daily order CCFs (jld2)" |
| 25 | + arg_type = String |
| 26 | + default = "daily_ccfs.jld2" |
| 27 | + "output" |
| 28 | + help = "Filename for output dailiy RVs (csv)" |
| 29 | + arg_type = String |
| 30 | + default = "daily_rvs.csv" |
| 31 | + "--overwrite" |
| 32 | + help = "Specify it's ok to overwrite the output file." |
| 33 | + #default = true |
| 34 | + action = :store_true |
| 35 | + end |
| 36 | + #= |
| 37 | + add_arg_group!(s, "CCF parameters", :argg_ccf_param) |
| 38 | + @add_arg_table! s begin |
| 39 | + "--orders_to_use" |
| 40 | + help = "First and last order _index_ to compute CCFs for" |
| 41 | + nargs = 2 |
| 42 | + arg_type = Int64 |
| 43 | + #default = [ min_order(NEID2D()), max_order(NEID2D()) ] |
| 44 | + #default = [ first(orders_to_use_default(NEID2D())), last(orders_to_use_default(NEID2D())) ] |
| 45 | + "--mask_scale_factor" |
| 46 | + help = "Specify CCF mask width scale as multiple of NEID default v width " * string(default_ccf_mask_v_width(NEID2D())) |
| 47 | + arg_type = Float64 |
| 48 | + #default = round(lsf_width/default_ccf_mask_v_width(NEID2D()), sigdigits=3) |
| 49 | + "--line_width_50_default" |
| 50 | + help = "Specify default line full width half maximum" |
| 51 | + arg_type = Float64 |
| 52 | + #default = 7.9e3 # m/2 |
| 53 | + end |
| 54 | + =# |
| 55 | + add_arg_group!(s, "Algorithm more measuring RVs ", :argg_alg_param) |
| 56 | + @add_arg_table! s begin |
| 57 | + "--rv_alg" |
| 58 | + help = "Algorithm for measuring RVs." |
| 59 | + arg_type = String |
| 60 | + default = "template" |
| 61 | + #default = "gaussian" |
| 62 | + end |
| 63 | + add_arg_group!(s, "Filter manifest for ", :argg_filter_param) |
| 64 | + @add_arg_table! s begin |
| 65 | + "--target" |
| 66 | + help = "Target field" |
| 67 | + arg_type = String |
| 68 | + default = "Sun" |
| 69 | + "--datestr" |
| 70 | + help = "Filenames that contain date string." |
| 71 | + arg_type = String |
| 72 | + "--max_solar_hour_angle" |
| 73 | + help = "Maximum absolute value of solar hour angle." |
| 74 | + arg_type = Float64 |
| 75 | + default = 3.12 |
| 76 | + "--max_airmass" |
| 77 | + help = "Maximum airmass." |
| 78 | + arg_type = Float64 |
| 79 | + default = 2.0 |
| 80 | + "--min_expmeter" |
| 81 | + help = "Minimum exposure meter flux relative to model flux." |
| 82 | + arg_type = Float64 |
| 83 | + default = 6e4 |
| 84 | +#= |
| 85 | + "--min_expmeter_factor" |
| 86 | + help = "Minimum exposure meter flux relative to model flux." |
| 87 | + arg_type = Float64 |
| 88 | + default = 0.9 |
| 89 | + "--max_expmeter_factor" |
| 90 | + help = "Maximum exposure meter flux relative to model flux." |
| 91 | + arg_type = Float64 |
| 92 | + default = 1.1 |
| 93 | +=# |
| 94 | + "--max_expmeter_frac_rms" |
| 95 | + help = "Maximum fractional RMS of exposure meter flux." |
| 96 | + arg_type = Float64 |
| 97 | + default = 0.01 |
| 98 | +#= |
| 99 | + "--min_pyrhelio_factor" |
| 100 | + help = "Minimum pyrheliometer flux relative to model flux." |
| 101 | + arg_type = Float64 |
| 102 | + default = 0.9 |
| 103 | + "--max_pyrhelio_factor" |
| 104 | + help = "Maximum pyrheliometer flux relative to model flux." |
| 105 | + arg_type = Float64 |
| 106 | + default = 1.1 |
| 107 | +=# |
| 108 | + "--max_pyrhelio_frac_rms" |
| 109 | + help = "Maximum fractional RMS of pyrheliometer meter flux." |
| 110 | + arg_type = Float64 |
| 111 | + default = 0.0035 |
| 112 | + #= |
| 113 | + "--min_drp_snr" |
| 114 | + help = "Minimum extracted SNR reported by NEID DRP." |
| 115 | + arg_type = Float64 |
| 116 | + #default = 0.0 |
| 117 | + =# |
| 118 | + #= |
| 119 | + "--start_time" |
| 120 | + help = "Specify daily start time for CCFs to be processed (HH MM)" |
| 121 | + nargs = 2 |
| 122 | + arg_type = Int64 |
| 123 | + default = [0, 0] |
| 124 | + #default = [18, 30] |
| 125 | + "--stop_time" |
| 126 | + help = "Specify daily stop time for CCFs to be processed (HH MM)" |
| 127 | + nargs = 2 |
| 128 | + arg_type = Int64 |
| 129 | + #default = [ min_order(NEID2D()), max_order(NEID2D()) ] |
| 130 | + #default = [22, 30] |
| 131 | + default = [23, 59] |
| 132 | + =# |
| 133 | + "--max_num_spectra" |
| 134 | + help = "Maximum number of spectra to process." |
| 135 | + arg_type = Int |
| 136 | + default = 300 # TODO: Increase after finish testing |
| 137 | + end |
| 138 | + |
| 139 | + return parse_args(s) |
| 140 | + end |
| 141 | + args = parse_commandline() |
| 142 | + @assert in(args["rv_alg"], ["template","gaussian","quadratic"] ) |
| 143 | + |
| 144 | +if verbose println("# Loading other packages 2/2") end |
| 145 | + using CSV, DataFrames, Query, Dates |
| 146 | + using JLD2, FileIO, MD5 #, SHA |
| 147 | + using StatsBase, Statistics, NaNMath |
| 148 | + |
| 149 | + # Filename arguments |
| 150 | + @assert isfile(args["input"]) || islink(args["input"]) |
| 151 | + @assert match(r"\.jld2$",args["input"]) != nothing |
| 152 | + daily_ccf_filename = args["input"] |
| 153 | + |
| 154 | + args["overwrite"] = true # TODO: Comment after done testing |
| 155 | + if isfile(args["output"]) && !args["overwrite"] |
| 156 | + error("# Output file " * args["output"] * " already exists (size = " * string(filesize(args["output"])) * " ).") |
| 157 | + end |
| 158 | + @assert !isfile(args["output"]) || args["overwrite"] |
| 159 | + @assert match(r"\.csv$",args["output"]) != nothing |
| 160 | + daily_rvs_filename = args["output"] |
| 161 | + touch(daily_rvs_filename) |
| 162 | + |
| 163 | +#= |
| 164 | + if isfile(args["report"]) && !args["overwrite"] |
| 165 | + error("# Report file " * args["report"] * " already exists (size = " * string(filesize(args["report"])) * " ).") |
| 166 | + end |
| 167 | + @assert !isfile(args["report"]) || args["overwrite"] == true |
| 168 | + @assert match(r"\.md$",args["report"]) != nothing |
| 169 | + daily_report_filename = args["report"] |
| 170 | + touch(daily_report_filename) |
| 171 | +=# |
| 172 | + |
| 173 | + file_hashes = Dict{String,String}() |
| 174 | + start_processing_time = now() |
| 175 | + |
| 176 | +if filesize(daily_ccf_filename) > 0 |
| 177 | + println("# Reading daily CCFs from ", daily_ccf_filename) |
| 178 | + input_data = load(daily_ccf_filename) |
| 179 | +else |
| 180 | + println("# Empy daily CCF file. Creating empty ", daily_rvs_filename) |
| 181 | + touch(daily_rvs_filename) |
| 182 | + exit(0) |
| 183 | +end |
| 184 | + |
| 185 | +println("# Filtering for usable observations...") |
| 186 | +manifest = input_data["manifest"] |
| 187 | + |
| 188 | +manifest_use = manifest |> |
| 189 | + @filter( args["target"] == nothing || _.target == args["target"] ) |> |
| 190 | + @filter( args["datestr"] == nothing || occursin(args["datestr"],_.target) ) |> |
| 191 | + @filter( _.driftfun == "dailymodel0" ) |> |
| 192 | + @filter( args["max_airmass"] == nothing || _.airmass <= args["max_airmass"] ) |> |
| 193 | + @filter( args["max_solar_hour_angle"] == nothing || abs(_.solar_hour_angle) <= args["max_solar_hour_angle"] ) |> |
| 194 | + #@filter( args["start_time"] == nothing || Time(julian2datetime(_.bjd)) >= start_time ) |> |
| 195 | + #@filter( args["stop_time"] == nothing || Time(julian2datetime(_.bjd)) <= stop_time ) |> # TODO for other instruments may need to deal wtih cross end of 24 UTC |
| 196 | + @filter( args["min_expmeter"] == nothing || _.expmeter_mean >= args["min_expmeter"] ) |> |
| 197 | + DataFrame |
| 198 | + |
| 199 | +if hasproperty(manifest_use,:mean_pyroflux) && hasproperty(manifest_use,:rms_pyroflux) |
| 200 | + manifest_use = manifest_use |> |
| 201 | + @filter( args["max_pyrhelio_frac_rms"] == nothing || _.rms_pyroflux <= args["max_pyrhelio_frac_rms"] * _.mean_pyroflux ) |> |
| 202 | + DataFrame |
| 203 | +else |
| 204 | + println("# Warning: mean_pyroflux and rms_pyroflux not found. Reverting to expmeter_mean and expmeter_rms.") |
| 205 | + manifest_use = manifest_use |> |
| 206 | + @filter( args["max_expmeter_frac_rms"] == nothing || _.expmeter_rms <= args["max_expmeter_frac_rms"] * _.expmeter_mean ) |> |
| 207 | + DataFrame |
| 208 | +end |
| 209 | + |
| 210 | +manifest_use = manifest_use |> |
| 211 | + @take(args["max_num_spectra"] ) |> @orderby(_.bjd) |> |
| 212 | + DataFrame |
| 213 | + |
| 214 | +#@assert size(manifest_use,1) >= 1 |
| 215 | +if !(size(manifest_use,1) >= 1) |
| 216 | + println("# No usable observations. Creating empty ", daily_rvs_filename) |
| 217 | + touch(daily_rvs_filename) |
| 218 | + exit(0) |
| 219 | +end |
| 220 | + |
| 221 | +println("# Found ", size(manifest_use,1), " files of ", size(manifest,1), " to use for RVs.") |
| 222 | + #df_out = manifest_use |> @rename(:drp_ccfjdmod=>:jd_drp,:drp_ccfrvmod => :rv_drp,:drp_dvrmsmod => :σrv_drp, |
| 223 | + df_out = manifest |> @rename(:drp_ccfjdmod=>:jd_drp,:drp_ccfrvmod => :rv_drp,:drp_dvrmsmod => :σrv_drp, |
| 224 | + :mean_pyroflux=>:pyrflux_mean, :rms_pyroflux=>:pyrflux_rms ) |> DataFrame |
| 225 | + obs_to_use = map(i->searchsortedfirst(manifest.bjd,manifest_use.bjd[i]),1:size(manifest_use,1) ) |
| 226 | + |
| 227 | +(num_vels, num_orders_to_use, num_obs) = size(input_data["order_ccfs"]) |
| 228 | + |
| 229 | +println("# Measuring RVs from RvSpectML's CCFs.") |
| 230 | +v_grid = collect(input_data["v_grid"]) |
| 231 | +@assert num_vels == length(v_grid) |
| 232 | +order_ccfs = input_data["order_ccfs"] |
| 233 | +non_nan_mask = map(i->!any(isnan.(view(order_ccfs,:,:,i))), obs_to_use) |
| 234 | +obs_to_use = obs_to_use[non_nan_mask] |
| 235 | + mask = falses(1:num_obs) |
| 236 | + mask[obs_to_use] .= true |
| 237 | + df_out[!,"mask"] = mask |
| 238 | + |
| 239 | +order_ccf_vars = input_data["order_ccf_vars"] |
| 240 | +ccf_sum = reshape(sum(order_ccfs,dims=2),num_vels,num_obs) |
| 241 | +ccf_var_sum = reshape(sum(input_data["order_ccf_vars"],dims=2),num_vels,num_obs) |
| 242 | +#rvs = measure_rv_from_ccf(v_grid,view(ccf_sum,:,1),view(ccf_var_sum,:,1)) |
| 243 | +if args["rv_alg"] == "template" |
| 244 | + ccf_template = EchelleCCFs.calc_ccf_template(view(ccf_sum,:,obs_to_use), view(ccf_var_sum,:,obs_to_use) ) |
| 245 | + alg_template = MeasureRvFromCCFTemplate(v_grid=collect(v_grid),template=ccf_template) |
| 246 | + rvs_template = DataFrame(map(i->measure_rv_from_ccf(v_grid,view(ccf_sum,:,i),view(ccf_var_sum,:,i),alg=alg_template),1:num_obs)) |
| 247 | + df_out[!,Symbol("rv_template")] = rvs_template.rv |
| 248 | + df_out[!,Symbol("σrv_template")] = rvs_template.σ_rv |
| 249 | +elseif args["rv_alg"] == "gaussian" |
| 250 | + alg_gauss = MeasureRvFromCCFGaussian(frac_of_width_to_fit=0.25) |
| 251 | + rvs_gauss = DataFrame(map(i->measure_rv_from_ccf(v_grid,view(ccf_sum,:,i),view(ccf_var_sum,:,i),alg=alg_gauss),1:num_obs)) |
| 252 | + df_out[!,Symbol("rv_gaussian")] = rvs_gauss.rv |
| 253 | + df_out[!,Symbol("σrv_gaussian")] = rvs_gauss.σ_rv |
| 254 | +elseif args["rv_alg"] == "quadratic" |
| 255 | + alg_quad = MeasureRvFromCCFQuadratic(frac_of_width_to_fit=0.25) |
| 256 | + rvs_quad = DataFrame(map(i->measure_rv_from_ccf(v_grid,view(ccf_sum,:,i),view(ccf_var_sum,:,i),alg=alg_quad),1:num_obs)) |
| 257 | + df_out[!,Symbol("rv_quadratic")] = rvs_quad.rv |
| 258 | + df_out[!,Symbol("σrv_quadratic")] = rvs_quad.σ_rv |
| 259 | +end |
| 260 | + |
| 261 | +orders_physical = collect(174 .-input_data["orders_to_use"]) |
| 262 | +for j in 1:num_orders_to_use |
| 263 | + if iszero(maximum(view(order_ccfs,:,j,:))) || any(isnan.(view(order_ccfs,:,j,:))) continue end |
| 264 | + #println(" j = ", j, " order_fits_idx = ", input_data["orders_to_use"][j], " order_physical = ", orders_physical[j]) |
| 265 | + if args["rv_alg"] == "template" |
| 266 | + # Templated-based |
| 267 | + local ccf_template = EchelleCCFs.calc_ccf_template(view(order_ccfs,:,j,obs_to_use), view(order_ccf_vars,:,j,obs_to_use) ) |
| 268 | + local alg_template = MeasureRvFromCCFTemplate(v_grid=collect(v_grid),template=ccf_template) |
| 269 | + order_rvs_template = DataFrame(map(i->measure_rv_from_ccf(v_grid,view(order_ccfs,:,j,i),view(order_ccf_vars,:,j,i),alg=alg_template),1:num_obs)) |
| 270 | + df_out[!,Symbol("rv_" * string(orders_physical[j]) * "_template")] = order_rvs_template.rv |
| 271 | + df_out[!,Symbol("σrv_" * string(orders_physical[j]) * "_template")] = order_rvs_template.σ_rv |
| 272 | + elseif args["rv_alg"] == "gaussian" |
| 273 | + # Gaussian fitting |
| 274 | + order_rvs_gauss = DataFrame(map(i->measure_rv_from_ccf(v_grid,view(order_ccfs,:,j,i),view(order_ccf_vars,:,j,i),alg=alg_gauss),1:num_obs)) |
| 275 | + df_out[!,Symbol("rv_" * string(orders_physical[j]) * "_gauss")] = order_rvs_gauss.rv |
| 276 | + df_out[!,Symbol("σrv_" * string(orders_physical[j]) * "_gauss")] = order_rvs_gauss.σ_rv |
| 277 | + elseif args["rv_alg"] == "quadratic" |
| 278 | + # Quadratic fitting |
| 279 | + order_rvs_quad = DataFrame(map(i->measure_rv_from_ccf(v_grid,view(order_ccfs,:,j,i),view(order_ccf_vars,:,j,i),alg=alg_quad),1:num_obs)) |
| 280 | + df_out[!,Symbol("rv_" * string(orders_physical[j]) * "_quad")] = order_rvs_quad.rv |
| 281 | + df_out[!,Symbol("σrv_" * string(orders_physical[j]) * "_quad")] = order_rvs_quad.σ_rv |
| 282 | + end |
| 283 | +end |
| 284 | + |
| 285 | +println("# Writing daily RVs.") |
| 286 | + CSV.write(daily_rvs_filename,df_out) |
| 287 | + |
0 commit comments