• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

icecube / flarestack / 8250807569

12 Mar 2024 02:42PM UTC coverage: 90.112% (+15.4%) from 74.702%
8250807569

Pull #355

github

web-flow
Merge ee343c671 into 0d8e01697
Pull Request #355: Transition to Coveralls github-action

4830 of 5360 relevant lines covered (90.11%)

2.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.56
/flarestack/shared.py
1
import os
3✔
2
import numpy as np
3✔
3
import socket
3✔
4
import pickle
3✔
5
import json
3✔
6
import zlib
3✔
7
import logging
3✔
8
from pathlib import Path
3✔
9
from typing import Optional
3✔
10

11
logger = logging.getLogger(__name__)
3✔
12

13
# ==============================================================================
14
# Directory substructure creation
15
# ==============================================================================
16

17
# fs_dir is the path of the
18

19
fs_dir = os.path.dirname(os.path.realpath(__file__)) + "/"
3✔
20

21
try:
3✔
22
    fs_scratch_dir = os.environ["FLARESTACK_SCRATCH_DIR"]
3✔
23
except KeyError:
3✔
24
    fs_scratch_dir = str(Path.home())
3✔
25
    logger.warning(
3✔
26
        "No scratch directory has been set. Using home directory as default."
3✔
27
    )
28

29
fs_scratch_dir = os.path.join(fs_scratch_dir, "flarestack__data/")
3✔
30

31
logger.info("Scratch Directory is: {0}".format(fs_scratch_dir))
3✔
32

33
input_dir = fs_scratch_dir + "input/"
3✔
34
storage_dir = fs_scratch_dir + "storage/"
3✔
35
output_dir = fs_scratch_dir + "output/"
3✔
36
cluster_dir = fs_scratch_dir + "cluster/"
3✔
37
log_dir = cluster_dir + "logs/"
3✔
38

39
cache_dir = storage_dir + "cache/"
3✔
40
cat_cache_dir = cache_dir + "catalogue_cache/"
3✔
41

42
public_dataset_dir = input_dir + "public_datasets/"
3✔
43
sim_dataset_dir = input_dir + "sim_datasets/"
3✔
44

45
catalogue_dir = input_dir + "catalogues/"
3✔
46
transients_dir = catalogue_dir + "transients/"
3✔
47
analysis_dir = input_dir + "analysis/"
3✔
48

49
pickle_dir = storage_dir + "pickles/"
3✔
50
inj_param_dir = pickle_dir + "injection_values/"
3✔
51

52
plots_dir = output_dir + "plots/"
3✔
53
unbliding_dir = output_dir + "unblinding_results/"
3✔
54
limits_dir = output_dir + "limits/"
3✔
55
dataset_plot_dir = output_dir + "dataset_plots/"
3✔
56
eff_a_plot_dir = dataset_plot_dir + "effective_area_plots/"
3✔
57
energy_proxy_plot_dir = dataset_plot_dir + "energy_proxy_map/"
3✔
58
ang_res_plot_dir = dataset_plot_dir + "angular_resolution_plots/"
3✔
59

60
illustration_dir = plots_dir + "illustrations/"
3✔
61

62
acc_f_dir = input_dir + "acceptance_functions/"
3✔
63
SoB_spline_dir = input_dir + "SoB_splines/"
3✔
64
energy_spline_dir = input_dir + "energy_pdf_splines/"
3✔
65
bkg_spline_dir = input_dir + "bkg_splines/"
3✔
66
energy_proxy_dir = input_dir + "energy_proxy_weighting/"
3✔
67
med_ang_res_dir = input_dir + "median_angular_resolution/"
3✔
68
pc_dir = input_dir + "pull_corrections/"
3✔
69
pull_dir = pc_dir + "pulls/"
3✔
70
floor_dir = pc_dir + "floors/"
3✔
71

72
all_dirs = [
3✔
73
    fs_scratch_dir,
3✔
74
    input_dir,
3✔
75
    storage_dir,
3✔
76
    output_dir,
3✔
77
    cluster_dir,
3✔
78
    pc_dir,
3✔
79
    log_dir,
3✔
80
    catalogue_dir,
3✔
81
    acc_f_dir,
3✔
82
    energy_spline_dir,
3✔
83
    pickle_dir,
3✔
84
    plots_dir,
3✔
85
    SoB_spline_dir,
3✔
86
    analysis_dir,
3✔
87
    illustration_dir,
3✔
88
    transients_dir,
3✔
89
    bkg_spline_dir,
3✔
90
    dataset_plot_dir,
3✔
91
    unbliding_dir,
3✔
92
    limits_dir,
3✔
93
    pull_dir,
3✔
94
    floor_dir,
3✔
95
    cache_dir,
3✔
96
    cat_cache_dir,
3✔
97
    public_dataset_dir,
3✔
98
    energy_proxy_dir,
3✔
99
    eff_a_plot_dir,
3✔
100
    med_ang_res_dir,
3✔
101
    ang_res_plot_dir,
3✔
102
    energy_proxy_plot_dir,
3✔
103
    sim_dataset_dir,
3✔
104
]
105

