PyThea 1.2.0__tar.gz → 1.3.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 (71) hide show
  1. {pythea-1.2.0 → pythea-1.3.0}/CHANGELOG.md +15 -1
  2. {pythea-1.2.0 → pythea-1.3.0}/PKG-INFO +4 -4
  3. {pythea-1.2.0 → pythea-1.3.0}/PyThea/PyThea_app.py +112 -14
  4. pythea-1.3.0/PyThea/_version.py +24 -0
  5. {pythea-1.2.0 → pythea-1.3.0}/PyThea/config/selected_imagers.py +26 -20
  6. {pythea-1.2.0 → pythea-1.3.0}/PyThea/geometrical_models.py +4 -5
  7. {pythea-1.2.0 → pythea-1.3.0}/PyThea/modules.py +14 -4
  8. pythea-1.3.0/PyThea/sunpy_dev/net/dataretriever/__init__.py +0 -0
  9. pythea-1.3.0/PyThea/sunpy_dev/net/dataretriever/sources/__init__.py +0 -0
  10. pythea-1.3.0/PyThea/sunpy_dev/net/dataretriever/sources/lasco.py +87 -0
  11. pythea-1.3.0/PyThea/sunpy_dev/net/dataretriever/sources/stereo.py +100 -0
  12. pythea-1.3.0/PyThea/test/__init__.py +0 -0
  13. {pythea-1.2.0 → pythea-1.3.0}/PyThea/utils.py +30 -28
  14. {pythea-1.2.0 → pythea-1.3.0}/PyThea/utils_database.py +3 -0
  15. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/PKG-INFO +4 -4
  16. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/SOURCES.txt +5 -0
  17. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/requires.txt +1 -1
  18. {pythea-1.2.0 → pythea-1.3.0}/pyproject.toml +3 -3
  19. pythea-1.2.0/PyThea/_version.py +0 -34
  20. {pythea-1.2.0 → pythea-1.3.0}/.flake8 +0 -0
  21. {pythea-1.2.0 → pythea-1.3.0}/.readthedocs.yaml +0 -0
  22. {pythea-1.2.0 → pythea-1.3.0}/LICENSE.md +0 -0
  23. {pythea-1.2.0 → pythea-1.3.0}/MANIFEST.in +0 -0
  24. {pythea-1.2.0 → pythea-1.3.0}/PyThea/__init__.py +0 -0
  25. {pythea-1.2.0 → pythea-1.3.0}/PyThea/callbacks.py +0 -0
  26. {pythea-1.2.0 → pythea-1.3.0}/PyThea/config/__init__.py +0 -0
  27. {pythea-1.2.0 → pythea-1.3.0}/PyThea/config/app_styles.py +0 -0
  28. {pythea-1.2.0 → pythea-1.3.0}/PyThea/config/config_sliders.py +0 -0
  29. {pythea-1.2.0 → pythea-1.3.0}/PyThea/config/selected_bodies.py +0 -0
  30. {pythea-1.2.0 → pythea-1.3.0}/PyThea/data/__init__.py +0 -0
  31. {pythea-1.2.0 → pythea-1.3.0}/PyThea/data/sample_data.py +0 -0
  32. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/LICENSE_gcs_python.md +0 -0
  33. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/Parker_spirals/__init__.py +0 -0
  34. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/Parker_spirals/utils.py +0 -0
  35. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/__init__.py +0 -0
  36. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/buttons.py +0 -0
  37. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/hek/__init__.py +0 -0
  38. {pythea-1.2.0 → pythea-1.3.0}/PyThea/extensions/hek/utils.py +0 -0
  39. {pythea-1.2.0 → pythea-1.3.0}/PyThea/pythea_cli.py +0 -0
  40. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/__init__.py +0 -0
  41. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/__init__.py +0 -0
  42. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/__init__.py +0 -0
  43. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/aia/__init__.py +0 -0
  44. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/aia/utils.py +0 -0
  45. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/lasco/__init__.py +0 -0
  46. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py +0 -0
  47. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/stereo/__init__.py +0 -0
  48. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py +0 -0
  49. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/map/__init__.py +0 -0
  50. {pythea-1.2.0 → pythea-1.3.0}/PyThea/sunpy_dev/map/maputils.py +0 -0
  51. {pythea-1.2.0/PyThea/test → pythea-1.3.0/PyThea/sunpy_dev/net}/__init__.py +0 -0
  52. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/Pythea_test.py +0 -0
  53. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/conftest.py +0 -0
  54. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/figure_hashes.json +0 -0
  55. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/test_extension_utils.py +0 -0
  56. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/test_figures.py +0 -0
  57. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/test_geometrical_models.py +0 -0
  58. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/test_imports.py +0 -0
  59. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/test_remote_clients.py +0 -0
  60. {pythea-1.2.0 → pythea-1.3.0}/PyThea/test/test_utils.py +0 -0
  61. {pythea-1.2.0 → pythea-1.3.0}/PyThea/version.py +0 -0
  62. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/.DS_Store +0 -0
  63. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/dependency_links.txt +0 -0
  64. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/entry_points.txt +0 -0
  65. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/not-zip-safe +0 -0
  66. {pythea-1.2.0 → pythea-1.3.0}/PyThea.egg-info/top_level.txt +0 -0
  67. {pythea-1.2.0 → pythea-1.3.0}/README.md +0 -0
  68. {pythea-1.2.0 → pythea-1.3.0}/README_pypi.md +0 -0
  69. {pythea-1.2.0 → pythea-1.3.0}/pytest.ini +0 -0
  70. {pythea-1.2.0 → pythea-1.3.0}/setup.cfg +0 -0
  71. {pythea-1.2.0 → pythea-1.3.0}/setup.py +0 -0
@@ -1,4 +1,18 @@
1
- # v1.1.0 (12-Feb-2025)
1
+ # v1.3.0 (12-Feb-2025)
2
+
3
+ ## Features
4
+ - Extend net sources for selected imagers
5
+ - Adds a method to manually import fits files from folder
6
+
7
+ ## Minor Changes
8
+ - Updates dependences with sunpy>=7.1.2
9
+
10
+ ## Bug Fixes
11
+ - Fixes a bug with figure creation
12
+ - Fixes a bug with kinematic plots
13
+ - Guard against uninitialized variables
14
+
15
+ # v1.2.0 (12-Feb-2025)
2
16
 
3
17
  ## Minor Changes
4
18
  - Change installation to pyproject.toml
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyThea
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: PyThea: A software package to reconstruct the 3D structure of CMEs and shock waves
5
5
  Author-email: Athanasios Kouloumvakos <athkouloumvakos@gmail.com>
6
6
  License:
@@ -662,17 +662,17 @@ Keywords: science,solar physics,solar,coronal mass ejections,shock waves,EUV wav
662
662
  Platform: any
663
663
  Classifier: Programming Language :: Python
664
664
  Classifier: Programming Language :: Python :: 3
665
- Classifier: Programming Language :: Python :: 3.11
665
+ Classifier: Programming Language :: Python :: 3.12
666
666
  Classifier: Development Status :: 5 - Production/Stable
667
667
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
668
668
  Classifier: Operating System :: OS Independent
669
669
  Classifier: Intended Audience :: Science/Research
670
670
  Classifier: Topic :: Scientific/Engineering :: Physics
671
671
  Provides: PyThea
672
- Requires-Python: >=3.11
672
+ Requires-Python: >=3.12
673
673
  Description-Content-Type: text/markdown
674
674
  License-File: LICENSE.md
