lime-stable 2.2.dev1__tar.gz → 2.2.dev5__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.
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/MANIFEST.in +1 -1
- {lime_stable-2.2.dev1/src/lime_stable.egg-info → lime_stable-2.2.dev5}/PKG-INFO +2 -2
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/pyproject.toml +2 -2
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/archives/read_fits.py +4 -19
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/fitting/lines.py +2 -2
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/lime.toml +1 -1
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/observations.py +26 -17
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/bokeh_plots.py +91 -16
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/format.py +0 -3
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/theme_lime.toml +1 -1
- lime_stable-2.2.dev5/src/lime/resources/lines_database_v2.0.6.txt +292 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/retrieve/line_bands.py +104 -3
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/tools.py +1 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/workflow.py +161 -4
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5/src/lime_stable.egg-info}/PKG-INFO +2 -2
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime_stable.egg-info/SOURCES.txt +1 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime_stable.egg-info/requires.txt +1 -1
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_read_fits.py +7 -5
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/LICENSE.rst +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/README.md +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/setup.cfg +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/__init__.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/archives/__init__.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/archives/tables.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/changelog.txt +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/fitting/__init__.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/fitting/redshift.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/inference/detection.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/inference/intensity_threshold.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/io.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/__init__.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/plots.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/plots_interactive.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/plotting/utils.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/resources/__init__.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/resources/generator_db.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/resources/generator_logo.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/resources/lines_database_v2.0.0.txt +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/resources/types_params.txt +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/retrieve/__init__.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/rsrc_manager.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime/transitions.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime_stable.egg-info/dependency_links.txt +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/src/lime_stable.egg-info/top_level.txt +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_astro.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_cube.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_io.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_line.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_model.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_plots.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_redshift.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_resources.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_sample.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_spectrum.py +0 -0
- {lime_stable-2.2.dev1 → lime_stable-2.2.dev5}/tests/test_tools.py +0 -0
|
@@ -3,4 +3,4 @@ include src/lime/changelog.txt
|
|
|
3
3
|
include src/lime/plotting/theme_lime.toml
|
|
4
4
|
include src/lime/resources/parent_mask.txt
|
|
5
5
|
include src/lime/resources/types_params.txt
|
|
6
|
-
include src/lime/resources/lines_database_v2.0.
|
|
6
|
+
include src/lime/resources/lines_database_v2.0.6.txt
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lime-stable
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.dev5
|
|
4
4
|
Summary: Line measuring algorithm for astronomical spectra
|
|
5
5
|
Author-email: Vital Fernández <vgf@stsci.edu>
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -18,7 +18,7 @@ Requires-Dist: scipy~=1.16
|
|
|
18
18
|
Requires-Dist: tomli>=2.0.0; python_version < "3.11"
|
|
19
19
|
Provides-Extra: full
|
|
20
20
|
Requires-Dist: asdf~=4.1; extra == "full"
|
|
21
|
-
Requires-Dist: aspect-stable~=0.7.
|
|
21
|
+
Requires-Dist: aspect-stable~=0.7.dev2; extra == "full"
|
|
22
22
|
Requires-Dist: bokeh~=3.8; extra == "full"
|
|
23
23
|
Requires-Dist: mplcursors~=0.6; extra == "full"
|
|
24
24
|
Requires-Dist: openpyxl~=3.1; extra == "full"
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lime-stable"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.dev5"
|
|
8
8
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
9
9
|
requires-python = ">=3.11"
|
|
10
10
|
license = {text = "GPL-3.0-or-later"}
|
|
@@ -24,7 +24,7 @@ dependencies = ["astropy~=7.1",
|
|
|
24
24
|
|
|
25
25
|
[project.optional-dependencies]
|
|
26
26
|
full = ["asdf~=4.1",
|
|
27
|
-
"aspect-stable~=0.7.
|
|
27
|
+
"aspect-stable~=0.7.dev2",
|
|
28
28
|
"bokeh~=3.8",
|
|
29
29
|
"mplcursors~=0.6",
|
|
30
30
|
"openpyxl~=3.1",
|
|
@@ -7,6 +7,7 @@ from astropy.wcs import WCS
|
|
|
7
7
|
|
|
8
8
|
from lime.io import LiMe_Error, lime_cfg
|
|
9
9
|
from urllib.parse import urlparse
|
|
10
|
+
from io import BytesIO
|
|
10
11
|
|
|
11
12
|
try:
|
|
12
13
|
import requests
|
|
@@ -265,7 +266,6 @@ def check_fits_instructions(fits_source, online_provider=False):
|
|
|
265
266
|
fits_reader = getattr(fits_manager, fits_source)
|
|
266
267
|
else:
|
|
267
268
|
source_type = 'instrument' if online_provider is False else 'survey'
|
|
268
|
-
# TODO show instruments supported
|
|
269
269
|
raise LiMe_Error(f'Input {source_type} "{fits_source}" is not recognized. LiMe observation cannot be created.')
|
|
270
270
|
|
|
271
271
|
else:
|
|
@@ -280,14 +280,12 @@ def load_txt(text_address, **kwargs):
|
|
|
280
280
|
out_array = np.loadtxt(text_address, **kwargs)
|
|
281
281
|
|
|
282
282
|
# File address
|
|
283
|
-
if
|
|
283
|
+
if isinstance(text_address, BytesIO) or (type(text_address).__name__ == "UploadedFile"):
|
|
284
|
+
lines = text_address.getvalue().decode("utf-8").splitlines()
|
|
285
|
+
else:
|
|
284
286
|
with open(text_address, "r") as f:
|
|
285
287
|
lines = f.readlines()
|
|
286
288
|
|
|
287
|
-
# Uploaded file
|
|
288
|
-
else:
|
|
289
|
-
lines = text_address.getvalue().decode("utf-8").splitlines()
|
|
290
|
-
|
|
291
289
|
# Reverse loop over the lines
|
|
292
290
|
params_dict = {}
|
|
293
291
|
for line in reversed(lines):
|
|
@@ -297,19 +295,6 @@ def load_txt(text_address, **kwargs):
|
|
|
297
295
|
key, value = line[1:].split(":", 1)
|
|
298
296
|
params_dict[key.strip()] = value.strip()
|
|
299
297
|
|
|
300
|
-
# # Transform foot comments as dictionary data
|
|
301
|
-
# params_dict = {}
|
|
302
|
-
# with open(text_address, "r") as f:
|
|
303
|
-
#
|
|
304
|
-
# # Reverse loop while the lines start by a "#"
|
|
305
|
-
# for line in reversed(f.readlines()):
|
|
306
|
-
# line = line.strip()
|
|
307
|
-
# if not line.startswith("#") or (line.startswith("# LiMe")):
|
|
308
|
-
# break
|
|
309
|
-
#
|
|
310
|
-
# # Extract key-value pairs
|
|
311
|
-
# key, value = line[1:].split(":", 1) # Split at the first ':'
|
|
312
|
-
# params_dict[key.strip()] = value.strip()
|
|
313
298
|
|
|
314
299
|
return out_array, params_dict
|
|
315
300
|
|
|
@@ -232,7 +232,7 @@ def voigt_area(line, idx, n_steps):
|
|
|
232
232
|
sigma = np.random.normal(line.sigma[idx], line.sigma_err[idx], n_steps)
|
|
233
233
|
gamma = np.random.normal(line.gamma[idx], line.gamma_err[idx], n_steps)
|
|
234
234
|
|
|
235
|
-
return gaussian_area(amp, sigma) + lorentz_area(amp, gamma)
|
|
235
|
+
return gaussian_area(amp, sigma, n_steps) + lorentz_area(amp, gamma, n_steps)
|
|
236
236
|
|
|
237
237
|
|
|
238
238
|
def pseudo_voigt_area(line, idx, n_steps):
|
|
@@ -268,7 +268,7 @@ def pseudo_power_area(line, idx, n_steps):
|
|
|
268
268
|
|
|
269
269
|
def velocity_to_wavelength_band(n_sigma, band_velocity_sigma, lambda_obs, delta_instr):
|
|
270
270
|
|
|
271
|
-
return n_sigma * ((band_velocity_sigma / c_KMpS) * lambda_obs + delta_instr)
|
|
271
|
+
return n_sigma * np.sqrt(np.square((band_velocity_sigma / c_KMpS) * lambda_obs) + np.square(delta_instr))
|
|
272
272
|
|
|
273
273
|
|
|
274
274
|
ALL_PARAMS = np.array(['m_cont', 'n_cont', 'amp', 'center', 'sigma', 'gamma', 'alpha', 'frac', 'a', 'b', 'c'])
|
|
@@ -340,6 +340,10 @@ class Spectrum:
|
|
|
340
340
|
internally for fitting and removed in reported measurements.
|
|
341
341
|
crop_waves : tuple or numpy.ndarray, optional
|
|
342
342
|
Two-element ``(min, max)`` wavelength range used to crop the input arrays.
|
|
343
|
+
crop_flux : tuple, optional
|
|
344
|
+
Two-element ``(min_percentile, max_percentile)`` range used to clip the flux array
|
|
345
|
+
by percentile. Defaults to ``None``, equivalent to ``(1, 100)`` (i.e. the 1st to
|
|
346
|
+
100th percentile).
|
|
343
347
|
res_power : float or numpy.ndarray, optional
|
|
344
348
|
Instrument resolving power :math:`R = \\lambda/\\Delta\\lambda`. If provided,
|
|
345
349
|
it can be used to compute and apply an instrumental broadening correction
|
|
@@ -923,14 +927,18 @@ class Spectrum:
|
|
|
923
927
|
|
|
924
928
|
# Confirm the lines in the log match the one of the spectrum
|
|
925
929
|
if line_0.units_wave != self.units_wave:
|
|
926
|
-
_logger.
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
# Confirm all the log lines have the same units
|
|
930
|
-
au_str = 'A' if line_0.units_wave == 'Angstrom' else str(line_0.units_wave)
|
|
931
|
-
same_units_check = np.flatnonzero(np.core.defchararray.find(line_list.astype(str), au_str) != -1).size == line_list.size
|
|
932
|
-
|
|
933
|
-
|
|
930
|
+
_logger.critical(f'Different units in the spectrum dispersion ({self.units_wave}) axis and the lines log'
|
|
931
|
+
f' in {line_0.units_wave[0]}')
|
|
932
|
+
|
|
933
|
+
# # Confirm all the log lines have the same units This fails if blended or merged in table
|
|
934
|
+
# au_str = 'A' if line_0.units_wave == 'Angstrom' else str(line_0.units_wave)
|
|
935
|
+
# same_units_check = np.flatnonzero(np.core.defchararray.find(line_list.astype(str), au_str) != -1).size == line_list.size
|
|
936
|
+
# same_units_check = np.char.find(line_list.astype(str), au_str)
|
|
937
|
+
# (np.char.find(line_list.astype(str), au_str) != -1).all()
|
|
938
|
+
# np.array([str(l) for l in line_list]).astype(str)
|
|
939
|
+
#
|
|
940
|
+
# if not same_units_check:
|
|
941
|
+
# _logger.warning(f'The log has lines with different units')
|
|
934
942
|
|
|
935
943
|
# Assign the log
|
|
936
944
|
self.frame = log_df
|
|
@@ -957,7 +965,7 @@ class Spectrum:
|
|
|
957
965
|
if frame is not None:
|
|
958
966
|
if line_label in frame.index:
|
|
959
967
|
bands_limits = frame.loc[line_label, 'w1':'w6']
|
|
960
|
-
idcs_bands = np.searchsorted(self.
|
|
968
|
+
idcs_bands = np.searchsorted(self.wave.data, bands_limits * (1 + self.redshift))
|
|
961
969
|
idcs = (idcs_bands[0], idcs_bands[5])
|
|
962
970
|
line_measured = True
|
|
963
971
|
else:
|
|
@@ -973,9 +981,9 @@ class Spectrum:
|
|
|
973
981
|
line_list = line.list_comps
|
|
974
982
|
|
|
975
983
|
# Compute the linear components
|
|
976
|
-
gaussian_arr = profiles_computation(line_list, frame, 1 + self.
|
|
977
|
-
x_array=self.
|
|
978
|
-
linear_arr = linear_continuum_computation(line_list, frame, 1 + self.
|
|
984
|
+
gaussian_arr = profiles_computation(line_list, frame, 1 + self.redshift, line.profile,
|
|
985
|
+
x_array=self.wave.data[idcs[0]: idcs[1]])
|
|
986
|
+
linear_arr = linear_continuum_computation(line_list, frame, 1 + self.redshift, x_array=self.wave.data[idcs[0]: idcs[1]])
|
|
979
987
|
|
|
980
988
|
# Determine which component you want to extract:
|
|
981
989
|
if split_components is False:
|
|
@@ -1145,13 +1153,14 @@ class Spectrum:
|
|
|
1145
1153
|
>>> spec.frame.shape
|
|
1146
1154
|
(0, 10)
|
|
1147
1155
|
"""
|
|
1156
|
+
if self.frame is not None:
|
|
1148
1157
|
|
|
1149
|
-
|
|
1150
|
-
|
|
1158
|
+
if line_data:
|
|
1159
|
+
self.frame = self.frame[0:0]
|
|
1151
1160
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1161
|
+
if cont_data:
|
|
1162
|
+
self.cont = None
|
|
1163
|
+
self.cont_std = None
|
|
1155
1164
|
|
|
1156
1165
|
return
|
|
1157
1166
|
|
|
@@ -39,6 +39,10 @@ category_conf_styles = {0: 'dotted',
|
|
|
39
39
|
1: 'dashed',
|
|
40
40
|
2: 'solid'}
|
|
41
41
|
|
|
42
|
+
def ensure_list(x):
|
|
43
|
+
return x if isinstance(x, list) else [x]
|
|
44
|
+
|
|
45
|
+
|
|
42
46
|
def update_bokeh_figure(figure_obj, config_dict):
|
|
43
47
|
|
|
44
48
|
# Set general figure properties
|
|
@@ -48,30 +52,40 @@ def update_bokeh_figure(figure_obj, config_dict):
|
|
|
48
52
|
if isinstance(value, dict):
|
|
49
53
|
match key:
|
|
50
54
|
case "xaxis":
|
|
51
|
-
for
|
|
52
|
-
|
|
53
|
-
|
|
55
|
+
for item_obj in ensure_list(figure_obj):
|
|
56
|
+
if item_obj is not None:
|
|
57
|
+
for axis in item_obj.xaxis: # Update all x-axes
|
|
58
|
+
for attr, val in value.items():
|
|
59
|
+
setattr(axis, attr, val)
|
|
54
60
|
|
|
55
61
|
case "yaxis":
|
|
56
|
-
for
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
for item_obj in ensure_list(figure_obj):
|
|
63
|
+
if item_obj is not None:
|
|
64
|
+
for axis in item_obj.yaxis: # Update all y-axes
|
|
65
|
+
for attr, val in value.items():
|
|
66
|
+
setattr(axis, attr, val)
|
|
59
67
|
|
|
60
68
|
case "title":
|
|
61
69
|
for attr, val in value.items():
|
|
62
|
-
|
|
70
|
+
for item_obj in ensure_list(figure_obj):
|
|
71
|
+
if item_obj is not None:
|
|
72
|
+
setattr(item_obj.title, attr, val)
|
|
63
73
|
|
|
64
74
|
case "xgrid":
|
|
65
|
-
for
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
75
|
+
for item_obj in ensure_list(figure_obj):
|
|
76
|
+
if item_obj is not None:
|
|
77
|
+
for grid in item_obj.xgrid: # Update all x-grids
|
|
78
|
+
for attr, val in value.items():
|
|
79
|
+
val = None if val == 'None' else val
|
|
80
|
+
setattr(grid, attr, val)
|
|
69
81
|
|
|
70
82
|
case "ygrid":
|
|
71
|
-
for
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
83
|
+
for item_obj in ensure_list(figure_obj):
|
|
84
|
+
if item_obj is not None:
|
|
85
|
+
for grid in item_obj.ygrid: # Update all y-grids
|
|
86
|
+
for attr, val in value.items():
|
|
87
|
+
val = None if val == 'None' else val
|
|
88
|
+
setattr(grid, attr, val)
|
|
75
89
|
|
|
76
90
|
# Single value entries
|
|
77
91
|
else:
|
|
@@ -86,7 +100,12 @@ def update_bokeh_figure(figure_obj, config_dict):
|
|
|
86
100
|
case 'active_tap':
|
|
87
101
|
figure_obj.toolbar.active_tap = figure_obj.select_one(getattr(models, value))
|
|
88
102
|
case _ :
|
|
89
|
-
|
|
103
|
+
if isinstance(figure_obj, list):
|
|
104
|
+
for ax in figure_obj:
|
|
105
|
+
if ax is not None:
|
|
106
|
+
setattr(ax, key, value)
|
|
107
|
+
else:
|
|
108
|
+
setattr(figure_obj, key, value)
|
|
90
109
|
|
|
91
110
|
# # Set zoom and pan as active
|
|
92
111
|
# figure_obj.toolbar.active_scroll = figure_obj.select_one(WheelZoomTool) # Activate zoom wheel
|
|
@@ -129,6 +148,7 @@ def save_close_fig_swicth(file_path, fig_obj, display_check):
|
|
|
129
148
|
|
|
130
149
|
return
|
|
131
150
|
|
|
151
|
+
|
|
132
152
|
def bokeh_bands(fig, bands, x, y, z_corr, redshift):
|
|
133
153
|
|
|
134
154
|
# Open the bands file the bands
|
|
@@ -314,6 +334,61 @@ def profile_bokeh(fig, line, z_cor, log, redshift, norm_flux):
|
|
|
314
334
|
return line_single
|
|
315
335
|
|
|
316
336
|
|
|
337
|
+
def redshift_fit_evaluation_bokeh(spectrum, z_infered, data_mask, gauss_arr, z_arr, flux_sum_arr, rest_frame=True):
|
|
338
|
+
|
|
339
|
+
# gauss_arr_max = compute_z_key(z_infer, theo_lambda, wave_matrix, 1, sigma_arr)
|
|
340
|
+
|
|
341
|
+
wave_plot, flux_plot, z_corr, idcs_mask = frame_mask_switch(spectrum.wave, spectrum.flux, z_infered, rest_frame)
|
|
342
|
+
wave_corr = wave_plot / z_corr
|
|
343
|
+
flux_corr = flux_plot * z_corr
|
|
344
|
+
|
|
345
|
+
# Masked flux (red overlay)
|
|
346
|
+
y_mask = np.full(flux_plot.size, np.nan)
|
|
347
|
+
y_mask[data_mask] = flux_corr[data_mask]
|
|
348
|
+
|
|
349
|
+
# --- Top panel: spectrum ---
|
|
350
|
+
p1 = figure(width=800, height=300,
|
|
351
|
+
x_axis_label=str(spectrum.units_wave),
|
|
352
|
+
y_axis_label=str(spectrum.units_flux),
|
|
353
|
+
tools="xpan,xwheel_zoom,reset,save")
|
|
354
|
+
|
|
355
|
+
# Full spectrum
|
|
356
|
+
p1.step(wave_corr, flux_corr, color='white', line_width=1, mode='center', legend_label='Spectrum')
|
|
357
|
+
|
|
358
|
+
# Masked region
|
|
359
|
+
p1.step(wave_corr, y_mask, color='red', line_width=1, mode='center', legend_label='Mask')
|
|
360
|
+
|
|
361
|
+
p1.legend.location = "top_right"
|
|
362
|
+
p1.legend.click_policy = "hide"
|
|
363
|
+
|
|
364
|
+
# --- Twin axis equivalent: gauss_arr on secondary y ---
|
|
365
|
+
# Bokeh doesn't have true twinx, so we normalize gauss_arr to flux scale for overlay
|
|
366
|
+
flux_max = np.nanmax(flux_corr)
|
|
367
|
+
gauss_scaled = gauss_arr * flux_max # scale to flux range
|
|
368
|
+
|
|
369
|
+
p1.step(wave_corr, gauss_scaled, color='yellow', line_width=1, mode='center', legend_label='Bands')
|
|
370
|
+
|
|
371
|
+
# --- Bottom panel: redshift search ---
|
|
372
|
+
title = f'z_prediction = {z_infered:.3f}'
|
|
373
|
+
p2 = figure(width=800, height=200, title=title,
|
|
374
|
+
x_axis_label='Redshift range',
|
|
375
|
+
y_axis_label='F_sum / max(F_sum)',
|
|
376
|
+
tools="xpan,xwheel_zoom,reset,save")
|
|
377
|
+
|
|
378
|
+
flux_sum_norm = flux_sum_arr / np.max(flux_sum_arr)
|
|
379
|
+
|
|
380
|
+
p2.step(z_arr, flux_sum_norm, color='white', line_width=1, mode='center')
|
|
381
|
+
|
|
382
|
+
# Peak marker
|
|
383
|
+
p2.scatter([z_infered], [1], marker='circle', color='red', size=10)
|
|
384
|
+
|
|
385
|
+
# y ticks
|
|
386
|
+
p2.yaxis.ticker = [0, 1]
|
|
387
|
+
|
|
388
|
+
# Link x ranges if rest_frame shares a common axis (optional)
|
|
389
|
+
# p2.x_range = p1.x_range # uncomment if x axes are the same
|
|
390
|
+
|
|
391
|
+
# streamlit_bokeh(column(p1, p2), use_container_width=True)
|
|
317
392
|
|
|
318
393
|
|
|
319
394
|
# Sentinel object for non input figures
|
|
@@ -201,8 +201,6 @@ class Themer:
|
|
|
201
201
|
# Figure colors for bokeh
|
|
202
202
|
colors_bokeh = nested_dict(deepcopy(self.conf['bokeh']['colors']), self.colors)
|
|
203
203
|
self.active_conf['bokeh'].update(colors_bokeh)
|
|
204
|
-
# for key, value in self.conf['bokeh']['colors'].items():
|
|
205
|
-
# self.active_conf['bokeh'][key] = self.colors.get(value, value)
|
|
206
204
|
|
|
207
205
|
# Set the size
|
|
208
206
|
if self.scale[0] in self.conf['matplotlib']['size']:
|
|
@@ -211,7 +209,6 @@ class Themer:
|
|
|
211
209
|
if self.scale[0] in self.conf['bokeh']['size']:
|
|
212
210
|
self.bokeh = self.conf['bokeh']['size'][self.scale[0]]
|
|
213
211
|
|
|
214
|
-
|
|
215
212
|
return
|
|
216
213
|
|
|
217
214
|
|
|
@@ -58,7 +58,7 @@ default."label_lines" = 10
|
|
|
58
58
|
[bokeh]
|
|
59
59
|
default."width" = 600
|
|
60
60
|
default."height" = 300
|
|
61
|
-
default."tools" = 'fullscreen,pan,wheel_zoom,box_zoom,
|
|
61
|
+
default."tools" = 'fullscreen,pan,wheel_zoom,box_zoom,xwheel_zoom,reset,save'
|
|
62
62
|
default.'active_drag' = 'PanTool'
|
|
63
63
|
default.'active_scroll' = 'WheelZoomTool'
|
|
64
64
|
|