lime-stable 2.0.dev3__tar.gz → 2.0.dev6__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 (51) hide show
  1. {lime_stable-2.0.dev3/src/lime_stable.egg-info → lime_stable-2.0.dev6}/PKG-INFO +2 -2
  2. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/pyproject.toml +2 -2
  3. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/__init__.py +1 -1
  4. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/archives/read_fits.py +1 -1
  5. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/archives/tables.py +3 -0
  6. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/changelog.txt +6 -1
  7. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/fitting/lines.py +8 -6
  8. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/fitting/redshift.py +3 -0
  9. lime_stable-2.0.dev6/src/lime/inference/intensity_threshold.py +137 -0
  10. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/io.py +18 -20
  11. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/lime.toml +1 -1
  12. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/observations.py +3 -1
  13. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/bokeh_plots.py +22 -15
  14. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/format.py +5 -1
  15. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/plots.py +101 -92
  16. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/plots_interactive.py +53 -34
  17. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/theme_lime.toml +3 -1
  18. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/tools.py +7 -1
  19. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/transitions.py +146 -107
  20. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/workflow.py +283 -190
  21. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6/src/lime_stable.egg-info}/PKG-INFO +2 -2
  22. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime_stable.egg-info/requires.txt +1 -1
  23. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_cube.py +2 -2
  24. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_io.py +46 -0
  25. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_line.py +19 -0
  26. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_spectrum.py +49 -5
  27. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_tools.py +102 -4
  28. lime_stable-2.0.dev3/src/lime/inference/intensity_threshold.py +0 -205
  29. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/LICENSE.rst +0 -0
  30. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/MANIFEST.in +0 -0
  31. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/README.rst +0 -0
  32. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/setup.cfg +0 -0
  33. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/archives/__init__.py +0 -0
  34. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/fitting/__init__.py +0 -0
  35. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/inference/detection.py +0 -0
  36. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/__init__.py +0 -0
  37. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/plotting/utils.py +0 -0
  38. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/resources/__init__.py +0 -0
  39. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/resources/lines_database_formatting.py +0 -0
  40. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/resources/lines_database_v2.0.0.txt +0 -0
  41. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/resources/logo.py +0 -0
  42. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/resources/types_params.txt +0 -0
  43. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/retrieve/__init__.py +0 -0
  44. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime/retrieve/peaks.py +0 -0
  45. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime_stable.egg-info/SOURCES.txt +0 -0
  46. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime_stable.egg-info/dependency_links.txt +0 -0
  47. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/src/lime_stable.egg-info/top_level.txt +0 -0
  48. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_astro.py +0 -0
  49. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_model.py +0 -0
  50. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_read_fits.py +0 -0
  51. {lime_stable-2.0.dev3 → lime_stable-2.0.dev6}/tests/test_sample.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lime-stable
3
- Version: 2.0.dev3
3
+ Version: 2.0.dev6
4
4
  Summary: Line measuring algorithm for astronomical spectra
5
5
  Author-email: Vital Fernández <vgf@umich.edu>
6
6
  License-Expression: GPL-3.0-or-later
@@ -19,7 +19,7 @@ Requires-Dist: aspect-stable~=0.3.1
19
19
  Requires-Dist: tomli>=2.0.0; python_version < "3.11"
20
20
  Provides-Extra: full
21
21
  Requires-Dist: asdf~=4.1; extra == "full"
22
- Requires-Dist: bokeh~=3.6; extra == "full"
22
+ Requires-Dist: bokeh~=3.7; extra == "full"
23
23
  Requires-Dist: mplcursors~=0.6; extra == "full"
24
24
  Requires-Dist: openpyxl~=3.1; extra == "full"
25
25
  Requires-Dist: PyLaTeX~=1.4; extra == "full"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "lime-stable"
7
- version = "2.0.dev3"
7
+ version = "2.0.dev6"
8
8
  readme = "README.rst"
9
9
  requires-python = ">=3.11"
10
10
  license = "GPL-3.0-or-later"
