Skip to content

Commit 80f1117

Browse files
authored
Merge pull request #50 from VERITAS-Observatory/2025-barcelona
Plotting improvements.
2 parents 00c51e3 + 3f1a9e7 commit 80f1117

File tree

7 files changed

+247
-68
lines changed

7 files changed

+247
-68
lines changed

eventdisplay_anasum/light_curve_analysis.C

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,16 +102,12 @@ void light_curve_analysis(
102102
string iAnaSumFile = "anasum/anasum.combined.root",
103103
string iMJDIntervalFile = "HESSJ0632p057.MJD.v3.txt",
104104
double iEnergyThreshold = 0.35,
105+
double iSpectralIndex = -2.5,
105106
double iSig_UL = -5., double iSig_minEvents = -10.)
106107
{
107-
// energy threshold in TeV
108-
double i_fixed_Emin = iEnergyThreshold;
109-
// assumed spectral index:
110-
double i_fixed_Index = -2.5;
111-
112108
cout << "# Light curve generation ";
113-
cout << "E_min > " << i_fixed_Emin << " TeV; ";
114-
cout << "Spectral index " << i_fixed_Index << endl;
109+
cout << "E_min > " << iEnergyThreshold << " TeV; ";
110+
cout << "Spectral index " << iSpectralIndex << endl;
115111

116112
vector<pair<double, double>> min_max;
117113
size_t n_intervals = 0;
@@ -138,8 +134,8 @@ void light_curve_analysis(
138134
min_max[i].second
139135
);
140136
a.setSignificanceParameters( iSig_UL, iSig_minEvents, 0.95 );
141-
a.setSpectralParameters( i_fixed_Emin, 1., i_fixed_Index );
142-
a.calculateIntegralFlux( i_fixed_Emin );
137+
a.setSpectralParameters( iEnergyThreshold, 1., iSpectralIndex);
138+
a.calculateIntegralFlux( iEnergyThreshold );
143139
a.printECSVLine();
144140
}
145141
}

eventdisplay_anasum/light_curve_analysis.sh

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
# Light-curve analysis using Eventdisplay container and podman
33
#
44

5-
if [ "$#" -ne 2 ]; then
6-
echo "Usage: $0 <anasum_file> <time_interval_file>"
5+
if [ "$#" -ne 4 ]; then
6+
echo "Usage: $0 <anasum_file> <time_interval_file> <energy threshold> <spectral index>"
77
echo " (use 'RUNWISE' for time interval file for run-wise analysis)"
88
echo
99
exit 1
@@ -22,9 +22,8 @@ light_curves()
2222
}
2323

2424
echo "Running light-curve analysis..."
25-
ETRESH="0.3"
2625
MINSIG="-5."
2726
MINEVT="-10."
28-
MINSIG="2."
29-
MINEVT="2."
30-
light_curves "$1" "$2" "$ETRESH" "$MINSIG" "$MINEVT"
27+
# MINSIG="2."
28+
# MINEVT="2."
29+
light_curves "$1" "$2" "$3" "$4" "$MINSIG" "$MINEVT"

