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.
- {PyThea-0.9.1 → pythea-0.10.0}/CHANGELOG.md +23 -0
- {PyThea-0.9.1/PyThea.egg-info → pythea-0.10.0}/PKG-INFO +3 -3
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/PyThea_app.py +98 -51
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/_version.py +2 -2
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/callbacks.py +2 -1
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/geometrical_models.py +12 -6
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/modules.py +25 -11
- pythea-0.10.0/PyThea/sunpy_dev/extern/sunkit_instruments/aia/utils.py +14 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/map/maputils.py +18 -11
- pythea-0.10.0/PyThea/test/Pythea_test.py +24 -0
- pythea-0.10.0/PyThea/test/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/utils.py +56 -8
- {PyThea-0.9.1 → pythea-0.10.0/PyThea.egg-info}/PKG-INFO +3 -3
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/SOURCES.txt +2 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/requires.txt +2 -2
- {PyThea-0.9.1 → pythea-0.10.0}/environment.yml +2 -2
- {PyThea-0.9.1 → pythea-0.10.0}/requirements.txt +2 -2
- PyThea-0.9.1/PyThea/test/Pythea_test.py +0 -8
- {PyThea-0.9.1 → pythea-0.10.0}/LICENSE.md +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/MANIFEST.in +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/app_styles.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/config_sliders.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/selected_bodies.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/config/selected_imagers.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/data/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/data/sample_data.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/extensions/LICENSE_gcs_python.md +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/extensions/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/extensions/buttons.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/pythea_cli.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/sunkit_instruments/__init__.py +0 -0
- {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
- {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
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/sunkit_instruments/lasco/utils.py +0 -0
- {PyThea-0.9.1/PyThea/sunpy_dev/map → pythea-0.10.0/PyThea/sunpy_dev/extern/sunkit_instruments/stereo}/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/sunpy_dev/extern/sunkit_instruments/stereo/utils.py +0 -0
- {PyThea-0.9.1/PyThea/test → pythea-0.10.0/PyThea/sunpy_dev/map}/__init__.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/conftest.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_geometrical_models.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_imports.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_remote_clients.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/test/test_utils.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea/version.py +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/.DS_Store +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/dependency_links.txt +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/entry_points.txt +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/not-zip-safe +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/PyThea.egg-info/top_level.txt +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/README.md +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/README_pypi.md +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/pyproject.toml +0 -0
- {PyThea-0.9.1 → pythea-0.10.0}/setup.cfg +0 -0
- {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.
|
|
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==
|
|
31
|
-
Requires-Dist: parfive==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,
|
|
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
|
|
35
|
-
from PyThea.sunpy_dev.map.maputils import get_closest
|
|
36
|
-
from PyThea.utils import (download_fits,
|
|
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
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
if star_field:
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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 =
|
|
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
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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
|
|
@@ -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
|
|
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
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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,
|
|
20
|
+
def maps_sequence_processing(map_sequence, **kwargs):
|
|
21
21
|
"""
|
|
22
|
-
Returns a sequence of maps which is processed as running
|
|
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
|
-
|
|
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-
|
|
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[
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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==
|
|
31
|
-
Requires-Dist: parfive==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
|
|
@@ -14,8 +14,8 @@ dependencies:
|
|
|
14
14
|
- astropy
|
|
15
15
|
- astroquery
|
|
16
16
|
- numexpr
|
|
17
|
-
- sunpy=
|
|
18
|
-
- parfive==
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|