@@ -25,7 +25,7 @@ classifiers = ["Programming Language :: Python :: 3",
25
25
 
26
26
  [project.optional-dependencies]
27
27
  full = ["asdf~=4.1",
28
- "bokeh~=3.6",
28
+ "bokeh~=3.7",
29
29
  "mplcursors~=0.6",
30
30
  "openpyxl~=3.1",
31
31
  "PyLaTeX~=1.4",
@@ -42,7 +42,7 @@ _logger.debug(f'Launching LiMe {__version__} in Python {__python_version__}')
42
42
  from lime.observations import Spectrum, Sample, Cube
43
43
  from lime.io import *
44
44
  from lime.tools import *
45
- from lime.transitions import Line, label_decomposition, bands_from_frame
45
+ from lime.transitions import Line, label_decomposition, bands_from_measurements
46
46
  from lime.archives.read_fits import OpenFits, show_instrument_cfg
47
47
  from lime.plotting.plots import theme
48
48
  from lime.workflow import line_bands
@@ -154,7 +154,7 @@ def check_fits_source(fits_source, lime_object=None, load_function=None):
154
154
  else:
155
155
 
156
156
  if load_function is None:
157
- raise LiMe_Error(f'Please introduce fits file instrument or a load function to import the fits file as a '
157
+ _logger.warning(f'Please introduce fits file instrument or a load function to import the fits file as a '
158
158
  f'LiMe observation')
159
159
 
160
160
  return fits_source, spectrum_type
@@ -122,6 +122,9 @@ def table_fluxes(lines_df, table_address, header_format_latex, table_type='pdf',
122
122
  except:
123
123
  print('\n-- PDF compilation failure')
124
124
 
125
+ elif table_type == 'tex':
126
+ pdf.pdfDoc.generate_tex(table_address.as_posix())
127
+
125
128
  return
126
129
 
127
130
 
@@ -105,4 +105,9 @@ LiMe Mayor update - 2.0.0 - XX/XX/XXXX
105
105
  - The central wavelenght bands are now calculated assuming a bands_vsigma=70 and n_sigma=4
106
106
  - The function lime.Spectrum.retrieve.spectrum() returns the spectrum axes as a recarray or saves it into a text file if the user provides a file address.
107
107
  - lime.Spectrum.from_file("fname", instrument='text') can now read the file from a text file following the format from the lime.Spectrum.retrieve.spectrum() function.
108
- - Bug the .plot.spectrum(include_fittings) does not show the combined profile
108
+ - Bug the .plot.spectrum(include_fittings) does not show the combined profile
109
+ - Rename term "_conf" to "_cfg" across function names and arguments for uniformity
110
+ - Fitting functions now have the update_default=True argument. In the default behaviour the default configuration updates the default one. If set to update_default=False only the obj_cfg is used if available else the default one.
111
+ - Creating a spectrum object where all the entries are masked now produces a critical warning instead of raising an error.
112
+ - By default line_bands.
113
+ - Added bands_from_log function
@@ -180,7 +180,6 @@ def pp_FWHM(line, idx):
180
180
 
181
181
 
182
182
  def gaussian_area(line, idx, n_steps):
183
-
184
183
  amp = np.random.normal(line.amp[idx], line.amp_err[idx], n_steps)
185
184
  sigma = np.random.normal(line.sigma[idx], line.sigma_err[idx], n_steps)
186
185
 
@@ -418,7 +417,9 @@ def sigma_corrections(line, idcs_line, wave_arr, R_arr, temperature):
418
417
  if np.isscalar(R_arr):
419
418
  line.sigma_instr = np.mean(wave_arr.compressed() / (R_arr * k_gFWHM))
420
419
  else:
421
- line.sigma_instr = np.mean(wave_arr[idcs_line].compressed() / (R_arr[idcs_line] * k_gFWHM))
420
+ mask_data = ~wave_arr.mask
421
+ line.sigma_instr = np.mean(wave_arr[mask_data] / (R_arr[idcs_line][mask_data] * k_gFWHM))
422
+ wave_arr[mask_data] / (R_arr[idcs_line][mask_data] * k_gFWHM)
422
423
  else:
423
424
  line.sigma_instr = np.nan
424
425
 
@@ -468,8 +469,6 @@ class ProfileModelCompiler:
468
469
  self.model.prefix = f'line0_'
469
470
 
470
471
  # Fix or not the continuum
471
- # m_cont_conf = _SLOPE_FIX_PAR if line._cont_from_adjacent else _SLOPE_FREE_PAR
472
- # n_cont_conf = _INTER_FIX_PAR if line._cont_from_adjacent else _INTER_FREE_PAR
473
472
  self.define_param(0, line, 'm_cont', line.m_cont, _SLOPE_FIX_PAR, user_conf)
474
473
  self.define_param(0, line, 'n_cont', line.n_cont, _INTER_FIX_PAR, user_conf)
475
474
 
@@ -614,8 +613,11 @@ class ProfileModelCompiler:
614
613
  # Check for negative -0.0 # TODO this needs a better place # FIXME -0.0 error
615
614
  if np.signbit(line.sigma_err[i]):
616
615
  line.sigma_err[i] = np.nan
617
- if self.output.errorbars:
618
- _logger.warning(f'Negative value for profile sigma at {line.label}')
616
+ _logger.warning(f'Negative scale value for amplitude at {comp_label}')
617
+
618
+ if np.signbit(line.amp_err[i]):
619
+ line.amp_err[i] = np.nan
620
+ _logger.warning(f'Negative scale value for amplitude at {comp_label}')
619
621
 
620
622
  # Compute the profile areas
621
623
  profile_flux_dist = AREA_FUNCTIONS[line._p_shape[i]](line, i, 1000)
@@ -283,6 +283,9 @@ class RedshiftFitting:
283
283
  else:
284
284
  pred_arr, conf_arr = self._spec.infer.pred_arr, self._spec.infer.conf_arr
285
285
 
286
+ # Resolving power # TODO this should be read at another point...
287
+ res_power = self._spec.res_power if res_power is None else res_power
288
+
286
289
  # Set the type of fitting and the components to use
287
290
  if mode == 'key':
288
291
  components = components if components is not None else ['emission', 'doublet']
@@ -0,0 +1,137 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ from scipy import signal
7
+ from lime.io import check_file_dataframe
8
+ from lime.transitions import label_decomposition
9
+ from lime.plotting.plots import plot_peaks_troughs
10
+
11
+ try:
12
+ import joblib
13
+ joblib_check = True
14
+ except ImportError:
15
+ joblib_check = False
16
+
17
+
18
+ _logger = logging.getLogger('LiMe')
19
+
20
+ MACHINE_PATH = None
21
+ FLUX_PIXEL_CONV = np.linspace(0,1,33)
22
+
23
+
24
+ def compute_line_width(idx_peak, spec_flux, delta_i, min_delta=2, emission_check=True):
25
+
26
+ """
27
+ Algororithm to measure emision line width given its peak location
28
+ :param idx_peak:
29
+ :param spec_flux:
30
+ :param delta_i:
31
+ :param min_delta:
32
+ :return:
33
+ """
34
+
35
+ i = idx_peak
36
+
37
+ if emission_check:
38
+ while (spec_flux[i] > spec_flux[i + delta_i]) or (np.abs(idx_peak - (i + delta_i)) <= min_delta):
39
+ i += delta_i
40
+ else:
41
+ while (spec_flux[i] < spec_flux[i + delta_i]) or (np.abs(idx_peak - (i + delta_i)) <= min_delta):
42
+ i += delta_i
43
+
44
+ return i
45
+
46
+
47
+
48
+
49
+ class LineFinder:
50
+
51
+ def __init__(self):
52
+
53
+ return
54
+
55
+ def peaks_troughs(self, bands, sigma_threshold=3, emission_type=True, width_tol=5,
56
+ continuum_array=None, continuum_std=None, plot_steps=False, **kwargs):
57
+
58
+ """
59
+
60
+ This function compares the input lines bands in the observation spectrum to confirm the presence of lines.
61
+
62
+ The input bands can be specified as a pandas dataframe or the path to its file via the ``bands_df`` argument.
63
+
64
+ The continuum needs to be fit a priori with the Spectrum.fit.continuum function or assigning a ``continuum_array``
65
+ and a ``continuum_std``.
66
+
67
+ The ``sigma_threshold`` establishes the standard deviation factor beyond which a positive line detection is assumed.
68
+
69
+ By default the algorithm seeks for emission lines, set ``emission_type`` equal to False for absorption lines.
70
+
71
+ The additional arguments provide additional utils to adjust the line detection and show the steps/results.
72
+
73
+ :param bands: Input bands dataframe or the address to its file.
74
+ :type bands: pandas.Dataframe, str, pathlib.Path
75
+
76
+ :param sigma_threshold: Continuum standard deviation factor for line detection. The default value is 3.
77
+ :type sigma_threshold: float, optional
78
+
79
+ :param emission_type: Line type. The default value is "True" for emission lines.
80
+ :type emission_type: str, optional
81
+
82
+ :param width_tol: Minimum number of pixels between peaks/troughs. The default value is 5.
83
+ :type width_tol: float, optional
84
+
85
+ :param ml_detection: Machine learning algorithm to detect lines. The default value is None.
86
+ :type ml_detection: str, optional
87
+
88
+ :param plot_steps: Plot the detected peaks/troughs. The default value is False
89
+ :type plot_steps: bool, optional
90
+
91
+ """
92
+
93
+ # TODO Lime2.0 replace by warning with new retrieve line bands
94
+ # Check for the peaks of the emission lines
95
+ continuum_array = self._spec.cont if continuum_array is None else continuum_array
96
+ continuum_std = self._spec.cont_std if continuum_std is None else continuum_std
97
+
98
+ # Get indeces of peaks
99
+ limit_threshold = sigma_threshold * continuum_std
100
+ limit_threshold = continuum_array + limit_threshold if emission_type else continuum_array + limit_threshold
101
+ idcs_peaks, _ = signal.find_peaks(self._spec.flux, height=limit_threshold, distance=width_tol)
102
+
103
+ # Match peaks with theoretical lines
104
+ bands = check_file_dataframe(bands)
105
+ matched_DF = self.label_peaks(idcs_peaks, bands, width_tol=width_tol, line_type=emission_type)
106
+
107
+ # Plot the results
108
+ if plot_steps:
109
+ plot_peaks_troughs(self._spec, idcs_peaks, limit_threshold, continuum_array, matched_DF, **kwargs)
110
+
111
+ return matched_DF
112
+
113
+
114
+ def label_peaks(self, idcs_peaks, matched_DF, line_type='emission', width_tol=5):
115
+
116
+ # Security check in case no lines detected
117
+ if len(idcs_peaks) == 0:
118
+ return pd.DataFrame(columns=matched_DF.columns)
119
+
120
+ # Add theoretical wavelength values if necessary
121
+ if 'wavelength' not in matched_DF.columns:
122
+ matched_DF['wavelength'] = label_decomposition(matched_DF.index.values, params_list=['wavelength'])[0]
123
+
124
+ # Get bands limits indexes
125
+ idcs_w3 = np.searchsorted(self._spec.wave_rest, matched_DF.w3)
126
+ idcs_w4 = np.searchsorted(self._spec.wave_rest, matched_DF.w4)
127
+
128
+ # Get the bands matching
129
+ band_contains_peak = (idcs_peaks[None, :] > idcs_w3[:, None]) & (idcs_peaks[None, :] < idcs_w4[:, None])
130
+ idcs_matched_bands = band_contains_peak.any(axis=1)
131
+ idcs_matched_peaks = idcs_peaks[band_contains_peak.argmax(axis=1)[idcs_matched_bands]]
132
+
133
+ # Crop the bands to the detection
134
+ matched_DF.loc[idcs_matched_bands, 'observation'] = 'detected'
135
+ matched_DF.loc[idcs_matched_bands, 'signal_peak'] = idcs_matched_peaks
136
+
137
+ return matched_DF.loc[idcs_matched_bands]
@@ -446,8 +446,8 @@ def save_frame(fname, dataframe, page='FRAME', parameters='all', header=None, co
446
446
  output_file.write(string_DF.encode('UTF-8'))
447
447
 
448
448
  # Pdf fluxes table
449
- elif file_type == '.pdf':
450
- table_fluxes(lines_log, log_path.parent / log_path.stem, header_format_latex=_LOG_COLUMNS_LATEX,
449
+ elif file_type == '.pdf' or file_type == '.tex':
450
+ table_fluxes(lines_log, log_path.parent / log_path.stem, table_type=file_type[1:], header_format_latex=_LOG_COLUMNS_LATEX,
451
451
  lines_notation=log.latex_label.values, **kwargs)
452
452
 
453
453
  # Log in a fits format
@@ -601,7 +601,8 @@ def check_file_dataframe(df_variable, variable_type=pd.DataFrame, ext='FRAME', s
601
601
  return output
602
602
 
603
603
 
604
- def check_fit_conf(fit_conf, default_key, group_key, group_list=None, fit_cfg_suffix='_line_fitting', line_detection=False):
604
+ def check_fit_conf(fit_conf, default_key, obj_key, update_default=True, group_list=None, fit_cfg_suffix='_line_fitting',
605
+ line_detection=False):
605
606
 
606
607
  # Check that there is an input configuration
607
608
  if fit_conf is not None:
@@ -634,33 +635,30 @@ def check_fit_conf(fit_conf, default_key, group_key, group_list=None, fit_cfg_su
634
635
 
635
636
  # Recover the configuration expected for the object
636
637
  default_cfg = input_cfg.get(f'{default_key}_line_fitting') if default_key is not None else None
637
- mask_cfg = input_cfg.get(f'{group_key}_line_fitting') if group_key is not None else None
638
+ custom_cfg = input_cfg.get(f'{obj_key}_line_fitting') if obj_key is not None else None
638
639
 
639
- # Case there are not leveled entries
640
- if (default_cfg is None) and (mask_cfg is None):
640
+ # Case there are not level entries
641
+ if (default_cfg is None) and (custom_cfg is None):
641
642
  output_cfg = input_cfg
642
643
 
643
644
  # Proceed to update the levels
644
645
  else:
645
646
 
646
647
  # Default configuration
647
- output_cfg = {} if default_cfg is None else default_cfg
648
- default_detect = output_cfg.get('line_detection')
648
+ default_cfg = {} if default_cfg is None else default_cfg
649
+ default_detect = default_cfg.get('line_detection', {})
649
650
 
650
- # Mask conf
651
- mask_conf = {} if mask_cfg is None else mask_cfg
652
- mask_detect = mask_conf.get('line_detection')
651
+ # Custom configuration
652
+ custom_cfg = {} if custom_cfg is None else custom_cfg
653
+ custom_detect = custom_cfg.get('line_detection', {})
653
654
 
654
- # Update the levels
655
- output_cfg = {**output_cfg, **mask_conf}
655
+ # Update default configuration if requested else use only custom
656
+ output_cfg = {**default_cfg, **custom_cfg} if update_default else (custom_cfg if custom_cfg else default_cfg)
656
657
 
657
- # If no line detection don't add it # TODO this is wrong lower should update upper
658
- if mask_detect is not None:
659
- output_cfg['line_detection'] = mask_detect
660
- elif default_detect is not None:
661
- output_cfg['line_detection'] = default_detect
662
- else:
663
- pass
658
+ # Update default detection if requested else use only custom
659
+ if line_detection:
660
+ output_cfg['line_detection'] = default_detect.update(custom_detect) if update_default else\
661
+ (custom_detect if custom_detect else default_detect)
664
662
 
665
663
  else:
666
664
  output_cfg = {}
@@ -1,3 +1,3 @@
1
1
  [metadata]
2
2
  name = 'lime-stable'
3
- version = "2.0.dev3"
3
+ version = "2.0.dev6"
@@ -142,7 +142,7 @@ def check_inputs_arrays(wave, flux, err_flux, pixel_mask, lime_object):
142
142
  # Check not all the pixels are masked
143
143
  if mask_check:
144
144
  if np.all(output_pixel_mask):
145
- raise LiMe_Error(f'All the input observation pixels are masked. Please check that only bad pixels entries'
145
+ _logger.critical(f'All the input observation pixels are masked. Please check that only bad pixels entries'
146
146
  f' are masked (in numpy arrays flux_arr[pixel_mask] = bad_entries)')
147
147
 
148
148
  return output_pixel_mask
@@ -1347,6 +1347,8 @@ class Sample(UserDict, OpenFits):
1347
1347
  else:
1348
1348
  obs = load_function_output
1349
1349
 
1350
+
1351
+
1350
1352
  return obs
1351
1353
 
1352
1354
  def __getitem__(self, id_key):
@@ -102,14 +102,7 @@ def bokeh_bands(fig, bands, x, y, z_corr, redshift):
102
102
 
103
103
  # Loop through the detections and plot the names
104
104
  for i in np.arange(latex.size):
105
- if idcs_band_limits[0, i] != idcs_band_limits[0, i]: # Y limit for the label check if same pixel
106
- max_region = np.max(y[idcs_band_limits[0, i]:idcs_band_limits[0, i]])
107
- else:
108
- max_region = y[idcs_band_limits[0, i]]
109
-
110
- label = 'Matched line' if i == 0 else '_'
111
105
  fig.add_layout(BoxAnnotation(left=w3_obs[i]/z_corr, right=w4_obs[i]/z_corr, fill_alpha=0.3, fill_color=theme.colors['match_line']))
112
- # axis.text(wave_array[i] * (1 + redshift) / z_corr, max_region * 0.9 * z_corr, latex[i], rotation=270)
113
106
 
114
107
  return
115
108
 
@@ -193,8 +186,8 @@ class BokehFigures:
193
186
 
194
187
  return
195
188
 
196
- def bands(self, label, output_address=None, ref_bands=None, include_fits=True, rest_frame=False, log_scale=True, fig_cfg=None,
197
- ax_cfg=None, return_fig=False):
189
+ def bands(self, label, output_address=None, ref_bands=None, include_fits=True, rest_frame=False, log_scale=True,
190
+ exclude_continua=True, fig_cfg=None, ax_cfg=None, return_fig=False):
198
191
 
199
192
 
200
193
  # Unpack variables
@@ -232,16 +225,13 @@ class BokehFigures:
232
225
  # Create figure with default utils if not provided
233
226
  fig = figure(tools=PLT_CONF.get('tools', "pan,wheel_zoom,box_zoom,reset,save"), y_axis_type=scale_str)
234
227
 
235
- # # Create figure with default utils if not provided
236
- # fig = figure(tools=PLT_CONF.get('tools', "pan,wheel_zoom,box_zoom,reset,save"))
237
-
238
228
  # Spectrum data source
239
229
  source = ColumnDataSource(data={"x": wave_plot[idcs_bands[0]:idcs_bands[5]] / z_corr,
240
230
  "y": flux_plot[idcs_bands[0]:idcs_bands[5]] * z_corr})
241
231
  fig.step("x", "y", source=source, color=theme.colors['fg'], line_width=1, mode='center')
242
232
 
243
233
  # Fille the bands
244
- bands_filling_bokeh(fig, wave_plot, flux_plot, z_corr, idcs_bands, line)
234
+ bands_filling_bokeh(fig, wave_plot, flux_plot, z_corr, idcs_bands, line, exclude_continua=exclude_continua)
245
235
 
246
236
  # Plot labels
247
237
  fig.xaxis.axis_label = AXES_CONF['xlabel']
@@ -250,6 +240,9 @@ class BokehFigures:
250
240
  # Adjust the format of the plot
251
241
  update_bokeh_figure(fig, PLT_CONF)
252
242
 
243
+ # Hide the legend if there are line profiles
244
+ fig.legend.visible = legend_check
245
+
253
246
  # Save or display the plot
254
247
  if return_fig:
255
248
  return fig
@@ -258,11 +251,14 @@ class BokehFigures:
258
251
  save(fig, filename=output_address)
259
252
 
260
253
  else:
261
- # output_notebook()
262
254
  show(fig)
263
255
 
256
+ else:
257
+ _logger.info(f'The input line {label} could not be found')
258
+
264
259
  return
265
260
 
261
+
266
262
  def grid(self, output_address=None, rest_frame=True, log_scale=False, n_cols=6, n_rows=None, col_row_scale=(2, 1.5),
267
263
  include_fits=True, in_fig=None, fig_cfg=None, ax_cfg=None, maximize=False):
268
264
 
@@ -270,7 +266,7 @@ class BokehFigures:
270
266
 
271
267
  def spectrum(self, output_address=None, label=None, bands=None, rest_frame=False, log_scale=False,
272
268
  include_fits=True, include_cont=False, include_components=False, return_fig=False, fig_cfg=None, ax_cfg=None, maximize=False,
273
- detection_band=None, show_masks=True, show_categories=False):
269
+ detection_band=None, show_masks=True, show_categories=False, show_err=False):
274
270
 
275
271
 
276
272
  # Set figure format with the user inputs overwriting the default conf
@@ -298,6 +294,14 @@ class BokehFigures:
298
294
  if bands is not None:
299
295
  bokeh_bands(fig, bands, wave_plot, flux_plot, z_corr, self._spec.redshift)
300
296
 
297
+ # Show uncertainty
298
+ if show_err and (self._spec.err_flux is not None):
299
+ err_plot = self._spec.err_flux.data
300
+ fig.varea_step(x=wave_plot / z_corr,
301
+ y1=(flux_plot - err_plot) * z_corr,
302
+ y2=(flux_plot + err_plot) * z_corr,
303
+ step_mode="center", fill_alpha=0.2, color=theme.colors['err_area'])
304
+
301
305
  # Include the continuum
302
306
  if include_cont and self._spec.cont is not None:
303
307
  fig.line(wave_plot/z_corr, self._spec.cont*z_corr, legend_label="Continuum.",
@@ -402,6 +406,9 @@ class BokehFigures:
402
406
  # Adjust the format of the plot
403
407
  update_bokeh_figure(fig, PLT_CONF)
404
408
 
409
+ # Hide the legend if there are line profiles
410
+ fig.legend.visible = legend_check
411
+
405
412
  # Save or display the plot
406
413
  if return_fig:
407
414
  return fig
@@ -54,6 +54,7 @@ class Themer:
54
54
  self.conf = None # All the formating data
55
55
  self.style = None # Label of the active style
56
56
  self.active_conf = None # Dictionary with the active figure configuration and library
57
+ self.default_lib = 'matplotlib'
57
58
 
58
59
  # LiMe plots personalization
59
60
  self.colors = None # Features individual colors
@@ -67,6 +68,8 @@ class Themer:
67
68
  self.conf = conf.copy()
68
69
  self.set_style(style)
69
70
 
71
+
72
+
70
73
  return
71
74
 
72
75
 
@@ -163,7 +166,7 @@ class Themer:
163
166
  return ax_cfg
164
167
 
165
168
 
166
- def set_style(self, style=None, scale=None, colors_conf=None, library=None):
169
+ def set_style(self, style=None, scale=None, colors_conf=None, library='matplotlib'):
167
170
 
168
171
  # Set the default style
169
172
  # self.style = ['default']
@@ -173,6 +176,7 @@ class Themer:
173
176
  # self.style += [style] if isinstance(style, str) else style
174
177
  self.style = 'default' if style is None else style
175
178
  self.scale = ['default'] if style is None else [scale]
179
+ self.default_lib = library
176
180
 
177
181
  # Set the library defaults
178
182
  self.active_conf = {'matplotlib': self.conf['matplotlib']['default'].copy(),