106
for dirname in all_dirs:
3✔
107
    if not os.path.isdir(dirname):
3✔
108
        logger.debug("Making Directory: {0}".format(dirname))
3✔
109
        os.makedirs(dirname)
3✔
110
    else:
×
111
        logger.debug("Found Directory: {0}".format(dirname))
×
112

113
# ==============================================================================
114
# Check host and specify path to dataset storage
115
# ==============================================================================
116

117
host = socket.getfqdn()
3✔
118

119
host_server: Optional[str] = None
3✔
120

121
if np.logical_or("ifh.de" in host, "zeuthen.desy.de" in host):
3✔
122
    host_server = "DESY"
×
123
elif "icecube.wisc.edu" in host:
3✔
124
    host_server = "WIPAC"
×
125

126
# gamma_range = [1., 4.]
127
# gamma_precision = .025
128
flarestack_gamma_precision = 0.025
3✔
129

130
default_gamma_precision = {"flarestack": flarestack_gamma_precision, "skylab": 0.1}
3✔
131

132
default_smoothing_order = {"flarestack": 2, "skylab": 1}
3✔
133

134

135
def deterministic_hash(hash_dict):
3✔
136
    """Generic function to convert a given dictionary into a 32bit hash
137
    value. This process is deterministic, so can be used as a general uid to
138
    save/load values.
139

140
    :param hash_dict: Dictionary contaiing relevant information
141
    :return: A 32bit number representing the data
142
    """
143
    return zlib.adler32((json.dumps(hash_dict, sort_keys=True)).encode())
3✔
144

145

146
# Sets the minimum angular error
147

148
min_angular_err = np.deg2rad(0.2)
3✔
149

150
# Sets an angular error floor based on the 25th quantile
151

152
base_floor_quantile = 0.25
3✔
153

154

155
def floor_pickle(floor_dict):
3✔
156
    hash_dict = dict(floor_dict)
3✔
157
    season = hash_dict["season"]
3✔
158
    hash_dict["season"] = season.sample_name + "/" + season.season_name
3✔
159
    try:
3✔
160
        del hash_dict["pull_name"]
3✔
161
    except KeyError:
3✔
162
        pass
3✔
163
    unique_key = deterministic_hash(hash_dict)
3✔
164
    return floor_dir + str(unique_key) + ".pkl"
3✔
165

166

167
def pull_pickle(pull_dict):
3✔
168
    hash_dict = dict(pull_dict)
3✔
169
    season = hash_dict["season"]
3✔
170
    hash_dict["season"] = season.sample_name + "/" + season.season_name
3✔
171
    unique_key = deterministic_hash(hash_dict)
3✔
172
    return pull_dir + str(unique_key) + ".pkl"
3✔
173

174

175
def llh_energy_hash_pickles(llh_dict, season):
3✔
176
    hash_dict = dict(llh_dict["llh_energy_pdf"])
3✔
177
    hash_dict["llh_name"] = llh_dict["llh_name"]
3✔
178

179
    precision = llh_dict.get("gamma_precision", "flarestack")
3✔
180
    smoothing_order = llh_dict.get("smoothing_order", "flarestack")
3✔
181

182
    key = deterministic_hash(hash_dict)
3✔
183
    season_path = (
3✔
184
        str(key)
3✔
185
        + "/"
3✔
186
        + season.sample_name
3✔
187
        + "/"
3✔
188
        + season.season_name
3✔
189
        + smoothing_precision_string(smoothing_order, precision)
3✔
190
        + ".pkl"
3✔
191
    )
192
    SoB_path = SoB_spline_dir + season_path
3✔
193
    acc_path = acc_f_dir + season_path
3✔
194
    return SoB_path, acc_path
3✔
195

196

197
band_mask_chunk_size = 100
3✔
198

199

200
def band_mask_hash_dir(catalogue):
3✔
201
    return cat_cache_dir + str(zlib.adler32(catalogue)) + "/"
×
202

203

204
def band_mask_cache_name(season, catalogue, injection_bandwidth):
3✔
205
    n_chunks = int((len(catalogue) + band_mask_chunk_size - 1) / band_mask_chunk_size)
×
206
    logger.info(
207
        "Breaking catalogue into {0} chunks of {1}".format(
208
            n_chunks, band_mask_chunk_size
209
        )
210
    )