v2dl5/binaries.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def binary_properties():
2121
"name": "HESS J0632+057",
2222
"orbital_period": 317.3,
2323
"mjd_0": 54857.0, # Bongiorno et al 2011
24+
"mjd_orbit_count": 52953.0, # Adams et al 2021
2425
},
2526
"LS I +61 303": {
2627
"name": "LS I +61 303",

v2dl5/light_curves/binary_plotting.py

Lines changed: 164 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,9 @@ def _get_number_columns_and_rows(self, number):
158158
n_columns = 4
159159
fig_size = (10, 8)
160160
if number > 16:
161+
n_columns = 5
162+
fig_size = (16, 10)
163+
if number > 21:
161164
n_columns = 8
162165
fig_size = (16, 10)
163166
return n_columns, math.ceil(number / n_columns), fig_size
@@ -236,7 +239,7 @@ def plot_flux_vs_orbit_number(
236239

237240
for i in range(phase_bins):
238241
axes = plt.subplot(n_rows, n_columns, i + 1)
239-
axes.set_xlim([min(orbits), max(orbits)])
242+
axes.set_xlim([min(orbits)-1., max(orbits)+1.])
240243
axes.set_ylim([y_min, y_max])
241244

242245
phase_min = i * (1.0 / phase_bins)
@@ -304,11 +307,11 @@ def _get_light_curve_in_mjd_limits(
304307
data,
305308
y_key,
306309
time_axis,
307-
mjd_min,
308-
mjd_max,
309-
orbit_number,
310-
phase_min,
311-
phase_max,
310+
mjd_min=None,
311+
mjd_max=None,
312+
orbit_number=None,
313+
phase_min=None,
314+
phase_max=None,
312315
min_significance=None,
313316
):
314317
"""
@@ -341,6 +344,9 @@ def _get_light_curve_in_mjd_limits(
341344
Light-curve data as lists.
342345
"""
343346
x, y, e, x_ul, y_ul = [], [], [], [], []
347+
if y_key not in data:
348+
self._logger.warning(f"Y-axis {y_key} not found in data")
349+
return x, y, e, x_ul, y_ul
344350
mjd = data["MJD"]
345351
for i, t in enumerate(mjd):
346352
if (
@@ -401,6 +407,158 @@ def get_marker_and_color(self, idx):
401407
)
402408
return color, marker
403409

410+
def plot_live_time_vs_phase_bin(
411+
self,
412+
instrument,
413+
phase_bins=10,
414+
file_type=".pdf",
415+
figure_dir="./figures/",
416+
):
417+
"""Plot live time histogram vs phase bin."""
418+
self._logger.info("Plotting live time histogram vs phase bin")
419+
420+
ax = plotting_utilities.paper_figures(None, None)
421+
ax.set_xlim([0, 1])
422+
423+
for idx, (instrument, data) in enumerate(self.data.items()):
424+
if self.config[idx].get("plot_live_time_histogram", False) is False:
425+
continue
426+
color, _ = self.get_marker_and_color(idx)
427+
428+
x, y, _, _, _ = self._get_light_curve_in_mjd_limits(
429+
data, "live_time", "orbital phase",
430+
)
431+
if len(x) == 0:
432+
continue
433+
x = np.array(x)
434+
y = np.array(y)
435+
bin_edges = np.linspace(0, 1, phase_bins + 1)
436+
bin_width = bin_edges[1] - bin_edges[0]
437+
bin_heights, _ = np.histogram(x, bins=bin_edges, weights=y)
438+
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
439+
plt.bar(
440+
bin_centers,
441+
bin_heights,
442+
width=bin_width*0.9,
443+
label=(
444+
instrument
445+
if self.config[idx].get("plot_label") is None
446+
else self.config[idx]["plot_label"]
447+
),
448+
color=color,
449+
alpha=0.7,
450+
)
451+
452+
plt.xlabel("Phase")
453+
plt.ylabel("Live Time (h)")
454+
455+
plt.legend()
456+
plotting_utilities.print_figure(
457+
f"Light-Curve-{self.binary['name']}-Live-Time-vs-Phase-Bin",
458+
file_type=file_type,
459+
figure_dir=figure_dir,
460+
)
461+
462+
def plot_index_vs_flux(
463+
self,
464+
instrument,
465+
file_type=".pdf",
466+
figure_dir="./figures/",
467+
):
468+
"""Plot spectral index vs flux."""
469+
self._logger.info("Plotting spectral index vs flux")
470+
471+
_ = plotting_utilities.paper_figures(None, None)
472+
473+
for idx, (instrument, data) in enumerate(self.data.items()):
474+
if self.config[idx].get("plot_flux_vs_index", False) is False:
475+
continue
476+
color, _ = self.get_marker_and_color(idx)
477+
478+
_, x, x_e, _, _ = self._get_light_curve_in_mjd_limits(
479+
data, "flux", "orbital phase",
480+
)
481+
_, y, y_e, _, _ = self._get_light_curve_in_mjd_limits(
482+
data, "index", "orbital phase",
483+
)
484+
if len(x) == 0 or len(y) == 0:
485+
continue
486+
x = np.array(x)
487+
y = np.array(y)
488+
plt.errorbar(
489+
x,
490+
y,
491+
xerr=x_e,
492+
yerr=y_e,
493+
label=(
494+
instrument
495+
if self.config[idx].get("plot_label") is None
496+
else self.config[idx]["plot_label"]
497+
),
498+
color=color,
499+
linestyle="none",
500+
marker='o',
501+
alpha=0.7,
502+
)
503+
504+
plt.xlabel(self.config[0].get("flux_axis_label", ""))
505+
plt.ylabel(self.config[0].get("index_axis_label", ""))
506+
507+
plt.legend()
508+
plotting_utilities.print_figure(
509+
f"Light-Curve-{self.binary['name']}-Flux-vs-Index",
510+
file_type=file_type,
511+
figure_dir=figure_dir,
512+
)
513+
514+
def plot_distribution(
515+
self,
516+
instrument,
517+
y_axis="flux",
518+
file_type=".pdf",
519+
figure_dir="./figures/",
520+
):
521+
"""Plot distribution of y-axis data."""
522+
self._logger.info(f"Plotting {y_axis} distribution")
523+
524+
_ = plotting_utilities.paper_figures(None, None)
525+
526+
for idx, (instrument, data) in enumerate(self.data.items()):
527+
if self.config[idx].get("plot_1d_distribution", False) is False:
528+
continue
529+
color, _ = self.get_marker_and_color(idx)
530+
531+
_, y, _, _, _ = self._get_light_curve_in_mjd_limits(
532+
data, y_axis, "orbital phase",
533+
)
534+
if len(y) == 0:
535+
continue
536+
y = np.array(y)
537+
plt.hist(
538+
y,
539+
bins=25,
540+
label=(
541+
instrument
542+
if self.config[idx].get("plot_label") is None
543+
else self.config[idx]["plot_label"]
544+
),
545+
color=color,
546+
alpha=0.7,
547+
)
548+
549+
plt.xlabel(self.config[0].get(y_axis + "_axis_label", ""))
550+
plt.ylabel("Counts")
551+
552+
plt.legend()
553+
plotting_utilities.print_figure(
554+
f"Light-Curve-{self.binary['name']}-{y_axis}-Distribution",
555+
file_type=file_type,
556+
figure_dir=figure_dir,
557+
)
558+
559+
560+
561+
404562

405563
# Temporary stuff - probably not needed
406564
#

v2dl5/light_curves/data_reader.py

Lines changed: 49 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,11 @@ def _read_fluxes_from_file(self, data_config):
7070

7171
if data_config["file_name"].endswith((".csv", ".ecsv")):
7272
self._logger.info("Reading data from %s", data_config["file_name"])
73-
return self._read_fluxes_from_ecsv_file(data_config)
73+
return self._read_fluxes_from_ecsv_file(
74+
file_name=data_config["file_name"],
75+
mjd_min=data_config.get("mjd_min", -1.0),
76+
mjd_max=data_config.get("mjd_max", -1.0),
77+
)
7478

7579
return None
7680

@@ -114,7 +118,11 @@ def _add_orbital_parameters(self, data):
114118
data["phase_err"] = [data["phase_err_low"], data["phase_err_hig"]]
115119

116120
data["orbit_number"] = [
117-
orbit.get_orbit_number(mjd=mjd, orbital_period=orbital_period, mjd_0=mjd_0)
121+
orbit.get_orbit_number(
122+
mjd=mjd,
123+
orbital_period=orbital_period,
124+
mjd_orbit_count=float(self.binary.get("mjd_orbit_count", self.binary.get("mjd_0")))
125+
)
118126
for mjd in data["MJD"]
119127
]
120128

@@ -126,15 +134,15 @@ def convert_photon_to_energy_flux(self, c_e, e_0, gamma):
126134
return [v * f for v in self], [e * f for e in c_e]
127135

128136
def _read_fluxes_from_ecsv_file(
129-
self, data_config, time_min_max=True, mjd_min=-1.0, mjd_max=-1.0
137+
self, file_name, time_min_max=True, mjd_min=-1.0, mjd_max=-1.0
130138
):
131139
"""
132140
Read gamma-ray fluxes from ecsv file (open gamma-ray format).
133141
134142
Parameters
135143
----------
136-
data_config: dict
137-
configuration dictionary
144+
file_name: str, Path
145+
Name of the file to read.
138146
time_min_max: bool
139147
flag to read time_min and time_max
140148
mjd_min: float
@@ -143,43 +151,41 @@ def _read_fluxes_from_ecsv_file(
143151
MJD max value for MJD cut
144152
145153
"""
146-
table = Table.read(data_config["file_name"])
154+
table = Table.read(file_name)
147155
f = {}
148-
try:
149-
if not time_min_max:
150-
table["time_min"] = table["time"].data
151-
table["time_max"] = table["time"].data + 0.1
152-
153-
# MJD filter
154-
condition = np.ones(len(table), dtype=bool)
155-
if mjd_min > -1:
156-
condition &= table["time_min"] > mjd_min
157-
if mjd_max > -1:
158-
condition &= table["time_max"] < mjd_max
159-
table = table[condition]
160-
161-
f = {}
162-
f["time_min"] = table["time_min"].data.tolist()
163-
f["time_max"] = table["time_max"].data.tolist()
164-
f["flux"] = table["flux"].data.flatten().tolist()
165-
if "flux_err" in table.colnames:
166-
f["flux_err"] = table["flux_err"].data.flatten().tolist()
167-
else:
168-
up = table["flux_up"].data.flatten().tolist()
169-
down = table["flux_down"].data.flatten().tolist()
170-
f["flux_err"] = [0.5 * abs(u - d) for u, d in zip(up, down)]
171-
f["MJD"] = [0.5 * (a + b) for a, b in zip(f["time_min"], f["time_max"])]
172-
f["MJD_err"] = [0.5 * (b - a) for a, b in zip(f["time_min"], f["time_max"])]
173-
if "flux_ul" in table.colnames:
174-
flux_ul = table["flux_ul"].data.flatten().tolist()
175-
is_ul = table["is_ul"].data.flatten().tolist()
176-
f["flux_ul"] = [flux if is_ul else -1.0 for flux, is_ul in zip(flux_ul, is_ul)]
177-
else:
178-
f["flux_ul"] = [-1.0 if fe > 0 else fl for fe, fl in zip(f["flux_err"], f["flux"])]
179-
for column_name in ["significance", "index", "index_err", "live_time"]:
180-
if column_name in table.colnames:
181-
f[column_name] = table[column_name].data.flatten().tolist()
182-
except KeyError:
183-
self._logger.error(f"Incomplete data file; key not found in {data_config['file_name']}")
184-
raise KeyError
156+
157+
if not time_min_max:
158+
table["time_min"] = table["time"].data
159+
table["time_max"] = table["time"].data + 0.1
160+
161+
# MJD filter
162+
condition = np.ones(len(table), dtype=bool)
163+
if mjd_min > -1:
164+
condition &= table["time_min"] > mjd_min
165+
if mjd_max > -1:
166+
condition &= table["time_max"] < mjd_max
167+
table = table[condition]
168+
169+
f = {}
170+
f["time_min"] = table["time_min"].data.tolist()
171+
f["time_max"] = table["time_max"].data.tolist()
172+
f["flux"] = table["flux"].data.flatten().tolist()
173+
if "flux_err" in table.colnames:
174+
f["flux_err"] = table["flux_err"].data.flatten().tolist()
175+
else:
176+
up = table["flux_up"].data.flatten().tolist()
177+
down = table["flux_down"].data.flatten().tolist()
178+
f["flux_err"] = [0.5 * abs(u - d) for u, d in zip(up, down)]
179+
f["MJD"] = [0.5 * (a + b) for a, b in zip(f["time_min"], f["time_max"])]
180+
f["MJD_err"] = [0.5 * (b - a) for a, b in zip(f["time_min"], f["time_max"])]
181+
if "flux_ul" in table.colnames:
182+
flux_ul = table["flux_ul"].data.flatten().tolist()
183+
is_ul = table["is_ul"].data.flatten().tolist()
184+
f["flux_ul"] = [flux if is_ul else -1.0 for flux, is_ul in zip(flux_ul, is_ul)]
185+
else:
186+
f["flux_ul"] = [-1.0 if fe > 0 else fl for fe, fl in zip(f["flux_err"], f["flux"])]
187+
for column_name in ["significance", "index", "index_err", "live_time"]:
188+
if column_name in table.colnames:
189+
f[column_name] = table[column_name].data.flatten().tolist()
190+
185191
return f

0 commit comments

Comments
 (0)