675
- Requires-Dist: sunpy>=7.0.1
675
+ Requires-Dist: sunpy>=7.1.2
676
676
  Requires-Dist: pandas
677
677
  Requires-Dist: scipy
678
678
  Requires-Dist: matplotlib
@@ -19,7 +19,9 @@
19
19
 
20
20
 
21
21
  import datetime
22
- from copy import copy
22
+ import glob
23
+ import os
24
+ from copy import deepcopy
23
25
 
24
26
  import astropy.units as u
25
27
  import numpy as np
@@ -49,11 +51,34 @@ def delete_from_state(vars):
49
51
  del st.session_state[var]
50
52
 
51
53
 
54
+ def call_extend_sources(instrument):
55
+ for imager in selected_imagers.providers[instrument]['imagers']:
56
+ st.session_state.selected_imagers_[imager]['fido'][2] = a.Provider(st.session_state[f'provider_{instrument}'])
57
+
58
+
52
59
  def highlight_row(row, row_index):
53
- if row.name.strftime('%Y-%m-%dT%H:%M:%S.%f') == row_index.strftime('%Y-%m-%dT%H:%M:%S.%f').values[0]:
54
- return ['background-color: lightpink']*len(row)
55
- else:
56
- return ['']*len(row)
60
+ try:
61
+ if not len(row_index):
62
+ return ['']*len(row)
63
+ if row.name.strftime('%Y-%m-%dT%H:%M:%S.%f') == row_index.strftime('%Y-%m-%dT%H:%M:%S.%f').values[0]:
64
+ return ['background-color: lightpink']*len(row)
65
+ except (IndexError, AttributeError):
66
+ pass
67
+ return ['']*len(row)
68
+
69
+
70
+ def clear_text_input():
71
+ st.session_state['manual_dir'] = st.session_state['manual_dir_text']
72
+ st.session_state['manual_dir_text'] = ''
73
+
74
+
75
+ def list_fits_files(directory):
76
+ """Returns a list of .fits and .fts file paths from the selected directory."""
77
+ if directory:
78
+ fits_files = glob.glob(os.path.join(directory, '*.fits')) + \
79
+ glob.glob(os.path.join(directory, '*.fts'))
80
+ return fits_files
81
+ return []
57
82
 
58
83
 
59
84
  def footer_text():
@@ -81,6 +106,10 @@ def footer_text():
81
106
  st.markdown("""
82
107
  **Citation**: Please cite the following paper [![https://www.frontiersin.org/articles/10.3389/fspas.2022.974137/](https://img.shields.io/static/v1?label=Paper&message=Frontiers&color=red)](https://www.frontiersin.org/articles/10.3389/fspas.2022.974137/) and [![https://doi.org/10.5281/zenodo.5713659](https://zenodo.org/badge/DOI/10.5281/zenodo.5713659.svg)](https://doi.org/10.5281/zenodo.5713659)
83
108
  """)
