PyThea 0.8.0__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.
Files changed (56) hide show
  1. {PyThea-0.8.0 → PyThea-0.9.0}/CHANGELOG.md +27 -3
  2. {PyThea-0.8.0/PyThea.egg-info → PyThea-0.9.0}/PKG-INFO +4 -2
  3. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/PyThea_app.py +48 -36
  4. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/_version.py +2 -2
  5. PyThea-0.9.0/PyThea/data/sample_data.py +15 -0
  6. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/pythea_cli.py +10 -5
  7. PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py +19 -0
  8. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py +64 -1
  9. PyThea-0.9.0/PyThea/sunpy_dev/map/__init__.py +0 -0
  10. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/sunpy_dev/map/maputils.py +31 -19
  11. PyThea-0.9.0/PyThea/test/__init__.py +0 -0
  12. PyThea-0.9.0/PyThea/test/test_remote_clients.py +88 -0
  13. PyThea-0.9.0/PyThea/test/test_utils.py +78 -0
  14. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/utils.py +84 -56
  15. {PyThea-0.8.0 → PyThea-0.9.0/PyThea.egg-info}/PKG-INFO +4 -2
  16. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/SOURCES.txt +4 -0
  17. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/requires.txt +2 -0
  18. {PyThea-0.8.0 → PyThea-0.9.0}/environment.yml +2 -0
  19. {PyThea-0.8.0 → PyThea-0.9.0}/requirements.txt +2 -0
  20. {PyThea-0.8.0 → PyThea-0.9.0}/setup.cfg +1 -0
  21. {PyThea-0.8.0 → PyThea-0.9.0}/setup.py +1 -1
  22. PyThea-0.8.0/PyThea/test/test_remote_clients.py +0 -25
  23. PyThea-0.8.0/PyThea/test/test_utils.py +0 -20
  24. {PyThea-0.8.0 → PyThea-0.9.0}/LICENSE.md +0 -0
  25. {PyThea-0.8.0 → PyThea-0.9.0}/MANIFEST.in +0 -0
  26. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/__init__.py +0 -0
  27. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/callbacks.py +0 -0
  28. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/config/__init__.py +0 -0
  29. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/config/app_styles.py +0 -0
  30. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/config/config_sliders.py +0 -0
  31. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/config/selected_bodies.py +0 -0
  32. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/config/selected_imagers.py +0 -0
  33. {PyThea-0.8.0/PyThea/extensions → PyThea-0.9.0/PyThea/data}/__init__.py +0 -0
  34. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/extensions/LICENSE_gcs_python.md +0 -0
  35. {PyThea-0.8.0/PyThea/sunpy_dev → PyThea-0.9.0/PyThea/extensions}/__init__.py +0 -0
  36. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/extensions/buttons.py +0 -0
  37. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/geometrical_models.py +0 -0
  38. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/modules.py +0 -0
  39. {PyThea-0.8.0/PyThea/sunpy_dev/extern → PyThea-0.9.0/PyThea/sunpy_dev}/__init__.py +0 -0
  40. {PyThea-0.8.0/PyThea/sunpy_dev/extern/sunkit_instruments → PyThea-0.9.0/PyThea/sunpy_dev/extern}/__init__.py +0 -0
  41. {PyThea-0.8.0/PyThea/sunpy_dev/extern/sunkit_instruments/stereo → PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments}/__init__.py +0 -0
  42. {PyThea-0.8.0/PyThea/sunpy_dev/map → PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments/lasco}/__init__.py +0 -0
  43. {PyThea-0.8.0/PyThea/test → PyThea-0.9.0/PyThea/sunpy_dev/extern/sunkit_instruments/stereo}/__init__.py +0 -0
  44. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/test/Pythea_test.py +0 -0
  45. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/test/conftest.py +0 -0
  46. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/test/test_geometrical_models.py +0 -0
  47. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/test/test_imports.py +0 -0
  48. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea/version.py +0 -0
  49. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/.DS_Store +0 -0
  50. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/dependency_links.txt +0 -0
  51. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/entry_points.txt +0 -0
  52. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/not-zip-safe +0 -0
  53. {PyThea-0.8.0 → PyThea-0.9.0}/PyThea.egg-info/top_level.txt +0 -0
  54. {PyThea-0.8.0 → PyThea-0.9.0}/README.md +0 -0
  55. {PyThea-0.8.0 → PyThea-0.9.0}/README_pypi.md +0 -0
  56. {PyThea-0.8.0 → PyThea-0.9.0}/pyproject.toml +0 -0
