Source code for mpol.onedim

import numpy as np

from mpol.utils import torch2npy


[docs] def radialI(icube, geom, chan=0, bins=None): r""" Obtain a 1D (radial) brightness profile I(r) from an image cube. Parameters ---------- icube : `mpol.images.ImageCube` object Instance of the MPoL `images.ImageCube` class geom : dict Dictionary of source geometry. Keys: "incl" : float, unit=[deg] Inclination "Omega" : float, unit=[deg] Position angle of the ascending node "omega" : float, unit=[deg] Argument of periastron "dRA" : float, unit=[arcsec] Phase center offset in right ascension. Positive is west of north. "dDec" : float, unit=[arcsec] Phase center offset in declination. chan : int, default=0 Channel of the image cube corresponding to the desired image bins : array, default=None, unit=[arcsec] Radial bin edges to use in calculating I(r). If None, bins will span the full image, with widths equal to the hypotenuse of the pixels Returns ------- bin_centers : array, unit=[arcsec] Radial coordinates of image at center of `bins` Is : array, unit=[Jy / arcsec^2] (if `image` has these units) Azimuthally averaged pixel brightness at `rs` """ # projected Cartesian pixel coordinates [arcsec] xx, yy = icube.coords.sky_x_centers_2D, icube.coords.sky_y_centers_2D # shift image center to source center xc, yc = xx - geom["dRA"], yy - geom["dDec"] # deproject image cos_PA = np.cos(geom["Omega"] * np.pi / 180) sin_PA = np.sin(geom["Omega"] * np.pi / 180) xd = xc * cos_PA - yc * sin_PA yd = xc * sin_PA + yc * cos_PA xd /= np.cos(geom["incl"] * np.pi / 180) # deprojected radial coordinates rr = np.ravel(np.hypot(xd, yd)) if bins is None: # choose sensible bin size and range step = np.hypot(icube.coords.cell_size, icube.coords.cell_size) bins = np.arange(0.0, np.max((abs(xc.ravel()), abs(yc.ravel()))), step) bin_counts, bin_edges = np.histogram(a=rr, bins=bins, weights=None) # cumulative binned brightness in each annulus Is, _ = np.histogram( a=rr, bins=bins, weights=torch2npy(icube.sky_cube[chan]).ravel() ) # mask empty bins mask = bin_counts == 0 Is = np.ma.masked_where(mask, Is) # average binned brightness in each annulus Is /= bin_counts bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 return bin_centers, Is
[docs] def radialV(fcube, geom, rescale_flux, chan=0, bins=None): r""" Obtain the 1D (radial) visibility model V(q) corresponding to a 2D MPoL image. Parameters ---------- fcube : `~mpol.fourier.FourierCube` object Instance of the MPoL `fourier.FourierCube` class geom : dict Dictionary of source geometry. Keys: "incl" : float, unit=[deg] Inclination "Omega" : float, unit=[deg] Position angle of the ascending node "omega" : float, unit=[deg] Argument of periastron "dRA" : float, unit=[arcsec] Phase center offset in right ascension. Positive is west of north. "dDec" : float, unit=[arcsec] Phase center offset in declination rescale_flux : bool If True, the visibility amplitudes are rescaled to account for the difference between the inclined (observed) brightness and the assumed face-on brightness, assuming the emission is optically thick. The source's integrated (2D) flux is assumed to be: :math:`F = \cos(i) \int_r^{r=R}{I(r) 2 \pi r dr}`. No rescaling would be appropriate in the optically thin limit. chan : int, default=0 Channel of the image cube corresponding to the desired image bins : array, default=None, unit=[k\lambda] Baseline bin edges to use in calculating V(q). If None, bins will span the model baseline distribution, with widths equal to the hypotenuse of the (u, v) coordinates Returns ------- bin_centers : array, unit=:math:[`\lambda`] Baselines corresponding to `u` and `v` Vs : array, unit=[Jy] Visibility amplitudes at `q` Notes ----- This routine requires the `frank <https://github.com/discsim/frank>`_ package """ from frank.geometry import apply_phase_shift, deproject # projected model (u,v) points [k\lambda] uu, vv = fcube.coords.ground_u_centers_2D, fcube.coords.ground_v_centers_2D # visibilities V = torch2npy(fcube.ground_vis[chan]).ravel() # phase-shift the visibilities Vp = apply_phase_shift( uu.ravel(), vv.ravel(), V, geom["dRA"], geom["dDec"], inverse=True ) # deproject the (u,v) points up, vp, _ = deproject( uu.ravel(), vv.ravel(), geom["incl"], geom["Omega"] ) # if the source is optically thick, rescale the deprojected V(q) if rescale_flux: Vp.real /= np.cos(geom["incl"] * np.pi / 180) # deprojected baselines qq = np.hypot(up, vp) if bins is None: # choose sensible bin size and range step = np.hypot(fcube.coords.du, fcube.coords.dv) / 2 bins = np.arange(0.0, max(qq), step) bin_counts, bin_edges = np.histogram(a=qq, bins=bins, weights=None) # cumulative binned visibility amplitude in each annulus Vs, _ = np.histogram(a=qq, bins=bins, weights=Vp) # mask empty bins mask = bin_counts == 0 Vs = np.ma.masked_where(mask, Vs) # average binned visibility amplitude in each annulus Vs /= bin_counts bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2 return bin_centers, Vs