109
+ st.success('''
110
+ **Don't miss these new features:**
111
+ - Import .fits files from folder.
112
+ ''', icon='ℹ️')
84
113
  st.info('''
85
114
  More imaging data have been added:
86
115
  - SDO/AIA images from 211A channel.
@@ -118,6 +147,8 @@ def run():
118
147
  #############################################################
119
148
  # Startup Variables
120
149
  if 'startup' not in st.session_state:
150
+ from PyThea.sunpy_dev.net.dataretriever.sources import lasco # noqa
151
+ from PyThea.sunpy_dev.net.dataretriever.sources import stereo # noqa
121
152
  st.session_state.startup = {'fitting': True}
122
153
 
123
154
  #############################################################
@@ -125,6 +156,29 @@ def run():
125
156
 
126
157
  if 'date_process' not in st.session_state:
127
158
  date_and_event_selection(st)
159
+ if 'selected_imagers_' not in st.session_state:
160
+ st.session_state.selected_imagers_ = deepcopy(selected_imagers.imager_dict)
161
+
162
+ st.sidebar.toggle('Change default data providers',
163
+ value=False,
164
+ help='Change data provider if you face issues or data are missing.',
165
+ key='extend_net_sources')
166
+ if st.session_state.extend_net_sources:
167
+ container = st.sidebar.container(border=True)
168
+ container.text('Select provider',
169
+ help='The default providers (left) use VSO to download data.')
170
+ for instrument in selected_imagers.providers.keys():
171
+ # container.text(f'{selected_imagers.imager_dict[prov]["source"]}')
172
+ col1, col2 = container.columns([0.3, 0.7], vertical_alignment='center')
173
+ col1.markdown(f'{instrument}:')
174
+ col2.segmented_control(f'{instrument}:',
175
+ options=selected_imagers.providers[instrument]['providers'],
176
+ selection_mode='single',
177
+ default=selected_imagers.providers[instrument]['providers'][0],
178
+ on_change=call_extend_sources,
179
+ args=[instrument],
180
+ key=f'provider_{instrument}',
181
+ label_visibility='collapsed')
128
182
  else:
129
183
  st.sidebar.markdown('## Processing Event|Date:')
130
184
  st.sidebar.info(f'{st.session_state.event_selected}')
@@ -163,6 +217,8 @@ def run():
163
217
  long_val = [0., 360.]
164
218
  elif st.session_state.coord_system == 'HGS':
165
219
  long_val = [-180., 180.]
220
+ else:
221
+ raise ValueError(f"Unsupported coordinate system: '{st.session_state.coord_system}'. Expected 'HGC' or 'HGS'.")
166
222
 
167
223
  longit = st.sidebar.slider(f'{st.session_state.coord_system} \
168
224
  Longitude [deg.]:',
@@ -180,7 +236,6 @@ def run():
180
236
 
181
237
  fitting_sliders(st)
182
238
 
183
- col1, col3, col2 = st.sidebar.columns(3)
184
239
  store_fit_button_pressed = st.sidebar.button('Store Fit')
185
240
 
186
241
  #############################################################
@@ -189,7 +244,7 @@ def run():
189
244
  with st.sidebar.expander('Download Options'):
190
245
  select_imagers_form = st.form(key='select_imagers_form', border=False)
191
246
  imagers_list = select_imagers_form.multiselect('Select Imagers',
192
- options=selected_imagers.imager_dict.keys(),
247
+ options=st.session_state.selected_imagers_.keys(),
193
248
  default=['LC2', 'LC3', 'COR2A'],
194
249
  key='imagers_list')
195
250
  select_imagers_form.form_submit_button(label='Submit', use_container_width=True)
@@ -203,6 +258,37 @@ def run():
203
258
  kwargs={'vars': ['map', 'map_', 'imagers_list_', 'hek_responses']},
204
259
  use_container_width=True)
205
260
 
261
+ # Import Imagers Manually
262
+ select_imager_manual_form = st.container()
263
+ if 'manual_dir' not in st.session_state:
264
+ st.session_state['manual_dir'] = ''
265
+
266
+ imager_manual = select_imager_manual_form.selectbox('Select an imager for manual import',
267
+ options={key for key in st.session_state.selected_imagers_.keys() if not key.endswith('m')})
268
+ select_imager_manual_form.text_input('Enter path containing the .fits files',
269
+ key='manual_dir_text', on_change=clear_text_input)
270
+
271
+ if st.session_state.manual_dir:
272
+ if os.path.isdir(st.session_state.manual_dir):
273
+ fits_files = list_fits_files(st.session_state.manual_dir)
274
+ if fits_files:
275
+ st.session_state.selected_imagers_[f'{imager_manual}m'] = deepcopy(st.session_state.selected_imagers_[imager_manual])
276
+ st.session_state.selected_imagers_[f'{imager_manual}m']['fido'] = {
277
+ 'path': st.session_state.manual_dir,
278
+ 'fits_files': fits_files}
279
+ st.session_state['manual_dir'] = ''
280
+ st.rerun()
281
+ else:
282
+ select_imager_manual_form.warning('No FITS files found in the selected directory.')
283
+ else:
284
+ select_imager_manual_form.error('Invalid directory. Please enter a valid folder path.')
285
+ st.session_state['manual_dir'] = ''
286
+
287
+ filtered_imager_dict = [key for key in st.session_state.selected_imagers_.keys() if key.endswith('m')]
288
+ if filtered_imager_dict:
289
+ formatted_list = ', '.join(filtered_imager_dict)
290
+ select_imager_manual_form.success(f'Imagers available for manual import: {formatted_list}')
291
+
206
292
  with st.sidebar.expander('Processing Options'):
207
293
  procoption_container = st.container()
208
294
  if 'imagers_list_' not in st.session_state:
@@ -294,13 +380,17 @@ def run():
294
380
  st.session_state['imagers_list_'] = imagers_list
295
381
  progress_bar = st.progress(0, text='Preparing to Download data')
296
382
  for i, imager in enumerate(imager_added):
297
- progress_bar.progress(i/len(imager_added), text=f'Download {imager} images from VSO')
383
+ progress_bar.progress((i + 1)/len(imager_added), text=f'Download {imager} images from VSO')
298
384
  if imager not in st.session_state.map_:
299
385
  timerange = a.Time(st.session_state.date_process + datetime.timedelta(hours=imaging_time_range[0]),
300
386
  st.session_state.date_process + datetime.timedelta(hours=imaging_time_range[1]))
301
387
 
302
388
  if st.session_state.offline_mode is False:
303
- downloaded_files = download_fits(timerange, imager)
389
+ if imager[-1] != 'm':
390
+ downloaded_files = download_fits(timerange, imager,
391
+ imager_dict=st.session_state.selected_imagers_)
392
+ else:
393
+ downloaded_files = st.session_state.selected_imagers_[imager]['fido']['fits_files']
304
394
  elif st.session_state.offline_mode is True:
305
395
  progress_bar.desc = f'Load {imager} images from local database.'
306
396
  event_id = st.session_state.event_selected.replace('-', '').replace(':', '').replace('|', 'D').replace('.', 'p') \
@@ -309,7 +399,7 @@ def run():
309
399
 
310
400
  st.session_state.map_[imager] = load_fits(downloaded_files)
311
401
  st.session_state.map_[imager] = single_imager_maps_process(st.session_state.map_[imager],
312
- **selected_imagers.imager_dict[imager]['process'],
402
+ **st.session_state.selected_imagers_[imager]['process'],
313
403
  skip='sequence_processing')
314
404
  processed_images = single_imager_maps_process(st.session_state.map_[imager],
315
405
  skip=['filter', 'prepare'],
@@ -394,6 +484,8 @@ def run():
394
484
  frame=frames.HeliographicStonyhurst,
395
485
  observer='earth',
396
486
  obstime=running_map_date)
487
+ else:
488
+ raise ValueError(f"Unsupported coordinate system: '{st.session_state.coord_system}'. Expected 'HGC' or 'HGS'.")
397
489
  st.session_state.center = center
398
490
 
399
491
  if st.session_state.geometrical_model == 'Spheroid':
@@ -402,6 +494,8 @@ def run():
402
494
  model = ellipsoid(center, radaxis, orthoaxis1, orthoaxis2, tilt)
403
495
  elif st.session_state.geometrical_model == 'GCS':
404
496
  model = gcs(center, height, alpha, kappa, tilt)
497
+ else:
498
+ raise ValueError(f"Unsupported geometrical model: '{st.session_state.geometrical_model}'. Expected 'Spheroid', 'Ellipsoid', or 'GCS'.")
405
499
 
406
500
  #############################################################
407
501
  # Plot main and supplement figure images
@@ -418,12 +512,14 @@ def run():
418
512
  st.markdown('*Active Regions without NOAA number have been removed from the table.')
419
513
 
420
514
  if plt_supp_imagers:
421
- if len(st.session_state.imagers_list_) < 3:
422
- other_element = [element for element in st.session_state.imagers_list_ if element != imager_select][0]
423
- fig, axis = figure_streamlit(st, get_closest(st.session_state.map[other_element], running_map_date), image_mode, other_element, model)
515
+ other_elements = [element for element in st.session_state.imagers_list_ if element != imager_select]
516
+ if not other_elements:
517
+ st.warning('No supplementary imagers available.')
518
+ elif len(st.session_state.imagers_list_) < 3:
519
+ fig, axis = figure_streamlit(st, get_closest(st.session_state.map[other_elements[0]], running_map_date), image_mode, other_elements[0], model)
424
520
  st.pyplot(fig)
425
521
  else:
426
- supl_imagers_list = copy(st.session_state.imagers_list_)
522
+ supl_imagers_list = deepcopy(st.session_state.imagers_list_)
427
523
  supl_imagers_list.remove(imager_select)
428
524
  supl_imagers = st.select_slider('Select supplement imagers',
429
525
  options=supl_imagers_list,
@@ -502,6 +598,8 @@ def run():
502
598
  fit_args_ = {'type': 'custom', 'expression': fitcustexpres_select,
503
599
  'bounds': ([-np.inf, -np.inf, -np.inf], [np.inf, np.inf, np.inf]), 'order': 3}
504
600
  st.info(f'A custom function {fit_args_["expression"]} was fitted when processing parameters.')
601
+ else:
602
+ raise ValueError(f"Unsupported fit mode: '{fit_mode}'. Expected 'polynomial', 'spline', or 'custom'.")
505
603
 
506
604
  if plt_kinematics_select == 'All':
507
605
  col1, col2 = st.columns(2)
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.3.0'
22
+ __version_tuple__ = version_tuple = (1, 3, 0)
23
+
24
+ __commit_id__ = commit_id = 'gf5bc63e11'
@@ -3,87 +3,93 @@
3
3
  How to use this:
4
4
 
5
5
  '''
6
+
6
7
  import astropy.units as u
7
8
  import sunpy_soar # noqa
8
9
  from sunpy.net import attrs as a
9
10
 
10
11
  imager_dict = {}
11
12
 
12
- imager_dict['LC2'] = {'fido': (a.Instrument.lasco, a.Detector.c2),
13
+ imager_dict['LC2'] = {'fido': [a.Instrument.lasco, a.Detector.c2, a.Provider('SDAC')],
13
14
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'polar': 'Clear', 'superpixel': 2},
14
15
  'source': 'SOHO', 'instrument': 'LASCO', 'detector': 'C2'}
15
16
 
16
- imager_dict['LC3'] = {'fido': (a.Instrument.lasco, a.Detector.c3),
17
+ imager_dict['LC3'] = {'fido': [a.Instrument.lasco, a.Detector.c3, a.Provider('SDAC')],
17
18
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'polar': 'Clear', 'superpixel': 2},
18
19
  'source': 'SOHO', 'instrument': 'LASCO', 'detector': 'C3'}
19
20
 
20
- imager_dict['AIA-193'] = {'fido': (a.Instrument.aia, a.Wavelength(19.3 * u.nm), a.Sample(1*u.minute)),
21
+ imager_dict['AIA-193'] = {'fido': [a.Instrument.aia, a.Wavelength(19.3 * u.nm), a.Sample(1*u.minute)],
21
22
  'process': {'dimensions': (4096*u.pixel, 4096*u.pixel), 'superpixel': 8, 'exposure': 1.90},
22
23
  'source': 'SDO', 'instrument': 'AIA', 'wavelength': '193'}
23
24
 
24
- imager_dict['AIA-211'] = {'fido': (a.Instrument.aia, a.Wavelength(21.1 * u.nm), a.Sample(1*u.minute)),
25
+ imager_dict['AIA-211'] = {'fido': [a.Instrument.aia, a.Wavelength(21.1 * u.nm), a.Sample(1*u.minute)],
25
26
  'process': {'dimensions': (4096*u.pixel, 4096*u.pixel), 'superpixel': 8, 'exposure': 1.90},
26
27
  'source': 'SDO', 'instrument': 'AIA', 'wavelength': '211'}
27
28
 
28
- imager_dict['COR2A'] = {'fido': (a.Source('STEREO_A'), a.Detector.cor2),
29
+ imager_dict['COR2A'] = {'fido': [a.Source('STEREO_A'), a.Detector.cor2, a.Provider('SSC')],
29
30
  'process': {'dimensions': (2048*u.pixel, 2048*u.pixel), 'polar': 1001, 'superpixel': 4},
30
31
  'source': 'STEREO_A', 'instrument': 'SECCHI', 'detector': 'COR2'}
31
32
 
32
- imager_dict['COR2B'] = {'fido': (a.Source('STEREO_B'), a.Detector.cor2),
33
+ imager_dict['COR2B'] = {'fido': [a.Source('STEREO_B'), a.Detector.cor2, a.Provider('SSC')],
33
34
  'process': {'dimensions': (2048*u.pixel, 2048*u.pixel), 'polar': 1001, 'superpixel': 4},
34
35
  'source': 'STEREO_B', 'instrument': 'SECCHI', 'detector': 'COR2'}
35
36
 
36
- imager_dict['EUVIA'] = {'fido': (a.Source('STEREO_A'), a.Detector.euvi, a.Wavelength(19.5 * u.nm)),
37
+ imager_dict['EUVIA'] = {'fido': [a.Source('STEREO_A'), a.Detector.euvi, a.Wavelength(19.5 * u.nm), a.Provider('SSC')],
37
38
  'process': {'dimensions': (2048*u.pixel, 2048*u.pixel), 'superpixel': 4},
38
39
  'source': 'STEREO_A', 'instrument': 'SECCHI', 'detector': 'EUVI'}
39
40
 
40
- imager_dict['EUVIB'] = {'fido': (a.Source('STEREO_B'), a.Detector.euvi, a.Wavelength(19.5 * u.nm)),
41
+ imager_dict['EUVIB'] = {'fido': [a.Source('STEREO_B'), a.Detector.euvi, a.Wavelength(19.5 * u.nm), a.Provider('SSC')],
41
42
  'process': {'dimensions': (2048*u.pixel, 2048*u.pixel), 'superpixel': 4},
42
43
  'source': 'STEREO_B', 'instrument': 'SECCHI', 'detector': 'EUVI'}
43
44
 
44
- imager_dict['COR1A'] = {'fido': (a.Source('STEREO_A'), a.Detector.cor1),
45
+ imager_dict['COR1A'] = {'fido': [a.Source('STEREO_A'), a.Detector.cor1, a.Provider('SSC'), a.Provider('SSC')],
45
46
  'process': {'dimensions': (512*u.pixel, 512*u.pixel)},
46
47
  'source': 'STEREO_A', 'instrument': 'SECCHI', 'detector': 'COR1'}
47
48
 
48
- imager_dict['COR1B'] = {'fido': (a.Source('STEREO_B'), a.Detector.cor1),
49
+ imager_dict['COR1B'] = {'fido': [a.Source('STEREO_B'), a.Detector.cor1, a.Provider('SSC'), a.Provider('SSC')],
49
50
  'process': {'dimensions': (512*u.pixel, 512*u.pixel)},
50
51
  'source': 'STEREO_B', 'instrument': 'SECCHI', 'detector': 'COR1'}
51
52
 
52
- imager_dict['HI1A'] = {'fido': (a.Source('STEREO_A'), a.Detector.hi1),
53
+ imager_dict['HI1A'] = {'fido': [a.Source('STEREO_A'), a.Detector.hi1, a.Provider('SSC')],
53
54
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'superpixel': 2},
54
55
  'source': 'STEREO_A', 'instrument': 'SECCHI', 'detector': 'HI1'}
55
56
 
56
- imager_dict['HI1B'] = {'fido': (a.Source('STEREO_B'), a.Detector.hi1),
57
+ imager_dict['HI1B'] = {'fido': [a.Source('STEREO_B'), a.Detector.hi1, a.Provider('SSC')],
57
58
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'superpixel': 2},
58
59
  'source': 'STEREO_B', 'instrument': 'SECCHI', 'detector': 'HI1'}
59
60
 
60
- imager_dict['HI2A'] = {'fido': (a.Source('STEREO_A'), a.Detector.hi2),
61
+ imager_dict['HI2A'] = {'fido': [a.Source('STEREO_A'), a.Detector.hi2, a.Provider('SSC')],
61
62
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'superpixel': 2},
62
63
  'source': 'STEREO_A', 'instrument': 'SECCHI', 'detector': 'HI2'}
63
64
 
64
- imager_dict['HI2B'] = {'fido': (a.Source('STEREO_B'), a.Detector.hi2),
65
+ imager_dict['HI2B'] = {'fido': [a.Source('STEREO_B'), a.Detector.hi2, a.Provider('SSC')],
65
66
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'superpixel': 2},
66
67
  'source': 'STEREO_B', 'instrument': 'SECCHI', 'detector': 'HI2'}
67
68
 
68
- imager_dict['WISPR1'] = {'fido': (a.Instrument.wispr, a.Detector.inner),
69
+ imager_dict['WISPR1'] = {'fido': [a.Instrument.wispr, a.Detector.inner],
69
70
  'process': {'dimensions': (960*u.pixel, 1024*u.pixel), 'processing_level': 3, 'superpixel': 2},
70
71
  'source': 'PSP', 'instrument': 'WISPR', 'detector': 'Inner'}
71
72
 
72
- imager_dict['WISPR2'] = {'fido': (a.Instrument.wispr, a.Detector.outer),
73
+ imager_dict['WISPR2'] = {'fido': [a.Instrument.wispr, a.Detector.outer],
73
74
  'process': {'dimensions': (960*u.pixel, 1024*u.pixel), 'processing_level': 3, 'superpixel': 2},
74
75
  'source': 'PSP', 'instrument': 'WISPR', 'detector': 'Outer'}
75
76
 
76
- imager_dict['EUI-FSI'] = {'fido': (a.Instrument('EUI'), a.soar.Product('EUI-FSI174-IMAGE'), a.Level(2)),
77
+ imager_dict['EUI-FSI'] = {'fido': [a.Instrument('EUI'), a.soar.Product('EUI-FSI174-IMAGE'), a.Level(2)],
77
78
  'process': {'superpixel': 1},
78
79
  'source': 'SOLO', 'instrument': 'EUI-FSI', 'wavelength': '174'}
79
80
 
80
- imager_dict['METIS'] = {'fido': (a.Instrument('METIS'), a.soar.Product('METIS-VL-TB'), a.Level(2)),
81
+ imager_dict['METIS'] = {'fido': [a.Instrument('METIS'), a.soar.Product('METIS-VL-TB'), a.Level(2)],
81
82
  'process': {'dimensions': (1024*u.pixel, 1024*u.pixel), 'superpixel': 2},
82
83
  'source': 'SOLO', 'instrument': 'METIS', 'detector': 'VLD', 'wavelength': 'TB'}
83
84
 
84
85
  for tile in range(1, 5):
85
86
  z = 'T' if tile in [1, 2] else 'G'
86
- imager_dict[f'SOLOHI-T{tile}'] = {'fido': (a.Instrument('SOLOHI'),
87
- a.soar.Product(f'SOLOHI-{tile}F{z}'), a.Level(2)),
87
+ imager_dict[f'SOLOHI-T{tile}'] = {'fido': [a.Instrument('SOLOHI'),
88
+ a.soar.Product(f'SOLOHI-{tile}F{z}'), a.Level(2)],
88
89
  'process': {'superpixel': 2},
89
90
  'source': 'SOLO', 'instrument': 'SOLOHI', 'detector': f'T{tile}'}
91
+
92
+ providers = {'LASCO': {'imagers': ['LC2', 'LC3'], 'providers': ['SDAC', 'NASCOM']},
93
+ 'STEREO': {'imagers': ['COR2A', 'COR2B', 'COR1A', 'COR1B',
94
+ 'EUVIA', 'EUVIB', 'HI1A', 'HI1B',
95
+ 'HI2A', 'HI2B'], 'providers': ['SSC', 'NASCOM']}, }
@@ -406,7 +406,7 @@ class spheroid:
406
406
  output += 'CRLT = %3.2f degrees \n'%center_.lat.to_value(u.degree)
407
407
  output += 'rcenter = %3.2f Rsun \n'%self.rcenter.to_value(u.R_sun)
408
408
  output += 'radaxis = %3.2f Rsun \n'%self.radaxis.to_value(u.R_sun)
409
- output += 'height = %3.2f Rsun \n'%self.radaxis.to_value(u.R_sun)
409
+ output += 'height = %3.2f Rsun \n'%self.height.to_value(u.R_sun)
410
410
  output += 'kappa = %3.2f \n'%self.kappa
411
411
  output += 'epsilon = %3.2f '%self.epsilon
412
412
  output += '>'
@@ -576,7 +576,7 @@ class ellipsoid(spheroid):
576
576
  output += 'radaxis = %3.2f Rsun \n'%self.radaxis.to_value(u.R_sun)
577
577
  output += 'orthoaxis1 = %3.2f Rsun \n'%self.orthoaxis1.to_value(u.R_sun)
578
578
  output += 'orthoaxis2 = %3.2f Rsun \n'%self.orthoaxis2.to_value(u.R_sun)
579
- output += 'height = %3.2f Rsun \n'%self.radaxis.to_value(u.R_sun)
579
+ output += 'height = %3.2f Rsun \n'%self.height.to_value(u.R_sun)
580
580
  output += 'kappa = %3.2f \n'%self.kappa
581
581
  output += 'alpha = %3.2f \n'%self.alpha
582
582
  output += 'epsilon = %3.2f '%self.epsilon
@@ -653,7 +653,7 @@ class gcs():
653
653
  """
654
654
  Returns the cross-section radius at the apex (see eq. 29 in T2011).
655
655
  """
656
- rapex = self.kappa * (self.h_() / np.cos(self.alpha) + self.h_() * np.tan(self.alpha)) / (1 - self.kappa ** 2)
656
+ rapex = self.kappa * (self.h / np.cos(self.alpha) + self.h * np.tan(self.alpha)) / (1 - self.kappa ** 2)
657
657
  return rapex
658
658
 
659
659
  @property
@@ -695,7 +695,6 @@ class gcs():
695
695
  Converted and modified from the IDL script shellskeleton.pro
696
696
  https://hesperia.gsfc.nasa.gov/ssw/stereo/secchi/idl/scraytrace/shellskeleton.pro
697
697
  """
698
- self.height.to_value(u.R_sun)
699
698
  alpha = self.alpha.to_value(u.rad)
700
699
  kappa = self.kappa
701
700
  h = self.h.to_value(u.R_sun)
@@ -795,7 +794,7 @@ class gcs():
795
794
  output += 'CRLN = %3.2f degrees \n'%center_.lon.to_value(u.degree)
796
795
  output += 'CRLT = %3.2f degrees \n'%center_.lat.to_value(u.degree)
797
796
  output += 'rcenter = %3.2f Rsun \n'%self.rcenter.to_value(u.R_sun)
798
- output += 'height = %3.2f Rsun \n'%self.radaxis.to_value(u.R_sun)
797
+ output += 'height = %3.2f Rsun \n'%self.height.to_value(u.R_sun)
799
798
  output += 'alpha = %3.2f \n'%self.alpha
800
799
  output += 'kappa = %3.2f \n'%self.kappa
801
800
  output += 'tilt = %3.2f '%self.tilt
@@ -88,8 +88,8 @@ def date_and_event_selection(st):
88
88
 
89
89
  elif initialisation == 'Manual':
90
90
  # Manual form for event ID selection and time input
91
- with st.sidebar.form('my_form'):
92
- col1, col2 = st.columns(2)
91
+ with st.sidebar.form('my_form') as form:
92
+ col1, col2 = form.columns(2)
93
93
  ev_id = col1.selectbox('Event ID',
94
94
  options=['Select', 'FL', 'CME', 'SHW'])
95
95
  time = col2.time_input('Time', datetime.time(12, 0))
@@ -256,6 +256,8 @@ def final_parameters_gmodel(st):
256
256
  rcenter, radaxis, orthoaxis1 = (st.session_state.rcenter * u.R_sun,
257
257
  st.session_state.radaxis * u.R_sun,
258
258
  st.session_state.orthoaxis1 * u.R_sun)
259
+ else:
260
+ raise ValueError(f"Unsupported representation mode for Spheroid: '{rmode}'. Expected 'h, e, k' or 'r, a, b'.")
259
261
  return rcenter, radaxis, orthoaxis1
260
262
 
261
263
  elif gmodel == 'Ellipsoid':
@@ -269,6 +271,8 @@ def final_parameters_gmodel(st):
269
271
  st.session_state.radaxis * u.R_sun,
270
272
  st.session_state.orthoaxis1 * u.R_sun,
271
273
  st.session_state.orthoaxis2 * u.R_sun)
274
+ else:
275
+ raise ValueError(f"Unsupported representation mode for Ellipsoid: '{rmode}'. Expected 'h, e, k, a' or 'r, a, b, c'.")
272
276
  tilt = st.session_state.tilt * u.degree
273
277
  return rcenter, radaxis, orthoaxis1, orthoaxis2, tilt
274
278
 
@@ -278,6 +282,8 @@ def final_parameters_gmodel(st):
278
282
  alpha = st.session_state.alpha * u.degree
279
283
  kappa = st.session_state.kappa
280
284
  rcenter = gcs.rcenter_(height, alpha, kappa)
285
+ else:
286
+ raise ValueError(f"Unsupported representation mode for GCS: '{rmode}'. Expected 'h, a, k, t'.")
281
287
  tilt = st.session_state.tilt * u.degree
282
288
  return rcenter, height, alpha, kappa, tilt
283
289
 
@@ -325,13 +331,17 @@ def figure_streamlit(st, running_map, image_mode, imager, model):
325
331
 
326
332
  # Plot the model based on the selected mesh mode
327
333
  plot_mode = st.session_state.plot_mesh_mode
328
- if plot_mode == 'Skeleton':
334
+ if plot_mode == 'No plot':
335
+ pass
336
+ elif plot_mode == 'Skeleton':
329
337
  model.plot(axis, mode='Skeleton')
330
338
  elif plot_mode == 'Full':
331
339
  model.plot(axis, mode='Skeleton')
332
340
  model.plot(axis, mode='Full')
333
341
  elif plot_mode == 'Surface':
334
342
  model.plot(axis, only_surface=True)
343
+ else:
344
+ raise ValueError(f"Unsupported plot mode: '{plot_mode}'. Expected 'No plot', 'Skeleton', 'Full', or 'Surface'.")
335
345
 
336
346
  # Optionally plot star field, solar reference lines, and Parker spirals
337
347
  if st.session_state.star_field:
@@ -352,7 +362,7 @@ def figure_streamlit(st, running_map, image_mode, imager, model):
352
362
  if 'hek_responses' not in st.session_state:
353
363
  st.session_state.hek_responses = {'Active Regions': [], 'Coronal Holes': [], 'Flares': []}
354
364
  for mode in st.session_state.hek_list:
355
- st.session_state.hek_responses[mode] = plot_hek(axis, running_map.date_average, mode,
365
+ st.session_state.hek_responses[mode] = plot_hek(axis, getattr(running_map, 'date_average', None) or running_map.date, mode,
356
366
  st.session_state.imaging_time_range,
357
367
  hek_responses=st.session_state.hek_responses)
358
368
 
@@ -0,0 +1,87 @@
1
+ from collections import OrderedDict
2
+
3
+ import pandas as pd
4
+ from sunpy.net.dataretriever import GenericClient, QueryResponse
5
+ from sunpy.time import TimeRange
6
+
7
+ __all__ = ['LASCOClient']
8
+
9
+
10
+ class LASCOClient(GenericClient):
11
+ """
12
+ Provides access to data from NASA/NASCOM for LASCO instrument on board SOHO.
13
+
14
+ Examples
15
+ --------
16
+ >>> from sunpy.net import Fido, attrs as a
17
+ >>> res = Fido.search(a.Time('2015-06-21 00:00', '2015-06-23 23:59'),
18
+ ... a.Detector('c2'),
19
+ a.Instrument('LASCO'),
20
+ a.Provider('NASCOM'))
21
+
22
+ """
23
+
24
+ baseurl = r'https://sohoftp.nascom.nasa.gov/qkl/lasco/quicklook/level_05/%y%m%d/'
25
+ # r'https://umbra.nascom.nasa.gov/pub/lasco_level05/%y%m%d/'
26
+
27
+ def post_search_hook(self, i, matchdict):
28
+
29
+ rowdict = OrderedDict()
30
+ rowdict['Start Time'] = i['start']
31
+ rowdict['End Time'] = i['start']
32
+ rowdict['Instrument'] = matchdict['Instrument'][0].upper()
33
+ rowdict['Source'] = matchdict['Source'][0]
34
+ rowdict['Provider'] = matchdict['Provider'][0]
35
+ rowdict['url'] = i['url']
36
+
37
+ return rowdict
38
+
39
+ def search(self, *args, **kwargs):
40
+ """
41
+ Query this client for a list of results.
42
+
43
+ Parameters
44
+ ----------
45
+ \\*args: `tuple`
46
+ `sunpy.net.attrs` objects representing the query.
47
+ \\*\\*kwargs: `dict`
48
+ Any extra keywords to refine the search.
49
+
50
+ Returns
51
+ -------
52
+ A `QueryResponse` instance containing the query result.
53
+ """
54
+
55
+ baseurl, _, matchdict = self.pre_search_hook(*args, **kwargs)
56
+
57
+ tr = TimeRange(matchdict['Start Time'], matchdict['End Time'])
58
+
59
+ metalist = []
60
+
61
+ for times in pd.date_range(tr.start.strftime('%Y-%m-%d %H:%M:%S'), tr.end.strftime('%Y-%m-%d %H:%M:%S')):
62
+ url = times.strftime(baseurl)
63
+
64
+ data = pd.read_fwf(url + matchdict['Detector'][0] + '/img_hdr.txt',
65
+ header=None, widths=(13, 13, 13),
66
+ names=['filename', 'date', 'time'])
67
+ data = data.set_index([pd.to_datetime(data.iloc[:, 1]+' '+data.iloc[:, 2], format='%Y/%m/%d %H:%M:%S')])
68
+ data = data[(data.index >= tr.start.strftime('%Y-%m-%d %H:%M:%S')) &
69
+ (data.index <= tr.end.strftime('%Y-%m-%d %H:%M:%S'))]
70
+ # files = 'https://sohoftp.nascom.nasa.gov/qkl/lasco/quicklook/level_05/210804/' + 'c2/' + files
71
+
72
+ for f in data.itertuples():
73
+ filemeta = {'start': f.Index,
74
+ 'url': url+matchdict['Detector'][0]+'/' + f.filename}
75
+ rowdict = self.post_search_hook(filemeta, matchdict)
76
+ metalist.append(rowdict)
77
+
78
+ return QueryResponse(metalist, client=self)
79
+
80
+ @classmethod
81
+ def register_values(cls):
82
+ from sunpy.net import attrs
83
+ adict = {attrs.Instrument: [('LASCO', '')],
84
+ attrs.Source: [('SOHO', '')],
85
+ attrs.Provider: [('NASCOM', '')],
86
+ attrs.Detector: [('c2', ''), ('c3', '')]}
87
+ return adict
@@ -0,0 +1,100 @@
1
+ from collections import OrderedDict
2
+
3
+ import pandas as pd
4
+ from sunpy.net.dataretriever import GenericClient, QueryResponse
5
+ from sunpy.time import TimeRange
6
+
7
+ __all__ = ['STEREOClient']
8
+
9
+
10
+ class STEREOClient(GenericClient):
11
+ """
12
+ Provides access to data from NASA/NASCOM for SECCHI instrument suite on board STEREO.
13
+
14
+ Examples
15
+ --------
16
+ >>> from sunpy.net import Fido, attrs as a
17
+ >>> res = Fido.search(a.Time('2015-06-21 00:00', '2015-06-23 23:59'),
18
+ ... a.Detector('cor2'),
19
+ a.Instrument('STEREO'),
20
+ a.Provider('NASCOM'))
21
+
22
+ """
23
+
24
+ baseurl = r'https://stereo-ssc.nascom.nasa.gov/data/ins_data/secchi/L0/a/img/INSTRUMENT/%Y%m%d/'
25
+
26
+ def post_search_hook(self, i, matchdict):
27
+
28
+ rowdict = OrderedDict()
29
+ rowdict['Start Time'] = i['start']
30
+ rowdict['End Time'] = i['start']
31
+ rowdict['Instrument'] = matchdict['Instrument'][0].upper()
32
+ rowdict['Source'] = matchdict['Source'][0]
33
+ rowdict['Provider'] = matchdict['Provider'][0]
34
+ rowdict['url'] = i['url']
35
+
36
+ return rowdict
37
+
38
+ def search(self, *args, **kwargs):
39
+ """
40
+ Query this client for a list of results.
41
+
42
+ Parameters
43
+ ----------
44
+ \\*args: `tuple`
45
+ `sunpy.net.attrs` objects representing the query.
46
+ \\*\\*kwargs: `dict`
47
+ Any extra keywords to refine the search.
48
+
49
+ Returns
50
+ -------
51
+ A `QueryResponse` instance containing the query result.
52
+ """
53
+
54
+ baseurl, _, matchdict = self.pre_search_hook(*args, **kwargs)
55
+ summaryurl = r'https://stereo-ssc.nascom.nasa.gov/data/ins_data/secchi/L0/??/summary/SCC%Y%m.img.DETECTOR'
56
+
57
+ tr = TimeRange(matchdict['Start Time'], matchdict['End Time'])
58
+
59
+ metalist = []
60
+ print(matchdict['Source'][0])
61
+ if matchdict['Source'][0].upper() == 'STEREO_A':
62
+ scl, scc = 'a', 'sccA'
63
+ elif matchdict['Source'][0].upper() == 'STEREO_B':
64
+ scl, scc = 'b', 'sccB'
65
+
66
+ if matchdict['Detector'][0].upper() == 'COR2':
67
+ detect = 'c2'
68
+
69
+ for times in pd.date_range(tr.start.strftime('%Y-%m-%d %H:%M:%S'), tr.end.strftime('%Y-%m-%d %H:%M:%S')):
70
+ urlsum = summaryurl.replace('??', scl).replace('SCC', scc).replace('DETECTOR', detect)
71
+ urlsum = times.strftime(urlsum) # sccA201105.img.h1
72
+
73
+ data = pd.read_fwf(urlsum,
74
+ header=None, colspecs=([0, 25], [28, 47], [50, 54]), skiprows=2,
75
+ names=['filename', 'date', 'telescope'])
76
+ data = data.set_index([pd.to_datetime(data.iloc[:, 1], format='%Y/%m/%d %H:%M:%S')])
77
+ data = data[(data.index >= tr.start.strftime('%Y-%m-%d %H:%M:%S')) &
78
+ (data.index <= tr.end.strftime('%Y-%m-%d %H:%M:%S'))]
79
+
80
+ # URL for example: files = 'https://stereo-ssc.nascom.nasa.gov/data/ins_data/secchi/L0/a/img/cor2/20211028/' + files
81
+ urldata = times.strftime(baseurl).replace('INSTRUMENT', matchdict['Detector'][0])
82
+
83
+ for f in data.itertuples():
84
+ filemeta = {'start': f.Index,
85
+ 'url': urldata + f.filename}
86
+ rowdict = self.post_search_hook(filemeta, matchdict)
87
+ metalist.append(rowdict)
88
+
89
+ return QueryResponse(metalist, client=self)
90
+
91
+ @classmethod
92
+ def register_values(cls):
93
+ from sunpy.net import attrs
94
+ adict = {
95
+ attrs.Instrument: [('SECCHI', '')],
96
+ attrs.Source: [('STEREO_A', ''), ('STEREO_B', '')],
97
+ attrs.Provider: [('NASCOM', '')],
98
+ attrs.Detector: [('COR2', ''), ('COR1', '')]
99
+ }
100
+ return adict
File without changes
@@ -91,7 +91,7 @@ def get_hek_flare(timerange, thresshold='B1.0'):
91
91
  return flare_list_
92
92
 
93
93
 
94
- def make_figure(map, cmap='Greys_r', clim=[-20, 20], clip_model=True, **kwargs):
94
+ def make_figure(smap, cmap='Greys_r', clim=[-20, 20], clip_model=True, **kwargs):
95
95
  """
96
96
  Creates the main imager figure and returns the figure and axis handles.
97
97
 
@@ -121,49 +121,49 @@ def make_figure(map, cmap='Greys_r', clim=[-20, 20], clip_model=True, **kwargs):
121
121
  This function customizes the plot by optionally applying a median filter,
122
122
  setting color limits, drawing the solar limb, and adjusting axis properties.
123
123
  """
124
- fig = kwargs.get('fig', plt.figure())
125
- axis = kwargs.get('axis', plt.subplot(projection=map))
124
+ fig = kwargs['fig'] if 'fig' in kwargs else plt.figure()
125
+ axis = kwargs['axis'] if 'axis' in kwargs else plt.subplot(projection=smap)
126
126
 
127
127
  median_filter_value = kwargs.get('median_filter', 1)
128
128
  if median_filter_value != 1:
129
- map = sunpy.map.Map(median_filter(map.data, size=int(median_filter_value)), map.meta)
129
+ smap = sunpy.map.Map(median_filter(smap.data, size=int(median_filter_value)), smap.meta)
130
130
 
131
- if map.instrument in ['WISPR', 'Metis'] or map.instrument.startswith('SoloHI'):
131
+ if smap.instrument in ['WISPR', 'Metis'] or smap.instrument.startswith('SoloHI'):
132
132
  clim = [-10**-clim[0], 10**-clim[1]]
133
133
 
134
134
  if cmap == 'default':
135
135
  # TODO: For plain images or when EUVIA-B are used, this does not work very well.
136
- map.plot(norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
136
+ smap.plot(norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
137
137
  else:
138
- map.plot(cmap=cmap, norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
138
+ smap.plot(cmap=cmap, norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
139
139
 
140
- map.draw_limb(resolution=90)
140
+ smap.draw_limb(resolution=90)
141
141
 
142
142
  yax = axis.coords[1]
143
143
  yax.set_ticklabel(rotation=90)
144
144
 
145
145
  if clip_model:
146
- axis.set_xlim([0, map.data.shape[0]])
147
- axis.set_ylim([0, map.data.shape[1]])
146
+ axis.set_xlim([0, smap.data.shape[0]])
147
+ axis.set_ylim([0, smap.data.shape[1]])
148
148
 
149
- cref = map.pixel_to_world(0*u.pix, 0*u.pix)
150
- if cref.Tx > 0 and (map.instrument != 'WISPR'):
149
+ cref = smap.pixel_to_world(0*u.pix, 0*u.pix)
150
+ if cref.Tx > 0 and (smap.instrument != 'WISPR'):
151
151
  axis.invert_xaxis()
152
152
  if cref.Ty > 0:
153
153
  axis.invert_yaxis()
154
154
 
155
- if map.instrument == 'SoloHI':
156
- title = 'SoloHI' + f' Tile-{map.detector}' ' $T_{AGV}:$' + parse_time(map.date_average).strftime('%Y-%m-%d %H:%M:%S')
157
- elif map.instrument == 'Metis':
155
+ if smap.instrument == 'SoloHI':
156
+ title = 'SoloHI' + f' Tile-{smap.detector}' ' $T_{AGV}:$' + parse_time(smap.date_average).strftime('%Y-%m-%d %H:%M:%S')
157
+ elif smap.instrument == 'Metis':
158
158
  title = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}',
159
- ' $T_{AGV}:$' + parse_time(map.date_average).strftime('%Y-%m-%d %H:%M:%S'),
160
- map.latex_name.replace('VLD', 'METIS-VDL'))
159
+ ' $T_{AGV}:$' + parse_time(smap.date_average).strftime('%Y-%m-%d %H:%M:%S'),
160
+ smap.latex_name.replace('VLD', 'METIS-VDL'))
161
161
  else:
162
162
  title = re.sub(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}',
163
- ' $T_{AGV}:$' + parse_time(map.date_average).strftime('%Y-%m-%d %H:%M:%S'),
164
- map.latex_name)
165
- print(map.instrument)
166
- print(map.latex_name)
163
+ ' $T_{AGV}:$' + parse_time(smap.date_average).strftime('%Y-%m-%d %H:%M:%S'),
164
+ smap.latex_name)
165
+ print(smap.instrument)
166
+ print(smap.latex_name)
167
167
  axis.set_title(title,
168
168
  fontsize=10, pad=8)
169
169
 
@@ -433,13 +433,15 @@ def single_imager_maps_process(map_list, skip=None, **kwargs):
433
433
  -----
434
434
  Operations can be skipped by specifying them in `skip`, e.g., 'filter', 'prepare', 'sequence_processing'.
435
435
  """
436
- if 'filter' not in str(skip or ''):
436
+ skip_set = set([skip] if isinstance(skip, str) else (skip or []))
437
+
438
+ if 'filter' not in skip_set:
437
439
  map_list = filter_maps(map_list, **kwargs)
438
440
 
439
- if 'prepare' not in str(skip or ''):
441
+ if 'prepare' not in skip_set:
440
442
  map_list = prepare_maps(map_list, **kwargs)
441
443
 
442
- if 'sequence_processing' not in str(skip or ''):
444
+ if 'sequence_processing' not in skip_set:
443
445
  map_list = maps_sequence_processing(map_list, **kwargs)
444
446
 
445
447
  return map_list
@@ -477,12 +479,12 @@ class model_fittings:
477
479
  Converts the model fittings to JSON format.
478
480
  """
479
481
 
480
- def __init__(self, event_selected, date_process, geometrical_model, model_parameters, kinematics={'fit_method': None}):
482
+ def __init__(self, event_selected, date_process, geometrical_model, model_parameters, kinematics=None):
481
483
  self.event_selected = event_selected
482
484
  self.date_process = date_process
483
485
  self.geometrical_model = geometrical_model
484
486
  self.parameters = model_parameters
485
- self.kinematics = kinematics
487
+ self.kinematics = kinematics if kinematics is not None else {'fit_method': None}
486
488
 
487
489
  @staticmethod
488
490
  def load_from_json(json_file):
@@ -920,8 +922,8 @@ def parameter_fit(x, y, fit_args):
920
922
  sv_bound_low = np.minimum(sv_bound_low, spl(xxx))
921
923
  sv_bound_dup = np.maximum(sv_bound_dup, np.gradient(spl(xxx), xxx))
922
924
  sv_bound_dlow = np.minimum(sv_bound_dlow, np.gradient(spl(xxx), xxx))
923
- sv_bound_ddup = np.maximum(sv_bound_ddup, np.gradient(np.gradient(spl(xxx), xxx)))
924
- sv_bound_ddlow = np.minimum(sv_bound_ddlow, np.gradient(np.gradient(spl(xxx), xxx)))
925
+ sv_bound_ddup = np.maximum(sv_bound_ddup, np.gradient(np.gradient(spl(xxx), xxx), xxx))
926
+ sv_bound_ddlow = np.minimum(sv_bound_ddlow, np.gradient(np.gradient(spl(xxx), xxx), xxx))
925
927
 
926
928
  fitting = {
927
929
  'spl': spl,
@@ -55,6 +55,9 @@ def update_nested_dict(d, u):
55
55
 
56
56
  def get_fits_filenames_from_database(event_id, timerange, imager):
57
57
 
58
+ if imager not in imager_dict:
59
+ raise KeyError(f"Unknown imager '{imager}'. Available imagers: {list(imager_dict.keys())}")
60
+
58
61
  db_args = [imager_dict[imager]['source'],
59
62
  imager_dict[imager]['instrument'],
60
63
  imager_dict[imager].get('detector', imager_dict[imager].get('wavelength'))]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyThea
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: PyThea: A software package to reconstruct the 3D structure of CMEs and shock waves
5
5
  Author-email: Athanasios Kouloumvakos <athkouloumvakos@gmail.com>
6
6
  License:
@@ -662,17 +662,17 @@ Keywords: science,solar physics,solar,coronal mass ejections,shock waves,EUV wav
662
662
  Platform: any
663
663
  Classifier: Programming Language :: Python
664
664
  Classifier: Programming Language :: Python :: 3
665
- Classifier: Programming Language :: Python :: 3.11
665
+ Classifier: Programming Language :: Python :: 3.12
666
666
  Classifier: Development Status :: 5 - Production/Stable
667
667
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
668
668
  Classifier: Operating System :: OS Independent
669
669
  Classifier: Intended Audience :: Science/Research
670
670
  Classifier: Topic :: Scientific/Engineering :: Physics
671
671
  Provides: PyThea
672
- Requires-Python: >=3.11
672
+ Requires-Python: >=3.12
673
673
  Description-Content-Type: text/markdown
674
674
  License-File: LICENSE.md
675
- Requires-Dist: sunpy>=7.0.1
675
+ Requires-Dist: sunpy>=7.1.2
676
676
  Requires-Dist: pandas
677
677
  Requires-Dist: scipy
678
678
  Requires-Dist: matplotlib
@@ -51,6 +51,11 @@ PyThea/sunpy_dev/extern/sunkit_instruments/stereo/__init__.py
51
51
  PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py
52
52
  PyThea/sunpy_dev/map/__init__.py
53
53
  PyThea/sunpy_dev/map/maputils.py
54
+ PyThea/sunpy_dev/net/__init__.py
55
+ PyThea/sunpy_dev/net/dataretriever/__init__.py
56
+ PyThea/sunpy_dev/net/dataretriever/sources/__init__.py
57
+ PyThea/sunpy_dev/net/dataretriever/sources/lasco.py
58
+ PyThea/sunpy_dev/net/dataretriever/sources/stereo.py
54
59
  PyThea/test/Pythea_test.py
55
60
  PyThea/test/__init__.py
56
61
  PyThea/test/conftest.py
@@ -1,4 +1,4 @@
1
- sunpy>=7.0.1
1
+ sunpy>=7.1.2
2
2
  pandas
3
3
  scipy
4
4
  matplotlib
@@ -9,13 +9,13 @@ build-backend = "setuptools.build_meta"
9
9
  [project]
10
10
  name = "PyThea"
11
11
  description = "PyThea: A software package to reconstruct the 3D structure of CMEs and shock waves"
12
- requires-python = ">=3.11"
12
+ requires-python = ">=3.12"
13
13
  readme = "README.md"
14
14
  license = { file = "LICENSE.md" }
15
15
  classifiers=[
16
16
  'Programming Language :: Python',
17
17
  'Programming Language :: Python :: 3',
18
- 'Programming Language :: Python :: 3.11',
18
+ 'Programming Language :: Python :: 3.12',
19
19
  'Development Status :: 5 - Production/Stable',
20
20
  'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
21
21
  'Operating System :: OS Independent',
@@ -28,7 +28,7 @@ authors = [
28
28
  ]
29
29
 
30
30
  dependencies = [
31
- "sunpy>=7.0.1",
31
+ "sunpy>=7.1.2",
32
32
  "pandas",
33
33
  "scipy",
34
34
  "matplotlib",
@@ -1,34 +0,0 @@
1
- # file generated by setuptools-scm
2
- # don't change, don't track in version control
3
-
4
- __all__ = [
5
- "__version__",
6
- "__version_tuple__",
7
- "version",
8
- "version_tuple",
9
- "__commit_id__",
10
- "commit_id",
11
- ]
12
-
13
- TYPE_CHECKING = False
14
- if TYPE_CHECKING:
15
- from typing import Tuple
16
- from typing import Union
17
-
18
- VERSION_TUPLE = Tuple[Union[int, str], ...]
19
- COMMIT_ID = Union[str, None]
20
- else:
21
- VERSION_TUPLE = object
22
- COMMIT_ID = object
23
-
24
- version: str
25
- __version__: str
26
- __version_tuple__: VERSION_TUPLE
27
- version_tuple: VERSION_TUPLE
28
- commit_id: COMMIT_ID
29
- __commit_id__: COMMIT_ID
30
-
31
- __version__ = version = '1.2.0'
32
- __version_tuple__ = version_tuple = (1, 2, 0)
33
-
34
- __commit_id__ = commit_id = 'g0f05ae06e'
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