diff --git a/src/linemeasurement/__main__.py b/src/linemeasurement/__main__.py index bd029d5..ec9f5b0 100644 --- a/src/linemeasurement/__main__.py +++ b/src/linemeasurement/__main__.py @@ -8,11 +8,7 @@ from linemeasurement.osa import open_file from linemeasurement.plotapp import PlotApp from linemeasurement.profiles import ( VoigtFitResult, - add_noise, - convolve, elevated_gaussian, - fft_gaussian, - fft_lorentzian, fft_voigt, gaussian, linear_direct_voigt_fit, @@ -36,6 +32,11 @@ def alert(s: str): dlg.exec() +def file_as_title(s) -> str: + p = Path(s).resolve() + return f"LineMeasurement - {p.parent.name}/{p.name}" + + def setup_axes(n: int, lims=(-10, 10)) -> tuple[np.ndarray, float, np.ndarray, float]: """Creates a time-like and a frequency-like array and returns their respective spacing""" x, dx = np.linspace(*lims, n, retstep=True) @@ -67,13 +68,13 @@ def open_new_measurement( if not file.exists(): alert(f"{file} does not exist") - return None + raise FileNotFoundError(file) try: x, y = open_file(file) except Exception: alert(f"Could not load {file}") - return None + raise return file, x, y @@ -84,155 +85,31 @@ def default_values() -> tuple[np.ndarray, np.ndarray]: return raw_wl, raw_intens -@click.group() -def main(): - pass - - -@main.command(help="fit a lorentzian with a gaussian") -def demo1(): - x = np.linspace(-10, 10, 501) - with PlotApp( - FWHM=np.geomspace(0.1, 10), - relative_noise=np.linspace(0, 2), - absolute_noise=[0, *np.geomspace(0.001, 1)], - ) as app: - app.set_antialiasing(True) - app.params["FWHM"].value = 3.7 - - @app.update - def draw(FWHM, relative_noise, absolute_noise): - lo = lorentzian(x, 1, FWHM, 0) - lo = add_noise(lo, absolute_noise, relative_noise * 0.04) - fit = single_func_fit(x, lo, gaussian) - app[0].set_line_data("Lorentzian", x, lo, width=2) - app[0].set_line_data( - "gaus fit", x, fit.curve, width=2, label=f"Gaussian fit : FWHM = {fit.width}" - ) - - -@main.command(name="default", help="illustrate the influence of noises and fit a voigt profile") -def demo2(): - with PlotApp( - n=np.arange(256, 2046), - lorentz_fwhm=np.geomspace(0.1, 1), - gauss_fwhm=np.linspace(1, 10), - relative_noise=np.linspace(0, 2), - absolute_noise=[0, *np.geomspace(1e-6, 1, 201)], - ) as app: - app.set_antialiasing(True) - v_ax = app["Voigt"] - l_ax = app["Lorentz"] - g_ax = app["Gauss"] - l_ax.link_x(v_ax) - g_ax.link_x(v_ax) - - app.params["gauss_fwhm"].value = 5 - app.params["n"].value = 1024 - app.params_layout.setSpacing(0) - - @app.update - def draw(n, lorentz_fwhm, gauss_fwhm, absolute_noise, relative_noise): - x, dx, f, df = setup_axes(n, (-20, 20)) - gaus = gaussian(x, 1, gauss_fwhm, 0) - lore = lorentzian(x, 1, lorentz_fwhm, 0) - g_ax.set_line_data("Gaussian", x, gaus / gaus.max(), width=2) - l_ax.set_line_data("Lorentz", x, lore / lore.max(), width=2) - - conv = convolve(gaus, lore) - conv /= conv.max() - conv_noise = add_noise(conv, absolute_noise, relative_noise * 0.1) - gaus_noise = add_noise(gaus, absolute_noise, relative_noise * 0.1) - v_ax.set_line_data("Voigt", x, conv, width=2) - v_ax.set_line_data("Noisy Voigt", x, conv_noise, width=2) - g_ax.set_line_data("Noisy Gaussian", x, gaus_noise, width=2) - - lin_fit = linear_direct_voigt_fit(x, conv_noise) - log_fit = log_direct_voigt_fit(x, conv_noise) - transfo = np.fft.fft(np.fft.fftshift(conv_noise)).real - fft_fit = linear_fft_voigt_fit(f, transfo, x, dx) - for fit in lin_fit, log_fit, fft_fit: - display_fit(x, fit) - - def display_fit(x: np.ndarray, fit: VoigtFitResult): - v_ax.set_line_data(fit.name, x, fit.curve) - g_ax.set_line_data( - fit.name + "gauss", - x, - gaussian(x, fit.amp, fit.gaussian_width, 0), - label=f"{fit.name} - Gaussian {fit.gaussian_width:.2f}", - width=1.5, - ) - l_ax.set_line_data( - fit.name + "loren", - x, - lorentzian(x, fit.amp, fit.lorentzian_width, 0), - label=f"{fit.name} - Lorentzian {fit.lorentzian_width:.2f}", - width=1.5, - ) - - -@main.command(help="Explore fft fitting") -def demo3(): - x, dx, f, df = setup_axes(1001, (-20, 20)) - with PlotApp( - lorentz_fwhm=np.geomspace(0.1, 1), - gauss_fwhm=np.linspace(1, 10), - relative_noise=np.linspace(0, 2), - absolute_noise=[0, *np.geomspace(1e-6, 1, 201)], - ) as app: - app.set_antialiasing(True) - - ax = app[0] - ind = np.argsort(f) - - @app.update - def draw(lorentz_fwhm, gauss_fwhm, absolute_noise, relative_noise): - gaus_clean = gaussian(x, 1, gauss_fwhm, 0) - lore_clean = lorentzian(x, 1, lorentz_fwhm, 0) - voig_clean = convolve(gaus_clean, lore_clean) - - gaus = add_noise(gaus_clean, absolute_noise, relative_noise * 0.1) - lore = add_noise(lore_clean, absolute_noise, relative_noise * 0.1) - voig = add_noise(voig_clean, absolute_noise, relative_noise * 0.1) - - gaus_f = np.fft.fft(np.fft.fftshift(gaus)).real - lore_f = np.fft.fft(np.fft.fftshift(lore)).real - voig_f = np.fft.fft(np.fft.fftshift(voig)).real - - ax.set_line_data("Transformed Gaussian", f[ind], gaus_f[ind]) - ax.set_line_data("Transformed Lorentz", f[ind], lore_f[ind]) - ax.set_line_data("Transformed Voigt", f[ind], voig_f[ind]) - - gaus_ff = fft_gaussian(f, dx, 1, gauss_fwhm) - lore_ff = fft_lorentzian(f, dx, 1, lorentz_fwhm) - voig_ff = fft_voigt(f, dx, gauss_fwhm, lorentz_fwhm) * voig_clean.sum() - - ax.set_line_data("Expected Gaussian", f[ind], gaus_ff[ind], width=1) - ax.set_line_data("Expected Lorentz", f[ind], lore_ff[ind], width=1) - ax.set_line_data("Expected Voigt", f[ind], voig_ff[ind], width=1) - - @click.option("--all/--no-all", "show_all", default=True, is_flag=True) @click.option("--file", "-f", type=click.Path()) -@main.command(name="fit") -def fit_measurement(file, show_all): - _ = pg.mkQApp() - raw_wl, raw_intens = default_values() - - def _load_new_measurement(): - nonlocal raw_wl, raw_intens, file - res = open_new_measurement(file) - if res is not None: - file, raw_wl, raw_intens = res +@click.command() +def main(file, show_all): + app = pg.mkQApp() if file is None: - _load_new_measurement() + while True: + try: + res = open_new_measurement(file) + except Exception: + continue + if res is None: + app.quit() + return + else: + file, raw_wl, raw_intens = res + break else: try: raw_wl, raw_intens = open_file(file) except Exception: - alert(f"Could not open {file}") + print(f"Could not load {file}") + app.quit() + return with PlotApp( center_wl=np.linspace(raw_wl.min(), raw_wl.max(), 8192), @@ -241,10 +118,17 @@ def fit_measurement(file, show_all): ) as app: def file_change(): - _load_new_measurement() - update_params() + nonlocal raw_wl, raw_intens, file + try: + res = open_new_measurement(file) + except Exception: + return + if res is not None: + file, raw_wl, raw_intens = res + update_params() def update_params(): + app.window.setWindowTitle(file_as_title(file)) app.params["span"].set_values(np.linspace(0.001, raw_wl[-1] - raw_wl[0], 1024)) app.params["center_wl"].set_values(np.linspace(raw_wl.min(), raw_wl.max(), 8192)) app.params["span"].value = app.params["span"].values()[-1] @@ -331,30 +215,5 @@ def fit_measurement(file, show_all): ) -@main.command(name="test", help="test scaling of voigt profile") -def test_amp(): - x, dx, f, df = setup_axes(1001, (-20, 20)) - with PlotApp( - lorentz_fwhm=np.geomspace(0.1, 1), gauss_fwhm=np.linspace(1, 10), amp=np.linspace(1, 7) - ) as app: - app.set_antialiasing(True) - - ax1 = app["Main"] - ax2 = app["Fourrier"] - ind = np.argsort(f) - - @app.update - def draw(lorentz_fwhm, gauss_fwhm, amp): - vo = voigt(x, amp, gauss_fwhm, lorentz_fwhm) - trans = np.fft.fft(np.fft.fftshift(vo)) - expected = fft_voigt(f, dx, amp, gauss_fwhm, lorentz_fwhm) - back_transformed = np.fft.fftshift(np.fft.ifft(expected).real) - - ax1.set_line_data("original", x, vo) - ax1.set_line_data("back tranformed", x, back_transformed) - ax2.set_line_data("expected", f[ind], expected[ind]) - ax2.set_line_data("original transformed", f[ind], trans[ind].real) - - if __name__ == "__main__": main()