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

victoriyaforsythe / PyIRTAM / 9420755657

07 Jun 2024 05:17PM UTC coverage: 48.745%. First build
9420755657

push

github

web-flow
Merge pull request #23 from victoriyaforsythe/rc_v0.0.4

Rc v0.0.4

11 of 14 new or added lines in 3 files covered. (78.57%)

233 of 478 relevant lines covered (48.74%)

3.41 hits per line

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

9.26
/PyIRTAM/lib.py
1
#!/usr/bin/env python
2
# --------------------------------------------------------
3
# Distribution statement A. Approved for public release.
4
# Distribution is unlimited.
5
# This work was supported by the Office of Naval Research.
6
# --------------------------------------------------------
7
"""This library contains components for PyIRTAM software.
8

9
References
10
----------
11
Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
12
International Reference Ionosphere Modeling Implemented in Python,
13
Space Weather, ESS Open Archive, September 28, 2023.
14

15
Bilitza et al. (2022), The International Reference Ionosphere
16
model: A review and description of an ionospheric benchmark, Reviews
17
of Geophysics, 60.
18

19
"""
20

21
import datetime as dt
7✔
22
import numpy as np
7✔
23
import os
7✔
24

25
import PyIRI
7✔
26
import PyIRI.main_library as ml
7✔
27

28
from PyIRTAM import coeff
7✔
29

30

31
def IRTAM_density(dtime, alon, alat, modip, TOV, irtam_dir='',
7✔
32
                  use_subdirs=True):
33
    """Output ionospheric parameters from daily set of IRTAM coefficients.
34

35
    Parameters
36
    ----------
37
    dtime : object
38
        Datetime Python object.
39
    alon : array-like
40
        Flattened array of geographic longitudes in degrees.
41
        Must be Numpy array
42
        of any size [N_G].
43
    alat : array-like
44
        Flattened array of geographic latitudes in degrees.
45
        Must be Numpy array
46
        of any size [N_G].
47
    modip : array-like
48
        Modified dip angle in degrees.
49
    TOV : float
50
        Time of Validity for IRtAM coefficients (use 24 if unknown).
51
    irtam_dir : str
52
        Directory for IRTAM coefficients, or '' to use package directory.
53
        (default='')
54
    use_subdirs : bool
55
        If True, adds YYYY/MMDD subdirectories to the filename path, if False
56
        assumes that the entire path to the coefficient directory is provided
57
        by `irtam_dir` (default=True)
58

59
    Returns
60
    -------
61
    F2 : dict
62
        'fo' is critical frequency of F2 region in MHz.
63
        'hm' is height of the F2 peak in km.
64
        'B0' is bottom thickness parameter of the F2 region in km.
65
        'B1' is bottom thickness parameter of the F2 region in km.
66
        Shape [N_T, N_G].
67

68
    Notes
69
    -----
70
    This function returns ionospheric parameters and 3-D electron density
71
    for a given time using IRTAM coefficients for that time frame.
72

73
    References
74
    ----------
75
    Galkin et al. (2015), GAMBIT Database and Explorer for Real-Time
76
    IRI Maps of F2 Layer Peak Height and Density, IES.
77

78
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
79
    International Reference Ionosphere Modeling Implemented in Python,
80
    Space Weather.
81

82
    """
83
    # ------------------------------------------------------------------------
84
    # Calculate geographic Jones and Gallet (JG) functions F_G for the given
85
    # grid and corresponding map of modip angles
86
    G_IRTAM = IRTAM_set_gl_G(alon, alat, modip)
×
87

88
    # ------------------------------------------------------------------------
89
    # Calculate diurnal Fourier functions F_D for the given time array aUT
90
    # create time array that matches IRTAM files (15 min)
91

92
    # Diurnal function for a single time frame (nat all, like in PyIRI)
93
    aUT = np.array([dtime.hour + dtime.minute / 60.])
×
94
    D_IRTAM = IRTAM_diurnal_functions(aUT, TOV)
×
95

96
    # ------------------------------------------------------------------------
97
    # Read IRTAM coefficients and form matrix U
98
    F_B0, F_B1, F_f0F2, F_hmF2 = IRTAM_read_coeff(dtime, irtam_dir, use_subdirs)
×
99

100
    # ------------------------------------------------------------------------
101
    # Multiply matrices (F_D U)F_G
102
    B0, B1, foF2, hmF2 = IRTAM_gamma(G_IRTAM, D_IRTAM,
×
103
                                     F_B0, F_B1, F_f0F2, F_hmF2)
104

105
    # Limit B1 values (same as in IRI) to prevent ugly thin profiles:
106
    B1 = np.clip(B1, 1.0, 6.0)
×
107
    B0 = np.clip(B0, 1.0, 350.0)
×
108
    # ------------------------------------------------------------------------
109
    # Add all parameters to dictionaries:
110
    F2 = {'B0': B0,
×
111
          'B1': B1,
112
          'fo': foF2,
113
          'hm': hmF2}
114

115
    return F2
×
116

117

118
def IRTAM_set_gl_G(alon, alat, modip):
7✔
119
    """Calculate global functions.
120

121
    Parameters
122
    ----------
123
    alon : array-like
124
        Flattened array of geographic longitudes in degrees.
125
    alat : array-like
126
        Flattened array of geographic latitudes in degrees.
127
    modip : array-like
128
        Modified dip angle in degrees.
129

130
    Returns
131
    -------
132
    G : array-like
133
        Global functions for IRTAM (same as for CCIR foF2).
134

135
    Notes
136
    -----
137
    This function sets Geographic Coordinate Functions G_k(position) page
138
    # 18 of Jones & Graham 1965 for F0F2, M3000, and Es coefficients
139

140
    References
141
    ----------
142
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
143
    International Reference Ionosphere Modeling Implemented in Python,
144
    Space Weather.
145

146
    Galkin et al. (2015), GAMBIT Database and Explorer for Real-Time
147
    IRI Maps of F2 Layer Peak Height and Density, IES.
148

149
    Jones, W. B., & Gallet, R. M. (1965). Representation of diurnal
150
    and geographic variations of ionospheric data by numerical methods,
151
    control of instability, ITU Telecommunication Journal , 32 (1), 18–28.
152

153
    """
154
    coef = ml.highest_power_of_extension()
×
155

