PyThea 0.8.1__tar.gz → 0.9.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {PyThea-0.8.1 → PyThea-0.9.0}/CHANGELOG.md +16 -0
- {PyThea-0.8.1/PyThea.egg-info → PyThea-0.9.0}/PKG-INFO +3 -1
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/PyThea_app.py +48 -36
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/_version.py +2 -2
- PyThea-0.9.0/PyThea/data/sample_data.py +15 -0
- PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py +19 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py +64 -1
- PyThea-0.9.0/PyThea/sunpy_dev/map/__init__.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/sunpy_dev/map/maputils.py +31 -19
- PyThea-0.9.0/PyThea/test/__init__.py +0 -0
- PyThea-0.9.0/PyThea/test/test_remote_clients.py +88 -0
- PyThea-0.9.0/PyThea/test/test_utils.py +78 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/utils.py +84 -56
- {PyThea-0.8.1 → PyThea-0.9.0/PyThea.egg-info}/PKG-INFO +3 -1
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/SOURCES.txt +4 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/requires.txt +2 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/environment.yml +2 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/requirements.txt +2 -0
- PyThea-0.8.1/PyThea/test/test_remote_clients.py +0 -25
- PyThea-0.8.1/PyThea/test/test_utils.py +0 -20
- {PyThea-0.8.1 → PyThea-0.9.0}/LICENSE.md +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/MANIFEST.in +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/__init__.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/callbacks.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/config/__init__.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/config/app_styles.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/config/config_sliders.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/config/selected_bodies.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/config/selected_imagers.py +0 -0
- {PyThea-0.8.1/PyThea/extensions → PyThea-0.9.0/PyThea/data}/__init__.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/extensions/LICENSE_gcs_python.md +0 -0
- {PyThea-0.8.1/PyThea/sunpy_dev → PyThea-0.9.0/PyThea/extensions}/__init__.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/extensions/buttons.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/geometrical_models.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/modules.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/pythea_cli.py +0 -0
- {PyThea-0.8.1/PyThea/sunpy_dev/extern → PyThea-0.9.0/PyThea/sunpy_dev}/__init__.py +0 -0
- {PyThea-0.8.1/PyThea/sunpy_dev/extern/sunkit_instruments → PyThea-0.9.0/PyThea/sunpy_dev/extern}/__init__.py +0 -0
- {PyThea-0.8.1/PyThea/sunpy_dev/extern/sunkit_instruments/stereo → PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments}/__init__.py +0 -0
- {PyThea-0.8.1/PyThea/sunpy_dev/map → PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments/lasco}/__init__.py +0 -0
- {PyThea-0.8.1/PyThea/test → PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments/stereo}/__init__.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/test/Pythea_test.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/test/conftest.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/test/test_geometrical_models.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/test/test_imports.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea/version.py +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/.DS_Store +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/dependency_links.txt +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/entry_points.txt +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/not-zip-safe +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/PyThea.egg-info/top_level.txt +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/README.md +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/README_pypi.md +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/pyproject.toml +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/setup.cfg +0 -0
- {PyThea-0.8.1 → PyThea-0.9.0}/setup.py +0 -0
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
# v0.9.0 (8-Feb-2024)
|
|
2
|
+
|
|
3
|
+
## Features
|
|
4
|
+
- Adds sample data methods for testing and document builds
|
|
5
|
+
- Adds tests: test_get_horizons_coord, test_vso_search, test_hek_client, and test_parameter_fit_polynomial
|
|
6
|
+
- Adds calibration for AIA, LASCO, and STEREO EUVI
|
|
7
|
+
|
|
8
|
+
## Major Changes
|
|
9
|
+
- Changes the fitting time input from map.date to map.date_average when exist (see #24)
|
|
10
|
+
|
|
11
|
+
## Minor Changes
|
|
12
|
+
- Improves the following utilities: make_figure, maps_process, plot_fitting_model
|
|
13
|
+
- Improves the test_get_hek_flare and test_hek_client tests
|
|
14
|
+
- Changes how best_fit_x is defined in the parameters_fit utility
|
|
15
|
+
- Changes the pipeline of imaging data download and processing in the app
|
|
16
|
+
|
|
1
17
|
# v0.8.1 (13-Jan-2024)
|
|
2
18
|
|
|
3
19
|
## Features
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyThea
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: PyThea: A software package to reconstruct the 3D structure of CMEs and shock waves
|
|
5
5
|
Home-page: https://github.com/AthKouloumvakos/PyThea
|
|
6
6
|
Author: Athanasios Kouloumvakos
|
|
@@ -23,11 +23,13 @@ License-File: LICENSE.md
|
|
|
23
23
|
Requires-Dist: numpy
|
|
24
24
|
Requires-Dist: pandas
|
|
25
25
|
Requires-Dist: scipy
|
|
26
|
+
Requires-Dist: aiapy
|
|
26
27
|
Requires-Dist: astropy
|
|
27
28
|
Requires-Dist: astroquery
|
|
28
29
|
Requires-Dist: numexpr
|
|
29
30
|
Requires-Dist: sunpy==4.1.0
|
|
30
31
|
Requires-Dist: parfive==1.5.1
|
|
32
|
+
Requires-Dist: pooch
|
|
31
33
|
Requires-Dist: matplotlib
|
|
32
34
|
Requires-Dist: seaborn
|
|
33
35
|
Requires-Dist: streamlit
|
|
@@ -33,8 +33,9 @@ from PyThea.modules import (date_and_event_selection, final_parameters_gmodel,
|
|
|
33
33
|
fitting_and_slider_options_container,
|
|
34
34
|
fitting_sliders, maps_clims)
|
|
35
35
|
from PyThea.sunpy_dev.map.maputils import get_closest
|
|
36
|
-
from PyThea.utils import (download_fits, make_figure,
|
|
37
|
-
|
|
36
|
+
from PyThea.utils import (download_fits, make_figure, model_fittings,
|
|
37
|
+
plot_bodies, plot_fitting_model,
|
|
38
|
+
single_imager_maps_process)
|
|
38
39
|
from PyThea.version import version
|
|
39
40
|
|
|
40
41
|
|
|
@@ -66,6 +67,10 @@ def footer_text():
|
|
|
66
67
|
**Version**: {version} (latest release [](https://github.com/AthKouloumvakos/PyThea/releases))
|
|
67
68
|
""")
|
|
68
69
|
left.image('https://github.com/AthKouloumvakos/PyThea/blob/master/docs/logo/pythea_logo.png?raw=true')
|
|
70
|
+
st.warning('''
|
|
71
|
+
⚠️ **NOTE: From PyThea >0.8.1 the JSON fitting files will be slightly different from the old ones.** ⚠️
|
|
72
|
+
* Due to a change in the fitting time input, the new fitting files may have slightly different times for the same images (see further information [here](https://github.com/AthKouloumvakos/PyThea/discussions/24)).
|
|
73
|
+
''')
|
|
69
74
|
st.markdown('---')
|
|
70
75
|
|
|
71
76
|
|
|
@@ -165,9 +170,7 @@ def run():
|
|
|
165
170
|
options=selected_imagers.imager_dict.keys(),
|
|
166
171
|
default=['LC2', 'LC3', 'COR2A'],
|
|
167
172
|
key='imagers_list')
|
|
168
|
-
select_imagers_form.form_submit_button(label='Submit'
|
|
169
|
-
on_click=delete_from_state,
|
|
170
|
-
kwargs={'vars': ['map', ]})
|
|
173
|
+
select_imagers_form.form_submit_button(label='Submit')
|
|
171
174
|
select_timerange_form = st.form(key='select_timerange_form', border=False)
|
|
172
175
|
imaging_time_range = select_timerange_form.slider('Time Range [hours]',
|
|
173
176
|
min_value=-3., max_value=6.,
|
|
@@ -175,7 +178,7 @@ def run():
|
|
|
175
178
|
key='imaging_time_range')
|
|
176
179
|
select_timerange_form.form_submit_button(label='Submit',
|
|
177
180
|
on_click=delete_from_state,
|
|
178
|
-
kwargs={'vars': ['map', 'map_']})
|
|
181
|
+
kwargs={'vars': ['map', 'map_', 'imagers_list_']})
|
|
179
182
|
|
|
180
183
|
with st.sidebar.expander('Processing Options'):
|
|
181
184
|
procoption_container = st.container()
|
|
@@ -184,7 +187,7 @@ def run():
|
|
|
184
187
|
st.session_state['imagers_list_'] = []
|
|
185
188
|
image_mode = procoption_container.selectbox('Map sequence processing',
|
|
186
189
|
options=['Running Diff.', 'Base Diff.', 'Plain'], key='image_mode',
|
|
187
|
-
on_change=delete_from_state, kwargs={'vars': ['map', ]})
|
|
190
|
+
on_change=delete_from_state, kwargs={'vars': ['map', 'imagers_list_']})
|
|
188
191
|
|
|
189
192
|
with st.sidebar.expander('Plot/View Options'):
|
|
190
193
|
plotviewopt_container = st.container()
|
|
@@ -198,22 +201,31 @@ def run():
|
|
|
198
201
|
#############################################################
|
|
199
202
|
# Download and Process the Images
|
|
200
203
|
# This part runs only if the map_ doesn't exits or the session_state.map_ does not contain all the imagers requested
|
|
201
|
-
|
|
204
|
+
imager_added = list(set(imagers_list) - set(st.session_state['imagers_list_']))
|
|
205
|
+
imager_removed = list(set(st.session_state['imagers_list_']) - set(imagers_list))
|
|
206
|
+
if 'map_' not in st.session_state or imager_added != []:
|
|
202
207
|
st.session_state.map_ = {} if 'map_' not in st.session_state else st.session_state.map_
|
|
203
|
-
|
|
208
|
+
st.session_state.map = {} if 'map' not in st.session_state else st.session_state.map
|
|
209
|
+
st.session_state['imagers_list_'] = imagers_list
|
|
210
|
+
progress_bar = stqdm.stqdm(imager_added, desc='Preparing to Download data')
|
|
204
211
|
for imager in progress_bar:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
else:
|
|
208
|
-
progress_bar.desc = f'Downloaded {imager} images from VSO'
|
|
212
|
+
progress_bar.desc = f'Downloaded {imager} images from VSO'
|
|
213
|
+
if imager not in st.session_state.map_:
|
|
209
214
|
st.session_state.map_[imager] = download_fits(st.session_state.date_process,
|
|
210
215
|
imager, time_range=imaging_time_range)
|
|
216
|
+
processed_images = single_imager_maps_process(st.session_state.map_[imager],
|
|
217
|
+
image_mode=image_mode,
|
|
218
|
+
**selected_imagers.imager_dict[imager][1])
|
|
219
|
+
if processed_images != []:
|
|
220
|
+
st.session_state.map[imager] = processed_images
|
|
221
|
+
else:
|
|
222
|
+
st.session_state.imagers_list_.remove(imager)
|
|
223
|
+
if imager_removed != []:
|
|
224
|
+
st.session_state['imagers_list_'] = imagers_list
|
|
225
|
+
for imager in imager_removed:
|
|
226
|
+
st.session_state.map_.pop(imager, None)
|
|
211
227
|
|
|
212
|
-
|
|
213
|
-
st.session_state.map, st.session_state.imagers_list_ = maps_process(st.session_state.map_,
|
|
214
|
-
imagers_list,
|
|
215
|
-
image_mode)
|
|
216
|
-
maps_clims(st, imagers_list)
|
|
228
|
+
maps_clims(st, st.session_state.imagers_list_)
|
|
217
229
|
|
|
218
230
|
if not st.session_state.imagers_list_:
|
|
219
231
|
st.error('No images have been downloaded or processed.') # TODO: Explain better
|
|
@@ -226,7 +238,7 @@ def run():
|
|
|
226
238
|
imager_select = col1.selectbox('Select an imager',
|
|
227
239
|
options=st.session_state.imagers_list_)
|
|
228
240
|
|
|
229
|
-
maps_date = [maps
|
|
241
|
+
maps_date = [getattr(maps, 'date_average', None) or getattr(maps, 'date', None) for maps in st.session_state.map[imager_select]]
|
|
230
242
|
if len(maps_date) > 1:
|
|
231
243
|
running_map_date = col2.select_slider('Slide to the image time',
|
|
232
244
|
options=maps_date, value=maps_date[0],
|
|
@@ -261,12 +273,12 @@ def run():
|
|
|
261
273
|
center = SkyCoord(np.sign(rcenter) * Spher_rep.to_cartesian(),
|
|
262
274
|
frame=frames.HeliographicCarrington,
|
|
263
275
|
observer='earth',
|
|
264
|
-
obstime=
|
|
276
|
+
obstime=running_map_date)
|
|
265
277
|
elif st.session_state.coord_system == 'HGS':
|
|
266
278
|
center = SkyCoord(np.sign(rcenter) * Spher_rep.to_cartesian(),
|
|
267
279
|
frame=frames.HeliographicStonyhurst,
|
|
268
280
|
observer='earth',
|
|
269
|
-
obstime=
|
|
281
|
+
obstime=running_map_date)
|
|
270
282
|
st.session_state.center = center
|
|
271
283
|
|
|
272
284
|
if st.session_state.geometrical_model == 'Spheroid':
|
|
@@ -373,29 +385,29 @@ def run():
|
|
|
373
385
|
|
|
374
386
|
if plt_kinematics_select == 'All':
|
|
375
387
|
col1, col2 = st.columns(2)
|
|
376
|
-
fig_ht = plot_fitting_model(st.session_state.model_fittings,
|
|
377
|
-
|
|
378
|
-
|
|
388
|
+
fig_ht, axis_ht = plot_fitting_model(st.session_state.model_fittings,
|
|
389
|
+
fit_args=fit_args_,
|
|
390
|
+
plt_type='HeightT')
|
|
379
391
|
col1.pyplot(fig_ht)
|
|
380
|
-
fig_vt = plot_fitting_model(st.session_state.model_fittings,
|
|
381
|
-
|
|
382
|
-
|
|
392
|
+
fig_vt, axis_vt = plot_fitting_model(st.session_state.model_fittings,
|
|
393
|
+
fit_args=fit_args_,
|
|
394
|
+
plt_type='SpeedT')
|
|
383
395
|
col2.pyplot(fig_vt)
|
|
384
396
|
elif plt_kinematics_select == 'Long-LatT':
|
|
385
397
|
fit_args_['bounds'] = ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf])
|
|
386
398
|
col1, col2 = st.columns(2)
|
|
387
|
-
fig_loT = plot_fitting_model(st.session_state.model_fittings,
|
|
388
|
-
|
|
389
|
-
|
|
399
|
+
fig_loT, axis_loT = plot_fitting_model(st.session_state.model_fittings,
|
|
400
|
+
fit_args=fit_args_,
|
|
401
|
+
plt_type='LongT')
|
|
390
402
|
col1.pyplot(fig_loT)
|
|
391
|
-
fig_laT = plot_fitting_model(st.session_state.model_fittings,
|
|
392
|
-
|
|
393
|
-
|
|
403
|
+
fig_laT, axis_laT = plot_fitting_model(st.session_state.model_fittings,
|
|
404
|
+
fit_args=fit_args_,
|
|
405
|
+
plt_type='LatT')
|
|
394
406
|
col2.pyplot(fig_laT)
|
|
395
407
|
else:
|
|
396
|
-
fig = plot_fitting_model(st.session_state.model_fittings,
|
|
397
|
-
|
|
398
|
-
|
|
408
|
+
fig, axis = plot_fitting_model(st.session_state.model_fittings,
|
|
409
|
+
fit_args=fit_args_,
|
|
410
|
+
plt_type=plt_kinematics_select)
|
|
399
411
|
st.pyplot(fig)
|
|
400
412
|
st.session_state.model_fittings.kinematics['fit_method'] = fit_args_
|
|
401
413
|
else:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pooch
|
|
5
|
+
|
|
6
|
+
database_dir = os.path.join(Path.home(), 'PyThea')
|
|
7
|
+
github_main_url = 'https://github.com/AthKouloumvakos/PyThea-sample-data'
|
|
8
|
+
|
|
9
|
+
aia_sample_data = pooch.create(
|
|
10
|
+
path=os.path.join(database_dir, 'sample_data'), # The cache folder
|
|
11
|
+
base_url=f'{github_main_url}/raw/main/data/', # The remote data url on Github
|
|
12
|
+
registry={
|
|
13
|
+
'aia_lev1_1600a_2012_06_06t04_07_29_12z_image_lev1_lowres.fits': 'sha256:396b9ef5cbc1cbe2f2af806758f449cfd36f352826b61295e6330859ac7f7652',
|
|
14
|
+
},
|
|
15
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from astropy.time import TimeDelta
|
|
2
|
+
from sunpy.coordinates.ephemeris import get_horizons_coord
|
|
3
|
+
|
|
4
|
+
__all__ = ['prep_lasco']
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def prep_lasco(map_sequence):
|
|
8
|
+
for map_ in map_sequence:
|
|
9
|
+
map_.meta['DATE-AVG'] = (map_.date + TimeDelta(map_.meta['exptime']/2, format='sec')).value
|
|
10
|
+
|
|
11
|
+
dates = [tmap.date_average for tmap in map_sequence]
|
|
12
|
+
|
|
13
|
+
observer = get_horizons_coord('SOHO', dates)
|
|
14
|
+
for map_, soho_ in zip(map_sequence, observer):
|
|
15
|
+
map_.meta['HGLN_OBS'] = soho_.lon.to('deg').value
|
|
16
|
+
map_.meta['HGLT_OBS'] = soho_.lat.to('deg').value
|
|
17
|
+
map_.meta['DSUN_OBS'] = soho_.radius.to('m').value
|
|
18
|
+
|
|
19
|
+
return map_sequence
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import copy
|
|
1
2
|
import numpy as np
|
|
2
3
|
import sunpy.map
|
|
4
|
+
import scipy
|
|
3
5
|
|
|
4
6
|
from PyThea.sunpy_dev.map import maputils
|
|
5
7
|
|
|
6
|
-
__all__ = ['cor_polariz']
|
|
8
|
+
__all__ = ['cor_polariz', 'euvi_prep']
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
def cor_polariz(map_sequence):
|
|
@@ -58,11 +60,15 @@ def cor_polariz(map_sequence):
|
|
|
58
60
|
|
|
59
61
|
t = [map_0.date, map_120.date, map_240.date]
|
|
60
62
|
obs_time = min(t) + (max(t)-min(t))/2
|
|
63
|
+
t = [map_0.date_average, map_120.date_average, map_240.date_average]
|
|
64
|
+
time_avg = min(t) + (max(t)-min(t))/2
|
|
65
|
+
|
|
61
66
|
crota = np.mean([map_0.meta['crota'], map_120.meta['crota'], map_240.meta['crota']])
|
|
62
67
|
|
|
63
68
|
new_map_meta = map_120.meta
|
|
64
69
|
# TODO: Check if any other keys need to change
|
|
65
70
|
new_map_meta['date-obs'] = obs_time.strftime('%Y-%m-%dT%H:%M:%S.%f')
|
|
71
|
+
new_map_meta['date-avg'] = time_avg.strftime('%Y-%m-%dT%H:%M:%S.%f')
|
|
66
72
|
new_map_meta['polar'] = '1001'
|
|
67
73
|
new_map_meta['crval1'] = np.mean([map_0.meta['crval1'], map_120.meta['crval1'], map_240.meta['crval1']])
|
|
68
74
|
new_map_meta['crval2'] = np.mean([map_0.meta['crval2'], map_120.meta['crval2'], map_240.meta['crval2']])
|
|
@@ -78,3 +84,60 @@ def cor_polariz(map_sequence):
|
|
|
78
84
|
smap.append(map_)
|
|
79
85
|
|
|
80
86
|
return sunpy.map.Map(smap, sequence=True)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def euvi_prep(map_sequence):
|
|
90
|
+
smap = []
|
|
91
|
+
for map_ in map_sequence:
|
|
92
|
+
|
|
93
|
+
image = map_.data
|
|
94
|
+
new_map_meta = copy.deepcopy(map_.meta)
|
|
95
|
+
|
|
96
|
+
# Bias Subtraction
|
|
97
|
+
if map_.meta['offsetcr'] == 0:
|
|
98
|
+
bias = map_.meta['BIASMEAN']
|
|
99
|
+
if map_.meta['ipsum'] > 1:
|
|
100
|
+
bias *= ((2.0**(map_.meta['ipsum'] - 1))**2.0)
|
|
101
|
+
if map_.meta['IP_PROG3'] == 95: # Added manually
|
|
102
|
+
bias *= 1.995 # Added manually
|
|
103
|
+
image = image - bias
|
|
104
|
+
|
|
105
|
+
# Normalize to Open filter position
|
|
106
|
+
if map_.meta['FILTER'] == 'OPEN':
|
|
107
|
+
filter_factor = 1 # no filter
|
|
108
|
+
elif map_.meta['FILTER'] == 'S1':
|
|
109
|
+
filter_factor = 0.49 # 1500A filter
|
|
110
|
+
elif map_.meta['FILTER'] == 'S2':
|
|
111
|
+
filter_factor = 0.49 # 1500A filter
|
|
112
|
+
elif map_.meta['FILTER'] == 'DBL':
|
|
113
|
+
filter_factor = 0.41 # 3000A filter
|
|
114
|
+
image = image / filter_factor
|
|
115
|
+
|
|
116
|
+
# Image Dejitter
|
|
117
|
+
offset = [0., 0.]
|
|
118
|
+
nsum = 2**(map_.meta['summed']-1)
|
|
119
|
+
|
|
120
|
+
scale = map_.dimensions[0].value/2048
|
|
121
|
+
|
|
122
|
+
offset[0] = scale * round(map_.meta['fpsoffz'] / 38.) / nsum
|
|
123
|
+
offset[1] = scale * round(map_.meta['fpsoffy'] / 38.) / nsum
|
|
124
|
+
|
|
125
|
+
if map_.meta['obsrvtry'] == 'STEREO_B':
|
|
126
|
+
offset = [-offset[0], -offset[1]]
|
|
127
|
+
|
|
128
|
+
# TODO: if hdr.date_obs lt '2015-05-19' then offset = -offset
|
|
129
|
+
|
|
130
|
+
new_map_meta['crpix1'] += offset[0]
|
|
131
|
+
new_map_meta['crpix2'] += offset[1]
|
|
132
|
+
new_map_meta['crpix1a'] += offset[0]
|
|
133
|
+
new_map_meta['crpix2a'] += offset[1]
|
|
134
|
+
new_map_meta['xcen'] -= map_.meta['cdelt1'] * offset[0]
|
|
135
|
+
new_map_meta['ycen'] -= map_.meta['cdelt2'] * offset[1]
|
|
136
|
+
|
|
137
|
+
image = scipy.ndimage.interpolation.affine_transform(
|
|
138
|
+
np.nan_to_num(image).T, [[1, 0], [0, 1]], offset=[-offset[0], -offset[1]], order=3,
|
|
139
|
+
mode='constant', cval=0.0).T
|
|
140
|
+
|
|
141
|
+
smap.append(sunpy.map.Map(image, new_map_meta))
|
|
142
|
+
|
|
143
|
+
return sunpy.map.Map(smap, sequence=True)
|
|
File without changes
|
|
@@ -8,7 +8,9 @@ import warnings
|
|
|
8
8
|
import astropy.units as u
|
|
9
9
|
import numpy as np
|
|
10
10
|
import sunpy.map
|
|
11
|
+
from aiapy.calibrate import fix_observer_location, update_pointing
|
|
11
12
|
|
|
13
|
+
import PyThea.sunpy_dev.extern.sunkit_instruments.lasco.utils # noqa
|
|
12
14
|
import PyThea.sunpy_dev.extern.sunkit_instruments.stereo.utils # noqa
|
|
13
15
|
|
|
14
16
|
__all__ = ['maps_sequence_processing', 'get_closest', 'normalize_exposure',
|
|
@@ -76,7 +78,7 @@ def get_closest(smap, date):
|
|
|
76
78
|
`~sunpy.map.GenericMap`
|
|
77
79
|
A SunPy map.
|
|
78
80
|
"""
|
|
79
|
-
delta_time = [abs(map_.
|
|
81
|
+
delta_time = [abs(map_.date_average-date) for map_ in smap]
|
|
80
82
|
map_closest = smap[delta_time.index(min(delta_time))]
|
|
81
83
|
return map_closest
|
|
82
84
|
|
|
@@ -108,7 +110,7 @@ def normalize_exposure(smap):
|
|
|
108
110
|
return smap_new
|
|
109
111
|
|
|
110
112
|
|
|
111
|
-
def filter_maps(map_sequence,
|
|
113
|
+
def filter_maps(map_sequence, **kwargs):
|
|
112
114
|
'''
|
|
113
115
|
Returns filtered maps.
|
|
114
116
|
|
|
@@ -121,7 +123,7 @@ def filter_maps(map_sequence, extra):
|
|
|
121
123
|
map_sequence : `~sunpy.map.GenericMap`
|
|
122
124
|
A SunPy map.
|
|
123
125
|
|
|
124
|
-
|
|
126
|
+
kwargs : A list with with the preparation arguments
|
|
125
127
|
|
|
126
128
|
Returns
|
|
127
129
|
-------
|
|
@@ -130,14 +132,14 @@ def filter_maps(map_sequence, extra):
|
|
|
130
132
|
|
|
131
133
|
'''
|
|
132
134
|
|
|
133
|
-
if 'exposure' in
|
|
134
|
-
map_sequence = [tmap for tmap in map_sequence if tmap.exposure_time >
|
|
135
|
+
if 'exposure' in kwargs.keys():
|
|
136
|
+
map_sequence = [tmap for tmap in map_sequence if tmap.exposure_time > kwargs['exposure']*u.second]
|
|
135
137
|
|
|
136
|
-
if 'dimensions' in
|
|
137
|
-
map_sequence = [tmap for tmap in map_sequence if (tmap.dimensions[0], tmap.dimensions[1]) ==
|
|
138
|
+
if 'dimensions' in kwargs.keys():
|
|
139
|
+
map_sequence = [tmap for tmap in map_sequence if (tmap.dimensions[0], tmap.dimensions[1]) == kwargs['dimensions']]
|
|
138
140
|
|
|
139
|
-
if 'polar' in
|
|
140
|
-
map_sequence = [tmap for tmap in map_sequence if tmap.meta['polar'] ==
|
|
141
|
+
if 'polar' in kwargs.keys():
|
|
142
|
+
map_sequence = [tmap for tmap in map_sequence if tmap.meta['polar'] == kwargs['polar']]
|
|
141
143
|
|
|
142
144
|
if len(map_sequence) != 0:
|
|
143
145
|
sequence_final = sunpy.map.Map(map_sequence, sequence=True)
|
|
@@ -146,7 +148,7 @@ def filter_maps(map_sequence, extra):
|
|
|
146
148
|
return sequence_final
|
|
147
149
|
|
|
148
150
|
|
|
149
|
-
def prepare_maps(map_sequence,
|
|
151
|
+
def prepare_maps(map_sequence, **kwargs):
|
|
150
152
|
'''
|
|
151
153
|
Returns prepared maps.
|
|
152
154
|
|
|
@@ -159,7 +161,7 @@ def prepare_maps(map_sequence, extra):
|
|
|
159
161
|
map_sequence : `~sunpy.map.GenericMap`
|
|
160
162
|
A SunPy map.
|
|
161
163
|
|
|
162
|
-
|
|
164
|
+
kwargs : A list with with the preparation arguments
|
|
163
165
|
|
|
164
166
|
Returns
|
|
165
167
|
-------
|
|
@@ -167,26 +169,36 @@ def prepare_maps(map_sequence, extra):
|
|
|
167
169
|
A SunPy map.
|
|
168
170
|
|
|
169
171
|
'''
|
|
172
|
+
detector = map_sequence[0].detector
|
|
173
|
+
print(f'Preparing image sequence for {detector}.')
|
|
174
|
+
|
|
170
175
|
if len(map_sequence) == 0:
|
|
171
176
|
return []
|
|
172
177
|
|
|
178
|
+
# Prepare the maps before anything else
|
|
179
|
+
if detector == 'AIA':
|
|
180
|
+
map_sequence = [update_pointing(tmap) for tmap in map_sequence]
|
|
181
|
+
map_sequence = [fix_observer_location(tmap) for tmap in map_sequence]
|
|
182
|
+
elif detector == 'COR1':
|
|
183
|
+
if 'polar' not in kwargs.keys():
|
|
184
|
+
map_sequence = PyThea.sunpy_dev.extern.sunkit_instruments.stereo.utils.cor_polariz(map_sequence)
|
|
185
|
+
elif detector == 'EUVI':
|
|
186
|
+
map_sequence = PyThea.sunpy_dev.extern.sunkit_instruments.stereo.utils.euvi_prep(map_sequence)
|
|
187
|
+
elif detector in ['C2', 'C3']:
|
|
188
|
+
map_sequence = PyThea.sunpy_dev.extern.sunkit_instruments.lasco.utils.prep_lasco(map_sequence)
|
|
189
|
+
|
|
173
190
|
map_sequence = [normalize_exposure(tmap) for tmap in map_sequence]
|
|
174
191
|
|
|
175
192
|
map_sequence = [mask_occulter(tmap) for tmap in map_sequence]
|
|
176
193
|
|
|
177
|
-
if 'superpixel' in
|
|
178
|
-
nsuper =
|
|
194
|
+
if 'superpixel' in kwargs.keys():
|
|
195
|
+
nsuper = kwargs['superpixel']
|
|
179
196
|
super_dim = u.Quantity([nsuper, nsuper] * u.pixel)
|
|
180
197
|
map_sequence = [tmap.superpixel(super_dim) for tmap in map_sequence]
|
|
181
198
|
|
|
182
199
|
# map_sequence = [tmap.rotate(recenter=True) for tmap in map_sequence]
|
|
183
200
|
|
|
184
|
-
|
|
185
|
-
if 'polar' not in extra:
|
|
186
|
-
sequence_final = PyThea.sunpy_dev.extern.sunkit_instruments.stereo.utils.cor_polariz(map_sequence)
|
|
187
|
-
else:
|
|
188
|
-
sequence_final = map_sequence
|
|
189
|
-
return sequence_final
|
|
201
|
+
return map_sequence
|
|
190
202
|
|
|
191
203
|
|
|
192
204
|
def difference_maps(smapi, smapm):
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test the remote clients
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pytest
|
|
7
|
+
from astropy.time import Time
|
|
8
|
+
from sunpy.net import Fido
|
|
9
|
+
from sunpy.net import attrs as a
|
|
10
|
+
from sunpy.net import hek
|
|
11
|
+
|
|
12
|
+
from PyThea.config.selected_imagers import imager_dict
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def time_query_hek():
|
|
16
|
+
for query in [a.Time('2011/08/09 07:23:56', '2011/08/09 12:40:29'),
|
|
17
|
+
a.Time('2012/09/10 08:24:57', '2012/09/10 13:41:30'),
|
|
18
|
+
a.Time('2013/10/11 09:25:58', '2013/10/11 14:42:31'), ]:
|
|
19
|
+
yield query
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.remote_data
|
|
23
|
+
@pytest.mark.parametrize('time_query', time_query_hek())
|
|
24
|
+
def test_hek_client(time_query):
|
|
25
|
+
"""
|
|
26
|
+
Tests that the HEK client returns a HEKTable and the hek_query results for EventType('FL') have consistent format
|
|
27
|
+
"""
|
|
28
|
+
h = hek.HEKClient()
|
|
29
|
+
hek_query = h.search(time_query, a.hek.EventType('FL'))
|
|
30
|
+
assert type(hek_query) == hek.hek.HEKTable
|
|
31
|
+
|
|
32
|
+
assert isinstance(hek_query['event_starttime'], Time)
|
|
33
|
+
assert isinstance(hek_query['event_peaktime'], Time)
|
|
34
|
+
assert isinstance(hek_query['event_endtime'], Time)
|
|
35
|
+
assert all(isinstance(h, np.str_) for h in hek_query['fl_goescls'])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.mark.remote_data
|
|
39
|
+
def test_hek_client_flare_data():
|
|
40
|
+
"""
|
|
41
|
+
Tests that the HEK client returns always the same results (flare class, ...)
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
startTime = '2011/08/09 07:23:56'
|
|
45
|
+
endTime = '2011/08/09 12:40:29'
|
|
46
|
+
eventType = 'FL'
|
|
47
|
+
|
|
48
|
+
hekTime = a.Time(startTime, endTime)
|
|
49
|
+
hekEvent = a.hek.EventType(eventType)
|
|
50
|
+
|
|
51
|
+
h = hek.HEKClient()
|
|
52
|
+
|
|
53
|
+
hek_query = h.search(hekTime, hekEvent, a.hek.FL.GOESCls > 'B1.0', a.hek.OBS.Observatory == 'GOES')
|
|
54
|
+
assert all(hek_query['fl_goescls'] == ['C1.4', 'X6.9'])
|
|
55
|
+
assert all(hek_query['event_starttime'] == Time(['2011-08-09 07:19:00.000', '2011-08-09 07:48:00.000']))
|
|
56
|
+
assert all(hek_query['event_peaktime'] == Time(['2011-08-09 07:23:00.000', '2011-08-09 08:05:00.000']))
|
|
57
|
+
assert all(hek_query['event_endtime'] == Time(['2011-08-09 07:27:00.000', '2011-08-09 08:08:00.000']))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def imager_query():
|
|
61
|
+
for imager in imager_dict.keys():
|
|
62
|
+
yield imager
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@pytest.mark.remote_data
|
|
66
|
+
@pytest.mark.parametrize('imager', imager_query())
|
|
67
|
+
def test_vso_search(imager):
|
|
68
|
+
"""
|
|
69
|
+
Tests that the Fido.search returns always the same number of files from VSO.
|
|
70
|
+
"""
|
|
71
|
+
file_num = {'AIA': [60, 61],
|
|
72
|
+
'LC2': [5], 'LC3': [5],
|
|
73
|
+
'COR2A': [7], 'COR2B': [7],
|
|
74
|
+
'EUVIA': [11], 'EUVIB': [11],
|
|
75
|
+
'COR1A': [18, 34], 'COR1B': [17, 66],
|
|
76
|
+
'HI1A': [1, 2, 3], 'HI1B': [1, 2],
|
|
77
|
+
'HI2A': [1, 2], 'HI2B': [1], }
|
|
78
|
+
|
|
79
|
+
for query in [a.Time('2011/01/01 00:00:00', '2011/01/01 01:00:00'),
|
|
80
|
+
a.Time('2012/01/01 00:00:00', '2012/01/01 01:00:00'),
|
|
81
|
+
a.Time('2013/01/01 00:00:00', '2013/01/01 01:00:00'), ]:
|
|
82
|
+
args = imager_dict[imager][0]
|
|
83
|
+
result = Fido.search(query, *args)
|
|
84
|
+
|
|
85
|
+
assert len(result) == 1
|
|
86
|
+
assert 'vso' in result.keys()
|
|
87
|
+
|
|
88
|
+
assert result.file_num in file_num[imager]
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Test the utilities
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
import matplotlib.dates as mdates
|
|
8
|
+
from astropy.coordinates import SkyCoord
|
|
9
|
+
from astropy.tests.helper import assert_quantity_allclose
|
|
10
|
+
from sunpy.coordinates import get_horizons_coord
|
|
11
|
+
from sunpy.coordinates.frames import HeliographicStonyhurst
|
|
12
|
+
from sunpy.net import hek
|
|
13
|
+
|
|
14
|
+
from PyThea.config.selected_bodies import bodies_dict
|
|
15
|
+
from PyThea.utils import get_hek_flare, parameter_fit
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_get_hek_flare():
|
|
19
|
+
"""
|
|
20
|
+
Test that the get_hek_flare returns a hek.hek.HEKTable and the results are formated correct.
|
|
21
|
+
"""
|
|
22
|
+
# This test requests the flare info for a date that returns a list of flares and compares against previous results
|
|
23
|
+
selectbox_list, flare_list = get_hek_flare(datetime(2017, 9, 10))
|
|
24
|
+
assert type(flare_list) == hek.hek.HEKTable
|
|
25
|
+
assert selectbox_list == ['FLM1.1|2017-09-09T23:53:00', 'FLC9.0|2017-09-10T03:09:00', 'FLC2.9|2017-09-10T09:20:00',
|
|
26
|
+
'FLC1.6|2017-09-10T14:23:00', 'FLC1.0|2017-09-10T15:26:00', 'FLX8.2|2017-09-10T16:06:00']
|
|
27
|
+
|
|
28
|
+
# This test requests the flare info for a date that returns an empty list
|
|
29
|
+
selectbox_list, flare_list = get_hek_flare(datetime(2117, 9, 10))
|
|
30
|
+
assert flare_list == []
|
|
31
|
+
assert selectbox_list == ['No events returned']
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_get_horizons_coord():
|
|
35
|
+
"""
|
|
36
|
+
Test that get_horizons_coord returns a Skycoord instance, in HeliographicStonyhurst frame, and returns the same coordinates every time.
|
|
37
|
+
"""
|
|
38
|
+
bodies_coord = []
|
|
39
|
+
for body in bodies_dict:
|
|
40
|
+
bodies_coord.append(get_horizons_coord(bodies_dict[body][0], '2022-01-01T00:00:00'))
|
|
41
|
+
|
|
42
|
+
coords = [[-106.56704928, 1.45508574, 0.3631443],
|
|
43
|
+
[-4.65750614, -1.34575135, 0.71937524],
|
|
44
|
+
[2.55987423e-06, -2.99926774, 0.98335562],
|
|
45
|
+
[135.77581107, -2.68402546, 1.53663218],
|
|
46
|
+
[-121.16524996, 6.08953762, 4.99274907],
|
|
47
|
+
[32.43818351, -5.96794509, 1.0004962],
|
|
48
|
+
[-34.99771814, 1.28725362, 0.96437702],
|
|
49
|
+
[-11.53530598, -0.39313636, 0.99738376],
|
|
50
|
+
[-138.81147107, 3.52846474, 0.74828612],
|
|
51
|
+
[144.12380481, -2.64432759, 0.67510736]
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
for i, body_coord in enumerate(bodies_coord):
|
|
55
|
+
assert isinstance(body_coord, SkyCoord)
|
|
56
|
+
assert isinstance(body_coord.frame, HeliographicStonyhurst)
|
|
57
|
+
assert_quantity_allclose([body_coord.lon.value, body_coord.lat.value, body_coord.radius.value], coords[i], rtol=1e-5, atol=1e-4)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_parameter_fit_polynomial():
|
|
61
|
+
x = [datetime(2020, 1, 1, 0, 0, 0), datetime(2020, 1, 1, 0, 10, 0), datetime(2020, 1, 1, 0, 20, 0), datetime(2020, 1, 1, 0, 30, 0)]
|
|
62
|
+
xx = (mdates.date2num(x) - mdates.date2num(x[0]))
|
|
63
|
+
|
|
64
|
+
y = [0, 1, 2, 3]
|
|
65
|
+
|
|
66
|
+
fit = parameter_fit(x, y, {'type': 'polynomial', 'order': 1})
|
|
67
|
+
assert_quantity_allclose([fit['popt'][0]*xx[1], fit['popt'][1]], [1, 0], atol=1e-7)
|
|
68
|
+
|
|
69
|
+
fit = parameter_fit(x, y, {'type': 'polynomial', 'order': 2})
|
|
70
|
+
assert_quantity_allclose([fit['popt'][0], fit['popt'][1]*xx[1], fit['popt'][2]], [0, 1, 0], atol=1e-5)
|
|
71
|
+
|
|
72
|
+
y = [0, 2, 4, 6]
|
|
73
|
+
|
|
74
|
+
fit = parameter_fit(x, y, {'type': 'polynomial', 'order': 1})
|
|
75
|
+
assert_quantity_allclose([fit['popt'][0]*xx[1], fit['popt'][1]], [2, 0], atol=1e-7)
|
|
76
|
+
|
|
77
|
+
fit = parameter_fit(x, y, {'type': 'polynomial', 'order': 2})
|
|
78
|
+
assert_quantity_allclose([fit['popt'][0], fit['popt'][1]*xx[1], fit['popt'][2]], [0, 2, 0], atol=1e-5)
|
|
@@ -22,6 +22,7 @@ import datetime
|
|
|
22
22
|
import io
|
|
23
23
|
import json
|
|
24
24
|
import os
|
|
25
|
+
import re
|
|
25
26
|
from copy import copy
|
|
26
27
|
from pathlib import Path
|
|
27
28
|
|
|
@@ -31,6 +32,7 @@ import matplotlib.dates as mdates
|
|
|
31
32
|
import matplotlib.pyplot as plt
|
|
32
33
|
import numexpr
|
|
33
34
|
import numpy as np
|
|
35
|
+
import pandas as pd
|
|
34
36
|
import seaborn as sns
|
|
35
37
|
import sunpy.map
|
|
36
38
|
from scipy.interpolate import UnivariateSpline
|
|
@@ -39,6 +41,7 @@ from sunpy.coordinates import get_horizons_coord
|
|
|
39
41
|
from sunpy.map.maputils import contains_coordinate
|
|
40
42
|
from sunpy.net import Fido
|
|
41
43
|
from sunpy.net import attrs as a
|
|
44
|
+
from sunpy.time import parse_time
|
|
42
45
|
|
|
43
46
|
from PyThea.config.selected_bodies import bodies_dict
|
|
44
47
|
from PyThea.config.selected_imagers import imager_dict
|
|
@@ -74,22 +77,26 @@ def get_hek_flare(day):
|
|
|
74
77
|
return selectbox_list, flare_list_
|
|
75
78
|
|
|
76
79
|
|
|
77
|
-
def make_figure(map, image_mode, clim=[-20, 20], clip_model=True):
|
|
80
|
+
def make_figure(map, image_mode, clim=[-20, 20], clip_model=True, **kwargs):
|
|
78
81
|
'''
|
|
79
82
|
Makes the main imager figure and returns the figure and axis handle.
|
|
80
83
|
'''
|
|
81
|
-
fig = plt.figure()
|
|
82
|
-
axis = plt.subplot(projection=map)
|
|
83
|
-
|
|
84
|
+
fig = kwargs.get('fig', plt.figure())
|
|
85
|
+
axis = kwargs.get('axis', plt.subplot(projection=map))
|
|
86
|
+
|
|
84
87
|
if image_mode == 'Plain':
|
|
88
|
+
# TODO: For plain images or when EUVIA-B are used, this does not work very well.
|
|
85
89
|
map.plot()
|
|
86
90
|
else:
|
|
87
91
|
map.plot(cmap='Greys_r',
|
|
88
92
|
norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
|
|
89
|
-
|
|
93
|
+
|
|
94
|
+
map.draw_limb(resolution=90)
|
|
90
95
|
# map.draw_grid(linewidth=2, color='red') # TODO: This takes too much computation time. Maybe for AIA or EUVI?
|
|
96
|
+
|
|
91
97
|
yax = axis.coords[1]
|
|
92
98
|
yax.set_ticklabel(rotation=90)
|
|
99
|
+
|
|
93
100
|
if clip_model:
|
|
94
101
|
axis.set_xlim([0, map.data.shape[0]])
|
|
95
102
|
axis.set_ylim([0, map.data.shape[1]])
|
|
@@ -102,6 +109,10 @@ def make_figure(map, image_mode, clim=[-20, 20], clip_model=True):
|
|
|
102
109
|
if cref.Ty > 0:
|
|
103
110
|
axis.invert_yaxis()
|
|
104
111
|
|
|
112
|
+
axis.set_title(re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}',
|
|
113
|
+
' $T_{AGV}:$' + parse_time(map.date_average).strftime('%Y-%m-%d %H:%M:%S'),
|
|
114
|
+
map.latex_name), fontsize=10, pad=8)
|
|
115
|
+
|
|
105
116
|
return fig, axis
|
|
106
117
|
|
|
107
118
|
|
|
@@ -110,7 +121,7 @@ def plot_bodies(axis, bodies_list, smap):
|
|
|
110
121
|
Plots in the images the possition of the pre-configured bodies (Earth, STA, Venus etc.)
|
|
111
122
|
'''
|
|
112
123
|
for body in bodies_list:
|
|
113
|
-
body_coo = get_horizons_coord(bodies_dict[body][0], smap.
|
|
124
|
+
body_coo = get_horizons_coord(bodies_dict[body][0], smap.date_average)
|
|
114
125
|
if contains_coordinate(smap, body_coo):
|
|
115
126
|
axis.plot_coord(body_coo, 'o', color=bodies_dict[body][1],
|
|
116
127
|
fillstyle='none', markersize=6, label=body)
|
|
@@ -143,28 +154,44 @@ def download_fits(date_process, imager, time_range=[-1, 1]):
|
|
|
143
154
|
return map_
|
|
144
155
|
|
|
145
156
|
|
|
146
|
-
def maps_process(
|
|
157
|
+
def maps_process(maps_dict_in, imagers_list_in, image_mode, **kwargs):
|
|
147
158
|
'''
|
|
148
|
-
Process the images for the selected imagers and return the final maps.
|
|
159
|
+
Process the images for the selected imagers and return the final maps and the list of imagers loaded.
|
|
149
160
|
|
|
150
161
|
Note
|
|
151
162
|
----
|
|
152
|
-
Here the
|
|
163
|
+
Here the maps_dict_in is the session_state.map_ when used from the application.
|
|
153
164
|
'''
|
|
154
|
-
|
|
165
|
+
maps_dict_out = {}
|
|
155
166
|
imagers_list_out = []
|
|
156
167
|
|
|
157
168
|
for imager in imagers_list_in:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
if imager in maps_dict_in and maps_dict_in[imager] != []:
|
|
170
|
+
if not kwargs:
|
|
171
|
+
extras = imager_dict[imager][1]
|
|
172
|
+
else:
|
|
173
|
+
if imager in kwargs:
|
|
174
|
+
extras = kwargs[imager]
|
|
175
|
+
else:
|
|
176
|
+
print(f'Warning [maps_process]: No extras provided for {imager}.')
|
|
177
|
+
maps_dict_out[imager] = single_imager_maps_process(maps_dict_in[imager],
|
|
178
|
+
image_mode=image_mode,
|
|
179
|
+
**extras)
|
|
180
|
+
if maps_dict_out[imager] != []:
|
|
165
181
|
imagers_list_out.append(imager)
|
|
166
182
|
|
|
167
|
-
return
|
|
183
|
+
return maps_dict_out, imagers_list_out
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def single_imager_maps_process(map_list_in, image_mode='Plain', **kwargs):
|
|
187
|
+
'''
|
|
188
|
+
Process the images for a single imager and return the final maps.
|
|
189
|
+
'''
|
|
190
|
+
map_list_out = filter_maps(map_list_in, **kwargs)
|
|
191
|
+
map_list_out = prepare_maps(map_list_out, **kwargs)
|
|
192
|
+
map_list_out = maps_sequence_processing(map_list_out, seq_type=image_mode)
|
|
193
|
+
|
|
194
|
+
return map_list_out
|
|
168
195
|
|
|
169
196
|
|
|
170
197
|
# TODO: Implement units here
|
|
@@ -232,29 +259,28 @@ def plot_fitting_model(model, fit_args, plt_type='HeightT'):
|
|
|
232
259
|
}
|
|
233
260
|
parameters = parameters[model.geometrical_model]
|
|
234
261
|
# Height vs time
|
|
235
|
-
fig = plt.
|
|
236
|
-
axis = plt.subplot()
|
|
262
|
+
fig, axis = plt.subplots(figsize=(5.5, 5.5), tight_layout=True)
|
|
237
263
|
if plt_type == 'HeightT':
|
|
238
264
|
for p in parameters.keys():
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
265
|
+
axis.plot(model.parameters.index,
|
|
266
|
+
model.parameters[p],
|
|
267
|
+
marker=parameters[p][0],
|
|
268
|
+
linestyle=parameters[p][1],
|
|
269
|
+
color=parameters[p][2],
|
|
270
|
+
label=parameters[p][3]) # label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)
|
|
245
271
|
if len(model.parameters[p])-1 > fit_args['order']:
|
|
246
272
|
# How to get confidence intervals from curve_fit?
|
|
247
273
|
fit = parameter_fit(model.parameters.index, model.parameters[p], fit_args)
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
274
|
+
axis.plot(fit['best_fit_x'], fit['best_fit_y'], '-', color=parameters[p][2])
|
|
275
|
+
axis.fill_between(fit['best_fit_x'], fit['sigma_bounds']['up'], fit['sigma_bounds']['low'],
|
|
276
|
+
color=parameters[p][2], alpha=0.20)
|
|
251
277
|
if fit_args['type'] == 'spline':
|
|
252
|
-
|
|
253
|
-
|
|
278
|
+
axis.fill_between(fit['best_fit_x'], fit['sigv_bounds']['up'], fit['sigv_bounds']['low'],
|
|
279
|
+
color=parameters[p][2], alpha=0.05)
|
|
254
280
|
else:
|
|
255
|
-
|
|
256
|
-
ylabel = 'Height
|
|
257
|
-
|
|
281
|
+
axis.plot(model.parameters.index, model.parameters[p], '--', color=parameters[p][2])
|
|
282
|
+
ylabel = 'Height of apex and length of flanks [Rsun]'
|
|
283
|
+
axis.set_ylim(bottom=0)
|
|
258
284
|
elif plt_type == 'SpeedT':
|
|
259
285
|
for p in parameters.keys():
|
|
260
286
|
if len(model.parameters[p])-1 > fit_args['order']:
|
|
@@ -265,34 +291,34 @@ def plot_fitting_model(model, fit_args, plt_type='HeightT'):
|
|
|
265
291
|
speed_best_fit = (Rs2km/sec) * np.gradient(fit['best_fit_y'], fit['best_fit_x_num'])
|
|
266
292
|
speed_bound_upper = (Rs2km/sec) * np.gradient(fit['sigma_bounds']['up'], fit['best_fit_x_num'])
|
|
267
293
|
speed_bound_lower = (Rs2km/sec) * np.gradient(fit['sigma_bounds']['low'], fit['best_fit_x_num'])
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
axis.plot(fit['best_fit_x'], speed_best_fit, '-', color=parameters[p][2], label=parameters[p][3])
|
|
295
|
+
axis.fill_between(fit['best_fit_x'], speed_bound_lower, speed_bound_upper,
|
|
296
|
+
color=parameters[p][2], alpha=0.20)
|
|
271
297
|
if fit_args['type'] == 'spline':
|
|
272
|
-
|
|
273
|
-
|
|
298
|
+
axis.fill_between(fit['best_fit_x'], (Rs2km/sec) * fit['sigv_bounds']['dlow'], (Rs2km/sec) * fit['sigv_bounds']['dup'],
|
|
299
|
+
color=parameters[p][2], alpha=0.05)
|
|
274
300
|
else:
|
|
275
301
|
pass
|
|
276
302
|
ylabel = 'Speed [km/s]'
|
|
277
|
-
|
|
303
|
+
axis.set_ylim(bottom=0)
|
|
278
304
|
if plt_type == 'LongT' or plt_type == 'LatT':
|
|
279
305
|
parameters = {'LongT': ['hgln', '+', palete[3], 'Longitude'],
|
|
280
306
|
'LatT': ['hglt', '+', palete[2], 'Latitude']}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
307
|
+
axis.plot(model.parameters.index,
|
|
308
|
+
model.parameters[parameters[plt_type][0]],
|
|
309
|
+
marker=parameters[plt_type][1],
|
|
310
|
+
linestyle='',
|
|
311
|
+
color=parameters[plt_type][2],
|
|
312
|
+
label=parameters[plt_type][3])
|
|
287
313
|
if len(model.parameters[parameters[plt_type][0]])-1 > 3:
|
|
288
314
|
fit = parameter_fit(model.parameters.index, model.parameters[parameters[plt_type][0]], fit_args)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
315
|
+
axis.plot(fit['best_fit_x'], fit['best_fit_y'], '-', color=parameters[plt_type][2])
|
|
316
|
+
axis.fill_between(fit['best_fit_x'], fit['sigma_bounds']['up'], fit['sigma_bounds']['low'],
|
|
317
|
+
color=parameters[plt_type][2], alpha=0.05)
|
|
292
318
|
ylabel = parameters[plt_type][3] + ' [degrees]'
|
|
293
319
|
|
|
294
|
-
|
|
295
|
-
|
|
320
|
+
axis.set_xlabel('Time [UTC]')
|
|
321
|
+
axis.set_ylabel(ylabel)
|
|
296
322
|
if fit_args['type'] == 'polynomial':
|
|
297
323
|
title = 'Event: ' + model.event_selected + ' | ' + fit_args['type'] + str(fit_args['order'])
|
|
298
324
|
elif fit_args['type'] == 'spline':
|
|
@@ -310,18 +336,20 @@ def plot_fitting_model(model, fit_args, plt_type='HeightT'):
|
|
|
310
336
|
if xlim[1] - xlim[0] <= 0.5:
|
|
311
337
|
axis.xaxis.set_minor_locator(mdates.MinuteLocator(byminute=[0, 10, 20, 30, 40, 50]))
|
|
312
338
|
fig.autofmt_xdate(bottom=0, rotation=0, ha='center')
|
|
313
|
-
|
|
339
|
+
axis.legend(loc='lower right')
|
|
314
340
|
|
|
315
|
-
return fig
|
|
341
|
+
return fig, axis
|
|
316
342
|
|
|
317
343
|
|
|
318
344
|
def parameter_fit(x, y, fit_args):
|
|
319
345
|
'''
|
|
320
346
|
Performs a polynomial or spline fit to a set of x, y parameters.
|
|
321
347
|
'''
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
348
|
+
minx = x[0].replace(second=0, microsecond=0)
|
|
349
|
+
xx = (mdates.date2num(x) - mdates.date2num(minx)) # fractional days from minx
|
|
350
|
+
step = 1/(60*24) # one minute timestep in units of xx (fractional days)
|
|
351
|
+
xxx = np.arange(0, xx.max()+step, step) # Makes an array with a timestep of one minute in fractional days
|
|
352
|
+
dd = pd.DatetimeIndex(mdates.num2date(xxx + mdates.date2num(minx)))
|
|
325
353
|
|
|
326
354
|
if fit_args['type'] == 'polynomial':
|
|
327
355
|
# scipy.optimize.curve_fit and numpy.polyfit
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PyThea
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.0
|
|
4
4
|
Summary: PyThea: A software package to reconstruct the 3D structure of CMEs and shock waves
|
|
5
5
|
Home-page: https://github.com/AthKouloumvakos/PyThea
|
|
6
6
|
Author: Athanasios Kouloumvakos
|
|
@@ -23,11 +23,13 @@ License-File: LICENSE.md
|
|
|
23
23
|
Requires-Dist: numpy
|
|
24
24
|
Requires-Dist: pandas
|
|
25
25
|
Requires-Dist: scipy
|
|
26
|
+
Requires-Dist: aiapy
|
|
26
27
|
Requires-Dist: astropy
|
|
27
28
|
Requires-Dist: astroquery
|
|
28
29
|
Requires-Dist: numexpr
|
|
29
30
|
Requires-Dist: sunpy==4.1.0
|
|
30
31
|
Requires-Dist: parfive==1.5.1
|
|
32
|
+
Requires-Dist: pooch
|
|
31
33
|
Requires-Dist: matplotlib
|
|
32
34
|
Requires-Dist: seaborn
|
|
33
35
|
Requires-Dist: streamlit
|
|
@@ -30,12 +30,16 @@ PyThea/config/app_styles.py
|
|
|
30
30
|
PyThea/config/config_sliders.py
|
|
31
31
|
PyThea/config/selected_bodies.py
|
|
32
32
|
PyThea/config/selected_imagers.py
|
|
33
|
+
PyThea/data/__init__.py
|
|
34
|
+
PyThea/data/sample_data.py
|
|
33
35
|
PyThea/extensions/LICENSE_gcs_python.md
|
|
34
36
|
PyThea/extensions/__init__.py
|
|
35
37
|
PyThea/extensions/buttons.py
|
|
36
38
|
PyThea/sunpy_dev/__init__.py
|
|
37
39
|
PyThea/sunpy_dev/extern/__init__.py
|
|
38
40
|
PyThea/sunpy_dev/extern/sunkit_instruments/__init__.py
|
|
41
|
+
PyThea/sunpy_dev/extern/sunkit_instruments/lasco/__init__.py
|
|
42
|
+
PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py
|
|
39
43
|
PyThea/sunpy_dev/extern/sunkit_instruments/stereo/__init__.py
|
|
40
44
|
PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py
|
|
41
45
|
PyThea/sunpy_dev/map/__init__.py
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Test the remote clients
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
from sunpy.net import attrs, hek
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.mark.remote_data
|
|
10
|
-
def test_hek_client():
|
|
11
|
-
"""
|
|
12
|
-
Tests that the HEK client returns a HEKTable
|
|
13
|
-
"""
|
|
14
|
-
startTime = '2011/08/09 07:23:56'
|
|
15
|
-
endTime = '2011/08/09 12:40:29'
|
|
16
|
-
eventType = 'FL'
|
|
17
|
-
|
|
18
|
-
hekTime = attrs.Time(startTime, endTime)
|
|
19
|
-
hekEvent = attrs.hek.EventType(eventType)
|
|
20
|
-
|
|
21
|
-
h = hek.HEKClient()
|
|
22
|
-
hek_query = h.search(hekTime, hekEvent)
|
|
23
|
-
assert type(hek_query) == hek.hek.HEKTable
|
|
24
|
-
|
|
25
|
-
# Test that the hek_query['event_peaktime'] is <class 'astropy.time.core.Time'>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Test the utilities
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from sunpy.net import hek
|
|
6
|
-
|
|
7
|
-
from PyThea.utils import get_hek_flare
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
# ------------------
|
|
11
|
-
# Test utilities
|
|
12
|
-
def test_get_hek_flare_HEKTable():
|
|
13
|
-
"""
|
|
14
|
-
Test that the get_hek_flare returns a hek.hek.HEKTable
|
|
15
|
-
"""
|
|
16
|
-
from datetime import datetime
|
|
17
|
-
day = datetime(2017, 9, 10)
|
|
18
|
-
_, flare_list_ = get_hek_flare(day)
|
|
19
|
-
|
|
20
|
-
assert type(flare_list_) == hek.hek.HEKTable
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|