Getting Started
The example below is available as gettingStarted.py in the oimodeler repository.
Note
This example uses OIFITS files located in the examples/data/ASPRO_MATISSE2 subdirectory of the oimodeler GitHub repository.
If you did not clone the repository, you will need to manually download the entire examples/ directory.
These data are a simulated “fake” dataset generated with the ASPRO software from the JMMC. ASPRO created three MATISSE observations of a binary star with one resolved component, including realistic noise on the interferometric quantities.
Let’s start by importing oimodeler and specifying the paths/directories.
from pprint import pprint
from pathlib import Path
import oimodeler as oim
path = Path(__file__).parent.parent.parent
data_dir = path / "examples" / "data" / "ASPRO_MATISSE2"
save_dir = path / "images"
if not save_dir.exists():
save_dir.mkdir(parents=True)
files = list(map(str, data_dir.glob("*.fits")))
If data_dir is correctly set, files should be a list of three OIFITS file paths.
Warning
Writing to a write-protected directory will raise an error. Change save_dir to
a writable location if needed.
Some examples also use a second product_dir which might need changing similarly.
We will now create a simple binary model with one resolved component using two components:
The point source has three parameters: coordinates x and y (mas by default) and flux f.
All component parameters are instances of the oimParam class.
The uniform disk has four parameters: x, y, f, and diameter d (mas by default). If not explicitly set, parameters default to 0 for x, y, and d, and 1 for f.
ud = oim.oimUD(d=3, f=0.5, x=5, y=-5)
pt = oim.oimPt(f=1)
We can print a description of the uniform disk component:
pprint(ud)
... Uniform Disk x=5.00 y=-5.00 f=0.50 d=3.00
To access a specific parameter, use the params dictionary. For example, the diameter d:
pprint(ud.params['d'])
... oimParam d = 3 ± 0 mas range=[-inf,inf] free
Similarly, for the x coordinate:
pprint(ud.params['x'])
... oimParam x = 5 ± 0 mas range=[-inf,inf] fixed
Note: The x parameter is fixed by default for fitting, while d is free. The oimParam() instance also stores units
(via unit as an astropy.units object), uncertainties (error), and fitting bounds
(mini and maxi).
You can access and modify parameter values and attributes in various ways (see the Building and using models section for details).
For this example, let’s free the uniform disk coordinates with ranges ±50 mas, allow the diameter between 0 and 20 mas, and flux between 0 and 10. The point source flux remains fixed at 1.
ud.params['d'].set(min=0, max=20)
ud.params['x'].set(min=-50, max=50, free=True)
ud.params['y'].set(min=-50, max=50, free=True)
ud.params['f'].set(min=0., max=10.)
pt.params['f'].free = False
Now, build the model with these two components:
model = oim.oimModel(ud, pt)
Print all model parameters inherited from components:
model.getParameters()
... {'c1_UD_x': oimParam at 0x1670462cca0 : x=5 ± 0 mas range=[-50,50] free=True,
'c1_UD_y': oimParam at 0x1670462cac0 : y=-5 ± 0 mas range=[-50,50] free=True,
'c1_UD_f': oimParam at 0x1670462cd60 : f=0.5 ± 0 range=[0.0,10.0] free=True,
'c1_UD_d': oimParam at 0x1670462ca90 : d=3 ± 0 mas range=[0.01,20] free=True,
'c2_Pt_x': oimParam at 0x1670462cc70 : x=0 ± 0 mas range=[-inf,inf] free=False,
'c2_Pt_y': oimParam at 0x1670462cb80 : y=0 ± 0 mas range=[-inf,inf] free=False,
'c2_Pt_f': oimParam at 0x167055de490 : f=1 ± 0 range=[-inf,inf] free=False}
Or only the free parameters:
pprint(model.getFreeParameters())
... {'c1_UD_x': oimParam at 0x167055ded30 : x=5 ± 0 mas range=[-50,50] free=True,
'c1_UD_y': oimParam at 0x167055deca0 : y=-5 ± 0 mas range=[-50,50] free=True,
'c1_UD_f': oimParam at 0x167055dec70 : f=0.5 ± 0 range=[0.0,10.0] free=True,
'c1_UD_d': oimParam at 0x167055de850 : d=3 ± 0 mas range=[0.01,20] free=True}
Let’s now compare our data and model using oimSimulator.
It computes simulated data at the spatial (and optionally spectral/temporal) frequencies from our data.
sim = oim.oimSimulator(data=files, model=model)
sim.compute(computeChi2=True, computeSimulatedData=True)
Print the reduced chi-square \(\chi^2_r\) from the data/model comparison:
pprint("Chi2r = {}".format(sim.chi2r))
... Chi2r = 22510.099167065073
Clearly, the initial model is a poor fit. Let’s plot model/data comparison for square visibility (VIS2DATA) and closure phase (T3PHI):
fig0, ax0 = sim.plot(["VIS2DATA", "T3PHI"])
The fig0 figure and ax0 axes list are returned by oimSimulator.plot(). You can save the figure directly by passing
the savefig=file_name keyword.
The oimSimulator() class only compares model and data; it does not fit the model.
To fit, we use oimFitterEmcee, which wraps the
emcee implementation of Goodman & Weare’s Affine Invariant MCMC sampler.
Create a simple MCMC fitter with 10 walkers. You can provide either an oimSimulator() object or data (list of filenames or oimData() object)
and a oimModel() class.
fit = oim.oimFitterEmcee(files, model, nwalkers=10)
Before running the fit, prepare the fitter by initializing walkers uniformly randomly within the parameter bounds:
fit.prepare(init="random")
Note
Alternatively, initialization can be “gaussian”, where walkers start near current parameter values with Gaussian spreads defined by the parameter errors.
The initial parameters are stored in fit.initialParams:
pprint(fit.initialParams)
... [[30.26628081 26.02405335 7.23061417 19.19829182]
[ 23.12647935 44.07636861 3.39149131 17.29408761]
[ -9.311772 47.50156564 9.49185499 4.79198633]
[-24.05134905 -12