PyThea 0.9.1__tar.gz → 0.10.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 (57) hide show
  1. {PyThea-0.9.1 → pythea-0.10.0}/CHANGELOG.md +23 -0
  2. {PyThea-0.9.1/PyThea.egg-info → pythea-0.10.0}/PKG-INFO +3 -3
  3. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/PyThea_app.py +98 -51
  4. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/_version.py +2 -2
  5. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/callbacks.py +2 -1
  6. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/geometrical_models.py +12 -6
  7. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/modules.py +25 -11
  8. pythea-0.10.0/PyThea/sunpy_dev/extern/sunkit_instruments/aia/utils.py +14 -0
  9. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/map/maputils.py +18 -11
  10. pythea-0.10.0/PyThea/test/Pythea_test.py +24 -0
  11. pythea-0.10.0/PyThea/test/__init__.py +0 -0
  12. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/utils.py +56 -8
  13. {PyThea-0.9.1 → pythea-0.10.0/PyThea.egg-info}/PKG-INFO +3 -3
  14. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/SOURCES.txt +2 -0
  15. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/requires.txt +2 -2
  16. {PyThea-0.9.1 → pythea-0.10.0}/environment.yml +2 -2
  17. {PyThea-0.9.1 → pythea-0.10.0}/requirements.txt +2 -2
  18. PyThea-0.9.1/PyThea/test/Pythea_test.py +0 -8
  19. {PyThea-0.9.1 → pythea-0.10.0}/LICENSE.md +0 -0
  20. {PyThea-0.9.1 → pythea-0.10.0}/MANIFEST.in +0 -0
  21. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/__init__.py +0 -0
  22. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/__init__.py +0 -0
  23. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/app_styles.py +0 -0
  24. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/config_sliders.py +0 -0
  25. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/selected_bodies.py +0 -0
  26. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/selected_imagers.py +0 -0
  27. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/data/__init__.py +0 -0
  28. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/data/sample_data.py +0 -0
  29. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/extensions/LICENSE_gcs_python.md +0 -0
  30. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/extensions/__init__.py +0 -0
  31. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/extensions/buttons.py +0 -0
  32. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/pythea_cli.py +0 -0
  33. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/__init__.py +0 -0
  34. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/__init__.py +0 -0
  35. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/sunkit_instruments/__init__.py +0 -0
  36. {PyThea-0.9.1/PyThea/sunpy_dev/extern/sunkit_instruments/lasco → pythea-0.10.0/PyThea/sunpy_dev/extern/sunkit_instruments/aia}/__init__.py +0 -0
  37. {PyThea-0.9.1/PyThea/sunpy_dev/extern/sunkit_instruments/stereo → pythea-0.10.0/PyThea/sunpy_dev/extern/sunkit_instruments/lasco}/__init__.py +0 -0
  38. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py +0 -0
  39. {PyThea-0.9.1/PyThea/sunpy_dev/map → pythea-0.10.0/PyThea/sunpy_dev/extern/sunkit_instruments/stereo}/__init__.py +0 -0
  40. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py +0 -0
  41. {PyThea-0.9.1/PyThea/test → pythea-0.10.0/PyThea/sunpy_dev/map}/__init__.py +0 -0
  42. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/conftest.py +0 -0
  43. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_geometrical_models.py +0 -0
  44. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_imports.py +0 -0
  45. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_remote_clients.py +0 -0
  46. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_utils.py +0 -0
  47. {PyThea-0.9.1 → pythea-0.10.0}/PyThea/version.py +0 -0
  48. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/.DS_Store +0 -0
  49. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/dependency_links.txt +0 -0
  50. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/entry_points.txt +0 -0
  51. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/not-zip-safe +0 -0
  52. {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/top_level.txt +0 -0
  53. {PyThea-0.9.1 → pythea-0.10.0}/README.md +0 -0
  54. {PyThea-0.9.1 → pythea-0.10.0}/README_pypi.md +0 -0
  55. {PyThea-0.9.1 → pythea-0.10.0}/pyproject.toml +0 -0
  56. {PyThea-0.9.1 → pythea-0.10.0}/setup.cfg +0 -0
  57. {PyThea-0.9.1 → pythea-0.10.0}/setup.py +0 -0
@@ -1,3 +1,26 @@
1
+ # v0.10.0 (17-April-2024)
2
+
3
+ ## Features
4
+ - Option added to choose the step for Running Difference or the background image for the Base Difference image processing.
5
+ - Fetures added in the app to plot limb or meridians from different observers and grid.
6
+ - Feture added in the app for manual change of the images colorbar limits.
7
+ - Feture added in the app to use median filter images processing.
8
+ - Tests added: A test to verify the existence of Pythea's database directory
9
+
10
+ ## Major Changes
11
+ - Updates sunpy dependencies to sunpy 5.1.2
12
+
13
+ ## Minor Changes
14
+ - Improvements on the pipeline of image download and processing in the app.
15
+ - Improvements and simplifications on the figures production in the app.
16
+ - Values of the colormap limits in the app are stored and are reused when changing the imager.
17
+
18
+ ## Bug Fixes
19
+ - Fixes a bug with test_database_dir_exists.
20
+ - Fixes a bug when corrupted fits files loaded and imager load skipped.
21
+ - Fixes a bug with AIA visuallization from missing date_average.
22
+ - Fixes a bug with supplementary imaging selection.
23
+
1
24
  # v0.9.1 (5-March-2024)
2
25
 
3
26
  ## Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyThea
3
- Version: 0.9.1
3
+ Version: 0.10.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
@@ -27,8 +27,8 @@ Requires-Dist: aiapy
27
27
  Requires-Dist: astropy
28
28
  Requires-Dist: astroquery
29
29
  Requires-Dist: numexpr
30
- Requires-Dist: sunpy==4.1.0
31
- Requires-Dist: parfive==1.5.1
30
+ Requires-Dist: sunpy==5.1.2
31
+ Requires-Dist: parfive==2.1.0
32
32
  Requires-Dist: pooch
33
33
  Requires-Dist: matplotlib
34
34
  Requires-Dist: seaborn
@@ -18,6 +18,8 @@
18
18
  """
19
19
 
20
20
 
21
+ from copy import copy
22
+
21
23
  import astropy.units as u
22
24
  import numpy as np
23
25
  import stqdm # See also https://github.com/tqdm/tqdm
@@ -29,12 +31,12 @@ from PyThea.callbacks import load_or_delete_fittings
29
31
  from PyThea.config import (app_styles, config_sliders, selected_bodies,
30
32
  selected_imagers)
31
33
  from PyThea.geometrical_models import ellipsoid, gcs, spheroid
32
- from PyThea.modules import (date_and_event_selection, final_parameters_gmodel,
34
+ from PyThea.modules import (date_and_event_selection, figure_streamlit,
35
+ final_parameters_gmodel,
33
36
  fitting_and_slider_options_container,
34
- fitting_sliders, maps_clims)
35
- from PyThea.sunpy_dev.map.maputils import get_closest
36
- from PyThea.utils import (download_fits, make_figure, model_fittings,
37
- plot_bodies, plot_fitting_model,
37
+ fitting_sliders)
38
+ from PyThea.sunpy_dev.map.maputils import get_closest, maps_clims
39
+ from PyThea.utils import (download_fits, model_fittings, plot_fitting_model,
38
40
  single_imager_maps_process)
39
41
  from PyThea.version import version
40
42
 
@@ -185,24 +187,58 @@ def run():
185
187
  if 'imagers_list_' not in st.session_state:
186
188
  # imagers_list_ is used later when we download or filter the images
187
189
  st.session_state['imagers_list_'] = []
188
- image_mode = procoption_container.selectbox('Map sequence processing',
190
+ image_mode = procoption_container.selectbox('Map Sequence Processing',
189
191
  options=['Running Diff.', 'Base Diff.', 'Plain'], key='image_mode',
190
- on_change=delete_from_state, kwargs={'vars': ['map', 'imagers_list_']})
192
+ on_change=delete_from_state,
193
+ kwargs={'vars': ['map', 'imagers_list_', 'clim', 'clim_manual_low', 'clim_manual_high']})
194
+ if image_mode in ['Running Diff.', 'Base Diff.']:
195
+ diff_value = procoption_container.number_input('Select Step/Image for Running/Base Diff.', 1, 5, key='diff_value',
196
+ on_change=delete_from_state,
197
+ kwargs={'vars': ['map', 'imagers_list_', 'clim', 'clim_manual_low', 'clim_manual_high']})
198
+ else:
199
+ diff_value = None
200
+
201
+ procoption_container.number_input('Median Filter', 1, 6, 1, 1, key='images_median_filter')
191
202
 
192
203
  with st.sidebar.expander('Plot/View Options'):
193
204
  plotviewopt_container = st.container()
194
- clip_model = plotviewopt_container.checkbox('Clip plot on image limits', value=True)
205
+ plotviewopt_container.checkbox('Clip plot on image limits', value=True, key='clip_model')
195
206
  plt_supp_imagers = plotviewopt_container.checkbox('Supplementary Imaging', value=False)
196
- star_field = plotviewopt_container.checkbox('View Bodies or s/c')
197
- if star_field:
198
- bodies_list = plotviewopt_container.multiselect('Select Bodies', options=selected_bodies.bodies_dict.keys(),
199
- default=['Mercury', 'Venus', 'Jupiter'])
207
+ plotviewopt_container.checkbox('View Bodies or s/c', key='star_field')
208
+ if st.session_state.star_field:
209
+ plotviewopt_container.multiselect('Select Bodies', options=selected_bodies.bodies_dict.keys(),
210
+ default=['Mercury', 'Venus', 'Jupiter'],
211
+ key='bodies_list')
212
+ plotviewopt_container.checkbox('View Limb and Meridians', key='plot_solar_reference_lines_')
213
+ if st.session_state.plot_solar_reference_lines_:
214
+ markdown = '''
215
+ **Limb**: plots the solar limb as observed # noqa
216
+ for the selected observers. # noqa
217
+ **Central Meridian**: plots the central meridian # noqa
218
+ observed for the selected observers. # noqa
219
+ **CR Meridan+Equator**: plots the primary meridian # noqa
220
+ and equator of Carrington coordinate system. # noqa
221
+ '''.strip()
222
+ plotviewopt_container.selectbox('Select plot option',
223
+ options=['Limb from Obs.', 'Central Meridian from Obs.', 'Carr. Prime Meridian+Solar Equator',
224
+ 'Stonyhurst Grid', 'Carrington Grid'],
225
+ help=markdown,
226
+ key='plot_solar_reference_lines_mode')
227
+ disabled = False if st.session_state.plot_solar_reference_lines_mode in ['Limb from Obs.', 'Central Meridian from Obs.'] else True
228
+ plotviewopt_container.multiselect('Select Bodies',
229
+ label_visibility='collapsed',
230
+ options=selected_bodies.bodies_dict.keys(),
231
+ default=['Earth'], disabled=disabled,
232
+ key='plot_solar_reference_lines_bodies_list')
200
233
 
201
234
  #############################################################
202
235
  # Download and Process the Images
203
236
  # This part runs only if the map_ doesn't exits or the session_state.map_ does not contain all the imagers requested
204
237
  imager_added = list(set(imagers_list) - set(st.session_state['imagers_list_']))
205
238
  imager_removed = list(set(st.session_state['imagers_list_']) - set(imagers_list))
239
+ if 'map_colormap_limits' not in st.session_state:
240
+ st.session_state['map_colormap_limits'] = {}
241
+
206
242
  if 'map_' not in st.session_state or imager_added != []:
207
243
  st.session_state.map_ = {} if 'map_' not in st.session_state else st.session_state.map_
208
244
  st.session_state.map = {} if 'map' not in st.session_state else st.session_state.map
@@ -213,20 +249,25 @@ def run():
213
249
  if imager not in st.session_state.map_:
214
250
  st.session_state.map_[imager] = download_fits(st.session_state.date_process,
215
251
  imager, time_range=imaging_time_range)
252
+ st.session_state.map_[imager] = single_imager_maps_process(st.session_state.map_[imager],
253
+ **selected_imagers.imager_dict[imager][1],
254
+ skip='sequence_processing')
216
255
  processed_images = single_imager_maps_process(st.session_state.map_[imager],
256
+ skip=['filter', 'prepare'],
217
257
  image_mode=image_mode,
218
- **selected_imagers.imager_dict[imager][1])
258
+ diff_num=diff_value)
219
259
  if processed_images != []:
220
260
  st.session_state.map[imager] = processed_images
261
+ st.session_state.map_colormap_limits[imager] = maps_clims(st.session_state.map[imager])
221
262
  else:
222
263
  st.session_state.imagers_list_.remove(imager)
264
+ st.session_state.map_colormap_limits.pop(imager, None)
265
+
223
266
  if imager_removed != []:
224
267
  st.session_state['imagers_list_'] = imagers_list
225
268
  for imager in imager_removed:
226
269
  st.session_state.map_.pop(imager, None)
227
270
 
228
- maps_clims(st, st.session_state.imagers_list_)
229
-
230
271
  if not st.session_state.imagers_list_:
231
272
  st.error('No images have been downloaded or processed.') # TODO: Explain better
232
273
  st.stop()
@@ -236,7 +277,8 @@ def run():
236
277
  st.markdown('### Multi-viewpoint imaging')
237
278
  col1, col2 = st.columns((1, 3))
238
279
  imager_select = col1.selectbox('Select an imager',
239
- options=st.session_state.imagers_list_)
280
+ options=st.session_state.imagers_list_,
281
+ on_change=delete_from_state, kwargs={'vars': ['clim', 'clim_manual_low', 'clim_manual_high']})
240
282
 
241
283
  maps_date = [getattr(maps, 'date_average', None) or getattr(maps, 'date', None) for maps in st.session_state.map[imager_select]]
242
284
  if len(maps_date) > 1:
@@ -248,14 +290,26 @@ def run():
248
290
 
249
291
  running_map = st.session_state.map[imager_select][maps_date.index(running_map_date)]
250
292
 
251
- qmin = st.session_state.map_clim[imager_select][0] # np.nanquantile(running_map.data, 0.20)
252
- qmax = st.session_state.map_clim[imager_select][1] # np.nanquantile(running_map.data, 0.80)
253
- col1, col2 = st.columns((1, 3))
254
- cmmin, cpmax = config_sliders.slider_image_pmclims[image_mode]
255
- clim = plotviewopt_container.slider('Images climits:',
256
- min_value=float(cmmin), max_value=float(cpmax),
257
- value=(float(qmin-10), float(qmax+10)),
258
- key='clim')
293
+ manual_clim = plotviewopt_container.checkbox('Provide clims values', on_change=delete_from_state, kwargs={'vars': ['clim']})
294
+ if manual_clim:
295
+ col1, col2 = plotviewopt_container.columns(2)
296
+ if 'clim_manual_low' not in st.session_state:
297
+ st.session_state.clim_manual_low = st.session_state.map_colormap_limits[imager_select][0]
298
+ qmin = col1.number_input('Low clim value', label_visibility='collapsed', key='clim_manual_low')
299
+ if 'clim_manual_high' not in st.session_state:
300
+ st.session_state.clim_manual_high = st.session_state.map_colormap_limits[imager_select][1]
301
+ qmax = col2.number_input('High clim value', label_visibility='collapsed', key='clim_manual_high')
302
+ st.session_state.map_colormap_limits[imager_select] = [qmin, qmax]
303
+ else:
304
+ qmin = st.session_state.map_colormap_limits[imager_select][0]
305
+ qmax = st.session_state.map_colormap_limits[imager_select][1]
306
+ cmmin, cpmax = config_sliders.slider_image_pmclims[image_mode]
307
+ if 'clim' not in st.session_state:
308
+ st.session_state.clim = [float(qmin), float(qmax)]
309
+ st.session_state.map_colormap_limits[imager_select] = \
310
+ plotviewopt_container.slider('Images colorbar limits:',
311
+ min_value=float(cmmin), max_value=float(cpmax),
312
+ key='clim')
259
313
 
260
314
  #############################################################
261
315
  # Finalize the geometrical model
@@ -290,35 +344,28 @@ def run():
290
344
 
291
345
  #############################################################
292
346
  # Plot main and supplement figure images
293
- fig, axis = make_figure(running_map, image_mode, clim=clim, clip_model=clip_model)
294
- if st.session_state.plot_mesh_mode == 'Skeleton':
295
- model.plot(axis, mode='Skeleton')
296
- elif st.session_state.plot_mesh_mode == 'Full':
297
- model.plot(axis, mode='Skeleton')
298
- model.plot(axis, mode='Full')
299
- elif st.session_state.plot_mesh_mode == 'Surface':
300
- model.plot(axis, only_surface=True)
301
-
302
- if star_field:
303
- plot_bodies(axis, bodies_list, running_map)
304
- axis.legend()
347
+ fig, axis = figure_streamlit(st, running_map, image_mode, imager_select, model)
305
348
  st.pyplot(fig)
306
349
 
307
- if plt_supp_imagers and len(st.session_state.imagers_list_) > 2:
308
- supl_imagers = st.select_slider('Select supplement imagers',
309
- options=st.session_state.imagers_list_,
310
- value=(st.session_state.imagers_list_[1],
311
- st.session_state.imagers_list_[-1]),
312
- key='supl_imagers')
313
- col1, col2 = st.columns(2)
314
- fig, axis = make_figure(get_closest(st.session_state.map[supl_imagers[0]],
315
- running_map_date), image_mode)
316
- model.plot(axis, mode='Skeleton')
317
- col1.pyplot(fig)
318
- fig, axis = make_figure(get_closest(st.session_state.map[supl_imagers[1]],
319
- running_map_date), image_mode)
320
- model.plot(axis, mode='Skeleton')
321
- col2.pyplot(fig)
350
+ if plt_supp_imagers:
351
+ if len(st.session_state.imagers_list_) < 3:
352
+ other_element = [element for element in st.session_state.imagers_list_ if element != imager_select][0]
353
+ fig, axis = figure_streamlit(st, get_closest(st.session_state.map[other_element], running_map_date), image_mode, other_element, model)
354
+ st.pyplot(fig)
355
+ else:
356
+ supl_imagers_list = copy(st.session_state.imagers_list_)
357
+ supl_imagers_list.remove(imager_select)
358
+ supl_imagers = st.select_slider('Select supplement imagers',
359
+ options=supl_imagers_list,
360
+ value=(supl_imagers_list[0],
361
+ supl_imagers_list[-1]),
362
+ key='supl_imagers')
363
+ col1, col2 = st.columns(2)
364
+ fig, axis = figure_streamlit(st, get_closest(st.session_state.map[supl_imagers[0]], running_map_date), image_mode, supl_imagers[0], model)
365
+ col1.pyplot(fig)
366
+ fig, axis = figure_streamlit(st, get_closest(st.session_state.map[supl_imagers[1]], running_map_date), image_mode, supl_imagers[1], model)
367
+ model.plot(axis, mode='Skeleton')
368
+ col2.pyplot(fig)
322
369
 
323
370
  #############################################################
324
371
  # Store the fitting
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.9.1'
16
- __version_tuple__ = version_tuple = (0, 9, 1)
15
+ __version__ = version = '0.10.0'
16
+ __version_tuple__ = version_tuple = (0, 10, 0)
@@ -39,7 +39,8 @@ def change_long_lat_sliders(st):
39
39
  if st.session_state.coord_system == 'HGS':
40
40
  center_ = st.session_state.center.transform_to(frames.HeliographicStonyhurst)
41
41
  elif st.session_state.coord_system == 'HGC':
42
- center_ = st.session_state.center.transform_to(frames.HeliographicCarrington)
42
+ center_ = st.session_state.center.transform_to(frames.HeliographicCarrington(observer='Earth',
43
+ obstime=st.session_state.center.obstime))
43
44
  st.session_state.longit = float(center_.lon.to_value(u.degree))
44
45
  st.session_state.latitu = float(center_.lat.to_value(u.degree))
45
46
 
@@ -283,7 +283,8 @@ class spheroid:
283
283
  """
284
284
  Returns the spheroid parameters as `~pandas.DataFrame`.
285
285
  """
286
- center_ = self.center.transform_to(frames.HeliographicCarrington)
286
+ center_ = self.center.transform_to(frames.HeliographicCarrington(observer='Earth',
287
+ obstime=self.center.obstime))
287
288
  data_dict = {
288
289
  'hgln': self.center.lon.to_value(u.degree),
289
290
  'hglt': self.center.lat.to_value(u.degree),
@@ -303,7 +304,8 @@ class spheroid:
303
304
  '''
304
305
  Returns the Spheroid object and parameters in a printable sting array.
305
306
  '''
306
- center_ = self.center.transform_to(frames.HeliographicCarrington)
307
+ center_ = self.center.transform_to(frames.HeliographicCarrington(observer='Earth',
308
+ obstime=self.center.obstime))
307
309
  output = '<Spheroid object \n'
308
310
  output += 'HGLN = %3.2f degrees \n'%self.center.lon.to_value(u.degree)
309
311
  output += 'HGLT = %3.2f degrees \n'%self.center.lat.to_value(u.degree)
@@ -402,7 +404,8 @@ class ellipsoid(spheroid):
402
404
  """
403
405
  Returns the ellipsoid parameters as `~pandas.DataFrame`.
404
406
  """
405
- center_ = self.center.transform_to(frames.HeliographicCarrington)
407
+ center_ = self.center.transform_to(frames.HeliographicCarrington(observer='Earth',
408
+ obstime=self.center.obstime))
406
409
  data_dict = {
407
410
  'hgln': self.center.lon.to_value(u.degree),
408
411
  'hglt': self.center.lat.to_value(u.degree),
@@ -469,7 +472,8 @@ class ellipsoid(spheroid):
469
472
  '''
470
473
  Returns the Ellipsoid object and parameters in a printable sting array.
471
474
  '''
472
- center_ = self.center.transform_to(frames.HeliographicCarrington)
475
+ center_ = self.center.transform_to(frames.HeliographicCarrington(observer='Earth',
476
+ obstime=self.center.obstime))
473
477
  output = '<Ellipsoid object \n'
474
478
  output += 'HGLN = %3.2f degrees \n'%self.center.lon.to_value(u.degree)
475
479
  output += 'HGLT = %3.2f degrees \n'%self.center.lat.to_value(u.degree)
@@ -669,7 +673,8 @@ class gcs():
669
673
  """
670
674
  Returns the GCS parameters as `~pandas.DataFrame`.
671
675
  """
672
- center_ = self.center.transform_to(frames.HeliographicCarrington)
676
+ center_ = self.center.transform_to(frames.HeliographicCarrington(observer='Earth',
677
+ obstime=self.center.obstime))
673
678
  data_dict = {
674
679
  'hgln': self.center.lon.to_value(u.degree),
675
680
  'hglt': self.center.lat.to_value(u.degree),
@@ -689,7 +694,8 @@ class gcs():
689
694
  '''
690
695
  Returns the GCS object and parameters in a printable sting array.
691
696
  '''
692
- center_ = self.center.transform_to(frames.HeliographicCarrington)
697
+ center_ = self.center.transform_to(frames.HeliographicCarrington(observer='Earth',
698
+ obstime=self.center.obstime))
693
699
  output = '<GCS object \n'
694
700
  output += 'HGLN = %3.2f degrees \n'%self.center.lon.to_value(u.degree)
695
701
  output += 'HGLT = %3.2f degrees \n'%self.center.lat.to_value(u.degree)
@@ -21,12 +21,12 @@
21
21
  import datetime
22
22
 
23
23
  import astropy.units as u
24
- import numpy as np
25
24
 
26
25
  from PyThea.callbacks import change_fitting_sliders, change_long_lat_sliders
27
26
  from PyThea.config.config_sliders import sliders_dict as sd
28
27
  from PyThea.geometrical_models import ellipsoid, gcs, spheroid
29
- from PyThea.utils import get_hek_flare, model_fittings
28
+ from PyThea.utils import (get_hek_flare, make_figure, model_fittings,
29
+ plot_bodies, plot_solar_reference_lines)
30
30
 
31
31
 
32
32
  def date_and_event_selection(st):
@@ -184,13 +184,27 @@ def final_parameters_gmodel(st):
184
184
  return rcenter, height, alpha, kappa, tilt
185
185
 
186
186
 
187
- def maps_clims(st, imagers_list):
188
- if 'map_clim' not in st.session_state:
189
- st.session_state['map_clim'] = {}
187
+ def figure_streamlit(st, running_map, image_mode, imager, model):
190
188
 
191
- for imager in imagers_list:
192
- if imager not in st.session_state.map or st.session_state.map[imager] == []:
193
- pass
194
- else:
195
- map_ = st.session_state.map[imager][0]
196
- st.session_state.map_clim[imager] = [np.nanquantile(map_.data, 0.20), np.nanquantile(map_.data, 0.80)]
189
+ fig, axis = make_figure(running_map, image_mode,
190
+ clim=st.session_state.map_colormap_limits[imager],
191
+ clip_model=st.session_state.clip_model,
192
+ median_filter=st.session_state.images_median_filter)
193
+
194
+ if st.session_state.plot_mesh_mode == 'Skeleton':
195
+ model.plot(axis, mode='Skeleton')
196
+ elif st.session_state.plot_mesh_mode == 'Full':
197
+ model.plot(axis, mode='Skeleton')
198
+ model.plot(axis, mode='Full')
199
+ elif st.session_state.plot_mesh_mode == 'Surface':
200
+ model.plot(axis, only_surface=True)
201
+
202
+ if st.session_state.star_field:
203
+ plot_bodies(axis, st.session_state.bodies_list, running_map)
204
+ axis.legend()
205
+
206
+ if st.session_state.plot_solar_reference_lines_:
207
+ plot_solar_reference_lines(axis, st.session_state.plot_solar_reference_lines_bodies_list,
208
+ running_map, mode=st.session_state.plot_solar_reference_lines_mode)
209
+
210
+ return fig, axis
@@ -0,0 +1,14 @@
1
+ from astropy.time import TimeDelta
2
+ from aiapy.calibrate import fix_observer_location, update_pointing
3
+
4
+ __all__ = ['prep_aia']
5
+
6
+
7
+ def prep_aia(map_sequence):
8
+ map_sequence = [update_pointing(tmap) for tmap in map_sequence]
9
+ map_sequence = [fix_observer_location(tmap) for tmap in map_sequence]
10
+
11
+ for map_ in map_sequence:
12
+ map_.meta['DATE-AVG'] = (map_.date + TimeDelta(map_.meta['exptime']/2, format='sec')).value
13
+
14
+ return map_sequence
@@ -8,8 +8,8 @@ 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
12
11
 
12
+ import PyThea.sunpy_dev.extern.sunkit_instruments.aia.utils # noqa
13
13
  import PyThea.sunpy_dev.extern.sunkit_instruments.lasco.utils # noqa
14
14
  import PyThea.sunpy_dev.extern.sunkit_instruments.stereo.utils # noqa
15
15
 
@@ -17,17 +17,17 @@ __all__ = ['maps_sequence_processing', 'get_closest', 'normalize_exposure',
17
17
  'prepare_maps', 'difference_maps', 'mask_occulter']
18
18
 
19
19
 
20
- def maps_sequence_processing(map_sequence, seq_type='Plain'):
20
+ def maps_sequence_processing(map_sequence, **kwargs):
21
21
  """
22
- Returns a sequence of maps which is processed as running difference or base difference or plain images.
22
+ Returns a sequence of maps which is processed as plain images, running or base difference images.
23
23
 
24
24
  Parameters
25
25
  ----------
26
26
  map_sequence : `~sunpy.map.GenericMap`
27
27
  A list SunPy maps.
28
28
 
29
- seq_type : '~str'
30
- The type of sequence processing
29
+ image_mode : '~str'
30
+ The type of sequence processing: 'Running Diff.', 'Base Diff.', 'Plain'
31
31
 
32
32
  Returns
33
33
  -------
@@ -38,18 +38,21 @@ def maps_sequence_processing(map_sequence, seq_type='Plain'):
38
38
  if len(map_sequence) == 0:
39
39
  return []
40
40
 
41
+ seq_type = kwargs.get('image_mode', 'Plain')
42
+ diff_num = kwargs.get('diff_num', 1 if seq_type == 'Running Diff.' else 0 if seq_type == 'Base Diff.' else None)
43
+
41
44
  normalized = True if True in [tmap.exposure_time == 1.0*u.second for tmap in map_sequence] else False
42
45
  if not normalized:
43
46
  warnings.warn('Warning [maps_sequence_processing]: The exposure time of the maps is not normalized.')
44
47
 
45
48
  smap = []
46
49
  if seq_type == 'Running Diff.':
47
- for i in range(1, len(map_sequence)):
48
- smap_diff = difference_maps(map_sequence[i], map_sequence[i-1])
50
+ for i in range(1+diff_num, len(map_sequence)):
51
+ smap_diff = difference_maps(map_sequence[i], map_sequence[i-diff_num])
49
52
  smap.append(smap_diff)
50
53
  if seq_type == 'Base Diff.':
51
54
  for i in range(1, len(map_sequence)):
52
- smap_diff = difference_maps(map_sequence[i], map_sequence[0])
55
+ smap_diff = difference_maps(map_sequence[i], map_sequence[diff_num])
53
56
  smap.append(smap_diff)
54
57
  if seq_type == 'Plain':
55
58
  for i in range(0, len(map_sequence)):
@@ -151,6 +154,7 @@ def filter_maps(map_sequence, **kwargs):
151
154
  sequence_final = sunpy.map.Map(map_sequence, sequence=True)
152
155
  else:
153
156
  sequence_final = []
157
+
154
158
  return sequence_final
155
159
 
156
160
 
@@ -180,12 +184,11 @@ def prepare_maps(map_sequence, **kwargs):
180
184
  return []
181
185
 
182
186
  detector = map_sequence[0].detector
183
- print(f'Preparing image sequence for {detector}.')
187
+ print(f'Preparing image sequence for {detector}. This could take a while...')
184
188
 
185
189
  # Prepare the maps before anything else
186
190
  if detector == 'AIA':
187
- map_sequence = [update_pointing(tmap) for tmap in map_sequence]
188
- map_sequence = [fix_observer_location(tmap) for tmap in map_sequence]
191
+ map_sequence = PyThea.sunpy_dev.extern.sunkit_instruments.aia.utils.prep_aia(map_sequence)
189
192
  elif detector == 'COR1':
190
193
  if 'polar' not in kwargs.keys():
191
194
  map_sequence = PyThea.sunpy_dev.extern.sunkit_instruments.stereo.utils.cor_polariz(map_sequence)
@@ -286,3 +289,7 @@ def mask_occulter(smap, apply_mask=True, mask_value=0):
286
289
  smap.data[mask_inner+mask_outer] = mask_value
287
290
  return sunpy.map.Map(smap.data, smap.meta)
288
291
  return mask_inner+mask_outer
292
+
293
+
294
+ def maps_clims(images):
295
+ return [np.nanquantile(images[1].data, 0.20)-10, np.nanquantile(images[1].data, 0.80)+10]
@@ -0,0 +1,24 @@
1
+ """
2
+ A place for the more generic tests that do not fit in the other categories
3
+
4
+ Note
5
+ ----
6
+ Before you start the test the PyThea pkg should be in the python path
7
+ export PYTHONPATH="${PYTHONPATH}:{top_level_dir_that_pythea_lives}/PyThea"
8
+ """
9
+
10
+ import os
11
+ from pathlib import Path
12
+
13
+
14
+ def test_database_dir_exists():
15
+ """
16
+ Tests that Pythea's database directory exists.
17
+ """
18
+ # Skip the test if running in GitHub Actions
19
+ if os.environ.get('CI') == 'true':
20
+ print("Skipping test as it's running in GitHub Actions.")
21
+ return
22
+
23
+ database_dir = os.path.join(Path.home(), 'PyThea')
24
+ assert os.path.exists(database_dir), f"PyThea's database directory '{database_dir}' does not exist."
File without changes
@@ -36,13 +36,17 @@ import numpy as np
36
36
  import pandas as pd
37
37
  import seaborn as sns
38
38
  import sunpy.map
39
+ from astropy.coordinates import SkyCoord
40
+ from astropy.visualization.wcsaxes.wcsapi import wcsapi_to_celestial_frame
39
41
  from scipy.interpolate import UnivariateSpline
42
+ from scipy.ndimage import median_filter
40
43
  from scipy.optimize import curve_fit
41
- from sunpy.coordinates import get_horizons_coord
44
+ from sunpy.coordinates import frames, get_horizons_coord
42
45
  from sunpy.map.maputils import contains_coordinate
43
46
  from sunpy.net import Fido
44
47
  from sunpy.net import attrs as a
45
48
  from sunpy.time import parse_time
49
+ from sunpy.visualization import drawing
46
50
 
47
51
  from PyThea.config.selected_bodies import bodies_dict
48
52
  from PyThea.config.selected_imagers import imager_dict
@@ -87,13 +91,15 @@ def make_figure(map, image_mode, clim=[-20, 20], clip_model=True, **kwargs):
87
91
 
88
92
  if image_mode == 'Plain':
89
93
  # TODO: For plain images or when EUVIA-B are used, this does not work very well.
90
- map.plot()
94
+ map.plot(norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
91
95
  else:
96
+ median_filter_value = kwargs.get('median_filter', 1)
97
+ if median_filter_value != 1:
98
+ map = sunpy.map.Map(median_filter(map.data, size=int(median_filter_value)), map.meta)
92
99
  map.plot(cmap='Greys_r',
93
100
  norm=colors.Normalize(vmin=clim[0], vmax=clim[1]))
94
101
 
95
102
  map.draw_limb(resolution=90)
96
- # map.draw_grid(linewidth=2, color='red') # TODO: This takes too much computation time. Maybe for AIA or EUVI?
97
103
 
98
104
  yax = axis.coords[1]
99
105
  yax.set_ticklabel(rotation=90)
@@ -128,6 +134,42 @@ def plot_bodies(axis, bodies_list, smap):
128
134
  fillstyle='none', markersize=6, label=body)
129
135
 
130
136
 
137
+ def plot_solar_reference_lines(axis, bodies_list, smap, mode='Limb from Obs.'):
138
+ '''
139
+ Plots solar reference lines (e.g. solar limb, central meridians, equator) in wcs axis.
140
+ '''
141
+ def draw_central_meridian(axis, body_coo, date, color):
142
+ body_coo = body_coo.transform_to(frames.HeliographicStonyhurst)
143
+ constant_lon = SkyCoord(body_coo.lon, np.linspace(-90, 90, 90) * u.deg,
144
+ frame=frames.HeliographicStonyhurst(obstime=date))
145
+ v, _ = drawing._plot_vertices(constant_lon, axis, wcsapi_to_celestial_frame(axis.wcs), None,
146
+ color=color, close_path=False, linewidth=1)
147
+
148
+ for body in bodies_list:
149
+ try:
150
+ body_coo = get_horizons_coord(bodies_dict[body][0], smap.date_average)
151
+ except ValueError as ve:
152
+ print(f'Error processing {body}: {ve}')
153
+ continue
154
+
155
+ if mode in ['Limb from Obs.', 'limb']:
156
+ drawing.limb(axis, body_coo, color=bodies_dict[body][1],
157
+ linewidth=1, resolution=90, label=f'Limb seen from {body}')
158
+ if mode in ['Central Meridian from Obs.', 'meridian']:
159
+ draw_central_meridian(axis, body_coo, smap.date_average, bodies_dict[body][1])
160
+
161
+ if mode in ['Carr. Prime Meridian+Solar Equator', ['carr-me']]:
162
+ drawing.equator(axis, linewidth=1, resolution=90)
163
+ drawing.prime_meridian(axis, linewidth=1, resolution=90)
164
+ elif mode in ['Stonyhurst Grid', ['stony-grid']]:
165
+ earth = get_horizons_coord(3, smap.date_average)
166
+ draw_central_meridian(axis, earth, smap.date_average, 'white')
167
+ smap.draw_grid(linewidth=1, color='red', system='stonyhurst', alpha=0.8)
168
+ elif mode in ['Carrington Grid', ['carr-grid']]:
169
+ drawing.prime_meridian(axis, linewidth=1, resolution=90)
170
+ smap.draw_grid(linewidth=1, color='red', system='carrington', alpha=0.8)
171
+
172
+
131
173
  def download_fits(date_process, imager, time_range=[-1, 1]):
132
174
  '''
133
175
  Downloads the imaging data (fits files) from VSO
@@ -186,15 +228,21 @@ def maps_process(maps_dict_in, imagers_list_in, image_mode, **kwargs):
186
228
  return maps_dict_out, imagers_list_out
187
229
 
188
230
 
189
- def single_imager_maps_process(map_list_in, image_mode='Plain', **kwargs):
231
+ def single_imager_maps_process(map_list, skip=None, **kwargs):
190
232
  '''
191
233
  Process the images for a single imager and return the final maps.
192
234
  '''
193
- map_list_out = filter_maps(map_list_in, **kwargs)
194
- map_list_out = prepare_maps(map_list_out, **kwargs)
195
- map_list_out = maps_sequence_processing(map_list_out, seq_type=image_mode)
196
235
 
197
- return map_list_out
236
+ if 'filter' not in str(skip or ''):
237
+ map_list = filter_maps(map_list, **kwargs)
238
+
239
+ if 'prepare' not in str(skip or ''):
240
+ map_list = prepare_maps(map_list, **kwargs)
241
+
242
+ if 'sequence_processing' not in str(skip or ''):
243
+ map_list = maps_sequence_processing(map_list, **kwargs)
244
+
245
+ return map_list
198
246
 
199
247
 
200
248
  # TODO: Implement units here
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyThea
3
- Version: 0.9.1
3
+ Version: 0.10.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
@@ -27,8 +27,8 @@ Requires-Dist: aiapy
27
27
  Requires-Dist: astropy
28
28
  Requires-Dist: astroquery
29
29
  Requires-Dist: numexpr
30
- Requires-Dist: sunpy==4.1.0
31
- Requires-Dist: parfive==1.5.1
30
+ Requires-Dist: sunpy==5.1.2
31
+ Requires-Dist: parfive==2.1.0
32
32
  Requires-Dist: pooch
33
33
  Requires-Dist: matplotlib
34
34
  Requires-Dist: seaborn
@@ -38,6 +38,8 @@ PyThea/extensions/buttons.py
38
38
  PyThea/sunpy_dev/__init__.py
39
39
  PyThea/sunpy_dev/extern/__init__.py
40
40
  PyThea/sunpy_dev/extern/sunkit_instruments/__init__.py
41
+ PyThea/sunpy_dev/extern/sunkit_instruments/aia/__init__.py
42
+ PyThea/sunpy_dev/extern/sunkit_instruments/aia/utils.py
41
43
  PyThea/sunpy_dev/extern/sunkit_instruments/lasco/__init__.py
42
44
  PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py
43
45
  PyThea/sunpy_dev/extern/sunkit_instruments/stereo/__init__.py
@@ -5,8 +5,8 @@ aiapy
5
5
  astropy
6
6
  astroquery
7
7
  numexpr
8
- sunpy==4.1.0
9
- parfive==1.5.1
8
+ sunpy==5.1.2
9
+ parfive==2.1.0
10
10
  pooch
11
11
  matplotlib
12
12
  seaborn
@@ -14,8 +14,8 @@ dependencies:
14
14
  - astropy
15
15
  - astroquery
16
16
  - numexpr
17
- - sunpy=4.1.0
18
- - parfive==1.5.1 # With parfive>=2.0.0 there is an issue with streamlit and asyncio (see #13)
17
+ - sunpy=5.1.2
18
+ - parfive==2.1.0 # The issue with streamlit and asyncio is resolved (see #13). Keep this for a while to make sure everything works smoothly.
19
19
  - streamlit
20
20
  - seaborn
21
21
  - pyvista
@@ -5,8 +5,8 @@ aiapy
5
5
  astropy
6
6
  astroquery
7
7
  numexpr
8
- sunpy==4.1.0
9
- parfive==1.5.1
8
+ sunpy==5.1.2
9
+ parfive==2.1.0
10
10
  pooch
11
11
  matplotlib
12
12
  seaborn
@@ -1,8 +0,0 @@
1
- """
2
- A place for the more generic tests that do not fit in the other categories
3
-
4
- Note
5
- ----
6
- Before you start the test the PyThea pkg should be in the python path
7
- export PYTHONPATH="${PYTHONPATH}:{top_level_dir_that_pythea_lives}/PyThea"
8
- """
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