211

212
    cats = []
×
213
    mask_indices = []
×
214
    source_indices = []
×
215

216
    for i in range(n_chunks):
×
217
        cat = catalogue[(i * band_mask_chunk_size) : ((i + 1) * band_mask_chunk_size)]
×
218
        cats.append(cat)
×
219
        mask_indices += [i for _ in range(band_mask_chunk_size)]
×
220
        source_indices += [x for x in range(band_mask_chunk_size)]
×
221

222
    paths = [
223
        band_mask_hash_dir(cat)
224
        + season.sample_name
225
        + f"_{injection_bandwidth:.8f}/"
226
        + season.season_name
227
        + ".npz"
228
        for cat in cats
229
    ]
230

231
    mask_indices = mask_indices[: len(catalogue)]
×
232
    source_indices = source_indices[: len(catalogue)]
×
233

234
    return cats, paths, mask_indices, source_indices
×
235

236

237
def name_pickle_output_dir(name):
3✔
238
    return os.path.join(pickle_dir, name)
3✔
239

240

241
def inj_dir_name(name):
3✔
242
    return os.path.join(inj_param_dir, name)
3✔
243

244

245
def plot_output_dir(name):
3✔
246
    return os.path.join(plots_dir, name)
3✔
247

248

249
def unblinding_output_path(name):
3✔
250
    path = os.path.join(unbliding_dir, name + "unblinding_results.pkl")
3✔
251
    return path
3✔
252

253

254
def limit_output_path(name):
3✔
255
    path = os.path.join(limits_dir, name + "limit.pkl")
3✔
256
    return path
3✔
257

258

259
def sim_dataset_dir_path(sample_name, season_name, bkg_flux_model):
3✔
260
    return (
261
        f"{sim_dataset_dir}{sample_name}/"
262
        f"{season_name}/{bkg_flux_model.unique_name()}/"
263
    )
264

265

266
def get_base_sob_plot_dir(season):
3✔
267
    return (
3✔
268
        dataset_plot_dir
3✔
269
        + "Signal_over_background/"
3✔
270
        + season.sample_name
3✔
271
        + "/"
3✔
272
        + season.season_name
3✔
273
        + "/"
3✔
274
    )
275

276

277
def smoothing_precision_string(smoothing_order="flarestack", gamma_precision="skylab"):
3✔
278
    if isinstance(smoothing_order, str):
3✔
279
        if smoothing_order in default_smoothing_order.keys():
3✔
280
            smoothing_order = default_smoothing_order[smoothing_order]
3✔
281
        else:
×
282
            raise ValueError(f"Smoothing order for {smoothing_order} not known!")
×
283

284
    if isinstance(gamma_precision, str):
3✔
285
        if gamma_precision in default_gamma_precision.keys():
3✔
286
            gamma_precision = default_gamma_precision[gamma_precision]
3✔
287
        else:
×
288
            raise ValueError(f"Smoothing order for {smoothing_order} not known!")
×
289

290
    logger.debug(
3✔
291
        f"smoothing order is {smoothing_order}, gamma precision is {gamma_precision}"
3✔
292
    )
293
    s = ""
3✔
294
    if smoothing_order != default_smoothing_order["flarestack"]:
3✔
295
        s += f"_smoothing{smoothing_order}"
×
296
    if gamma_precision != default_gamma_precision["flarestack"]:
3✔
297
        s += f"_precision{gamma_precision}"
3✔
298
    return s
3✔
299

300

301
def acceptance_path(season):
3✔
302
    return acc_f_dir + season.sample_name + "/" + season.season_name + ".pkl"
3✔
303

304

305
def SoB_spline_path(season, *args, **kwargs):
3✔
306
    return (
3✔
307
        SoB_spline_dir
3✔
308
        + season.sample_name
3✔
309
        + "/"
3✔
310
        + season.season_name
3✔
311
        + smoothing_precision_string(*args, **kwargs)
3✔
312
        + ".pkl"
3✔
313
    )
314

315

316
def bkg_spline_path(season):
3✔
317
    return bkg_spline_dir + season.sample_name + "/" + season.season_name + ".pkl"
3✔
318

319

320
def energy_proxy_path(season):
3✔
321
    return energy_proxy_dir + season.sample_name + "/" + season.season_name + ".pkl"
3✔
322

323

324
def med_ang_res_path(season):
3✔
325
    return med_ang_res_dir + season.sample_name + "/" + season.season_name + ".pkl"
3✔
326

327

328
def ang_res_plot_path(season):
3✔
329
    return ang_res_plot_dir + season.sample_name + "/" + season.season_name + ".pdf"
3✔
330

331