@@ -1,3 +1,30 @@
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
+
17
+ # v0.8.1 (13-Jan-2024)
18
+
19
+ ## Features
20
+ - Implements PyThea test to cli
21
+
22
+ ## Minor Changes
23
+ - Includes Python version 3.10
24
+
25
+ ## Bug Fixes
26
+ - Fixes a runtime bug with streamlit 1.30.0
27
+
1
28
  # v0.8.0 (11-Jan-2024)
2
29
 
3
30
  ## Major Changes
@@ -10,9 +37,6 @@
10
37
  - Replaces deprecated st.experimental_rerun with st.rerun
11
38
  - Improves the datetick format of the kinematic plots
12
39
 
13
- ## Bug Fixes
14
- None
15
-
16
40
  # v0.7.4 (09-Jul-2023)
17
41
 
18
42
  ## Minor Changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyThea
3
- Version: 0.8.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
@@ -17,17 +17,19 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Intended Audience :: Science/Research
19
19
  Classifier: Topic :: Scientific/Engineering :: Physics
20
- Requires-Python: >=3.8, <3.10
20
+ Requires-Python: >=3.8, <3.11
21
21
  Description-Content-Type: text/markdown
22
22
  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, maps_process,
37
- model_fittings, plot_bodies, plot_fitting_model)
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 [![Version](https://img.shields.io/github/v/release/AthKouloumvakos/PyThea)](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
- if 'map_' not in st.session_state or [False for lst in imagers_list if lst not in st.session_state.map_]:
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
- progress_bar = stqdm.stqdm(imagers_list, desc='Preparing to Download data')
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
- if imager in st.session_state.map_:
206
- pass
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
- if 'map' not in st.session_state:
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.date for maps in st.session_state.map[imager_select]]
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=running_map.date)
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=running_map.date)
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
- fit_args=fit_args_,
378
- plt_type='HeightT')
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
- fit_args=fit_args_,
382
- plt_type='SpeedT')
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
- fit_args=fit_args_,
389
- plt_type='LongT')
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
- fit_args=fit_args_,
393
- plt_type='LatT')
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
- fit_args=fit_args_,
398
- plt_type=plt_kinematics_select)
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:
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.8.0'
16
- __version_tuple__ = version_tuple = (0, 8, 0)
15
+ __version__ = version = '0.9.0'
16
+ __version_tuple__ = version_tuple = (0, 9, 0)
@@ -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
+ )
@@ -9,11 +9,12 @@ import sys
9
9
  from typing import Optional
10
10
 
11
11
  import click
12
+ import pytest
12
13
  import streamlit
13
14
  import streamlit.web.bootstrap as bootstrap
14
15
  from streamlit.runtime.credentials import check_credentials
15
16
 
16
- from PyThea.version import version as _vesrsion
17
+ from PyThea.version import version as _version
17
18
 
18
19
 
19
20
  @click.group()
@@ -38,14 +39,14 @@ def _main_run(file, args=None, flag_options=None):
38
39
  if flag_options is None:
39
40
  flag_options = {}
40
41
 
41
- command_line = _get_command_line_as_string()
42
+ is_command_streamlit = _get_command_line_as_string() == 'PyThea streamlit'
42
43
 
