Coverage for frank/io.py: 79%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Frankenstein: 1D disc brightness profile reconstruction from Fourier data
2# using non-parametric Gaussian Processes
3#
4# Copyright (C) 2019-2020 R. Booth, J. Jennings, M. Tazzari
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program. If not, see <https://www.gnu.org/licenses/>
18#
19"""This module has functions that read in a UVTable and save fit results to
20 file.
21"""
23import os
24import numpy as np
25import pickle
26import logging
28from frank.utilities import get_fit_stat_uncer
30def load_uvtable(data_file):
31 r"""
32 Read in a UVTable with data to be fit
34 Parameters
35 ----------
36 data_file : string
37 UVTable with data to be fit.
38 If in ASCII format, the table should have columns:
39 u [lambda] v [lambda] Re(V) [Jy] Im(V) [Jy] Weight [Jy^-2]
40 If in .npz format, the file should have arrays:
41 "u" [lambda], "v" [lambda], "V" [Jy; complex: real + imag * 1j],
42 "weights" [Jy^-2]
44 Returns
45 -------
46 u, v : array, unit = :math:`\lambda`
47 u and v coordinates of observations
48 vis : array, unit = Jy
49 Observed visibilities (complex: real + imag * 1j)
50 weights : array, unit = Jy^-2
51 Weights on the visibilities, of the form
52 :math:`1 / \sigma^2`
53 """
55 logging.info(' Loading UVTable')
57 # Get extension removing compressed part
58 base, extension = os.path.splitext(data_file)
59 if extension in {'.gz', '.bz2'}:
60 extension = os.path.splitext(base)[1]
61 if extension not in {'.txt', '.dat'}:
62 raise ValueError("Compressed UV tables (`.gz` or `.bz2`) must be in "
63 "one of the formats `.txt` or `.dat`.")
65 if extension in {'.txt', '.dat'}:
66 u, v, re, im, weights = np.genfromtxt(data_file).T
67 vis = re + 1j*im
69 elif extension == '.npz':
70 dat = np.load(data_file)
71 u, v, vis, weights = [dat[i] for i in ['u', 'v', 'V', 'weights']]
72 if not np.iscomplexobj(vis):
73 raise ValueError("You provided a UVTable with the extension {}."
74 " This extension requires the UVTable's variable 'V' to be"
75 " complex (of the form Re(V) + Im(V) * 1j).".format(extension))
77 else:
78 raise ValueError("You provided a UVTable with the extension {}."
79 " Please provide it as a `.txt`, `.dat`, or `.npz`."
80 " Formats .txt and .dat may optionally be"
81 " compressed (`.gz`, `.bz2`).".format(extension))
83 return u, v, vis, weights
86def save_uvtable(filename, u, v, vis, weights):
87 r"""Save a uvtable to file.
89 Parameters
90 ----------
91 filename : string
92 File to save the uvtable to.
93 u, v : array, unit = :math:`\lambda`
94 u and v coordinates of observations
95 vis : array, unit = Jy
96 Complex visibilities
97 weights : array, unit = Jy^-2
98 Weights on the visibilities, of the form
99 :math:`1 / \sigma^2`
100 """
102 extension = os.path.splitext(filename)[1]
103 if extension not in {'.txt', '.dat', '.npz'}:
104 raise ValueError("file extension must be 'npz', 'txt', or 'dat'.")
106 if extension in {'.txt', '.dat'}:
107 header = 'u [lambda]\tv [lambda]\tRe(V) [Jy]\tIm(V) [Jy]\tWeight [Jy^-2]'
109 np.savetxt(filename,
110 np.stack([u, v, vis.real, vis.imag,
111 weights], axis=-1),
112 header=header)
114 elif extension == '.npz':
115 np.savez(filename,
116 u=u, v=v, V=vis, weights=weights,
117 units={'u': 'lambda', 'v': 'lambda',
118 'V': 'Jy', 'weights': "Jy^-2"})
121def load_sol(sol_file):
122 """Load a frank solution object
124 Parameters
125 ----------
126 sol_file : string
127 Filename for frank solution object, '*.obj'
129 Returns
130 ----------
131 sol : _HankelRegressor object
132 frank solution object
133 (see frank.radial_fitters.FrankFitter)
134 """
136 with open(sol_file, 'rb') as f:
137 sol = pickle.load(f)
139 return sol
142def save_fit(u, v, vis, weights, sol, prefix, save_solution=True,
143 save_profile_fit=True, save_vis_fit=True, save_uvtables=True,
144 save_iteration_diag=False, iteration_diag=None,
145 format='npz',
146 ):
147 r"""
148 Save datafiles of fit results
150 Parameters
151 ----------
152 u, v : array, unit = :math:`\lambda`
153 u and v coordinates of observations
154 vis : array, unit = Jy
155 Complex visibilities
156 weights : array, unit = Jy^-2
157 Weights on the visibilities, of the form
158 :math:`1 / \sigma^2`
159 sol : _HankelRegressor object
160 Reconstructed profile using Maximum a posteriori power spectrum
161 (see frank.radial_fitters.FrankFitter)
162 prefix : string
163 Base part of the filename to which files will be saved
164 save_solution : bool
165 Whether to save `sol` object (see frank.radial_fitters.FrankFitter)
166 save_profile_fit : bool
167 Whether to save fitted brightness profile
168 save_vis_fit : bool
169 Whether to save fitted visibility distribution
170 NOTE: This is deprojected
171 save_uvtables : bool
172 Whether to save fitted and residual UVTables
173 NOTE: These are reprojected
174 save_iteration_diag : bool
175 Whether to save diagnostics of the fit iteration
176 iteration_diag : dict
177 Diagnostics of the fit iteration
178 (see radial_fitters.FrankFitter.fit)
179 format : string, default = 'npz'
180 File format in which to save the fit's output UVTable(s)
181 """
183 logging.info(' Saving fit results to {}*'.format(prefix))
186 if not format in {'txt', 'dat', 'npz'}:
187 raise ValueError("'format' must be 'npz', 'txt', or 'dat'.")
189 if save_solution:
190 with open(prefix + '_frank_sol.obj', 'wb') as f:
191 pickle.dump(sol, f)
193 if save_iteration_diag:
194 with open(prefix + '_frank_iteration_diagnostics.obj', 'wb') as f:
195 pickle.dump(iteration_diag, f)
197 if save_profile_fit:
198 unc = get_fit_stat_uncer(sol)
199 np.savetxt(prefix + '_frank_profile_fit.txt',
200 np.array([sol.r, sol.I, unc]).T,
201 header='r [arcsec]\tI [Jy/sr]\tI_uncer [Jy/sr]')
203 if save_vis_fit:
204 np.savetxt(prefix + '_frank_vis_fit.' + format,
205 np.array([sol.q, sol.predict_deprojected(sol.q).real]).T,
206 header='Baseline [lambda]\tProjected Re(V) [Jy]')
209 if save_uvtables:
210 logging.info(' Saving fit and residual UVTables. N.B.: These will'
211 ' be of comparable size to your input UVTable')
213 V_pred = sol.predict(u, v)
215 save_uvtable(prefix + '_frank_uv_fit.' + format,
216 u, v, V_pred, weights)
217 save_uvtable(prefix + '_frank_uv_resid.' + format,
218 u, v, vis - V_pred, weights)