def test_auto_correct_dispersion(f=750, phi=-6, gr=2400, verbose=True, plot=True, close_plots=True, *args, **kwargs): ''' A test case to show the effect of wavelength dispersion (cf spectrometer reciprocal function) on the slit function Parameters ---------- f: focal length (mm) default 750 (SpectraPro 2750i) phi: angle in degrees (°) default -6 gr: grooves spacing (gr/mm) default 2400 ''' from radis.test.utils import getTestFile from publib import set_style, fix_style from radis.misc.warning import SlitDispersionWarning if plot: plt.ion() # dont get stuck with Matplotlib if executing through pytest if close_plots: plt.close('all') w_slit_632, I_slit_632 = import_experimental_slit(getTestFile('slitfunction.txt')) slit_measured_632nm = getTestFile('slitfunction.txt') w, I = np.loadtxt(getTestFile('calc_N2C_spectrum_Trot1200_Tvib3000.txt')).T s = calculated_spectrum(w, I, conditions={'Tvib': 3000, 'Trot': 1200}, Iunit='mW/cm2/sr/µm') slit_dispersion = lambda w: linear_dispersion(w, f=f, phi=phi, m=1, gr=gr) s.apply_slit(slit_measured_632nm) if plot: w_full_range = np.linspace(w.min(), w_slit_632.max()) set_style('origin') plt.figure('Spectrometer Dispersion (f={0}mm, phi={1}°, gr={2}'.format( f, phi, gr)) plt.plot(w_full_range, slit_dispersion(w_full_range)) plt.xlabel('Wavelength (nm)') plt.ylabel('Reciprocal Linear Dispersion') # Compare 2 spectra s.plot(nfig='Linear dispersion effect', color='r', label='not corrected') with pytest.warns(SlitDispersionWarning): # expect a "large slit dispersion" warning s.apply_slit(slit_measured_632nm, slit_dispersion=slit_dispersion) if plot: s.plot(nfig='same', color='k', label='corrected') plt.legend() # Plot different slits: s.plot_slit() # plt.plot(w_slit_632, I_slit_632, color='r', label='Not corrected') # plt.legend() return True # nothing defined yet
def test_linear_dispersion_effect(verbose=True, plot=True, close_plots=True, *args, **kwargs): ''' A test case to show the effect of wavelength dispersion (cf spectrometer reciprocal function) on the slit function Test succeeds if a :py:data:`~radis.misc.warning.SlitDispersionWarning` is correctly triggered ''' from radis.test.utils import getTestFile from publib import set_style, fix_style if plot: set_style('origin') plt.ion() # dont get stuck with Matplotlib if executing through pytest if close_plots: plt.close('all') w_slit, I_slit = import_experimental_slit(getTestFile('slitfunction.txt')) if plot: plt.figure(fig_prefix+'Linear dispersion effect') plt.plot(w_slit, I_slit, '--k', label='Exp: FWHM @{0}nm: {1:.3f} nm'.format(632.8, get_effective_FWHM(w_slit, I_slit))) from radis.misc.warning import SlitDispersionWarning with pytest.warns(SlitDispersionWarning): # expect a "large slit dispersion" warning # Test how slit function FWHM scales with linear_dispersion for w0, FWHM in zip([380, 1000, 4200, 5500], [0.396, 0.388, 0.282, 0.188]): w, I = dirac(w0) wc, Ic = convolve_with_slit(w, I, w_slit, I_slit, norm_by='area', slit_dispersion=linear_dispersion, verbose=False) assert np.isclose(FWHM, get_effective_FWHM(wc, Ic), atol=0.001) if plot: plt.plot(wc, Ic, label='FWHM @{0:.2f} nm: {1:.3f} nm'.format(w0, get_effective_FWHM(wc, Ic))) if plot: plt.xlabel('Wavelength (nm)') plt.ylabel('Dirac $x$ slit function') plt.legend(loc='best', prop={'size': 15}) fix_style('article') return True
result = np.empty(100) # In[23]: for i, n in enumerate(s): int = Integrate(func, 2, n) result[i]=int # In[40]: from publib import set_style, fix_style set_style('article') fig = plt.figure(figsize=(10,10)) ax = plt.subplot() ax.scatter(ns, result, label="Logarithmic integral, $L_i(n)$") plt.title("Primes") plt.xlabel("$n$") plt.legend() fix_style('article') plt.show() # In[ ]:
def test_normalisation_mode(plot=True, close_plots=True, verbose=True, *args, **kwargs): """ Test norm_by = 'area' vs norm_by = 'max' """ from radis.test.utils import getTestFile if plot: plt.ion() # dont get stuck with Matplotlib if executing through pytest if close_plots: plt.close("all") from publib import set_style set_style("origin") # %% Compare spectra convolved with area=1 and max=1 # Slit in nm # Spectrum in nm # Specair units: mW/cm2/sr/µm w, I = np.loadtxt(getTestFile("calc_N2C_spectrum_Trot1200_Tvib3000.txt")).T s = calculated_spectrum(w, I, conditions={ "Tvib": 3000, "Trot": 1200 }, Iunit="mW/cm2/sr/µm") FWHM = 2 s.apply_slit(FWHM, norm_by="area") # spectrum convolved with area=1 w_area, I_area = s.get("radiance") if plot: fig = plt.figure(fig_prefix + "Spectrum in nm + slit in nm") fig.clear() ax = fig.gca() s.plot(nfig=fig.number, wunit="nm", label="norm_by: area", lw=3) s.apply_slit(FWHM, norm_by="max") # spectrum convolved with max=1 w_max, I_max = s.get("radiance", wunit="nm") if plot: ax.plot(w_max, I_max / FWHM, "r", label="(norm_by:max)/FWHM") ax.legend(loc="best") assert np.allclose(I_area, I_max / FWHM) if verbose: print("equivalence of normalisation mode for spectrum in 'nm': OK") # %% Compare spectra convolved with area=1 and max=1 # Slit in nm # Spectrum in cm-1 s = load_spec(getTestFile("CO_Tgas1500K_mole_fraction0.01.spec"), binary=True) s.update() # spectrum convolved with area=1 s.apply_slit(FWHM, norm_by="area", plot_slit=plot) w_area, I_area = s.get("radiance") if plot: fig = plt.figure(fig_prefix + "Spectrum in cm-1 + slit in nm") fig.clear() ax = fig.gca() s.plot(nfig=fig.number, wunit="nm", label="norm_by: area", lw=3) # spectrum convolved with max=1 s.apply_slit(FWHM, norm_by="max", plot_slit=plot) w_max, I_max = s.get("radiance", wunit="nm") if plot: ax.plot(w_max, I_max / FWHM, "r", label="(norm_by:max)/FWHM") ax.legend(loc="best") assert np.allclose(I_area, I_max / FWHM) if verbose: print( "equivalence of normalisation mode for spectrum in 'cm-1': {0}: OK" ) assert is_homogeneous(s.units["radiance"], "mW/cm2/sr") if verbose: print(("radiance unit ({0}) is homogeneous to 'mW/cm2/sr': OK".format( s.units["radiance"]))) return True
def plot_diff( s1, s2, var=None, wunit="default", Iunit="default", resample=True, method="diff", diff_window=0, show_points=False, label1=None, label2=None, figsize=None, title=None, nfig=None, normalize=False, verbose=True, save=False, show=True, show_residual=False, lw_multiplier=1, diff_scale_multiplier=1, discard_centile=0, plot_medium="vacuum_only", legendargs={"loc": "best"}, ): """Plot two spectra, and the difference between them. ``method=`` allows you to plot the absolute difference, ratio, or both. If waveranges dont match, ``s2`` is interpolated over ``s1``. Parameters ---------- s1, s2: Spectrum objects var: str, or None spectral quantity to plot (ex: ``'abscoeff'``). If None, plot the first one in the Spectrum from ``'radiance'``, ``'radiance_noslit'``, ``'transmittance'``, etc. wunit: ``'default'``, ``'nm'``, ``'cm-1'``, ``'nm_vac'`` wavespace unit: wavelength air, wavenumber, wavelength vacuum. If ``'default'``, use first spectrum wunit Iunit: str if ``'default'``, use first spectrum unit method: ``'distance'``, ``'diff'``, ``'ratio'``, or list of them. If ``'diff'``, plot difference of the two spectra. If ``'distance'``, plot Euclidian distance (note that units are meaningless then) If ``'ratio'``, plot ratio of two spectra Default ``'diff'``. .. warning:: with ``'distance'``, calculation scales as ~N^2 with N the number of points in a spectrum (against ~N with ``'diff'``). This can quickly override all memory. Can also be a list:: method=['diff', 'ratio'] normalize: bool Normalize the spectra to be ploted Other Parameters ---------------- diff_window: int If non 0, calculates diff by offsetting s1 by ``diff_window`` number of units on either side, and returns the minimum. Kinda compensates for experimental errors on the w axis. Default 0. (look up code to understand...) show_points: boolean if ``True``, make all points appear with 'o' label1, label2: str curve names figsize figure size nfig: int, str figure number of name title: str title verbose: boolean if ``True``, plot stuff such as rescale ratio in normalize mode. Default ``True`` save: str Default is ``False``. By default won't save anything, type the path of the destination if you want to save it (format in the name). show: Bool Default is ``True``. Will show the plots : bad if there are more than 20. show_residual: bool if ``True``, calculates and shows on the graph the residual in L2 norm. See :func:`~radis.spectrum.compare.get_residual`. ``diff_window`` is used in the residual calculation too. ``normalize`` has no effect. diff_scale_multiplier: float dilate the diff plot scale. Default ``1`` discard_centile: int if not ``0``, discard the firsts and lasts centile when setting the limits of the diff window. Example:: discard_centile=1 # --> discards the smallest 1% and largest 1% discard_centile=10 # --> discards the smallest 10% and largest 10% Useful to remove spikes in a ratio, for instance. Note that this does not change the values of the residual. It's just a plot feature. Default ``0`` plot_medium: bool, ``'vacuum_only'`` if ``True`` and ``wunit`` are wavelengths, plot the propagation medium in the xaxis label (``[air]`` or ``[vacuum]``). If ``'vacuum_only'``, plot only if ``wunit=='nm_vac'``. Default ``'vacuum_only'`` (prevents from inadvertently plotting spectra with different propagation medium on the same graph). legendargs: dict format arguments forwarded to the legend Returns ------- fig: figure fig [ax0, ax1]: axes spectra and difference axis Examples -------- Simple use:: from radis import plot_diff plot_diff(s10, s50) # s10, s50 are two spectra Advanced use, plotting the total power in the label, and getting the figure and axes handle to edit them afterwards:: Punit = 'mW/cm2/sr' fig, axes = plot_diff(s10, s50, 'radiance_noslit', figsize=(18,6), label1='brd 10 cm-1, P={0:.2f} {1}'.format(s10.get_power(unit=Punit),Punit), label2='brd 50 cm-1, P={0:.2f} {1}'.format(s50.get_power(unit=Punit),Punit) ) # modify fig, axes.. See an example in :ref:`label_spectrum_howto_compare`, which produces the output below: .. image:: https://radis.readthedocs.io/en/latest/_images/cdsd4000_vs_hitemp_3409K.svg If you wish to plot in a logscale, it can be done in the following way:: fig, [ax0, ax1] = plot_diff(s0, s1, normalize=False, verbose=False) ylim0 = ax0.get_ybound() ax0.set_yscale("log") ax0.set_ybound(ylim0) See Also -------- :func:`~radis.spectrum.compare.get_diff`, :func:`~radis.spectrum.compare.get_ratio`, :func:`~radis.spectrum.compare.get_distance`, :func:`~radis.spectrum.compare.get_residual`, :meth:`~radis.spectrum.spectrum.compare_with` """ if (not show) and ( not save ): # I added this line to avoid calculus in the case there is nothing to do (Minou) if verbose: print("plot_diff : Nothing to do") return None, None # Normal behaviour (Minou) # Get defaults # --- if var is None: # if nothing is defined, try these first: params = s1.get_vars() if "radiance" in params: var = "radiance" elif "radiance_noslit" in params: var = "radiance_noslit" elif "transmittance" in params: var = "transmittance" elif "transmittance_noslit" in params: var = "transmittance_noslit" else: # or plot the first variable we find var = list(params)[0] if var.replace("_noslit", "") in params: var = var.replace("_noslit", "") # ... check variable exist if var not in s1.get_vars(): raise ValueError( "{0} not defined in Spectrum {1}. Use one of : {2}".format( var, s1.get_name(), s1.get_vars())) if var not in s2.get_vars(): raise ValueError( "{0} not defined in Spectrum {1}. Use one of : {2}".format( var, s2.get_name(), s2.get_vars())) if Iunit == "default": try: Iunit = s1.units[var] except KeyError: # unit not defined in dictionary raise KeyError( "Iunit not defined in spectrum for variable {0}. ".format(var) + "Cant use default unit. Specify unit in s.units['{0}'].". format(var)) if wunit == "default": wunit = s1.get_waveunit() if isinstance(method, list): methods = method else: methods = [method] for method in methods: if diff_window != 0 and method != "diff": raise NotImplementedError( "diff_window with method {0}".format(method)) # Get data # ---- if normalize: # copy before modifying directly in spectrum s1 = s1.copy() s2 = s2.copy() w1, I1 = s1.get(var, wunit=wunit, copy=False) w2, I2 = s2.get(var, wunit=wunit, copy=False) ratio = np.nanmax(I1) / np.nanmax(I2) I1 /= np.nanmax(I1) I2 /= np.nanmax(I2) if verbose: print(("Rescale factor: " + str(ratio))) def get_wdiff_Idiff(): wdiffs, Idiffs = [], [] for method in methods: if not normalize: if method == "distance": wdiff, Idiff = get_distance(s1, s2, var=var, wunit=wunit, Iunit=Iunit) elif method == "diff": wdiff, Idiff = get_diff( s1, s2, var=var, wunit=wunit, Iunit=Iunit, diff_window=diff_window, ) elif method == "ratio": wdiff, Idiff = get_ratio(s1, s2, var=var, wunit=wunit, Iunit=Iunit) else: raise ValueError( "Unknown comparison method: {0}".format(method)) wdiffs.append(wdiff) Idiffs.append(Idiff) else: if method == "distance": raise ValueError( "{0} was not implemented yet for normalized spectra". format(method)) elif method == "diff": wdiff, Idiff = curve_substract(w1, I1, w2, I2) elif method == "ratio": wdiff, Idiff = get_ratio(s1, s2, var=var, wunit=wunit, Iunit=Iunit) else: raise ValueError( "Unknown comparison method: {0}".format(method)) wdiffs.append(wdiff) Idiffs.append(Idiff) return wdiffs, Idiffs wdiffs, Idiffs = get_wdiff_Idiff() # Plot # ---- # Format units xlabel = format_xlabel(wunit, plot_medium) # Init figure set_style("origin") fig = plt.figure(num=nfig, figsize=figsize) gs = gridspec.GridSpec(1 + len(methods), 1, height_ratios=[3] + [1] * len(methods)) ax0 = plt.subplot(gs[0]) ax0.ticklabel_format(useOffset=False) ax1 = [] for i in range(len(methods)): ax1i = plt.subplot(gs[i + 1]) ax1i.get_shared_x_axes().join(ax0, ax1i) ax1i.ticklabel_format(useOffset=False) ax1.append(ax1i) # Plotting style if show_points: style = "-o" else: style = "-" # Get labels and names if label1 is None: label1 = s1.get_name() if label2 is None: label2 = s2.get_name() # Max label length: if len(label1) > 60: label1 = label1[:58] + "..." if len(label2) > 60: label2 = label2[:58] + "..." # Plot compared spectra if normalize: # TODO: add option to norm_on ax0.plot(w1, I1, style, color="k", lw=3 * lw_multiplier, label=label1) ax0.plot(w2, I2, style, color="r", lw=1 * lw_multiplier, label=label2) else: ax0.plot(*s1.get(var, wunit=wunit, Iunit=Iunit), style, color="k", lw=3 * lw_multiplier, label=label1) ax0.plot(*s2.get(var, wunit=wunit, Iunit=Iunit), style, color="r", lw=1 * lw_multiplier, label=label2) # cosmetic changes Iunit = make_up_unit(Iunit, var) ax0.tick_params(labelbottom=False) if label1 is not None or label2 is not None: ax0.legend(**legendargs) # Start to 0 if var in ["radiance_noslit", "radiance", "abscoeff", "absorbance"]: ax0.set_ylim(bottom=0) # plot difference (sorted) for ax1i, wdiff, Idiff in zip(ax1, wdiffs, Idiffs): b = np.argsort(wdiff) ax1i.plot(wdiff[b], Idiff[b], style, color="k", lw=1 * lw_multiplier) # Write labels ax1[-1].set_xlabel(make_up(xlabel)) if normalize: fig.text( 0.02, 0.5, ("{0} (norm.)".format(make_up(var))), va="center", rotation="vertical", ) else: fig.text( 0.02, 0.5, ("{0} ({1})".format(make_up(var), Iunit)), va="center", rotation="vertical", ) # Set limits of 'diff' window for i, method in enumerate(methods): if method == "diff": # symmetrize error scale: # auto-zoom on min, max, but discard first and last centile (case of spikes / divergences) Idiff = Idiffs[i] if discard_centile: Idiff_sorted = np.sort(Idiff[~np.isnan(Idiff)]) ymax = max( abs(Idiff_sorted[-int(discard_centile * len(Idiff_sorted) // 100)]), abs(Idiff_sorted[int(discard_centile * len(Idiff_sorted) // 100)]), ) else: ymax = np.nanmax(abs(Idiff)) ax1[i].set_ylim(-ymax * diff_scale_multiplier, ymax * diff_scale_multiplier) elif method == "distance": if discard_centile: raise NotImplementedError( "discard_centile not implemented for method=distance") _, ymax = ax1[i].get_ylim() ax1[i].set_ylim(0, ymax * diff_scale_multiplier) elif method == "ratio": # auto-zoom on min, max, but discard first and last centile (case of spikes / divergences) Idiff = Idiffs[i] if discard_centile: Idiff_sorted = np.sort(Idiff[~np.isnan(Idiff)]) ymin = Idiff_sorted[int(discard_centile * len(Idiff_sorted) // 100)] ymax = Idiff_sorted[-int(discard_centile * len(Idiff_sorted) // 100)] else: ymin = np.nanmin(Idiff) ymax = np.nanmax(Idiff) ax1[i].set_ylim( bottom=((ymin - 1) * diff_scale_multiplier + 1), top=((ymax - 1) * diff_scale_multiplier + 1), ) # ymax = max(abs(Idiff_sorted[len(Idiff_sorted)//100]-1), # abs(Idiff_sorted[len(-Idiff_sorted)//100]-1)) # ax1[i].set_ylim(ymax*diff_scale_multiplier+1, -ymax*diff_scale_multiplier+1) if title: fig.suptitle(title) # Fix format fix_style("origin", ax=ax0) for ax1i in ax1: fix_style("origin", ax=ax1i) plt.tight_layout() if title: plt.subplots_adjust(left=0.15, top=0.92) else: plt.subplots_adjust(left=0.15) # Plot difference text for i, method in enumerate(methods): if method == "diff": difftext = "diff" ax1[i].axhline(y=0, color="grey", zorder=-1) elif method == "distance": difftext = "distance" ax1[i].axhline(y=0, color="grey", zorder=-1) elif method == "ratio": difftext = "ratio" ax1[i].axhline(y=1, color="grey", zorder=-1) # Show residualget_residual if show_residual: difftext += " (residual={0:.2g})".format( get_residual(s1, s2, var=var, norm="L2", ignore_nan=True, diff_window=diff_window)) pos = ax1[i].get_position() fig.text(0.09, pos.ymax + 0.02, difftext) # Add cursors axes = [ax0] + ax1 fig.cursors = MultiCursor( fig.canvas, axes, color="r", lw=1 * lw_multiplier, alpha=0.2, horizOn=False, vertOn=True, ) if show: plt.show() if save: fig.savefig(save) if not show: plt.close(fig) # to avoid memory load # Return graphs return fig, axes
def Integrate(f, a, b): I, _ = integrate.quad(f, a, b) return I ft = lambda t: 1 / log(t) limits = list(linspace(10, 10**10, 100)) Li = [] n = [] for i in limits: temp = Integrate(ft, 2, i) #Append results i times Li.append(temp) n.append(i) #Creating figure set_style('article') #before the first plot plt.figure() ax = plt.subplot() ax.plot(n, Li, 'o', label='Logarithmic integral') plt.xlabel(r'$n \in [10,10^{10}]$') plt.ylabel(r'$Li(n)=\int_{2}^n \frac{1}{\log \: t}dt$') plt.title('A nice title') plt.legend(loc='upper left') fix_style('article') plt.show()
def test_normalisation_mode(plot=True, close_plots=True, verbose=True, *args, **kwargs): ''' Test norm_by = 'area' vs norm_by = 'max' ''' from radis.test.utils import getTestFile if plot: plt.ion() # dont get stuck with Matplotlib if executing through pytest if close_plots: plt.close('all') from publib import set_style set_style('origin') # %% Compare spectra convolved with area=1 and max=1 # Slit in nm # Spectrum in nm # Specair units: mW/cm2/sr/µm w, I = np.loadtxt(getTestFile('calc_N2C_spectrum_Trot1200_Tvib3000.txt')).T s = calculated_spectrum(w, I, conditions={ 'Tvib': 3000, 'Trot': 1200 }, Iunit='mW/cm2/sr/µm') FWHM = 2 s.apply_slit(FWHM, norm_by='area') # spectrum convolved with area=1 w_area, I_area = s.get('radiance') if plot: fig = plt.figure(fig_prefix + 'Spectrum in nm + slit in nm') fig.clear() ax = fig.gca() s.plot(nfig=fig.number, wunit='nm', label='norm_by: area', lw=3) s.apply_slit(FWHM, norm_by='max') # spectrum convolved with max=1 w_max, I_max = s.get('radiance', wunit='nm') if plot: ax.plot(w_max, I_max / FWHM, 'r', label='(norm_by:max)/FWHM') ax.legend(loc='best') assert np.allclose(I_area, I_max / FWHM) if verbose: print("equivalence of normalisation mode for spectrum in 'nm': OK") # %% Compare spectra convolved with area=1 and max=1 # Slit in nm # Spectrum in cm-1 s = load_spec(getTestFile('CO_Tgas1500K_mole_fraction0.01.spec'), binary=True) s.update() # spectrum convolved with area=1 s.apply_slit(FWHM, norm_by='area', plot_slit=plot) w_area, I_area = s.get('radiance') if plot: fig = plt.figure(fig_prefix + 'Spectrum in cm-1 + slit in nm') fig.clear() ax = fig.gca() s.plot(nfig=fig.number, wunit='nm', label='norm_by: area', lw=3) # spectrum convolved with max=1 s.apply_slit(FWHM, norm_by='max', plot_slit=plot) w_max, I_max = s.get('radiance', wunit='nm') if plot: ax.plot(w_max, I_max / FWHM, 'r', label='(norm_by:max)/FWHM') ax.legend(loc='best') assert np.allclose(I_area, I_max / FWHM) if verbose: print( "equivalence of normalisation mode for spectrum in 'cm-1': {0}: OK" ) assert is_homogeneous(s.units['radiance'], 'mW/cm2/sr') if verbose: print(("radiance unit ({0}) is homogeneous to 'mW/cm2/sr': OK".format( s.units['radiance']))) return True
import numpy as np from scipy import integrate import matplotlib.pyplot as plt import math %matplotlib inline from publib import set_style, fix_style set_style(['origin','latex']) # the style sheet is from https://github.com/erwanp/publib def Integrate(f, a, b): I, _ = integrate.quad(f, a, b) return I f = lambda x : 1/(math.log(x)) fig, ax = plt.subplots(figsize=(16,6)) Ns = np.linspace(10,10**10,100) Is = [Integrate(f,n,2) for n in Ns] ax.plot(Ns,Is, label="Li(n)") ax.legend(bbox_to_anchor=(1.2, 1)) fix_style(['origin', 'latex'])
from radis import cm2nm from radis.misc import centered_diff from publib import set_style, fix_style #%% =========================================================================== # Read Data and get Inputs # ============================================================================= SAVE_OUTPUT = True #%% Read atmosphere data atm = pd.read_csv(r'data/atmosphere_standard_profile_1976.csv', comment='#') atm['path_length'] = centered_diff(atm.z_km) #%% Plot atmosphere data set_style('origin') fig, ax = plt.subplots() ax.plot(atm.P_Pa * 1e-5, atm.z_km) ax.set_xlabel('Pressure [bar]') ax.set_ylabel('Altitude [km]') ax2 = ax.twiny() ax2.plot(atm.T_K, atm.z_km, 'r') ax2.set_xlabel('Temperature [K]', color='r') ax2.tick_params('x', colors='r') plt.tight_layout() fix_style('origin') if SAVE_OUTPUT: from radis.misc import make_folders
def plot_diff(s1, s2, var=None, wunit='default', Iunit='default', medium='default', resample=True, method='diff', show_points=False, label1=None, label2=None, figsize=None, title=None, nfig=None, normalize=False, verbose=True): ''' Plot two spectra, and the difference between them If waveranges dont match, `s2` is interpolated over `s1`. Parameters ---------- s1, s2: Spectrum objects var: str, or None spectral quantity to plot (ex: 'abscoeff'). If None, plot the first one in the Spectrum from 'radiance', 'radiance_noslit', 'transmittance', etc. wunit: 'default', 'nm', 'cm-1' if 'default', use first spectrum wunit Iunit: str if 'default', use first spectrum unit medium: 'air', 'vacuum', 'default' if 'default', use first spectrum propagating medium method: 'distance', 'diff', 'ratio' If 'diff', plot difference at same wavespace position. If 'distance', plot Euclidian distance (note that units are meaningless then) If 'ratio', plot ratio of two spectra Default 'diff'. Warning: with 'distance', calculation scales as ~N^2 with N the number of points in a spectrum (against ~N with 'diff'). This can quickly override all memory. normalize: bool Normalize the spectra to be ploted Other Parameters ---------------- show_points: boolean if True, make all points appear with 'o' label1, label2: str curve names figsize figure size nfig: int, str figure number of name title: str title verbose: boolean if True, plot stuff such as rescale ratio in normalize mode. Default True Examples -------- >>> Punit = 'mW/cm2/sr' >>> fig, axes = plot_diff(s10, s50, figsize=(18,6), >>> label1='brd 10 cm-1, P={0:.2f} {1}'.format(s10.get_power(unit=Punit),Punit), >>> label2='brd 50 cm-1, P={0:.2f} {1}'.format(s50.get_power(unit=Punit),Punit) >>> ) See Also -------- :func:`~radis.spectrum.compare.get_diff`, :func:`~radis.spectrum.compare.get_ratio`, :func:`~radis.spectrum.compare.get_distance`, :func:`~radis.spectrum.compare.get_residual`, :meth:`~radis.spectrum.spectrum.compare_with` ''' # Get defaults # --- if var is None: # if nothing is defined, try these first: params = s1.get_vars() if 'radiance' in params: var = 'radiance' elif 'radiance_noslit' in params: var = 'radiance_noslit' elif 'transmittance' in params: var = 'transmittance' elif 'transmittance_noslit' in params: var = 'transmittance_noslit' else: # or plot the first variable we find var = list(params)[0] if var.replace('_noslit', '') in params: var = var.replace('_noslit', '') if Iunit == 'default': try: Iunit = s1.units[var] except KeyError: # unit not defined in dictionary raise KeyError('Iunit not defined in spectrum. Cant plot') if wunit == 'default': wunit = s1.get_waveunit() if medium == 'default': medium = s1.conditions.get('medium', None) # Get data # ---- if method == 'distance': wdiff, Idiff = get_distance(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium) elif method == 'diff': wdiff, Idiff = get_diff(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium) elif method == 'ratio': wdiff, Idiff = get_ratio(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium) else: raise ValueError('Unknown comparison method: {0}'.format(method)) # Plot # ---- # Format units wunit = cast_waveunit(wunit) if wunit == 'cm-1': xlabel = 'Wavenumber (cm-1)' elif wunit == 'nm': xlabel = 'Wavelength (nm)' # Init figure set_style('origin') fig = plt.figure(num=nfig, figsize=figsize) gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1]) ax0 = plt.subplot(gs[0]) ax1 = plt.subplot(gs[1]) ax1.get_shared_x_axes().join(ax0, ax1) # Plotting style if show_points: style = '-o' else: style = '-' # Get labels and names if label1 is None: label1 = s1.get_name() if label2 is None: label2 = s2.get_name() # Plot compared spectra if normalize: w1, I1 = s1.get(var, wunit, Iunit, medium) w2, I2 = s2.get(var, wunit, Iunit, medium) if verbose: print(('Rescale factor: ' + str(np.max(I1) / np.max(I2)))) ax0.plot(w1, I1 / np.max(I1), ls=style, color='k', lw=3, label=label1) ax0.plot(w2, I2 / np.max(I2), ls=style, color='r', lw=1, label=label2) else: ax0.plot(*s1.get(var, wunit, Iunit, medium), ls=style, color='k', lw=3, label=label1) ax0.plot(*s2.get(var, wunit, Iunit, medium), ls=style, color='r', lw=1, label=label2) Iunit = make_up(Iunit) # cosmetic changes ax0.tick_params(labelbottom='off') if label1 is not None or label2 is not None: ax0.legend(loc='best') # Start to 0 if var in ['radiance_noslit', 'radiance', 'abscoeff', 'absorbance']: ax0.set_ylim(bottom=0) # plot difference (sorted) b = np.argsort(wdiff) ax1.plot(wdiff[b], Idiff[b], style, color='k', lw=1) if method == 'diff': fig.text(0.09, 0.38, 'diff') elif method == 'distance': fig.text(0.09, 0.38, 'distance') elif method == 'ratio': fig.text(0.09, 0.38, 'ratio') # Write labels ax1.set_xlabel(make_up(xlabel)) if normalize: fig.text(0.02, 0.5, 'Arb. Units', va='center', rotation='vertical') else: fig.text(0.02, 0.5, ('{0} ({1})'.format(make_up(var), Iunit)), va='center', rotation='vertical') # Set limits if method == 'diff': # symmetrize error scale: ymin, ymax = ax1.get_ylim() ymax = max(abs(ymin), abs(ymax)) ax1.set_ylim(-ymax, ymax) elif method == 'distance': ax1.set_ylim(bottom=0) elif method == 'ratio': # auto-zoom on min, max, but discard first decile (case of spikes / divergences) Idiff_s = np.sort(Idiff) ax1.set_ylim(bottom=0, top=Idiff_s[len(Idiff_s) // 10] + Idiff_s[-len(Idiff_s) // 10]) if title: fig.suptitle(title) # Fix format fix_style('origin', ax=ax0) fix_style('origin', ax=ax1) plt.tight_layout() if title: plt.subplots_adjust(left=0.15, top=0.92) else: plt.subplots_adjust(left=0.15) # Add cursors fig.cursors = MultiCursor(fig.canvas, (ax0, ax1), color='r', lw=1, alpha=0.2, horizOn=False, vertOn=True) return fig, [ax0, ax1]
def plot_diff(s1, s2, var=None, wunit='default', Iunit='default', medium='default', resample=True, method='diff', diff_window=0, show_points=False, label1=None, label2=None, figsize=None, title=None, nfig=None, normalize=False, verbose=True, save=False, show=True, show_residual=False, lw_multiplier=1, diff_scale_multiplier=1, discard_centile=0): ''' Plot two spectra, and the difference between them. ``method=`` allows you to plot the absolute difference, ratio, or both. If waveranges dont match, ``s2`` is interpolated over ``s1``. Parameters ---------- s1, s2: Spectrum objects var: str, or None spectral quantity to plot (ex: ``'abscoeff'``). If None, plot the first one in the Spectrum from ``'radiance'``, ``'radiance_noslit'``, ``'transmittance'``, etc. wunit: ``'default'``, ``'nm'``, ``'cm-1'`` if ``'default'``, use first spectrum wunit Iunit: str if ``'default'``, use first spectrum unit medium: ``'air'``, ``'vacuum'``, ``'default'`` if ``'default'``, use first spectrum propagating medium method: ``'distance'``, ``'diff'``, ``'ratio'``, or list of them. If ``'diff'``, plot difference of the two spectra. If ``'distance'``, plot Euclidian distance (note that units are meaningless then) If ``'ratio'``, plot ratio of two spectra Default ``'diff'``. .. warning:: with ``'distance'``, calculation scales as ~N^2 with N the number of points in a spectrum (against ~N with ``'diff'``). This can quickly override all memory. Can also be a list:: method=['diff', 'ratio'] normalize: bool Normalize the spectra to be ploted Other Parameters ---------------- diff_window: int If non 0, calculates diff by offsetting s1 by ``diff_window`` number of units on either side, and returns the minimum. Kinda compensates for experimental errors on the w axis. Default 0. (look up code to understand...) show_points: boolean if ``True``, make all points appear with 'o' label1, label2: str curve names figsize figure size nfig: int, str figure number of name title: str title verbose: boolean if ``True``, plot stuff such as rescale ratio in normalize mode. Default ``True`` save: str Default is ``False``. By default won't save anything, type the path of the destination if you want to save it (format in the name). show: Bool Default is ``True``. Will show the plots : bad if there are more than 20. show_residual: bool if ``True``, calculates and shows on the graph the residual in L2 norm. See :func:`~radis.spectrum.compare.get_residual`. ``diff_window`` is used in the residual calculation too. ``normalize`` has no effect. diff_scale_multiplier: float dilate the diff plot scale. Default ``1`` discard_centile: int if not ``0``, discard the firsts and lasts centile when setting the limits of the diff window. Example:: discard_centile=1 # --> discards the smallest 1% and largest 1% discard_centile=10 # --> discards the smallest 10% and largest 10% Useful to remove spikes in a ratio, for instance. Note that this does not change the values of the residual. It's just a plot feature. Default ``0`` Returns ------- fig: figure fig [ax0, ax1]: axes spectra and difference axis Examples -------- Simple use:: from radis import plot_diff plot_diff(s10, s50) # s10, s50 are two spectra Advanced use, plotting the total power in the label, and getting the figure and axes handle to edit them afterwards:: Punit = 'mW/cm2/sr' fig, axes = plot_diff(s10, s50, 'radiance_noslit', figsize=(18,6), label1='brd 10 cm-1, P={0:.2f} {1}'.format(s10.get_power(unit=Punit),Punit), label2='brd 50 cm-1, P={0:.2f} {1}'.format(s50.get_power(unit=Punit),Punit) ) # modify fig, axes.. See an example output in :ref:`Compare two Spectra <label_spectrum_howto_compare>` See Also -------- :func:`~radis.spectrum.compare.get_diff`, :func:`~radis.spectrum.compare.get_ratio`, :func:`~radis.spectrum.compare.get_distance`, :func:`~radis.spectrum.compare.get_residual`, :meth:`~radis.spectrum.spectrum.compare_with` ''' if (not show) and ( not save ): #I added this line to avoid calculus in the case there is nothing to do (Minou) if verbose: print('plot_diff : Nothing to do') return None, None #Normal behaviour (Minou) # Get defaults # --- if var is None: # if nothing is defined, try these first: params = s1.get_vars() if 'radiance' in params: var = 'radiance' elif 'radiance_noslit' in params: var = 'radiance_noslit' elif 'transmittance' in params: var = 'transmittance' elif 'transmittance_noslit' in params: var = 'transmittance_noslit' else: # or plot the first variable we find var = list(params)[0] if var.replace('_noslit', '') in params: var = var.replace('_noslit', '') # ... check variable exist if var not in s1.get_vars(): raise ValueError( '{0} not defined in Spectrum {1}. Use one of : {2}'.format( var, s1.get_name(), s1.get_vars())) if var not in s2.get_vars(): raise ValueError( '{0} not defined in Spectrum {1}. Use one of : {2}'.format( var, s2.get_name(), s2.get_vars())) if Iunit == 'default': try: Iunit = s1.units[var] except KeyError: # unit not defined in dictionary raise KeyError('Iunit not defined in spectrum for variable {0}. '.format(var)+\ "Cant use default unit. Specify unit in s.units['{0}'].".format(var)) if wunit == 'default': wunit = s1.get_waveunit() if medium == 'default': medium = s1.conditions.get('medium', None) if isinstance(method, list): methods = method else: methods = [method] for method in methods: if diff_window != 0 and method != 'diff': raise NotImplementedError( 'diff_window with method {0}'.format(method)) # Get data # ---- if normalize: # copy before modifying directly in spectrum s1 = s1.copy() s2 = s2.copy() w1, I1 = s1.get(var, copy=False) w2, I2 = s2.get(var, copy=False) I1 /= np.max(I1) I2 /= np.max(I2) if verbose: print(('Rescale factor: ' + str(np.max(I1) / np.max(I2)))) def get_wdiff_Idiff(): wdiffs, Idiffs = [], [] for method in methods: if not normalize: if method == 'distance': wdiff, Idiff = get_distance(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium) elif method == 'diff': wdiff, Idiff = get_diff(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium, diff_window=diff_window) elif method == 'ratio': wdiff, Idiff = get_ratio(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium) else: raise ValueError( 'Unknown comparison method: {0}'.format(method)) wdiffs.append(wdiff) Idiffs.append(Idiff) else: if method == 'distance': raise ValueError( '{0} was not implemented yet for normalized spectra'. format(method)) elif method == 'diff': wdiff, Idiff = curve_substract(w1, I1, w2, I2) elif method == 'ratio': wdiff, Idiff = get_ratio(s1, s2, var=var, wunit=wunit, Iunit=Iunit, medium=medium) else: raise ValueError( 'Unknown comparison method: {0}'.format(method)) wdiffs.append(wdiff) Idiffs.append(Idiff) return wdiffs, Idiffs wdiffs, Idiffs = get_wdiff_Idiff() # Plot # ---- # Format units wunit = cast_waveunit(wunit) if wunit == 'cm-1': xlabel = 'Wavenumber (cm-1)' elif wunit == 'nm': xlabel = 'Wavelength (nm)' # Init figure set_style('origin') fig = plt.figure(num=nfig, figsize=figsize) gs = gridspec.GridSpec(1 + len(methods), 1, height_ratios=[3] + [1] * len(methods)) ax0 = plt.subplot(gs[0]) ax0.ticklabel_format(useOffset=False) ax1 = [] for i in range(len(methods)): ax1i = plt.subplot(gs[i + 1]) ax1i.get_shared_x_axes().join(ax0, ax1i) ax1i.ticklabel_format(useOffset=False) ax1.append(ax1i) # Plotting style if show_points: style = '-o' else: style = '-' # Get labels and names if label1 is None: label1 = s1.get_name() if label2 is None: label2 = s2.get_name() # Max label length: if len(label1) > 60: label1 = label1[:58] + '...' if len(label2) > 60: label2 = label2[:58] + '...' # Plot compared spectra if normalize: # TODO: add option to norm_on ax0.plot(w1, I1, ls=style, color='k', lw=3 * lw_multiplier, label=label1) ax0.plot(w2, I2, ls=style, color='r', lw=1 * lw_multiplier, label=label2) else: ax0.plot(*s1.get(var, wunit, Iunit, medium), ls=style, color='k', lw=3 * lw_multiplier, label=label1) ax0.plot(*s2.get(var, wunit, Iunit, medium), ls=style, color='r', lw=1 * lw_multiplier, label=label2) Iunit = make_up(Iunit) # cosmetic changes ax0.tick_params(labelbottom=False) if label1 is not None or label2 is not None: ax0.legend(loc='best') # Start to 0 if var in ['radiance_noslit', 'radiance', 'abscoeff', 'absorbance']: ax0.set_ylim(bottom=0) # plot difference (sorted) for ax1i, wdiff, Idiff in zip(ax1, wdiffs, Idiffs): b = np.argsort(wdiff) ax1i.plot(wdiff[b], Idiff[b], style, color='k', lw=1 * lw_multiplier) # Write labels ax1[-1].set_xlabel(make_up(xlabel)) if normalize: fig.text(0.02, 0.5, 'Arb. Units', va='center', rotation='vertical') else: fig.text(0.02, 0.5, ('{0} ({1})'.format(make_up(var), Iunit)), va='center', rotation='vertical') # Set limits of 'diff' window for i, method in enumerate(methods): if method == 'diff': # symmetrize error scale: # auto-zoom on min, max, but discard first and last centile (case of spikes / divergences) Idiff = Idiffs[i] if discard_centile: Idiff_sorted = np.sort(Idiff[~np.isnan(Idiff)]) ymax = max( abs(Idiff_sorted[-int(discard_centile * len(Idiff_sorted) // 100)]), abs(Idiff_sorted[int(discard_centile * len(Idiff_sorted) // 100)])) else: ymax = np.nanmax(abs(Idiff)) ax1[i].set_ylim(-ymax * diff_scale_multiplier, ymax * diff_scale_multiplier) elif method == 'distance': if discard_centile: raise NotImplementedError( 'discard_centile not implemented for method=distance') _, ymax = ax1[i].get_ylim() ax1[i].set_ylim(0, ymax * diff_scale_multiplier) elif method == 'ratio': # auto-zoom on min, max, but discard first and last centile (case of spikes / divergences) Idiff = Idiffs[i] if discard_centile: Idiff_sorted = np.sort(Idiff[~np.isnan(Idiff)]) ymin = Idiff_sorted[int(discard_centile * len(Idiff_sorted) // 100)] ymax = Idiff_sorted[-int(discard_centile * len(Idiff_sorted) // 100)] else: ymin = np.nanmin(Idiff) ymax = np.nanmax(Idiff) ax1[i].set_ylim(bottom=((ymin - 1) * diff_scale_multiplier + 1), top=((ymax - 1) * diff_scale_multiplier + 1)) # ymax = max(abs(Idiff_sorted[len(Idiff_sorted)//100]-1), # abs(Idiff_sorted[len(-Idiff_sorted)//100]-1)) # ax1[i].set_ylim(ymax*diff_scale_multiplier+1, -ymax*diff_scale_multiplier+1) if title: fig.suptitle(title) # Fix format fix_style('origin', ax=ax0) for ax1i in ax1: fix_style('origin', ax=ax1i) plt.tight_layout() if title: plt.subplots_adjust(left=0.15, top=0.92) else: plt.subplots_adjust(left=0.15) # Plot difference text for i, method in enumerate(methods): if method == 'diff': difftext = 'diff' ax1[i].axhline(y=0, color='grey', zorder=-1) elif method == 'distance': difftext = 'distance' ax1[i].axhline(y=0, color='grey', zorder=-1) elif method == 'ratio': difftext = 'ratio' ax1[i].axhline(y=1, color='grey', zorder=-1) # Show residualget_residual if show_residual: difftext += ' (residual={0:.2g})'.format( get_residual(s1, s2, var=var, norm='L2', ignore_nan=True, diff_window=diff_window)) pos = ax1[i].get_position() fig.text(0.09, pos.ymax + 0.02, difftext) # Add cursors axes = [ax0] + ax1 fig.cursors = MultiCursor(fig.canvas, axes, color='r', lw=1 * lw_multiplier, alpha=0.2, horizOn=False, vertOn=True) if show: plt.show() if save: fig.savefig(save) if not show: plt.close(fig) #to avoid memory load if # Return graphs return fig, axes