156
    G = ml.set_global_functions(coef['QM']['F0F2'],
×
157
                                coef['nk']['F0F2'],
158
                                alon, alat, modip)
159

160
    return G
×
161

162

163
def IRTAM_diurnal_functions(time_array, TOV):
7✔
164
    """Set diurnal functions for F2, M3000, and Es.
165

166
    Parameters
167
    ----------
168
    time_array : array-like
169
        Array of UTs in hours.
170

171
    Returns
172
    -------
173
    D : array-like
174
        Diurnal functions for IRTAM.
175

176
    Notes
177
    -----
178
    This function calculates diurnal functions for IRTAM
179
    coefficients
180

181
    References
182
    ----------
183
    Galkin et al. (2015), GAMBIT Database and Explorer for Real-Time
184
    IRI Maps of F2 Layer Peak Height and Density, IES.
185

186
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
187
    International Reference Ionosphere Modeling Implemented in Python,
188
    Space Weather.
189

190
    Jones, W. B., Graham, R. P., & Leftin, M. (1966). Advances
191
    in ionospheric mapping by numerical methods.
192

193
    """
194
    # nj is the highest order of the expansion
195
    coef = IRTAM_highest_power_of_extension()
×
196
    time_array = time_array
×
197
    # First find CCIR-like diurnal functions
198
    D_CCIR = ml.set_diurnal_functions(coef['nj']['F0F2'], time_array)
×
199

200
    # Then we need to reorder them to insert additional term for IRTAM
201
    D = np.zeros((coef['nj']['IRTAM'], time_array.size))
×
202
    D[0, :] = D_CCIR[0, :]
×
203
    D[1, :] = (time_array - TOV) * 60. + 720.
×
204
    D[2:, :] = D_CCIR[1:, :]
×
205

206
    # Transpose array so that multiplication can be done later
207
    D = np.transpose(D)
×
208

209
    return D
×
210

211

212
def IRTAM_read_coeff(dtime, coeff_dir='', use_subdirs=True):
7✔
213
    """Read coefficients from IRTAM.
214

215
    Parameters
216
    ----------
217
    dtime : int
218
        Month.
219
    coeff_dir : str
220
        Directory for IRTAM coefficients, or '' to use package directory.
221
        (default='')
222
    use_subdirs : bool
223
        If True, adds YYYY/MMDD subdirectories to the filename path, if False
224
        assumes that the entire path to the coefficient directory is provided
225
        by `irtam_dir` (default=True)
226

227
    Returns
228
    -------
229
    b0_irtam : array-like
230
        IRTAM coefficients for B0 thickness.
231
    b1_irtam : array-like
232
        IRTAM coefficients for B1 thickness.
233
    fof2_irtam : array-like
234
        IRTAM coefficients for F2 frequency.
235
    hmf2_irtam : array-like
236
        IRTAM coefficients for F2 peak height.
237

238
    Notes
239
    -----
240
    This function reads U_jk coefficients (from IRTAM and Es maps).
241
    Acknowledgement for Es coefficients:
242
    Mrs. Estelle D. Powell and Mrs. Gladys I. Waggoner in supervising the
243
    collection, keypunching and processing of the foEs data.
244
    This work was sponsored by U.S. Navy as part of the SS-267 program.
245
    The final development work and production of the foEs maps was supported
246
    by the U.S Information Agency.
247
    Acknowledgments to Doug Drob (NRL) for giving me these coefficients.
248

249
    References
250
    ----------
251
    Galkin et al. (2015), GAMBIT Database and Explorer for Real-Time
252
    IRI Maps of F2 Layer Peak Height and Density, IES.
253

254
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
255
    International Reference Ionosphere Modeling Implemented in Python,
256
    Space Weather.
257

258
    Jones, W. B., Graham, R. P., & Leftin, M. (1966). Advances
259
    in ionospheric mapping 476 by numerical methods.
260

261
    """
262
    # IRTAM coefficient files have the first 988 numbers in same format as CCIR
263
    # followed by 76 coefficients for the additional diurnal term. Load the
264
    # parameter data into arrays from the various files.
265
    #
266
    # B0
267
    file_b0 = coeff.get_irtam_param_filename(dtime, 'B0', irtam_dir=coeff_dir,
7✔
268
                                             use_subdirs=use_subdirs)
269
    b0_irtam = IRTAM_read_files(file_b0)
7✔
270

271
    # B1
272
    file_b1 = coeff.get_irtam_param_filename(dtime, 'B1', irtam_dir=coeff_dir,
×
273
                                             use_subdirs=use_subdirs)
274
    b1_irtam = IRTAM_read_files(file_b1)
×
275

276
    # f0F2
277
    file_f0f2 = coeff.get_irtam_param_filename(dtime, 'foF2',
×
278
                                               irtam_dir=coeff_dir,
279
                                               use_subdirs=use_subdirs)
280
    f0f2_irtam = IRTAM_read_files(file_f0f2)
×
281

282
    # hmF2
283
    file_hmf2 = coeff.get_irtam_param_filename(dtime, 'hmF2',
×
284
                                               irtam_dir=coeff_dir,
285
                                               use_subdirs=use_subdirs)
286
    hmf2_irtam = IRTAM_read_files(file_hmf2)
×
287

288
    return b0_irtam, b1_irtam, f0f2_irtam, hmf2_irtam
×
289

290

291
def IRTAM_read_files(filename):
7✔
292
    """Read IRTAM file.
293

294
    Parameters
295
    ----------
296
    filename : str
297
        Path to the IRTAM folder file.
298

299
    Returns
300
    -------
301
    F_IRTAM : array-like
302
        Array of IRTAM coefficients.
303

304
    Raises
305
    ------
306
    IOError
307
        If filename is unknown
308

309
    Notes
310
    -----
311
    This function reads IRTAM file and outputs [14, 96] array.
312

313
    """
314
    # Test that the desired file exists
315
    if not os.path.isfile(filename):
7✔
316
        raise IOError('unknown IRTAM coefficient file: {:}'.format(filename))
7✔
317

318
    # Read in the file
319
    full_array = []
×
320
    with open(filename, mode='r') as file_f:
×
321
        for line in file_f:
×
322
            if line[0] != '#':
×
323
                line_vals = np.fromstring(line, dtype=float, sep=' ')
×
324
                full_array = np.concatenate((full_array, line_vals), axis=None)
×
325

326
    # Assign the file input into separate arrays
327
    array_main = full_array[0:988]
×
328
    array_add = full_array[988:]
×
329

330
    # Pull the predefined sizes of the function extensions
331
    coef = IRTAM_highest_power_of_extension()
×
332

333
    # For IRTAM: reshape array to [nj, nk] shape
334
    F_CCIR = np.zeros((coef['nj']['F0F2'], coef['nk']['F0F2']))
×
335
    F_CCIR_like = np.reshape(array_main, F_CCIR.shape, order='F')
×
336

337
    # Insert column between 0 and 1, for additional b0 coefficients
338
    F_IRTAM = np.zeros((coef['nj']['IRTAM'], coef['nk']['IRTAM']))
×
339
    F_IRTAM[0, :] = F_CCIR_like[0, :]
×
340
    F_IRTAM[1, :] = array_add
×
341
    F_IRTAM[2:, :] = F_CCIR_like[1:, :]
×
342

343
    return F_IRTAM
×
344

345

346
def IRTAM_gamma(G, D, F_B0, F_B1, F_foF2, F_hmF2):
7✔
347
    """Calculate foF2, M3000 propagation parameter, and foEs.
348

349
    Parameters
350
    ----------
351
    G : array-like
352
        Global functions for F2 region.
353
    D : array-like
354
        Diurnal functions for F2 region.
355
    F_B0 : array-like
356
        IRTAM coefficients for B0.
357
    F_B1 : array-like
358
        IRTAM coefficients for B1.
359
    F_foF2 : array-like
360
        IRTAM coefficients for foF2.
361
    F_hmF2 : array-like
362
        IRTAM coefficients for hmF2.
363

364
    Returns
365
    -------
366
    gamma_B0 : array-like
367
        Thickness B0 in km.
368
    gamma_B1 : array-like
369
        Thickness B1 in km.
370
    gamma_foF2 : array-like
371
        Critical frequency of F2 layer in MHz.
372
    gamma_hmF2 : array-like
373
        Height of F2 layer.
374

375
    Notes
376
    -----
377
    This function calculates numerical maps for B0, B1, foF2, and hmF2
378
    using matrix multiplication
379

380
    References
381
    ----------
382
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
383
    International Reference Ionosphere Modeling Implemented in Python,
384
    Space Weather.
385

386
    """
387

388
    # Find numerical map
389
    gamma_B0 = np.matmul(np.matmul(D, F_B0), G)
×
390
    gamma_B1 = np.matmul(np.matmul(D, F_B1), G)
×
391
    gamma_foF2 = np.matmul(np.matmul(D, F_foF2), G)
×
392
    gamma_hmF2 = np.matmul(np.matmul(D, F_hmF2), G)
×
393

394
    return gamma_B0, gamma_B1, gamma_foF2, gamma_hmF2
×
395

396

397
def IRTAM_highest_power_of_extension():
7✔
398
    """Provide the highest power of extension.
399

400
    Returns
401
    -------
402
    const : dict
403
        Dictionary that has QM, nk, and nj parameters.
404

405
    Notes
406
    -----
407
    This function sets a common set of constants that define the power of
408
    expansions.
409
    QM = array of highest power of sin(x).
410
    nk = highest order of geographic extension.
411
    e.g. there are 76 functions in Table 3 on page 18 in Jones & Graham 1965.
412
    nj = highest order in diurnal variation.
413

414
    References
415
    ----------
416
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
417
    International Reference Ionosphere Modeling Implemented in Python,
418
    Space Weather.
419

420
    Jones, W. B., & Gallet, R. M. (1965). Representation of diurnal
421
    and geographic variations of ionospheric data by numerical methods,
422
    control of instability, ITU Telecommunication Journal , 32 (1), 18–28.
423

424
    """
425
    # Degree of extension
426
    QM_F0F2 = [12, 12, 9, 5, 2, 1, 1, 1, 1]
×
427
    QM_M3000 = [7, 8, 6, 3, 2, 1, 1]
×
428
    QM_Es_upper = [11, 12, 6, 3, 1]
×
429
    QM_Es_median = [11, 13, 7, 3, 1, 1]
×
430
    QM_Es_lower = [11, 13, 7, 1, 1]
×
431
    QM = {'F0F2': QM_F0F2, 'M3000': QM_M3000,
×
432
          'Es_upper': QM_Es_upper, 'Es_median': QM_Es_median,
433
          'Es_lower': QM_Es_lower}
434

435
    # Geographic
436
    nk_F0F2 = 76
×
437
    nk_IRTAM = 76
×
438
    nk_M3000 = 49
×
439
    nk_Es_upper = 55
×
440
    nk_Es_median = 61
×
441
    nk_Es_lower = 55
×
442
    nk = {'F0F2': nk_F0F2, 'M3000': nk_M3000,
×
443
          'Es_upper': nk_Es_upper, 'Es_median': nk_Es_median,
444
          'Es_lower': nk_Es_lower, 'IRTAM': nk_IRTAM}
445

446
    # Diurnal
447
    nj_F0F2 = 13
×
448
    nj_IRTAM = 14
×
449
    nj_M3000 = 9
×
450
    nj_Es_upper = 5
×
451
    nj_Es_median = 7
×
452
    nj_Es_lower = 5
×
453

454
    nj = {'F0F2': nj_F0F2, 'M3000': nj_M3000,
×
455
          'Es_upper': nj_Es_upper, 'Es_median': nj_Es_median,
456
          'Es_lower': nj_Es_lower, 'IRTAM': nj_IRTAM}
457

458
    const = {'QM': QM, 'nk': nk, 'nj': nj}
×
459

460
    return const
×
461

462

463
def IRTAM_find_hmF1(B0, B1, NmF2, hmF2, NmF1):
7✔
464
    """Return hmF1 for the given parameters.
465

466
    Parameters
467
    ----------
468
    B0 : array-like
469
        Thickness parameter in km.
470
    B1 : array-like
471
        Thickness parameter in km.
472
    NmF2 : array-like
473
        Peak of F2 layer in m-3.
474
    hmF2 : int
475
        Height of F2 layer in km.
476
    NmF1 : array-like
477
        Peak of F1 layer in m-3.
478

479
    Returns
480
    -------
481
    hmF1 : array-like
482
        Height of F1 layer in km.
483

484
    Notes
485
    -----
486
    This function returns height of F1 layer if the bottom of F2 is
487
    constructed using Ramakrishnan & Rawer equation.
488

489
    References
490
    ----------
491
    Bilitza et al. (2022), The International Reference Ionosphere
492
    model: A review and description of an ionospheric benchmark, Reviews
493
    of Geophysics, 60.
494

495
    """