43
44
  # Set a global flag indicating that we're "within" streamlit.
44
45
  streamlit._is_running_with_streamlit = True
45
46
 
46
47
  check_credentials()
47
48
 
48
- bootstrap.run(file, command_line, args, flag_options)
49
+ bootstrap.run(file, is_command_streamlit, args, flag_options)
49
50
 
50
51
 
51
52
  def _get_command_line_as_string() -> Optional[str]:
@@ -79,7 +80,7 @@ def help(ctx):
79
80
  @main.command('version')
80
81
  def version():
81
82
  """Print PyThea's version number."""
82
- print(f'PyThea installed version is: {_vesrsion}')
83
+ print(f'PyThea installed version is: {_version}')
83
84
 
84
85
 
85
86
  @main.command('docs')
@@ -96,7 +97,11 @@ def update():
96
97
  """Update PyThea."""
97
98
  print('Not implemented yet.')
98
99
 
99
- # TODO Include the test here.
100
+
101
+ @main.command('test')
102
+ def main_test():
103
+ """Test PyThea."""
104
+ pytest.main(['-W', 'ignore', '--pyargs', 'PyThea'])
100
105
 
101
106
 
102
107
  if __name__ == '__main__':
@@ -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_.date-date) for map_ in smap]
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, extra):
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
- extra : A list with with the preparation arguments
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 extra:
134
- map_sequence = [tmap for tmap in map_sequence if tmap.exposure_time > extra['exposure']*u.second]
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 extra:
137
- map_sequence = [tmap for tmap in map_sequence if (tmap.dimensions[0], tmap.dimensions[1]) == extra['dimensions']]
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 extra:
140
- map_sequence = [tmap for tmap in map_sequence if tmap.meta['polar'] == extra['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, extra):
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
- extra : A list with with the preparation arguments
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 extra:
178
- nsuper = extra['superpixel']
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
- if map_sequence[0].detector == 'COR1':
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
- # TODO: For plain images or when EUVIA-B are used, this does not work very well.
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
- map.draw_limb(resolution=180)
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.date)
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(ninstr_map_in, imagers_list_in, image_mode):
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 ninstr_map_in is the session_state.map_ when used from the application.
163
+ Here the maps_dict_in is the session_state.map_ when used from the application.
153
164
  '''
154
- ninstr_map_out = {}
165
+ maps_dict_out = {}
155
166
  imagers_list_out = []
156
167
 
157
168
  for imager in imagers_list_in:
158
- extra = imager_dict[imager][1]
159
- if imager in ninstr_map_in and ninstr_map_in[imager] != []:
160
- ninstr_map_out[imager] = filter_maps(ninstr_map_in[imager], extra)
161
- ninstr_map_out[imager] = prepare_maps(ninstr_map_out[imager], extra)
162
- ninstr_map_out[imager] = maps_sequence_processing(ninstr_map_out[imager],
163
- seq_type=image_mode)
164
- if ninstr_map_out[imager] != []:
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 ninstr_map_out, imagers_list_out
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.figure(figsize=(5.5, 5.5), tight_layout=True)
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
- plt.plot(model.parameters.index,
240
- model.parameters[p],
241
- marker=parameters[p][0],
242
- linestyle=parameters[p][1],
243
- color=parameters[p][2],
244
- label=parameters[p][3]) # label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)
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
- plt.plot(fit['best_fit_x'], fit['best_fit_y'], '-', color=parameters[p][2])
249
- plt.fill_between(fit['best_fit_x'], fit['sigma_bounds']['up'], fit['sigma_bounds']['low'],
250
- color=parameters[p][2], alpha=0.20)
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
- plt.fill_between(fit['best_fit_x'], fit['sigv_bounds']['up'], fit['sigv_bounds']['low'],
253
- color=parameters[p][2], alpha=0.05)
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
- plt.plot(model.parameters.index, model.parameters[p], '--', color=parameters[p][2])
256
- ylabel = 'Height or Length [Rsun]'
257
- plt.gca().set_ylim(bottom=0)
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
- plt.plot(fit['best_fit_x'], speed_best_fit, '-', color=parameters[p][2], label=parameters[p][3])
269
- plt.fill_between(fit['best_fit_x'], speed_bound_lower, speed_bound_upper,
270
- color=parameters[p][2], alpha=0.20)
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
- plt.fill_between(fit['best_fit_x'], (Rs2km/sec) * fit['sigv_bounds']['dlow'], (Rs2km/sec) * fit['sigv_bounds']['dup'],
273
- color=parameters[p][2], alpha=0.05)
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
- plt.gca().set_ylim(bottom=0)
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
- plt.plot(model.parameters.index,
282
- model.parameters[parameters[plt_type][0]],
283
- marker=parameters[plt_type][1],
284
- linestyle='',
285
- color=parameters[plt_type][2],
286
- label=parameters[plt_type][3])
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
- plt.plot(fit['best_fit_x'], fit['best_fit_y'], '-', color=parameters[plt_type][2])
290
- plt.fill_between(fit['best_fit_x'], fit['sigma_bounds']['up'], fit['sigma_bounds']['low'],
291
- color=parameters[plt_type][2], alpha=0.05)
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
- plt.xlabel('Time [UT]')
295
- plt.ylabel(ylabel)
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
- plt.legend(loc='lower right')
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
- xx = (mdates.date2num(x) - mdates.date2num(x[0]))
323
- xxx = np.linspace(xx.min(), xx.max(), 120)
324
- dd = mdates.num2date(xxx + mdates.date2num(x[0]))
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.8.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
@@ -17,17 +17,19 @@ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Intended Audience :: Science/Research
19
19
  Classifier: Topic :: Scientific/Engineering :: Physics
20
- Requires-Python: >=3.8, <3.10
20
+ Requires-Python: >=3.8, <3.11
21
21
  Description-Content-Type: text/markdown
22
22
  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,11 +1,13 @@
1
1
  numpy
2
2
  pandas
3
3
  scipy
4
+ aiapy
4
5
  astropy
5
6
  astroquery
6
7
  numexpr
7
8
  sunpy==4.1.0
8
9
  parfive==1.5.1
10
+ pooch
9
11
  matplotlib
10
12
  seaborn
11
13
  streamlit
@@ -10,6 +10,7 @@ dependencies:
10
10
  - pandas
11
11
  - scipy
12
12
  - matplotlib
13
+ - aiapy
13
14
  - astropy
14
15
  - astroquery
15
16
  - numexpr
@@ -23,4 +24,5 @@ dependencies:
23
24
  - pytest-sugar
24
25
  - pip
25
26
  - pip:
27
+ - pooch
26
28
  - stqdm
@@ -1,11 +1,13 @@
1
1
  numpy
2
2
  pandas
3
3
  scipy
4
+ aiapy
4
5
  astropy
5
6
  astroquery
6
7
  numexpr
7
8
  sunpy==4.1.0
8
9
  parfive==1.5.1
10
+ pooch
9
11
  matplotlib
10
12
  seaborn
11
13
  streamlit
@@ -11,6 +11,7 @@ classifiers =
11
11
  Programming Language :: Python :: 3
12
12
  Programming Language :: Python :: 3.8
13
13
  Programming Language :: Python :: 3.9
14
+ Programming Language :: Python :: 3.10
14
15
  Topic :: Scientific/Engineering :: Physics
15
16
 
16
17
  [options]
@@ -45,7 +45,7 @@ setup(
45
45
  'Topic :: Scientific/Engineering :: Physics',
46
46
  ],
47
47
 
48
- python_requires='>=3.8, <3.10',
48
+ python_requires='>=3.8, <3.11',
49
49
  install_requires=requirements,
50
50
 
51
51
  packages=find_packages(),
@@ -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