-
-
Notifications
You must be signed in to change notification settings - Fork 573
/
goes.py
1380 lines (1189 loc) · 59 KB
/
goes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
"""
Contains functions useful for analysing GOES/XRS data.
Each of the Geostationary Operational Environmental Satellite (GOES) series
since the mid-1970s has carried an X-Ray Sensor (XRS) which observes
full-disk-integrated solar flux in two broadband channels:
1--8 angstrom (long); and 0.5--4 angstrom (short). For more information on
the GOES/XRS instrument, see [Ref1]_. GOES/XRS has become
the "standard candle" for solar coronal observations due its longevity and
consistency. The GOES event list, based on GOES/XRS observations, has
become the standard solar flare catalogue.
See https://www.ngdc.noaa.gov/stp/solar/solarflares.html for information
on the GOES event list definitions and data.
The functions in this module provide useful software to analyse GOES/XRS
observations. First they allow the GOES event list to be imported into a
Python session (`~sunpy.instr.goes.get_goes_event_list`).
They also allow the thermodynamic properties of the emitting solar plasma to be
determined. Temperature and emission measure are obtained using
`~sunpy.instr.goes.calculate_temperature_em`, this function currently relies on
lookup tables relating the GOES fluxes to the isothermal temperature and volume
emission measure. These tables were calculated by functions in SolarSoftWare
(SSW) using the CHIANTI atomic physics database ([Ref2]_). For more detail, see
the docstring of calculate_temperature_em` and references therein.
The radiative loss rate of the soft X-ray-emitting plasma across all
wavelengths can be found with
`~sunpy.instr.goes.calculate_radiative_loss_rate`, which makes use of a look up
table calculated by functions in SSW using CHIANTI. This table relates the
temperature and emission measure of the emitting solar plasma to the thermal
energy radiated over all wavelengths. For more information on how this is
done, see the docstring of `~sunpy.instr.goes._calc_rad_loss` and reference
therein.
Meanwhile, the X-ray luminosity in the two GOES passbands can be obtained by
`~sunpy.instr.goes.calculate_xray_luminosity`. To do so, this function calls
`~sunpy.instr.goes._goes_lx` and `~sunpy.instr.goes.calc_xraylum`.
References
----------
.. [Ref1] Hanser, F.A., & Sellers, F.B. 1996, Proc. SPIE, 2812, 344
.. [Ref2] Dere, K.P., et al. 2009 A&A, 498, 915 DOI: `10.1051/0004-6361/200911712 <https://doi.org/10.1051/0004-6361/200911712>`__
"""
import csv
import copy
import socket
import os.path
import datetime
from itertools import dropwhile
import numpy as np
import astropy.units as u
from astropy.time import TimeDelta
from scipy import interpolate
from scipy.integrate import trapz, cumtrapz
from sunpy import sun
from sunpy import timeseries
from sunpy.time import parse_time
from sunpy.util.net import check_download_file
from sunpy.coordinates import get_sunearth_distance
from sunpy.util.config import get_and_create_download_dir
GOES_CONVERSION_DICT = {'X': u.Quantity(1e-4, "W/m^2"),
'M': u.Quantity(1e-5, "W/m^2"),
'C': u.Quantity(1e-6, "W/m^2"),
'B': u.Quantity(1e-7, "W/m^2"),
'A': u.Quantity(1e-8, "W/m^2")}
__all__ = ['get_goes_event_list', 'calculate_temperature_em',
'calculate_radiative_loss_rate', 'calculate_xray_luminosity', 'flux_to_flareclass',
'flareclass_to_flux']
try:
# Check required data files are present in user's default download dir
# Define location where GOES data files are stored.
# Manually resolve the hostname
HOST = socket.gethostbyname_ex('hesperia.gsfc.nasa.gov')[0]
except socket.gaierror:
HOST = ''
GOES_REMOTE_PATH = "http://{0}/ssw/gen/idl/synoptic/goes/".format(HOST)
# Define variables for file names
FILE_TEMP_COR = "goes_chianti_temp_cor.csv"
FILE_TEMP_PHO = "goes_chianti_temp_pho.csv"
FILE_EM_COR = "goes_chianti_em_cor.csv"
FILE_EM_PHO = "goes_chianti_em_pho.csv"
FILE_RAD_COR = "chianti7p1_rad_loss.txt"
def get_goes_event_list(timerange, goes_class_filter=None):
"""
Retrieve list of flares detected by GOES within a given time range.
Parameters
----------
timerange : `sunpy.time.TimeRange`
The time range to download the event list for.
goes_class_filter: (optional) str
A string specifying a minimum GOES class for inclusion in the list,
e.g. 'M1', 'X2'.
"""
# Importing hek here to avoid calling code that relies on optional dependencies.
from sunpy.net import hek
# use HEK module to search for GOES events
client = hek.HEKClient()
event_type = 'FL'
tstart = timerange.start
tend = timerange.end
# query the HEK for a list of events detected by the GOES instrument
# between tstart and tend (using a GOES-class filter)
if goes_class_filter:
result = client.search(hek.attrs.Time(tstart, tend),
hek.attrs.EventType(event_type),
hek.attrs.FL.GOESCls > goes_class_filter,
hek.attrs.OBS.Observatory == 'GOES')
else:
result = client.search(hek.attrs.Time(tstart, tend),
hek.attrs.EventType(event_type),
hek.attrs.OBS.Observatory == 'GOES')
# want to condense the results of the query into a more manageable
# dictionary
# keep event data, start time, peak time, end time, GOES-class,
# location, active region source (as per GOES list standard)
# make this into a list of dictionaries
goes_event_list = []
for r in result:
goes_event = {
'event_date': parse_time(r['event_starttime']).strftime(
'%Y-%m-%d'),
'start_time': parse_time(r['event_starttime']),
'peak_time': parse_time(r['event_peaktime']),
'end_time': parse_time(r['event_endtime']),
'goes_class': str(r['fl_goescls']),
'goes_location': (r['event_coord1'], r['event_coord2']),
'noaa_active_region': r['ar_noaanum']
}
goes_event_list.append(goes_event)
return goes_event_list
def calculate_temperature_em(goests, abundances="coronal",
download=False, download_dir=None):
"""
Calculates temperature and emission measure from a `~sunpy.timeseries.XRSTimeSeries`.
This function calculates the isothermal temperature and
corresponding volume emission measure of the solar soft X-ray
emitting plasma observed by the GOES/XRS. This is done using the
observed flux ratio of the short (0.5-4 angstrom) to long (1-8 angstrom)
channels. The results are returned in a new LightCurve object which
contains metadata and flux data of the input LightCurve object in
addition to the newly found temperature and emission measure values.
Parameters
----------
goeslc : `~sunpy.timeseries.XRSTimeSeries`
LightCurve object containing GOES flux data which MUST
be in units of W/m^2.
abundances : (optional) string equalling 'coronal' or 'photospheric'
States whether photospheric or coronal abundances should be
assumed.
Default='coronal'
download : (optional) `bool`
If True, the GOES temperature and emission measure data files are
downloaded. It is important to do this if a new version of the files
has been generated due to a new CHIANTI version being released or the
launch of new GOES satellites since these files were last downloaded.
Default=False
download_dir : (optional) `string`
The directory to download the GOES temperature and emission measure
data files to.
Default=SunPy default download directory
Returns
-------
ts_new : `~sunpy.timeseries.XRSTimeSeries`
Contains same metadata and data as input timeseries with the
following two additional data columns:
| ts_new.data.temperature - Array of temperatures [MK]
| ts_new.data.em - Array of volume emission measures [cm**-3]
Notes
-----
The temperature and volume emission measure are calculated here
using the methods of White et al. (2005) who used the
CHIANTI atomic physics database to model the response of the ratio
of the short (0.5-4 angstrom) to long (1-8 angstrom) channels of the
XRSs onboard various GOES satellites. This method assumes an
isothermal plasma, the ionisation equilibria of
[2]_, and a constant density of 10**10 cm**-3.
(See [1]_ for justification of this last assumption.)
This function is based on goes_chianti_tem.pro in SolarSoftWare
written in IDL by Stephen White.
Recent fluxes released to the public are scaled to be consistent
with GOES-7. In fact these recent fluxes are correct and so this
correction must be removed before proceeding to use transfer
functions.
Email Rodney Viereck (NOAA) for more information.
Measurements of short channel flux of less than 1e-10 W/m**2 or
long channel flux less than 3e-8 W/m**2 are not considered good.
Ratio values corresponding to such fluxes are set to 0.003.
References
----------
.. [1] White, S. M., Thomas, R. J., & Schwartz, R. A. 2005,
Sol. Phys., 227, 231, DOI: 10.1007/s11207-005-2445-z
.. [2] Mazzotta, P., Mazzitelli, G., Colafrancesco, S., &
Vittorio, N. 1998, A&AS, 133, 339, DOI: 10.1051/aas:1998330
Examples
--------
>>> import sunpy.timeseries as ts
>>> from sunpy.instr.goes import calculate_temperature_em
>>> from sunpy.data.sample import GOES_XRS_TIMESERIES # doctest: +REMOTE_DATA
>>> goests = ts.TimeSeries(GOES_XRS_TIMESERIES) # doctest: +REMOTE_DATA
>>> goests.data[0:10] # doctest: +REMOTE_DATA
xrsa xrsb
2011-06-06 23:59:59.961999893 1.000000e-09 1.887100e-07
2011-06-07 00:00:02.008999944 1.000000e-09 1.834600e-07
2011-06-07 00:00:04.058999896 1.000000e-09 1.860900e-07
2011-06-07 00:00:06.104999900 1.000000e-09 1.808400e-07
2011-06-07 00:00:08.151999950 1.000000e-09 1.860900e-07
2011-06-07 00:00:10.201999903 1.000000e-09 1.808400e-07
2011-06-07 00:00:12.248999953 1.000000e-09 1.860900e-07
2011-06-07 00:00:14.298999906 1.000000e-09 1.834600e-07
2011-06-07 00:00:16.344999909 1.000000e-09 1.808400e-07
2011-06-07 00:00:18.391999960 1.000000e-09 1.834600e-07
>>> goests_new = calculate_temperature_em(goests) # doctest: +REMOTE_DATA
>>> goests_new.data[0:10] # doctest: +REMOTE_DATA
xrsa xrsb temperature em
2011-06-06 23:59:59.961999893 1.000000e-09 1.887100e-07 3.503510 2.190626e+48
2011-06-07 00:00:02.008999944 1.000000e-09 1.834600e-07 3.534262 2.055847e+48
2011-06-07 00:00:04.058999896 1.000000e-09 1.860900e-07 3.518700 2.122771e+48
2011-06-07 00:00:06.104999900 1.000000e-09 1.808400e-07 3.550100 1.990333e+48
2011-06-07 00:00:08.151999950 1.000000e-09 1.860900e-07 3.518700 2.122771e+48
2011-06-07 00:00:10.201999903 1.000000e-09 1.808400e-07 3.550100 1.990333e+48
2011-06-07 00:00:12.248999953 1.000000e-09 1.860900e-07 3.518700 2.122771e+48
2011-06-07 00:00:14.298999906 1.000000e-09 1.834600e-07 3.534262 2.055847e+48
2011-06-07 00:00:16.344999909 1.000000e-09 1.808400e-07 3.550100 1.990333e+48
2011-06-07 00:00:18.391999960 1.000000e-09 1.834600e-07 3.534262 2.055847e+48
"""
# Check that input argument is of correct type
if not isinstance(goests, timeseries.XRSTimeSeries):
raise TypeError("goests must be a XRSTimeSeries object")
if not download_dir:
download_dir = get_and_create_download_dir()
# Find temperature and emission measure with _goes_chianti_tem
temp, em = _goes_chianti_tem(
goests.quantity("xrsb"),
goests.quantity("xrsa"),
satellite=goests.meta.metas[0]["TELESCOP"].split()[1],
date=goests.data.index[0],
abundances=abundances, download=download, download_dir=download_dir)
ts_new = timeseries.XRSTimeSeries(meta=copy.deepcopy(goests.meta),
data=copy.deepcopy(goests.data),
units=copy.deepcopy(goests.units))
ts_new = ts_new.add_column("temperature", temp)
ts_new = ts_new.add_column("em", em)
return ts_new
@u.quantity_input
def _goes_chianti_tem(longflux: u.W/u.m/u.m, shortflux: u.W/u.m/u.m, satellite=8,
date=datetime.datetime.today(), abundances="coronal",
download=False, download_dir=None):
"""
Calculates temperature and emission measure from GOES/XRS data.
This function calculates the isothermal temperature and volume
emission measure of the solar soft X-ray emitting plasma observed by
the GOES/XRS. This is done using the observed flux ratio of the
short (0.5-4 angstrom) to long (1-8 angstrom) channels.
Parameters
----------
longflux, shortflux : `~astropy.units.Quantity`
Arrays containing the long and short GOES/XRS flux measurements
respectively as a function of time. Must be of same length. [W/m**2].
satellite : int (optional)
Number of GOES satellite used to make observations, important for
correct calibration of data.
Default=8
date : `astropy.time.Time` or `str`
Date when observations made. Important for correctcalibration.
Default=today
abundances : (optional) string equalling 'coronal' or 'photospheric'
States whether photospheric or coronal abundances should be
assumed.
Default='coronal'
download : (optional) bool
If True, the GOES temperature and emission measure data files are
downloaded. It is important to do this if a new version of the files
has been generated due to a new CHIANTI version being released or the
launch of new GOES satellites since these files were last downloaded.
Default=False
download_dir : (optional) str
The directory to download the GOES temperature and emission measure
data files to.
Default=SunPy default download directory
Returns
-------
temp : `~astropy.units.Quantity`
Array of temperature values of same length as longflux and
shortflux. Units=[MK]
em : `~astropy.units.Quantity`
Array of volume emission measure values of same length as longflux
and shortflux. Units=[10**49 cm**-3]
Notes
-----
The temperature and volume emission measure are calculated here
using the methods of [1]_ who used the
CHIANTI atomic physics database to model the response of the ratio
of the short (0.5-4 angstrom) to long (1-8 angstrom) channels of the
XRSs onboard various GOES satellites. This method assumes an
isothermal plasma, the ionisation equilibria of
[2]_, and a constant density of 10**10 cm**-3.
(See White et al. 2005 for justification of this last assumption.)
This function is based on goes_chianti_tem.pro in SolarSoftWare
written in IDL by Stephen White.
Recent fluxes released to the public are scaled to be consistent
with GOES-7. In fact these recent fluxes are correct and so this
correction must be removed before proceeding to use transfer
functions.
Email Rodney Viereck (NOAA) for more information.
Measurements of short channel flux of less than 1e-10 W/m**2 or
long channel flux less than 3e-8 W/m**2 are not considered good.
Ratio values corresponding to such fluxes are set to 0.003.
References
----------
.. [1] White, S. M., Thomas, R. J., & Schwartz, R. A. 2005,
Sol. Phys., 227, 231, DOI: 10.1007/s11207-005-2445-z
.. [2] Mazzotta, P., Mazzitelli, G., Colafrancesco, S., &
Vittorio, N. 1998, A&AS, 133, 339, DOI: 10.1051/aas:1998330
Examples
--------
>>> from sunpy.instr.goes import _goes_chianti_tem
>>> from astropy.units import Quantity
>>> longflux = Quantity([7e-6, 7e-6], unit="W/m/m")
>>> shortflux = Quantity([7e-7, 7e-7], unit="W/m/m")
>>> temp, em = _goes_chianti_tem(longflux, shortflux, satellite=15,
... date='2014-04-16',
... abundances="coronal") # doctest: +REMOTE_DATA
>>> temp # doctest: +REMOTE_DATA
<Quantity [11.28295376, 11.28295376] MK>
>>> em # doctest: +REMOTE_DATA
<Quantity [4.78577516e+48, 4.78577516e+48] 1 / cm3>
"""
if not download_dir:
download_dir = get_and_create_download_dir()
# ENSURE INPUTS ARE OF CORRECT TYPE AND VALID VALUES
longflux = longflux.to(u.W/u.m/u.m)
shortflux = shortflux.to(u.W/u.m/u.m)
satellite = int(satellite)
if satellite < 1:
raise ValueError("satellite must be the number of a "
"valid GOES satellite (>1).")
date = parse_time(date)
# Check flux arrays are of same length.
if len(longflux) != len(shortflux):
raise ValueError(
"longflux and shortflux must have same number of elements.")
# PREPARE DATA
# GOES 6 long channel flux before 1983-Jun-28 must be corrected by a
# factor of 4.43/5.32
if date < parse_time((1983, 6, 28)) and satellite == 6:
longflux_corrected = longflux*(4.43/5.32)
else:
longflux_corrected = longflux
# Un-scale fluxes if GOES satellite is after 7. See 2nd paragraph
# in Notes section of docstring above.
if satellite > 7:
longflux_corrected = longflux_corrected / 0.7
shortflux_corrected = shortflux / 0.85
else:
shortflux_corrected = shortflux
# Calculate short to long channel ratio.
# Data which is not good have their ratio value set to 0.003.
# See Notes section in docstring above.
index = np.logical_or(
shortflux_corrected < u.Quantity(1e-10, unit="W/m**2"),
longflux_corrected < u.Quantity(3e-8, unit="W/m**2"))
fluxratio = shortflux_corrected / longflux_corrected
fluxratio.value[index] = u.Quantity(0.003, unit="W/m**2")
# FIND TEMPERATURE AND EMISSION MEASURE FROM FUNCTIONS BELOW
temp = _goes_get_chianti_temp(fluxratio, satellite=satellite,
abundances=abundances, download=download,
download_dir=download_dir)
em = _goes_get_chianti_em(longflux_corrected, temp, satellite=satellite,
abundances=abundances, download=download,
download_dir=download_dir)
return temp, em
@u.quantity_input
def _goes_get_chianti_temp(fluxratio: u.one, satellite=8, abundances="coronal",
download=False, download_dir=None):
"""
Calculates temperature from GOES flux ratio.
This function calculates the isothermal temperature of the solar
soft X-ray emitting plasma observed by the GOES/XRS from the
observed flux ratio of the short (0.5-4 angstrom) to
long (1-8 angstrom) channels. This function is not intended to be
called directly but by _goes_chianti_tem(), although it can be used
independently. However, if used independently data preparation,
such as correctly rescaling fluxes for some satellites etc. will
not be carried out. This is done in _goes_chianti_tem().
Parameters
----------
fluxratio : `~astropy.units.Quantity`
Array containing the ratio of short channel to long channel
GOES/XRS flux measurements.
satellite : int (optional)
Number of GOES satellite used to make observations. Important for
correct calibration of data.
Default=8
abundances : (optional) string equalling 'coronal' or 'photospheric'
States whether photospheric or coronal abundances should be
assumed.
Default='coronal'
download : (optional) bool
If True, the GOES temperature data files are downloaded.
It is important to do this if a new version of the files has been
generated due to a new CHIANTI version being released or the launch
of new GOES satellites since these files were last downloaded.
Default=False
download_dir : (optional) str
The directory to download the GOES temperature data file to.
Default=SunPy default download directory
Returns
-------
temp : `~astropy.units.Quantity`
Array of temperature values of same length as longflux and
shortflux. Units=[MK]
Notes
-----
This function uses csv files representing the modelled relationship
between temperature of the soft X-ray emitting plasma and the
short to long channel GOES flux ratio. goes_chianti_temp_cor.csv
is used when coronal abundances are assumed while
goes_chianti_temp_pho.csv is used when photospheric abundances are
assumed. (See make_goes_chianti_temp.py for more detail.)
These files were calculated using the methods of [1]_
who used the CHIANTI atomic physics database to model the response
of the ratio of the short (0.5-4 angstrom) to long (1-8 angstrom)
channels of the XRSs onboard various GOES satellites. This method
assumes an isothermal plasma, the ionisation equilibria of
(See White et al. 2005 for justification of this last assumption.)
This function is based on goes_get_chianti_temp.pro in
SolarSoftWare written in IDL by Stephen White.
For correct preparation of GOES data before calculating temperature
see _goes_chianti_tem() (Notes section of docstring).
References
----------
.. [1] White, S. M., Thomas, R. J., & Schwartz, R. A. 2005,
Sol. Phys., 227, 231, DOI: 10.1007/s11207-005-2445-z
.. [2] Mazzotta, P., Mazzitelli, G., Colafrancesco, S., &
Vittorio, N. 1998, A&AS, 133, 339, DOI: 10.1051/aas:1998330
Examples
--------
>>> from astropy.units import Quantity
>>> from sunpy.instr.goes import _goes_get_chianti_temp
>>> fluxratio = Quantity([0.1,0.1])
>>> temp = _goes_get_chianti_temp(fluxratio, satellite=15,
... abundances="coronal") # doctest: +REMOTE_DATA
>>> temp # doctest: +REMOTE_DATA
<Quantity [12.27557778, 12.27557778] MK>
"""
if not download_dir:
download_dir = get_and_create_download_dir()
# If download kwarg is True, or required data files cannot be
# found locally, download required data files.
check_download_file(FILE_TEMP_COR, GOES_REMOTE_PATH, download_dir,
replace=download)
check_download_file(FILE_TEMP_PHO, GOES_REMOTE_PATH, download_dir,
replace=download)
# check inputs are correct
fluxratio = fluxratio.decompose()
int(satellite)
if satellite < 1:
raise ValueError("satellite must be the number of a "
"valid GOES satellite (>1).")
# if abundance input is valid create file suffix, abund, equalling
# of 'cor' or 'pho'.
if abundances == "coronal":
data_file = FILE_TEMP_COR
elif abundances == "photospheric":
data_file = FILE_TEMP_PHO
else:
raise ValueError("abundances must be a string equalling "
"'coronal' or 'photospheric'.")
# Initialize lists to hold model data of flux ratio - temperature
# relationship read in from csv file
modeltemp = [] # modelled temperature is in log_10 space in units of MK
modelratio = []
# Determine name of column in csv file containing model ratio values
# for relevant GOES satellite
label = "ratioGOES{0}".format(satellite)
# Read data representing appropriate temperature--flux ratio
# relationship depending on satellite number and assumed abundances.
with open(os.path.join(get_and_create_download_dir(), data_file), "r") as csvfile:
startline = dropwhile(lambda l: l.startswith("#"), csvfile)
csvreader = csv.DictReader(startline, delimiter=";")
for row in csvreader:
modeltemp.append(float(row["log10temp_MK"]))
modelratio.append(float(row[label]))
modeltemp = np.asarray(modeltemp)
modelratio = np.asarray(modelratio)
# Ensure input values of flux ratio are within limits of model table
if np.min(fluxratio) < np.min(modelratio) or \
np.max(fluxratio) > np.max(modelratio):
raise ValueError(
"For GOES {0}, all values in fluxratio input must be within " +
"the range {1} - {2}.".format(satellite, np.min(modelratio),
np.max(modelratio)))
# Perform spline fit to model data to get temperatures for input
# values of flux ratio
spline = interpolate.splrep(modelratio, modeltemp, s=0)
temp = 10.**interpolate.splev(fluxratio.value, spline, der=0)
temp = u.Quantity(temp, unit='MK')
return temp
@u.quantity_input
def _goes_get_chianti_em(longflux: u.W/u.m/u.m, temp: u.MK, satellite=8,
abundances="coronal", download=False,
download_dir=None):
"""
Calculates emission measure from GOES 1-8A flux and temperature.
This function calculates the emission measure of the solar
soft X-ray emitting plasma observed by the GOES/XRS from the
the ratio of the isothermal temperature and observed long channel
(1-8 angstrom) flux which scales with the emission measure.
This function is not intended to be called directly but by
_goes_chianti_tem(), although it can be used independently.
However, if used independently data preparation, such as correctly
rescaling fluxes for some satellites etc. will not be carried out.
This is done in _goes_chianti_tem().
Parameters
----------
longflux : `~astropy.units.Quantity`
Array containing the observed GOES/XRS long channel flux.
Units=[W/m**2]
temp : `~astropy.units.Quantity`
Array containing the GOES temperature. Units=[MK]
satellite : int (optional)
Number of GOES satellite used to make observations.
Important for correct calibration of data.
Default=8
abundances : (optional) {'coronal' | 'photospheric'}
States whether photospheric or coronal abundances should be
assumed.
Default='coronal'
download : (optional) `bool`
If True, the GOES emission measure data file is downloaded.
It is important to do this if a new version of the file has been
generated due to a new CHIANTI version being released or the launch of
new GOES satellites since these file was last downloaded.
Default=False
download_dir : (optional) `str`
The directory to download the GOES emission measure data file to.
Default=SunPy default download directory
Returns
-------
em : `~astropy.units.Quantity`
Array of emission measure values of same length as longflux
and temp. [cm**-3]
Notes
-----
This function uses csv files representing the modelled relationship
between the temperature of the solar soft X-ray emitting plasma
and the resulting observed flux in the GOES/XRS long channel
(1-8 angstroms). goes_chianti_em_cor.csv is used when coronal
abundances are assumed while goes_chianti_em_pho.csv is used when
photospheric abundances are assumed.
(See make_goes_chianti_temp.py for more detail.)
These files were calculated using the methods of White et al. (2005)
who used the CHIANTI atomic physics database and GOES transfer
functions to model the response of the long channel to the
temperature of the emitting plasma for XRSs onboard various GOES
satellites. The emission measure can then be found by scaling the
ratio of these two properties. This method assumes an isothermal
plasma, the ionisation equilibria of Mazzotta et al. (1998), and
a constant density of 10**10 cm**-3.
(See White et al. 2005 for justification of this last assumption.)
This function is based on goes_get_chianti_temp.pro in
SolarSoftWare written in IDL by Stephen White.
For correct preparation of GOES data before calculating temperature
see _goes_chianti_tem() (Notes section of docstring).
References
----------
.. [1] White, S. M., Thomas, R. J., & Schwartz, R. A. 2005,
Sol. Phys., 227, 231, DOI: 10.1007/s11207-005-2445-z
.. [2] Mazzotta, P., Mazzitelli, G., Colafrancesco, S., &
Vittorio, N. 1998, A&AS, 133, 339, DOI: 10.1051/aas:1998330
Examples
--------
>>> import astropy.units as u
>>> from sunpy.instr.goes import _goes_get_chianti_em
>>> longflux = u.Quantity([7e-6,7e-6], unit=u.W/u.m/u.m)
>>> temp = u.Quantity([11, 11], unit=u.MK)
>>> em = _goes_get_chianti_em(longflux, temp, satellite=15,
... abundances="coronal") # doctest: +REMOTE_DATA
>>> em # doctest: +REMOTE_DATA
<Quantity [3.45200672e+48, 3.45200672e+48] 1 / cm3>
"""
if not download_dir:
download_dir = get_and_create_download_dir()
# If download kwarg is True, or required data files cannot be
# found locally, download required data files.
check_download_file(FILE_EM_COR, GOES_REMOTE_PATH, download_dir,
replace=download)
check_download_file(FILE_EM_PHO, GOES_REMOTE_PATH, download_dir,
replace=download)
# Check inputs are of correct type
longflux = longflux.to(u.W/u.m**2)
temp = temp.to(u.MK)
log10_temp = np.log10(temp.value)
int(satellite)
if satellite < 1:
raise ValueError("satellite must be the number of a "
"valid GOES satellite (>1).")
# if abundance input is valid create file suffix, abund, equalling
# of 'cor' or 'pho'.
if abundances == "coronal":
data_file = FILE_EM_COR
elif abundances == "photospheric":
data_file = FILE_EM_PHO
else:
raise ValueError("abundances must be a string equalling "
"'coronal' or 'photospheric'.")
# check input arrays are of same length
if len(longflux) != len(temp):
raise ValueError("longflux and temp must have same number of "
"elements.")
# Initialize lists to hold model data of temperature - long channel
# flux relationship read in from csv file.
modeltemp = [] # modelled temperature is in log_10 space in units of MK
modelflux = []
# Determine name of column in csv file containing model ratio values
# for relevant GOES satellite
label = "longfluxGOES{0}".format(satellite)
# Read data representing appropriate temperature--long flux
# relationship depending on satellite number and assumed abundances.
with open(os.path.join(get_and_create_download_dir(), data_file), "r") as csvfile:
startline = dropwhile(lambda l: l.startswith("#"), csvfile)
csvreader = csv.DictReader(startline, delimiter=";")
for row in csvreader:
modeltemp.append(float(row["log10temp_MK"]))
modelflux.append(float(row[label]))
modeltemp = np.asarray(modeltemp)
modelflux = np.asarray(modelflux)
# Ensure input values of flux ratio are within limits of model table
if np.min(log10_temp) < np.min(modeltemp) or \
np.max(log10_temp) > np.max(modeltemp) or \
np.isnan(np.min(log10_temp)):
raise ValueError("All values in temp must be within the range "
"{0} - {1} MK.".format(np.min(10**modeltemp),
np.max(10**modeltemp)))
# Perform spline fit to model data
spline = interpolate.splrep(modeltemp, modelflux, s=0)
denom = interpolate.splev(log10_temp, spline, der=0)
em = longflux.value/denom * 1e55
em = u.Quantity(em, unit='cm**(-3)')
return em
def calculate_radiative_loss_rate(goests, force_download=False,
download_dir=None):
"""
Calculates radiative loss rate from GOES observations.
This function calculates the radiative loss rate as a function of
time of solar soft X-ray-emitting plasma across all wavelengths given a
LightCurve object containing GOES data. The radiative loss rate is
determined from the GOES isothermal temperature and volume emission
measure as a function of time, as calculated by
`~calculate_temperature_em()`. See docstring of that function for more
details. If the LightCurve object does not contain the temperatures and
emission measures, but only contain the GOES fluxes, then the temperature
and emission measures are calculated using calculate_temperature_em().
The unit of the resulting radiative loss rates is W. Once
the radiative loss rates have been found, they are returned as part of a
new LightCurve object also containing the metadata, GOES fluxes and
corresponding temperatures and emission measures of the input LightCurve
object.
Parameters
----------
goests : `~sunpy.timeseries.XRSTimeSeries`
TimeSeries object containing GOES data. The units of these
data MUST be W/m^2 (flux), MK (temperature) and cm^-3
(emission measure). If LightCurve object does not contain
temperature and emission measure values, they are calculated from
the flux values using calculate_temperature_em().
force_download : (optional) `bool`
If True, the GOES radiative loss data file is downloaded even if
already locally stored. It is important to do this if a new version
of the file has been generated due to a new CHIANTI version being
released or the launch of new GOES satellites.
Default=False
download_dir : (optional) `str`
The directory to download the GOES radiative loss data file to.
Default=SunPy default download directory
Returns
-------
ts_new : `~sunpy.timeseries.XRSTimeSeries`
Contains same metadata and data as input LightCurve with the
following additional data columns:
| ts_new.data.temperature - Array of temperature values [MK]
| ts_new.data.em - Array of volume emission measure values [cm**-3]
| ts_new.data.rad_loss_rate - radiative loss rate of the coronal soft
X-ray-emitting plasma across all wavelengths [W]
Notes
-----
The GOES radiative loss rates are calculated using a csv file containing
a table of radiative loss rate per unit emission measure at various
temperatures. The appropriate values are then found via interpolation.
This table was generated using CHIANTI atomic physics database employing
the methods of [1]_. Coronal abundances, a default
density of 10**10 cm**-3, and ionization equilibrium of
[2]_ were used.
References
----------
.. [1] Cox, D.P., Tucker, W.H. 1969, ApJ, 157, 1157, DOI: 10.1086/150144
.. [2] Mazzotta, P., Mazzitelli, G., Colafrancesco, S., & Vittorio, N.
1998, A&AS, 133, 339, DOI: 10.1051/aas:1998330
Examples
--------
>>> import sunpy.timeseries as ts
>>> from sunpy.instr.goes import calculate_radiative_loss_rate
>>> from sunpy.data.sample import GOES_XRS_TIMESERIES # doctest: +REMOTE_DATA
>>> goests = ts.TimeSeries(GOES_XRS_TIMESERIES) # doctest: +REMOTE_DATA
>>> goests.data[0:10] # doctest: +REMOTE_DATA
xrsa xrsb
2011-06-06 23:59:59.961999893 1.000000e-09 1.887100e-07
2011-06-07 00:00:02.008999944 1.000000e-09 1.834600e-07
2011-06-07 00:00:04.058999896 1.000000e-09 1.860900e-07
2011-06-07 00:00:06.104999900 1.000000e-09 1.808400e-07
2011-06-07 00:00:08.151999950 1.000000e-09 1.860900e-07
2011-06-07 00:00:10.201999903 1.000000e-09 1.808400e-07
2011-06-07 00:00:12.248999953 1.000000e-09 1.860900e-07
2011-06-07 00:00:14.298999906 1.000000e-09 1.834600e-07
2011-06-07 00:00:16.344999909 1.000000e-09 1.808400e-07
2011-06-07 00:00:18.391999960 1.000000e-09 1.834600e-07
>>> goests_new = calculate_radiative_loss_rate(goests) # doctest: +REMOTE_DATA
>>> goests_new.data[0:10] # doctest: +REMOTE_DATA
xrsa xrsb temperature em rad_loss_rate
2011-06-06 23:59:59.961999893 1.000000e-09 1.887100e-07 3.503510 2.190626e+48 1.781001e+19
2011-06-07 00:00:02.008999944 1.000000e-09 1.834600e-07 3.534262 2.055847e+48 1.660031e+19
2011-06-07 00:00:04.058999896 1.000000e-09 1.860900e-07 3.518700 2.122771e+48 1.719931e+19
2011-06-07 00:00:06.104999900 1.000000e-09 1.808400e-07 3.550100 1.990333e+48 1.601718e+19
2011-06-07 00:00:08.151999950 1.000000e-09 1.860900e-07 3.518700 2.122771e+48 1.719931e+19
2011-06-07 00:00:10.201999903 1.000000e-09 1.808400e-07 3.550100 1.990333e+48 1.601718e+19
2011-06-07 00:00:12.248999953 1.000000e-09 1.860900e-07 3.518700 2.122771e+48 1.719931e+19
2011-06-07 00:00:14.298999906 1.000000e-09 1.834600e-07 3.534262 2.055847e+48 1.660031e+19
2011-06-07 00:00:16.344999909 1.000000e-09 1.808400e-07 3.550100 1.990333e+48 1.601718e+19
2011-06-07 00:00:18.391999960 1.000000e-09 1.834600e-07 3.534262 2.055847e+48 1.660031e+19
"""
if not download_dir:
download_dir = get_and_create_download_dir()
# Check that input argument is of correct type
if not isinstance(goests, timeseries.XRSTimeSeries):
raise TypeError("goests must be a XRSTimeSeries object.")
# extract temperature and emission measure from GOESLightCurve
# object and change type to that required by _calc_rad_loss().
# If LightCurve object does not contain temperature and
# emission measure, calculate using calculate_temperature_em()
if 'temperature' in goests.columns and 'em' in goests.columns:
# Use copy.deepcopy for replicating meta and data so that input
# lightcurve is not altered.
ts_new = timeseries.XRSTimeSeries(meta=copy.deepcopy(goests.meta),
data=copy.deepcopy(goests.data),
units=copy.deepcopy(goests.units))
else:
ts_new = calculate_temperature_em(goests)
temp = u.Quantity(np.asarray(ts_new.data.temperature, dtype=np.float64),
unit=u.MK)
em = u.Quantity(np.asarray(ts_new.data.em, dtype=np.float64),
unit=u.cm**(-3))
# Find radiative loss rate with _calc_rad_loss()
rad_loss_out = _calc_rad_loss(temp, em, force_download=force_download,
download_dir=download_dir)
# Enter results into new version of GOES LightCurve Object
ts_new = ts_new.add_column("rad_loss_rate", rad_loss_out['rad_loss_rate'].to("W"))
return ts_new
@u.quantity_input
def _calc_rad_loss(temp: u.MK, em: u.cm**-3, obstime=None, force_download=False,
download_dir=None):
"""
Finds radiative loss rate of coronal plasma over all wavelengths.
This function calculates the radiative loss rate of solar coronal
soft X-ray-emitting plasma across all wavelengths given an isothermal
temperature and emission measure. The units of the results are
W. This function is based on calc_rad_loss.pro in SSW IDL.
In addition, if obstime keyword is set, giving the times to which
the temperature and emission measure values correspond, the
radiated losses integrated over time are also calculated.
Parameters
----------
temp : `~astropy.units.Quantity`
Array containing the temperature of the coronal plasma at
different times. Units=[MK]
em : `~astropy.units.Quantity`
Array containing the emission measure of the coronal plasma
at the same times corresponding to the temperatures in temp.
Must be same length as temp. Units=[cm**-3]
obstime : (optional) array-like of `~sunpy.time.parse_time` parsable objects
Array of measurement times to which temperature and
emission measure values correspond. Must be same length
as temp and em. If this keyword is set, the integrated
radiated energy is calculated.
force_download : (optional) bool
If True, the GOES radiative loss data file is downloaded. It is
important to do this if a new version of the files has been
generated due to a new CHIANTI version being released or the
launch of new GOES satellites.
Default=False
download_dir : (optional) str
The directory to download the GOES radiative loss data file to.
Default=SunPy default download directory
Returns
-------
rad_loss_out : `dict` of `~astropy.units.quantity.Quantity` objects
Contains the following keys.
| "rad_loss_rate" - radiative loss rate of the soft X-ray-emitting
plasma across all wavelengths corresponding to temperatures and
emission measures in temp and em Quantity inputs.
| "rad_loss_cumul" - cumulative radiative losses as a function of
time. (Only if obstime kwarg is NOT None.)
| "rad_loss_int" - total radiative losses as a function of time.
(Only if obstime kwarg is not None.) Array containing radiative
loss rates of the coronal plasma corresponding to temperatures and
emission measures in temp and em arrays.
Notes
-----
This function calls a csv file containing a table of radiative loss
rate per unit emission measure at various temperatures. The
appropriate values are then found via interpolation. This table
was generated using CHIANTI atomic physics database employing the
methods of Cox & Tucker (1969). Coronal abundances, a default
density of 10**10 cm**-3, and ionization equilibrium of
Mazzotta et al. (1998) were used.
References
----------
.. [1] Cox, D.P., Tucker, W.H. 1969, ApJ, 157, 1157, DOI: 10.1086/150144
.. [2] Mazzotta, P., Mazzitelli, G., Colafrancesco, S., & Vittorio, N.
1998, A&AS, 133, 339, DOI: 10.1051/aas:1998330
Examples
--------
>>> from sunpy.instr.goes import _calc_rad_loss
>>> from astropy.units.quantity import Quantity
>>> temp = Quantity([11.0, 11.0], unit="MK")
>>> em = Quantity([4.0e+48, 4.0e+48], unit="cm**(-3)")
>>> rad_loss = _calc_rad_loss(temp, em) # doctest: +REMOTE_DATA
>>> rad_loss["rad_loss_rate"] # doctest: +REMOTE_DATA
<Quantity [3.01851392e+19, 3.01851392e+19] J / s>
"""
if not download_dir:
download_dir = get_and_create_download_dir()
# Check inputs are correct
temp = temp.to(u.K)
em = em.to(1/u.cm**3)
if len(temp) != len(em):
raise ValueError("temp and em must all have same number of elements.")
# If force_download kwarg is True, or required data files cannot be
# found locally, download required data files.
check_download_file(FILE_RAD_COR, GOES_REMOTE_PATH, download_dir,
replace=force_download)
# Initialize lists to hold model data of temperature - rad loss rate
# relationship read in from csv file
modeltemp = [] # modelled temperature is in log_10 space in units of MK
model_loss_rate = []
# Read data from csv file into lists, being sure to skip commented
# lines beginning with "#"
with open(os.path.join(get_and_create_download_dir(), FILE_RAD_COR),
"r") as csvfile:
startline = csvfile.readlines()[7:]
csvreader = csv.reader(startline, delimiter=" ")
for row in csvreader:
modeltemp.append(float(row[0]))
model_loss_rate.append(float(row[1]))
modeltemp = np.asarray(modeltemp)
model_loss_rate = np.asarray(model_loss_rate)
# Ensure input values of flux ratio are within limits of model table
if temp.value.min() < modeltemp.min() or \
temp.value.max() > modeltemp.max():
raise ValueError("All values in temp must be within the range " +
"{0} - {1} MK.".format(np.min(modeltemp/1e6),
np.max(modeltemp/1e6)))
# Perform spline fit to model data to get temperatures for input
# values of flux ratio
spline = interpolate.splrep(modeltemp, model_loss_rate, s=0)
rad_loss = em.value * interpolate.splev(temp.value, spline, der=0)
rad_loss = u.Quantity(rad_loss, unit='erg/s')
rad_loss = rad_loss.to(u.J/u.s)
# If obstime keyword giving measurement times is set, calculate
# radiative losses integrated over time.
if obstime is not None:
# First ensure obstime is of same length as temp and em and of