496

497
    B0 = np.transpose(B0)
×
498
    B1 = np.transpose(B1)
×
499
    NmF2 = np.transpose(NmF2)
×
500
    hmF2 = np.transpose(hmF2)
×
501
    NmF1 = np.transpose(NmF1)
×
502

503
    # Create a random array of heights with high resolution
504
    h = np.arange(0, 700, 1)
×
505

506
    hmF1 = np.zeros((1, B0.size)) + np.nan
×
507

508
    a = np.where(np.isfinite(NmF1))[0]
×
509

510
    # Ramakrishnan_and_Rawer:
511
    for i in range(0, a.size):
×
512
        den = Ramakrishnan_Rawer_function(NmF2[a[i]], hmF2[a[i]],
×
513
                                          B0[a[i]], B1[a[i]], h)
514
        hmF1[0, a[i]] = np.interp(NmF1[a[i]], den, h)
×
515

516
    return hmF1
×
517

518

519
def IRTAM_freq_to_Nm(f):
7✔
520
    """Convert critical frequency to plasma density.
521

522
    Parameters
523
    ----------
524
    f : array-like
525
        Critical frequency in MHz.
526

527
    Returns
528
    -------
529
    Nm : array-like
530
       Peak density in m-3.
531

532
    Notes
533
    -----
534
    This function returns maximum density for the given critical frequency and
535
    limits it to 1 if it is below zero.
536

537
    """
538

539
    Nm = 0.124 * f**2 * 1e11
×
540

541
    # Exclude negative values, in case there are any
542
    Nm[np.where(Nm <= 0)] = 1.
×
543

544
    return Nm
×
545

546

547
def IRTAM_F2_top_thickness(foF2, hmF2, B_0, F107):
7✔
548
    """Return thicknesses of ionospheric layers.
549

550
    Parameters
551
    ----------
552
    foF2 : array-like
553
        Critical frequency of F2 region in MHz.
554
    hmF2 : array-like
555
        Height of the F2 layer.
556
    hmE : array-like
557
        Height of the E layer.
558
    mth : int
559
        Month of the year.
560
    F107 : array-like
561
        Solar flux index in SFU.
562

563
    Returns
564
    -------
565
    B_F2_top : array-like
566
        Thickness of F2 top in km.
567
    B_E_bot : array-like
568
        Thickness of E bottom in km.
569
    B_E_top : array-like
570
        Thickness of E top in km.
571

572
    Notes
573
    -----
574
    This function returns thicknesses of ionospheric layers.
575
    We assume that B0 from IRTAM is equivalent to the
576
    thickness of the bottom side formulated in PyIRI.
577
    In reality, it could be different, since PyIRI uses
578
    Epstein function for the bottom side and IRTAM uses
579
    Ramakrishnan & Rawer function.
580

581
    References
582
    ----------
583
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
584
    International Reference Ionosphere Modeling Implemented in Python,
585
    Space Weather.
586

587
    """
588

589
    # B_F2_top................................................................
590
    # Shape parameter depends on solar activity:
591
    # Effective sunspot number
592
    R12 = ml.F107_2_R12(F107)
×
593
    k = (3.22 - 0.0538 * foF2 - 0.00664 * hmF2 + (0.113 * hmF2 / B_0)
×
594
         + 0.00257 * R12)
595

596
    # Auxiliary parameters x and v:
597
    x = (k * B_0 - 150.) / 100.
×
598
    # Thickness
599
    B_F2_top = (100. * x + 150.) / (0.041163 * x**2 - 0.183981 * x + 1.424472)
×
600

601
    return B_F2_top
×
602

603

604
def IRTAM_reconstruct_density_from_parameters(F2, F1, E, alt):
7✔
605
    """Construct vertical EDP for 2 levels of solar activity.
606

607
    Parameters
608
    ----------
609
    F2 : dict
610
        Dictionary of parameters for F2 layer.
611
    F1 : dict
612
        Dictionary of parameters for F1 layer.
613
    E : dict
614
        Dictionary of parameters for E layer.
615
    alt : array-like
616
        1-D array of altitudes [N_V] in km.
617

618
    Returns
619
    -------
620
    EDP : array-like
621
        Electron density [N_V, N_G]
622
        in m-3.
623

624
    Notes
625
    -----
626
    This function calculates 3-D density from given dictionaries of
627
    the parameters.
628

629
    References
630
    ----------
631
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
632
    International Reference Ionosphere Modeling Implemented in Python,
633
    Space Weather.
634

635
    """
636
    s = F2['Nm'].shape
×
637
    N_G = s[1]
×
638

639
    x = np.full((12, N_G), np.nan)
×
640
    x[0, :] = F2['Nm'][0, :]
×
641
    x[1, :] = F1['Nm'][0, :]
×
642
    x[2, :] = E['Nm'][0, :]
×
643
    x[3, :] = F2['hm'][0, :]
×
644
    x[4, :] = F1['hm'][0, :]
×
645
    x[5, :] = E['hm'][0, :]
×
646
    x[6, :] = F2['B0'][0, :]
×
647
    x[7, :] = F2['B1'][0, :]
×
648
    x[8, :] = F2['B_top'][0, :]
×
649
    x[9, :] = F1['B_bot'][0, :]
×
650
    x[10, :] = E['B_bot'][0, :]
×
651
    x[11, :] = E['B_top'][0, :]
×
652

653
    EDP = IRTAM_EDP_builder(x, alt)
×
654

655
    return EDP
×
656

657

658
def IRTAM_EDP_builder(x, aalt):
7✔
659
    """Construct vertical EDP.
660

661
    Parameters
662
    ----------
663
    x : array-like
664
        Array where 1st dimension indicates the parameter (total 11
665
        parameters), second dimension is time, and third is horizontal grid
666
        [11, N_T, N_G].
667
    aalt : array-like
668
        1-D array of altitudes [N_V] in km.
669

670
    Returns
671
    -------
672
    density : array-like
673
        3-D electron density [N_T, N_V, N_G] in m-3.
674

675
    Notes
676
    -----
677
    This function builds the EDP from the provided parameters for all time
678
    frames, all vertical and all horizontal points.
679

680
    References
681
    ----------
682
    Forsythe et al. (2023), PyIRI: Whole-Globe Approach to the
683
    International Reference Ionosphere Modeling Implemented in Python,
684
    Space Weather.
685

686
    """