332
def energy_proxy_plot_path(season):
3✔
333
    return (
3✔
334
        energy_proxy_plot_dir + season.sample_name + "/" + season.season_name + ".pdf"
3✔
335
    )
336

337

338
def effective_area_plot_path(season):
3✔
339
    return eff_a_plot_dir + season.sample_name + "/" + season.season_name + ".pdf"
×
340

341

342
k_flux_factor = 10**-9
3✔
343

344

345
def k_to_flux(k):
3✔
346
    """k is a flux scale, with k=1 equal to (10)^-9 x (Gev)^-1 (s)^-1 (cm)^-2.
347
    The k values can be converted into values for the flux.
348

349
    :param k: Flux scale
350
    :return: Flux value
351
    """
352
    return k * k_flux_factor
3✔
353

354

355
def flux_to_k(flux):
3✔
356
    """k is a flux scale, with k=1 equal to (10)^-9 x (Gev)^-1 (s)^-1 (cm)^-2.
357
    The flux values can be converted into values for k.
358

359
    :param flux: Flux value
360
    :return: Flux scale (k)
361
    """
362
    return flux / k_flux_factor
3✔
363

364

365
def scale_shortener(scale):
3✔
366
    """Function to trim number of significant figures for flux scales when
367
    required for dictionary keys or saving pickle files.
368

369
    :param scale: Flux Scale
370
    :return: Flux Scale to 4.s.f
371
    """
372
    return "{0:.4G}".format(float(scale))
3✔
373

374

375
def analysis_pickle_path(mh_dict=None, name=None):
3✔
376
    """Converts a Minimisation Handler dictionary to a corresponding analysis
377
    config pickle. This pickle can be used to run a Minimisation Handler.
378

379
    :param mh_dict: Minimisation Handler dictionary
380
    :param name: unique Minimisation Handler name
381
    :return: Path to analysis pickle
382
    """
383

384
    dict_name = "dict.pkl"
3✔
385

386
    if mh_dict is not None:
3✔
387
        try:
3✔
388
            if np.logical_and(name is not None, name != mh_dict["name"]):
3✔
389
                raise Exception(
390
                    f"Both name and mh_dict arguments provided. "
391
                    f"There is a conflict between: \n"
392
                    f"'name' parameter: {name} \n"
393
                    f"'mh_dict'-derived name: {mh_dict['name']} \n"
394
                    f"Please resolve this conflict, by specifying the correct name."
395
                )
396

397
            name = mh_dict["name"]
3✔
398
        except KeyError:
×
399
            raise Exception(
400
                "No field 'name' was specified in mh_dict object. "
401
                "Cannot save results without a unique directory"
402
                " name being specified."
403
            )
404

405
        if "fixed_scale" in mh_dict.keys():
3✔
406
            dict_name = f"fixed_scale_{mh_dict['fixed_scale']}_dict.pkl"
×
407

408
    analysis_path = os.path.join(analysis_dir, name)
3✔
409

410
    try:
3✔
411
        os.makedirs(analysis_path)
3✔
412
    except OSError:
3✔
413
        pass
3✔
414

415
    pkl_file = os.path.join(analysis_path, dict_name)
3✔
416
    return pkl_file
3✔
417

418

419
def make_analysis_pickle(mh_dict):
3✔
420
    """Takes a Minimisation Handler Dictionary, finds the corresponding
421
    analysis pickle path, and saves the dictionary to this path
422

423
    :param mh_dict: Minimisation Handler dictionary
424
    """
425

426
    for season in mh_dict["dataset"].values():
3✔
427
        season.clean_season_cache()
3✔
428

429
    pkl_file = analysis_pickle_path(mh_dict)
3✔
430

431
    with open(pkl_file, "wb") as f:
3✔
432
        pickle.dump(mh_dict, f)
3✔
433

434
    return pkl_file
3✔
435

436

437
def weighted_quantile(values, quantiles, weight):
3✔
438
    """Calculated quantiles accounting for weights.
439

440
    :param values: numpy.array with data
441
    :param quantiles: array-like with many quantiles needed
442
    :param weight: array-like of the same length as `array`
443
    :return: numpy.array with computed quantiles.
444
    """
445
    values = np.array(values)
×
446
    quantiles = np.array(quantiles)
×
447
    sample_weight = np.array(weight)
×
448

449
    sorter = np.argsort(values)
×
450
    values = values[sorter]
×
451
    sample_weight = sample_weight[sorter]
×
452

453
    weighted_quantiles = np.cumsum(sample_weight) - 0.5 * sample_weight
×
454
    weighted_quantiles /= np.sum(sample_weight)
×
455
    return np.interp(quantiles, weighted_quantiles, values)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc