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

icecube / flarestack / 4536597083

pending completion
4536597083

Pull #268

github

GitHub
Merge 0d13f2846 into e550ab59c
Pull Request #268: Compliance with black v23

26 of 26 new or added lines in 11 files covered. (100.0%)

4405 of 5755 relevant lines covered (76.54%)

2.29 hits per line

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

82.09
/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

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

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

16
# fs_dir is the path of the
17

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

116
host = socket.getfqdn()
3✔
117

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

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

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

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

133

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

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

144

145
# Sets the minimum angular error
146

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

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

151
base_floor_quantile = 0.25
3✔
152

153

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

165

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

173

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

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

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

195

196
band_mask_chunk_size = 100
3✔
197

198

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

202

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

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

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

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

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

233
    return cats, paths, mask_indices, source_indices
×
234

235

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

239

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

243

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

247

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

252

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

257

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

264

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

275

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

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

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

299

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

303

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

314

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

318

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

322

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

326

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

330

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

336

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

340

341
k_flux_factor = 10**-9
3✔
342

343

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

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

353

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

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

363

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

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

373

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

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

383
    dict_name = "dict.pkl"
3✔
384

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

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

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

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

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

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

417

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

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

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

428
    pkl_file = analysis_pickle_path(mh_dict)
3✔
429

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

433
    return pkl_file
3✔
434

435

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

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

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

452
    weighted_quantiles = np.cumsum(sample_weight) - 0.5 * sample_weight
×
453
    weighted_quantiles /= np.sum(sample_weight)
×
454
    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