687

688
    # Number of elements in horizontal dimension of grid
689
    ngrid = x.shape[1]
×
690

691
    # vertical dimention
692
    nalt = aalt.size
×
693

694
    # Empty arrays
695
    density_F2 = np.zeros((nalt, ngrid))
×
696
    density_F1 = np.zeros((nalt, ngrid))
×
697
    density_E = np.zeros((nalt, ngrid))
×
698
    drop_1 = np.zeros((nalt, ngrid))
×
699
    drop_2 = np.zeros((nalt, ngrid))
×
700

701
    # Shapes:
702
    # for filling with altitudes because the last dimensions should match
703
    # the source
704
    shape1 = (ngrid, nalt)
×
705

706
    # For filling with horizontal maps because the last dimentsions should
707
    # match the source
708
    shape2 = (nalt, ngrid)
×
709

710
    order = 'F'
×
711
    NmF2 = np.reshape(x[0, :], ngrid, order=order)
×
712
    NmF1 = np.reshape(x[1, :], ngrid, order=order)
×
713
    NmE = np.reshape(x[2, :], ngrid, order=order)
×
714
    hmF2 = np.reshape(x[3, :], ngrid, order=order)
×
715
    hmF1 = np.reshape(x[4, :], ngrid, order=order)
×
716
    hmE = np.reshape(x[5, :], ngrid, order=order)
×
717
    B0 = np.reshape(x[6, :], ngrid, order=order)
×
718
    B1 = np.reshape(x[7, :], ngrid, order=order)
×
719
    B_F2_top = np.reshape(x[8, :], ngrid, order=order)
×
720
    B_F1_bot = np.reshape(x[9, :], ngrid, order=order)
×
721
    B_E_bot = np.reshape(x[10, :], ngrid, order=order)
×
722
    B_E_top = np.reshape(x[11, :], ngrid, order=order)
×
723

724
    # Set to some parameters if zero or lower:
725
    B_F1_bot[np.where(B_F1_bot <= 0)] = 10
×
726
    B_F2_top[np.where(B_F2_top <= 0)] = 30
×
727

728
    # Array of hmFs with same dimensions as result, to later search
729
    # using argwhere
730
    a_alt = np.full(shape1, aalt, order='F')
×
731
    a_alt = np.swapaxes(a_alt, 0, 1)
×
732

733
    # Fill arrays with parameters to add height dimension and populate it
734
    # with same values, this is important to keep all operations in matrix
735
    # form
736
    a_NmF2 = np.full(shape2, NmF2)
×
737
    a_NmF1 = np.full(shape2, NmF1)
×
738
    a_NmE = np.full(shape2, NmE)
×
739
    a_hmF2 = np.full(shape2, hmF2)
×
740
    a_hmF1 = np.full(shape2, hmF1)
×
741
    a_hmE = np.full(shape2, hmE)
×
742
    a_B_F2_top = np.full(shape2, B_F2_top)
×
743
    a_B0 = np.full(shape2, B0)
×
744
    a_B1 = np.full(shape2, B1)
×
745
    a_B_F1_bot = np.full(shape2, B_F1_bot)
×
746
    a_B_E_top = np.full(shape2, B_E_top)
×
747
    a_B_E_bot = np.full(shape2, B_E_bot)
×
748

749
    # Amplitude for Epstein functions
750
    a_A1 = 4. * a_NmF2
×
751
    a_A2 = 4. * a_NmF1
×
752
    a_A3 = 4. * a_NmE
×
753

754
    # !!! do not use a[0], because all 3 dimensions are needed. this is
755
    # the same as density[a]= density[a[0], a[1], a[2]]
756

757
    # F2 top (same for yes F1 and no F1)
758
    a = np.where(a_alt >= a_hmF2)
×
759
    density_F2[a] = ml.epstein_function_top_array(a_A1[a], a_hmF2[a],
×
760
                                                  a_B_F2_top[a], a_alt[a])
761

762
    # E bottom (same for yes F1 and no F1)
763
    a = np.where(a_alt <= a_hmE)
×
764
    density_E[a] = ml.epstein_function_array(a_A3[a], a_hmE[a], a_B_E_bot[a],
×
765
                                             a_alt[a])
766

767
    # when F1 is present-----------------------------------------
768
    # F2 bottom down to F1
769
    a = np.where((np.isfinite(a_NmF1)) & (a_alt < a_hmF2) & (a_alt >= a_hmF1))
×
770
    density_F2[a] = Ramakrishnan_Rawer_function(a_NmF2[a], a_hmF2[a],
×
771
                                                a_B0[a], a_B1[a], a_alt[a])
772

773
    # E top plus F1 bottom (hard boundaries)
774
    a = np.where((a_alt > a_hmE) & (a_alt < a_hmF1))
×
775
    drop_1[a] = 1. - ((a_alt[a] - a_hmE[a]) / (a_hmF1[a] - a_hmE[a]))**4.
×
776
    drop_2[a] = 1. - ((a_hmF1[a] - a_alt[a]) / (a_hmF1[a] - a_hmE[a]))**4.
×
777

778
    density_E[a] = ml.epstein_function_array(a_A3[a],
×
779
                                             a_hmE[a],
780
                                             a_B_E_top[a],
781
                                             a_alt[a]) * drop_1[a]
782
    density_F1[a] = ml.epstein_function_array(a_A2[a],
×
783
                                              a_hmF1[a],
784
                                              a_B_F1_bot[a],
785
                                              a_alt[a]) * drop_2[a]
786

787
    # When F1 is not present(hard boundaries)--------------------
788
    a = np.where((np.isnan(a_NmF1)) & (a_alt < a_hmF2) & (a_alt > a_hmE))
×
789
    drop_1[a] = 1.0 - ((a_alt[a] - a_hmE[a]) / (a_hmF2[a] - a_hmE[a]))**4.0
×
790
    drop_2[a] = 1.0 - ((a_hmF2[a] - a_alt[a]) / (a_hmF2[a] - a_hmE[a]))**4.0
×
791
    density_E[a] = ml.epstein_function_array(a_A3[a], a_hmE[a], a_B_E_top[a],
×
792
                                             a_alt[a]) * drop_1[a]
