def test_model_binaryres_error(example_oifits): param = {"model": "binary_res", "sep": 1, "dm": -1, "theta": 45, "diam": 0.1} f_model = amical.analysis.fitting.select_model(param["model"]) d = amical.loadc(example_oifits) model = f_model(d.u, d.v, d.wl, param) assert np.isnan(model[0])
def test_model_binaryres(example_oifits): param = {"model": "binary_res", "sep": 1, "dm": 1, "theta": 45, "diam": 0.1} f_model = amical.analysis.fitting.select_model(param["model"]) d = amical.loadc(example_oifits) model = f_model(d.u, d.v, d.wl, param) assert len(d.u) == len(model) assert type(model[0]) == np.complex128
def test_calibrate_method(method, cli_datadir, tmp_path, monkeypatch): for i in range(2): monkeypatch.setattr("builtins.input", lambda _: str(i)) isz = 78 ret = main( [ "clean", "--datadir", str(cli_datadir), "--outdir", str(tmp_path), "--isz", str(isz), ] ) assert ret == 0 plt.close("all") for i in range(2): monkeypatch.setattr("builtins.input", lambda _: str(i)) ret = main( [ "extract", "--datadir", str(tmp_path), "--outdir", str(tmp_path), "--peakmethod", method, ] ) assert ret == 0 plt.close("all") responses = iter(["1", "0"]) monkeypatch.setattr("builtins.input", lambda msg: next(responses)) main(["calibrate", "--datadir", str(tmp_path), "--outdir", str(tmp_path)]) output_file = sorted(Path(tmp_path).glob("*calibrated.fits")) cal = loadc(output_file[0]) cal_keys = list(cal.keys()) true_value_vis2 = 0.98479018 true_value_wl = 4.286e-06 assert len(output_file) == 1 assert len(cal_keys) == 20 assert cal.vis2[0] == pytest.approx(true_value_vis2, 1e-3) assert cal.wl[0] == pytest.approx(true_value_wl, 1e-9)
def test_model_disk(example_oifits): param = { "model": "disk", "diam": 10, "x0": 0, "y0": 0, } f_model = amical.analysis.fitting.select_model(param["model"]) d = amical.loadc(example_oifits) model = f_model(d.u, d.v, d.wl, param) assert len(d.u) == len(model) assert type(model[0]) == np.complex128
def test_model_Clumpydisk(example_oifits): param = { "model": "clumpyDebrisDisk", "majorAxis": 10, "incl": 0, "posang": 45, "thickness": 1, "cr": 0.1, "x0": 0, "y0": 0, "d_clump": 0.5, "cr_clump": 10, } f_model = amical.analysis.fitting.select_model(param["model"]) d = amical.loadc(example_oifits) model = f_model(d.u, d.v, d.wl, param) assert len(d.u) == len(model) assert type(model[0]) == np.complex128
def test_model_edisk(example_oifits): param = { "model": "edisk", "majorAxis": 10, "incl": 0, "posang": 45, "thickness": 1, "cr": 0.1, "x0": 0, "y0": 0, } f_model = amical.analysis.fitting.select_model(param["model"]) d = amical.loadc(example_oifits) V2 = amical.analysis.fitting.comput_V2([d.u, d.v, d.wl], param, f_model) model = f_model(d.u, d.v, d.wl, param) assert len(d.u) == len(model) assert len(d.u) == len(V2) assert type(model[0]) == np.complex128
def plot_model( inputdata, param, save=False, outputfile=None, extra_error_v2=0, extra_error_cp=0, err_scale=1, d_freedom=3, v2_min=None, v2_max=1.1, cp_max=None, unit="m", ): """Plot the model compared to the data (V2 and CP) and the associated residuals. Parameters: ----------- `inputdata`: {str} Oifits file,\n `param`: {dict} Parameters of the fit (**tips**: use fit['best'] if you want to use the output of `amical.candid_grid()`.),\n `save`: {boolean} If True, figure is saved using the inputdata file as name followed by '_fit_candid.pdf'. Optionnaly, you can use `outputfile` to change the output file name (e.g.: outputfile='my_fit.pdf'),\n `extra_error_v2`: {float} Additional uncertainty of the V2 (added quadraticaly),\n `extra_error_cp`: {float} Additional uncertainty of the CP (added quadraticaly),\n `err_scale`: {float} Scaling factor applied on the CP uncertainties usualy used to include the non-independant CP correlation,\n `d_freedom` {int}: Degree of freedom (3 by default: sep, theta and dm),\n v2_min, v2_max, cp_max: {float} Limits of the y-axis,\n """ import matplotlib.pyplot as plt d = amical.loadc(inputdata) e_vis2 = np.sqrt(d.e_vis2**2 + extra_error_v2**2) e_cp = np.sqrt(d.e_cp**2 + extra_error_cp**2) * err_scale model_target = select_model(param["model"]) u, v, wl = d.u, d.v, d.wl mod_v2 = comput_V2([u, v, wl], param, model_target) u1, u2, v1, v2 = d.u1, d.u2, d.v1, d.v2 u3, v3 = u1 + u2, v1 + v2 X = [u1, u2, u3, v1, v2, v3, wl] mod_cp = comput_CP(X, param, model_target) chi2_cp = np.sum((d.cp - mod_cp) ** 2 / (e_cp) ** 2) / (len(e_cp) - (d_freedom - 1)) chi2_vis2 = np.sum((d.vis2 - mod_v2) ** 2 / (e_vis2) ** 2) / ( len(e_vis2) - (d_freedom - 1) ) chi2 = ( np.sum((d.cp - mod_cp) ** 2 / (e_cp) ** 2) + np.sum((d.vis2 - mod_v2) ** 2 / (e_vis2) ** 2) ) / (len(e_cp) + len(e_vis2) - (d_freedom - 1)) res_vis2 = (mod_v2 - d.vis2) / e_vis2 res_cp = (mod_cp - d.cp) / e_cp f_unit = { "m": 1, "lambda": 1 / d.wl / 1e6, "arcsec": 1 / d.wl / ((3600 * 180) / np.pi), } label_unit = {"m": "m", "lambda": r"M$\lambda$", "arcsec": r"arcsec$^{-1}$"} x_vis2 = d.bl * f_unit[unit] x_cp = d.bl_cp * f_unit[unit] fig = plt.figure(constrained_layout=True, figsize=(11, 4)) axd = fig.subplot_mosaic( [["vis", "cp"], ["res_vis2", "res_cp"]], gridspec_kw={"width_ratios": [2, 2], "height_ratios": [3, 1]}, ) axd["res_vis2"].sharex(axd["vis"]) axd["res_cp"].sharex(axd["cp"]) axd["vis"].errorbar(x_vis2, d.vis2, yerr=e_vis2, **err_pts_style, color="#3d84a8") axd["vis"].plot( x_vis2, mod_v2, "x", color="#f6416c", zorder=100, ms=10, label=r"model ($\chi^2_r=%2.1f$)" % chi2_vis2, ) axd["vis"].legend() if v2_min is not None: axd["vis"].set_ylim(v2_min, v2_max) axd["vis"].grid(alpha=0.2) axd["vis"].set_ylabel(r"V$^2$") axd["res_vis2"].plot(x_vis2, res_vis2, ".", color="#3d84a8") axd["res_vis2"].axhspan(-1, 1, alpha=0.2, color="#418fde") axd["res_vis2"].axhspan(-2, 2, alpha=0.2, color="#8bb8e8") axd["res_vis2"].axhspan(-3, 3, alpha=0.2, color="#c8d8eb") if res_vis2.max() > 5: res_mas = res_vis2.max() else: res_mas = 5 axd["res_cp"].set_ylim(-res_mas, res_mas) axd["res_vis2"].set_ylim(-5, 5) axd["res_vis2"].set_ylabel(r"Residual [$\sigma$]") axd["res_vis2"].set_xlabel("Sp. Freq. [%s]" % label_unit[unit]) axd["cp"].errorbar(x_cp, d.cp, yerr=e_cp, **err_pts_style, color="#2ca02c") axd["cp"].plot( x_cp, mod_cp, "x", color="#f6416c", zorder=100, ms=10, label=r"model ($\chi^2_r=%2.1f$)" % chi2_cp, ) if cp_max is not None: axd["cp"].set_ylim(-cp_max, cp_max) axd["cp"].grid(alpha=0.2) axd["cp"].set_ylabel("Closure phases [deg]") axd["cp"].legend() axd["res_cp"].plot(x_cp, res_cp, ".", color="#1e7846") axd["res_cp"].axhspan(-1, 1, alpha=0.3, color="#28a16c") # f5c893 axd["res_cp"].axhspan(-2, 2, alpha=0.2, color="#28a16c") axd["res_cp"].axhspan(-3, 3, alpha=0.1, color="#28a16c") if res_cp.max() > 5: res_mas = res_cp.max() else: res_mas = 5 axd["res_cp"].set_ylim(-res_mas, res_mas) axd["res_cp"].set_ylabel(r"Residual [$\sigma$]") axd["res_cp"].set_xlabel("Sp. Freq. [%s]" % label_unit[unit]) if save: import matplotlib.pyplot as plt filename = os.path.basename(inputdata) + "_fit_candid.pdf" if outputfile is not None: filename = outputfile plt.savefig(filename, dpi=300) return mod_v2, mod_cp, chi2
def fits2obs( inputdata, use_flag=True, cond_wl=False, wl_min=None, wl_max=None, cond_uncer=False, rel_max=None, extra_error_v2=0, extra_error_cp=0, err_scale=1, input_rad=False, verbose=True, ): """ Convert and select data from an oifits file. Parameters: ----------- `inputdata`: {str} Oifits file,\n `use_flag`: {boolean} If True, use flag from the original oifits file,\n `cond_wl`: {boolean} If True, apply wavelenght restriction between wl_min and wl_max,\n `wl_min`, `wl_max`: {float} if cond_wl, limits of the wavelength domain [µm],\n `cond_uncer`: {boolean} If True, select the best data according their relative uncertainties (rel_max),\n `rel_max`: {float} if cond_uncer, maximum sigma uncertainties allowed [%],\n `extra_error_v2`: {float} Additional uncertainty of the V2 (added quadraticaly),\n `extra_error_cp`: {float} Additional uncertainty of the CP (added quadraticaly),\n `err_scale`: {float} Scaling factor applied on the CP uncertainties usualy used to include the non-independant CP correlation,\n `verbose`: {boolean} If True, display useful information about the data selection,\n Return: ------- Obs: {tuple} Tuple containing all the selected data in an appropriate format to perform the fit. """ data = amical.loadc(inputdata) nwl = len(data.wl) nbl = data.vis2.shape[0] ncp = data.cp.shape[0] vis2_data = data.vis2.flatten() # * 0.97 e_vis2_data = (data.e_vis2.flatten() ** 2 + extra_error_v2**2) ** 0.5 flag_V2 = data.flag_vis.flatten() if input_rad: cp_data = np.rad2deg(data.cp.flatten()) e_cp_data = np.rad2deg( np.sqrt(data.e_cp.flatten() ** 2 + extra_error_cp**2) * err_scale ) else: cp_data = data.cp.flatten() e_cp_data = np.sqrt(data.e_cp.flatten() ** 2 + extra_error_cp**2) * err_scale flag_CP = data.flag_cp.flatten() if use_flag: pass else: flag_V2 = [False] * len(vis2_data) flag_CP = [False] * len(cp_data) u_data, v_data = [], [] u1_data, v1_data, u2_data, v2_data = [], [], [], [] for i in range(nbl): for _ in range(nwl): u_data.append(data.u[i]) v_data.append(data.v[i]) for i in range(ncp): for _ in range(nwl): u1_data.append(data.u1[i]) v1_data.append(data.v1[i]) u2_data.append(data.u2[i]) v2_data.append(data.v2[i]) u_data = np.array(u_data) v_data = np.array(v_data) u1_data = np.array(u1_data) v1_data = np.array(v1_data) u2_data = np.array(u2_data) v2_data = np.array(v2_data) wl_data = np.array(list(data.wl) * nbl) wl_data_cp = np.array(list(data.wl) * ncp) obs = [] for i in range(nbl * nwl): if flag_V2[i] & use_flag: pass else: if not cond_wl: tmp = [u_data[i], v_data[i], wl_data[i]] typ = "V2" obser = vis2_data[i] err = e_vis2_data[i] if cond_uncer: if err / obser <= rel_max * 1e-2: obs.append([tmp, typ, obser, err]) else: pass else: obs.append([tmp, typ, obser, err]) else: if (wl_data[i] >= wl_min * 1e-6) & (wl_data[i] <= wl_max * 1e-6): tmp = [u_data[i], v_data[i], wl_data[i]] typ = "V2" obser = vis2_data[i] err = e_vis2_data[i] if cond_uncer: if err / obser <= rel_max * 1e-2: obs.append([tmp, typ, obser, err]) else: pass else: obs.append([tmp, typ, obser, err]) else: pass N_v2_rest = len(obs) for i in range(ncp * nwl): if flag_CP[i]: pass else: if not cond_wl: tmp = [ u1_data[i], u2_data[i], (u1_data[i] + u2_data[i]), v1_data[i], v2_data[i], (v1_data[i] + v2_data[i]), wl_data_cp[i], ] typ = "CP" obser = cp_data[i] err = e_cp_data[i] if cond_uncer: if err / obser <= rel_max * 1e-2: obs.append([tmp, typ, obser, err]) else: pass else: obs.append([tmp, typ, obser, err]) else: if (wl_data_cp[i] >= wl_min * 1e-6) & (wl_data_cp[i] <= wl_max * 1e-6): tmp = [ u1_data[i], u2_data[i], (u1_data[i] + u2_data[i]), v1_data[i], v2_data[i], (v1_data[i] + v2_data[i]), wl_data_cp[i], ] typ = "CP" obser = cp_data[i] err = e_cp_data[i] if cond_uncer: if err / obser <= rel_max * 1e-2: obs.append([tmp, typ, obser, err]) else: pass else: obs.append([tmp, typ, obser, err]) else: pass N_cp_rest = len(obs) - N_v2_rest Obs = np.array(obs, dtype=object) if verbose: print( "\nTotal # of data points: %i (%i V2, %i CP)" % (len(Obs), N_v2_rest, N_cp_rest) ) if use_flag: print("-> Flag in oifits files used.") if cond_wl: print( r"-> Restriction on wavelenght: %2.2f < %s < %2.2f µm" % (wl_min, chr(955), wl_max) ) if cond_uncer: print(rf"-> Restriction on uncertainties: {chr(949)} < {rel_max:2.1f} %") return Obs
def plot_model(inputdata, param, extra_error_v2=0, extra_error_cp=0, err_scale=1, d_freedom=1, v2_min=None, v2_max=1.1, cp_max=None, unit='m', display=True): """ Plot the model compared to the data (V2 and CP) and the associated residuals. """ d = amical.loadc(inputdata) e_vis2 = np.sqrt(d.e_vis2**2 + extra_error_v2**2) e_cp = np.sqrt(d.e_cp**2 + extra_error_cp**2) * err_scale model_target = select_model(param['model']) u, v, wl = d.u, d.v, d.wl mod_v2 = comput_V2([u, v, wl], param, model_target) u1, u2, v1, v2 = d.u1, d.u2, d.v1, d.v2 u3, v3 = u1 + u2, v1 + v2 X = [u1, u2, u3, v1, v2, v3, wl] mod_cp = comput_CP(X, param, model_target) chi2_cp = np.sum(((d.cp - mod_cp)**2/(e_cp)**2)) / \ (len(e_cp) - (d_freedom - 1)) chi2_vis2 = np.sum(((d.vis2 - mod_v2)**2/(e_vis2)**2)) / \ (len(e_vis2) - (d_freedom - 1)) chi2 = (np.sum(((d.cp - mod_cp)**2 / (e_cp)**2)) + np.sum( ((d.vis2 - mod_v2)**2 / (e_vis2)**2))) / (len(e_cp) + len(e_vis2) - (d_freedom - 1)) res_vis2 = (mod_v2 - d.vis2) / e_vis2 res_cp = (mod_cp - d.cp) / e_cp f_unit = { 'm': 1, 'lambda': 1 / d.wl / 1e6, 'arcsec': 1 / d.wl / ((3600 * 180) / np.pi) } label_unit = { 'm': 'm', 'lambda': r'M$\lambda$', 'arcsec': r'arcsec$^{-1}$' } x_vis2 = d.bl * f_unit[unit] x_cp = d.bl_cp * f_unit[unit] if display: fig = plt.figure(constrained_layout=True, figsize=(11, 4)) axd = fig.subplot_mosaic([['vis', 'cp'], ['res_vis2', 'res_cp']], gridspec_kw={ 'width_ratios': [2, 2], 'height_ratios': [3, 1] }) axd['res_vis2'].sharex(axd['vis']) axd['res_cp'].sharex(axd['cp']) axd['vis'].errorbar(x_vis2, d.vis2, yerr=e_vis2, **err_pts_style, color='#3d84a8') axd['vis'].plot(x_vis2, mod_v2, 'x', color='#f6416c', zorder=100, ms=10, label='model ($\chi^2_r=%2.1f$)' % chi2_vis2) axd['vis'].legend() if v2_min is not None: axd['vis'].set_ylim(v2_min, v2_max) axd['vis'].grid(alpha=.2) axd['vis'].set_ylabel(r'V$^2$') axd['res_vis2'].plot(x_vis2, res_vis2, '.', color='#3d84a8') axd['res_vis2'].axhspan(-1, 1, alpha=.2, color='#418fde') axd['res_vis2'].axhspan(-2, 2, alpha=.2, color='#8bb8e8') axd['res_vis2'].axhspan(-3, 3, alpha=.2, color='#c8d8eb') if res_vis2.max() > 5: res_mas = res_vis2.max() else: res_mas = 5 axd['res_cp'].set_ylim(-res_mas, res_mas) axd['res_vis2'].set_ylim(-5, 5) axd['res_vis2'].set_ylabel('Residual [$\sigma$]') axd['res_vis2'].set_xlabel('Sp. Freq. [%s]' % label_unit[unit]) axd['cp'].errorbar(x_cp, d.cp, yerr=e_cp, **err_pts_style, color='#2ca02c') axd['cp'].plot(x_cp, mod_cp, 'x', color='#f6416c', zorder=100, ms=10, label='model ($\chi^2_r=%2.1f$)' % chi2_cp) if cp_max is not None: axd['cp'].set_ylim(-cp_max, cp_max) axd['cp'].grid(alpha=.2) axd['cp'].set_ylabel('Closure phases [deg]') axd['cp'].legend() axd['res_cp'].plot(x_cp, res_cp, '.', color='#1e7846') axd['res_cp'].axhspan(-1, 1, alpha=.3, color='#28a16c') # f5c893 axd['res_cp'].axhspan(-2, 2, alpha=.2, color='#28a16c') axd['res_cp'].axhspan(-3, 3, alpha=.1, color='#28a16c') if res_cp.max() > 5: res_mas = res_cp.max() else: res_mas = 5 axd['res_cp'].set_ylim(-res_mas, res_mas) axd['res_cp'].set_ylabel('Residual [$\sigma$]') axd['res_cp'].set_xlabel('Sp. Freq. [%s]' % label_unit[unit]) return mod_v2, mod_cp, chi2
def test_loadc_file(example_oifits): s = loadc(example_oifits) assert isinstance(s, munch.Munch)
def test_loadc_file(filepath): s = loadc(filepath) assert isinstance(s, munch.Munch)