793

794
    density_F2[a] = Ramakrishnan_Rawer_function(a_NmF2[a], a_hmF2[a], a_B0[a],
×
795
                                                a_B1[a], a_alt[a]) * drop_2[a]
796
    density = density_F2 + density_F1 + density_E
×
797

798
    # Make 1 everything that is <= 0
799
    density[np.where(density <= 1.0)] = 1.0
×
800

801
    return density
×
802

803

804
def Ramakrishnan_Rawer_function(NmF2, hmF2, B0, B1, h):
7✔
805
    """Construct density Ramakrishnan & Rawer F2 bottomside.
806

807
    Parameters
808
    ----------
809
    NmF2 : array-like
810
        F2 region peak in m-3.
811
    hmF2 : array-like
812
        Height of F2 layer in km.
813
    B0 : array-like
814
        Thickness parameter for F2 region in km.
815
    B1 : array-like
816
        Thickness parameter for F2 region in km.
817
    h : array-like
818
        Altitude in km.
819

820
    Returns
821
    -------
822
    den : array-like
823
        Constructed density in m-3.
824

825
    Notes
826
    -----
827
    This function constructs bottomside of F2 layer using
828
    Ramakrishnan & Rawer equation (as in IRI). All inputs are supposed
829
    to have same size.
830

831
    References
832
    ----------
833
    Bilitza et al. (2022), The International Reference Ionosphere
834
    model: A review and description of an ionospheric benchmark, Reviews
835
    of Geophysics, 60.
836

837
    """
838
    x = (hmF2 - h) / B0
×
839
    den = NmF2 * ml.fexp(-(np.sign(x) * (np.abs(x)**B1))) / np.cosh(x)
×
840

841
    return den
×
842

843

844
def call_IRTAM_PyIRI(aUT, dtime, alon, alat, aalt, f2, f1, e_peak, es_peak,
7✔
845
                     modip, TOV, irtam_dir='', use_subdirs=True):
846
    """Update parameters and build EDP for IRTAM for one time frame.
847

848
    Parameters
849
    ----------
850
    aUT : array-like
851
        Time array that was used for PyIRI in hours.
852
    dtime : dtime object
853
        F2 region peak in m-3.
854
    alon : array-like
855
        Longitudes 1-D array in degrees.
856
    alat : array-like
857
        Latitudes 1-D array in degrees.
858
    aalt : array-like
859
        Altitude 1-D array in km for EDP construction.
860
    f2 : dict
861
        'Nm' is peak density of F2 region in m-3.
862
        'fo' is critical frequency of F2 region in MHz.
863
        'M3000' is the obliquity factor for a distance of 3,000 km.
864
        Defined as refracted in the ionosphere, can be received at a
865
        distance of 3,000 km, unitless.
866
        'hm' is height of the F2 peak in km.
867
        'B_topi is top thickness of the F2 region in km.
868
        'B_bot' is bottom thickness of the F2 region in km.
869
        Shape [N_T, N_G].
870
    f1 : dict
871
        'Nm' is peak density of F1 region in m-3.
872
        'fo' is critical frequency of F1 region in MHz.
873
        'P' is the probability occurrence of F1 region, unitless.
874
        'hm' is height of the F1 peak in km.
875
        'B_bot' is bottom thickness of the F1 region in km.
876
        Shape [N_T, N_G].
877
    e_peak : dict
878
        'Nm' is peak density of E region in m-3.
879
        'fo' is critical frequency of E region in MHz.
880
        'hm' is height of the E peak in km.
881
        'B_top' is bottom thickness of the E region in km.
882
        'B_bot' is bottom thickness of the E region in km.
883
        Shape [N_T, N_G].
884
    es_peak : dict
885
        'Nm' is peak density of Es region in m-3.
886
        'fo' is critical frequency of Es region in MHz.
887
        'hm' is height of the Es peak in km.
888
        'B_top' is bottom thickness of the Es region in km.
889
        'B_bot' is bottom thickness of the Es region in km.
890
        Shape [N_T, N_G].
891
    modip : array-like
892
        Modified dip angle in degrees.
893
    TOV : float
894
        Time of Validity in decimal hours. Use 24 if not known.
895
    irtam_dir : str
896
        Directory for IRTAM coefficients, or '' to use package directory.
897
        (default='')
898
    use_subdirs : bool
899
        If True, adds YYYY/MMDD subdirectories to the filename path, if False
900
        assumes that the entire path to the coefficient directory is provided
901
        by `irtam_dir` (default=True)
902

903
    Returns
904
    -------
905
    f2 : dict
906
        'Nm' is peak density of F2 region in m-3.
907
        'hm' is height of the F2 peak in km.
908
        'B_top is top thickness of the F2 region in km.
909
        'B0' is bottom thickness parameter of the F2 region in km.
910
        'B1' is bottom thickness parameter of the F2 region in km.
911
        Shape [N_T, N_G].
912
    f1 : dict
913
        'Nm' is peak density of F1 region in m-3.
914
        'hm' is height of the F1 peak in km.
915
        'B_bot' is bottom thickness of the F1 region in km.
916
        Shape [N_T, N_G].
917
    e_peak : dict
918
        'Nm' is peak density of E region in m-3.
919
        'hm' is height of the E peak in km.
920
        'B_top' is bottom thickness of the E region in km.
921
        'B_bot' is bottom thickness of the E region in km.
922
        Shape [N_T, N_G].
923
    es_peak : dict
924
        'Nm' is peak density of Es region in m-3.
925
        'hm' is height of the Es peak in km.
926
        'B_top' is bottom thickness of the Es region in km.
927
        'B_bot' is bottom thickness of the Es region in km.
928
        Shape [N_T, N_G].
929
    EDP_result : array-like
930
        Electron density profile in m-3. Shape [N_T, N_V, N_G].
931

932
    Notes
933
    -----
934
    This function uses IRTAM coefficients to construct NmF2, hmF2, B0, B1
935
    maps, collects other parameters from PyIRI, updates hmF1 and B_F1_bot,
936
    and constructs the EDP.
937

938
    """
939
    # Find time index
940
    UT = dtime.hour + dtime.minute / 60.0 + dtime.second / 3600.0
×
941
    it = np.where(aUT == UT)[0]
×
942

943
    # Find IRTAM parameters
944
    IRTAM_f2 = IRTAM_density(dtime, alon, alat, modip, TOV, irtam_dir,
×
945
                             use_subdirs)
946

947
    # Create empty arrays with needed shape for 1 time frame
948
    # to fill with updated values
949
    shape = IRTAM_f2['fo'].shape
×
950
    NmF1 = np.zeros((shape))
×
951
    NmE = np.zeros((shape))
×
952
    hmE = np.zeros((shape))
×
953
    B_F2_top = np.zeros((shape))
×
954
    B_E_bot = np.zeros((shape))
×
955
    B_E_top = np.zeros((shape))
×
956
    P_F1 = np.zeros((shape))
×
957
    NmEs = np.zeros((shape))
×
958
    hmEs = np.zeros((shape))
×
959
    B_Es_bot = np.zeros((shape))
×
960
    B_Es_top = np.zeros((shape))
×
961
    hmF1 = np.zeros((shape))
×
962
    B_F2_top = np.zeros((shape))
×
963

964
    # Fill with PyIRI parameters that will not
965
    # need to be updated
966
    NmF1[:, :] = f1['Nm'][it, :]
×
967
    NmE[:, :] = e_peak['Nm'][it, :]
×
968
    hmE[:, :] = e_peak['hm'][it, :]
×
969
    B_F2_top[:, :] = f2['B_top'][it, :]
×
970
    B_E_bot[:, :] = f1['P'][it, :]
×
971
    B_E_top[:, :] = e_peak['B_top'][it, :]
×
972
    P_F1[:, :] = f1['P'][it, :]
×
973
    NmEs[:, :] = es_peak['Nm'][it, :]
×
974
    hmEs[:, :] = es_peak['hm'][it, :]
×
975
    B_Es_bot[:, :] = es_peak['B_bot'][it, :]
×
976
    B_Es_top[:, :] = es_peak['B_top'][it, :]
×
977
    B_F2_top[:, :] = f2['B_top'][it, :]
×
978

979
    # UPDATING PARAMETERS that depend of NmF2, hmF2, and thickness:
980
    # Convert critical frequency to the electron density (m-3)
981
    NmF2 = IRTAM_freq_to_Nm(IRTAM_f2['fo'])
×
982
    hmF1 = IRTAM_find_hmF1(IRTAM_f2['B0'], IRTAM_f2['B1'], NmF2,
×
983
                           IRTAM_f2['hm'], NmF1)
984
    B_F1_bot = ml.find_B_F1_bot(hmF1, hmE, P_F1)
×
985

986
    # combine parameters from PyIRI and IRTAM to merged dictionary
987
    F2_result = {'Nm': NmF2,
×
988
                 'hm': IRTAM_f2['hm'],
989
                 'B_top': B_F2_top,
990
                 'B0': IRTAM_f2['B0'],
991
                 'B1': IRTAM_f2['B1']}
992
    F1_result = {'Nm': NmF1,
×
993
                 'hm': hmF1,
994
                 'B_bot': B_F1_bot}
995
    E_result = {'Nm': NmE,
×
996
                'hm': hmE,
997
                'B_bot': B_E_bot,
998
                'B_top': B_E_top}
999
    Es_result = {'Nm': NmEs,
×
1000
                 'hm': hmEs,
1001
                 'B_bot': B_Es_bot,
1002
                 'B_top': B_Es_top}
1003

1004
    EDP_result = IRTAM_reconstruct_density_from_parameters(F2_result,
×
1005
                                                           F1_result,
1006
                                                           E_result,
1007
                                                           aalt)
1008

1009
    return F2_result, F1_result, E_result, Es_result, EDP_result
×
1010

1011

1012
def run_PyIRTAM(year, month, day, aUT, alon, alat, aalt, F107, irtam_dir='',
7✔
1013
                use_subdirs=True, download=False):
1014
    """Update parameters and build EDP for IRTAM for one time frame.
1015

1016
    Parameters
1017
    ----------
1018
    year : int
1019
        Year.
1020
    mth : int
1021
        Month of year.
1022
    aUT : array-like
1023
        Array of universal time (UT) in hours. Must be Numpy array of any size
1024
        [N_T].
1025
    alon : array-like
1026
        Flattened array of geographic longitudes in degrees. Must be Numpy
1027
        array of any size [N_G].
1028
    alat : array-like
1029
        Flattened array of geographic latitudes in degrees. Must be Numpy array
1030
        of any size [N_G].
1031
    aalt : array-like
1032
        Array of altitudes in km. Must be Numpy array of any size [N_V].
1033
    F107 : float
1034
        User provided F10.7 solar flux index in SFU.
1035
    irtam_dir : str
1036
        Directory with IRTAM coefficients, or '' to use package directory.
1037
        (default='')
1038
    use_subdirs : bool
1039
        If True, adds YYYY/MMDD subdirectories to the filename path, if False
1040
        assumes that the entire path to the coefficient directory is provided
1041
        by `irtam_dir` (default=True)
1042
    download : bool
1043
        If True, downloads coefficient files (default=False)
1044

1045
    Returns
1046
    -------
1047
    f2_b : dict
1048
        F2 parameters form PyIRI
1049
        'Nm' is peak density of F2 region in m-3.
1050
        'fo' is critical frequency of F2 region in MHz.
1051
        'M3000' is the obliquity factor for a distance of 3,000 km.
1052
        Defined as refracted in the ionosphere, can be received at a distance
1053
        of 3,000 km, unitless.
1054
        'hm' is height of the F2 peak in km.
1055
        'B_topi is top thickness of the F2 region in km.
1056
        'B_bot' is bottom thickness of the F2 region in km.
1057
        Shape [N_T, N_G, 2].
1058
    f1_b : dict
1059
        F1 parameters form PyIRI
1060
        'Nm' is peak density of F1 region in m-3.
1061
        'fo' is critical frequency of F1 region in MHz.
1062
        'P' is the probability occurrence of F1 region, unitless.
1063
        'hm' is height of the F1 peak in km.
1064
        'B_bot' is bottom thickness of the F1 region in km.
1065
        Shape [N_T, N_G, 2].
1066
    e_b : dict
1067
        E parameters form PyIRI
1068
        'Nm' is peak density of E region in m-3.
1069
        'fo' is critical frequency of E region in MHz.
1070
        'hm' is height of the E peak in km.
1071
        'B_top' is bottom thickness of the E region in km.
1072
        'B_bot' is bottom thickness of the E region in km.
1073
        Shape [N_T, N_G, 2].
1074
    es_b : dict
1075
        Es parameters form PyIRI
1076
        'Nm' is peak density of Es region in m-3.
1077
        'fo' is critical frequency of Es region in MHz.
1078
        'hm' is height of the Es peak in km.
1079
        'B_top' is bottom thickness of the Es region in km.
1080
        'B_bot' is bottom thickness of the Es region in km.
1081
        Shape [N_T, N_G, 2].
1082
    sun : dict
1083
        'lon' is longitude of subsolar point in degrees.
1084
        'lat' is latitude of subsolar point in degrees.
1085
        Shape [N_G].
1086
    mag : dict
1087
        'inc' is inclination of the magnetic field in degrees.
1088
        'modip' is modified dip angle in degrees.
1089
        'mag_dip_lat' is magnetic dip latitude in degrees.
1090
        Shape [N_G].
1091
    edp_b : array-like
1092
        Electron density profiles from PyIRI in m-3 with shape
1093
        [N_T, N_V, N_G]
1094
    f2_day : dict
1095
        F2 parameters form PyIRTAM
1096
        'Nm' is peak density of F2 region in m-3.
1097
        'hm' is height of the F2 peak in km.
1098
        'B_top is top thickness of the F2 region in km.
1099
        'B0' is bottom thickness parameter of the F2 region in km.
1100
        'B1' is bottom thickness parameter of the F2 region in km.
1101
        Shape [N_T, N_G].
1102
    f1_day : dict
1103
        F1 parameters form PyIRTAM
1104
        'Nm' is peak density of F1 region in m-3.
1105
        'hm' is height of the F1 peak in km.
1106
        'B_bot' is bottom thickness of the F1 region in km.
1107
        Shape [N_T, N_G].
1108
    e_day : dict
1109
        E parameters form PyIRTAM
1110
        'Nm' is peak density of E region in m-3.
1111
        'hm' is height of the E peak in km.
1112
        'B_top' is bottom thickness of the E region in km.
1113
        'B_bot' is bottom thickness of the E region in km.
1114
        Shape [N_T, N_G].
1115
    es_day : dict
1116
        Es parameters form PyIRTAM
1117
        'Nm' is peak density of Es region in m-3.
1118
        'hm' is height of the Es peak in km.
1119
        'B_top' is bottom thickness of the Es region in km.
1120
        'B_bot' is bottom thickness of the Es region in km.
1121
        Shape [N_T, N_G].
1122
    edp_day : array-like
1123
        Electron density profile from PyIRTAM in m-3. Shape
1124
        [N_T, N_V, N_G].
1125

1126
    Notes
1127
    -----
1128
    This function runs PyIRTAM for a day of interest.
1129

1130
    """
1131
    # First, determine the standard PyIRI parameters for the day of interest
1132
    # It is better to do it in the beginning (outside the time loop),
1133
    # so that PyIRI is called only once for the whole day.
1134

1135
    # Use CCIR (not URSI) since IRTAM uses CCIR models.
1136
    ccir_or_ursi = 0  # 0 = CCIR, 1 = URSI
×
1137

1138
    # Run PyIRI
1139
    f2_b, f1_b, e_b, es_b, sun, mag, edp_b = ml.IRI_density_1day(
×
1140
        year, month, day, aUT, alon, alat, aalt, F107, PyIRI.coeff_dir,
1141
        ccir_or_ursi)
1142

1143
    # Create empty dictionaries to store daily parameters.
1144
    empt = np.array([])
×
1145
    f2_day = {'Nm': empt, 'hm': empt, 'B0': empt, 'B1': empt, 'B_top': empt}
×
1146
    f1_day = {'Nm': empt, 'hm': empt, 'B_bot': empt}
×
1147
    e_day = {'Nm': empt, 'hm': empt, 'B_bot': empt, 'B_top': empt}
×
1148
    es_day = {'Nm': empt, 'hm': empt, 'B_bot': empt, 'B_top': empt}
×
1149
    edp_day = edp_b * 0.
×
1150
    f1_day['P'] = f1_b['P']
×
1151

1152
    for it in range(0, aUT.size):
×
1153
        # Dtime for one time frame
1154
        hour = int(np.fix(aUT[it]))
×
1155
        minute = int((aUT[it] - hour) * 60.)
×
1156
        dtime = dt.datetime(year, month, day, hour, minute, 0)
×
1157

1158
        # If the user specifies, the coefficients are
1159
        # downloaded form the UMass Lowell data base
NEW
1160
        if download:
×
NEW
1161
            for param in ['B0', 'B1', 'hmF2', 'foF2']:
×
NEW
1162
                coeff.download_irtam_coeffs(dtime, param, irtam_dir=irtam_dir)
×
1163

1164
        # Call PyIRTAM:
1165
        F2, F1, E, Es, EDP = call_IRTAM_PyIRI(aUT, dtime, alon, alat, aalt,
×
1166
                                              f2_b, f1_b, e_b, es_b,
1167
                                              mag['modip'], aUT[it], irtam_dir,
1168
                                              use_subdirs=use_subdirs)
1169
        # Save results.
1170
        if it == 0:
×
1171
            for key in F2:
×
1172
                f2_day[key] = F2[key][:]
×
1173
            for key in F1:
×
1174
                f1_day[key] = F1[key][:]
×
1175
            for key in E:
×
1176
                e_day[key] = E[key][:]
×
1177
            for key in Es:
×
1178
                es_day[key] = Es[key][:]
×
1179
        else:
1180
            for key in F2:
×
1181
                f2_day[key] = np.concatenate((f2_day[key], F2[key][:]), axis=0)
×
1182
            for key in F1:
×
1183
                f1_day[key] = np.concatenate((f1_day[key], F1[key][:]), axis=0)
×
1184
            for key in E:
×
1185
                e_day[key] = np.concatenate((e_day[key], E[key][:]), axis=0)
×
1186
            for key in Es:
×
1187
                es_day[key] = np.concatenate((es_day[key], Es[key][:]), axis=0)
×
1188
        edp_day[it, :, :] = EDP
×
1189

1190
    return (f2_b, f1_b, e_b, es_b, sun, mag, edp_b, f2_day, f1_day, e_day,
×
1191
            es_day, edp_day)
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