Exemple #1
0
    def apply_interface_thin(self,
                             interface: trains.Interface,
                             rs_center=(0, 0),
                             shape: str = 'circle'):
        """Return copy of self with interface applied as a thin phase mask.

        Args:
            interface:
            rs_center:
            shape: 'circle', 'square' or None.
        """
        dx = self.x - rs_center[0]
        dy = self.y - rs_center[1]
        rho = (dx**2 + dy**2)**0.5
        assert np.isclose(self.n, interface.n1(self.lamb))

        # if shape is not None:
        #     aperture = interface.calc_aperture(dx, dy, shape)
        #     apertured = self.mask_binary(aperture)
        #     cropped = apertured.crop_zeros()

        f, gradrf = interface.calc_mask(self.lamb, rho, True)
        if shape is not None:
            aperture = interface.calc_aperture(dx, dy, shape)
            f *= aperture
            gradrf *= aperture

        gradxyf = mathx.divide0(dx, rho) * gradrf, mathx.divide0(dy,
                                                                 rho) * gradrf

        return self.mask(f, gradxyf, interface.n2(self.lamb)).center_q()
Exemple #2
0
 def recalc_gradxyE(self, gradphi):
     # See p116 Dane's logbook 2.
     gradxE = mathx.divide0((self.gradxyE[0] * self.Er.conj()).real,
                            self.Er.conj()) + 1j * gradphi[0] * self.Er
     gradyE = mathx.divide0((self.gradxyE[1] * self.Er.conj()).real,
                            self.Er.conj()) + 1j * gradphi[1] * self.Er
     return gradxE, gradyE
Exemple #3
0
def moment(x, f, n, nrm_fac=None, axis=None):
    """sum[x^n*f]/nrm_fac
    nrm_fac defaults to sum(f)."""
    if nrm_fac is None:
        nrm_fac = f.sum(axis, keepdims=True)
    return mx.squeeze_leading(
        mx.divide0((x**n * f).sum(axis, keepdims=True), nrm_fac))
Exemple #4
0
 def get_xixpt(xpi):
     xpt = mathx.take_broadcast(xp, [xpi], axis)
     # Find index of xb, the last value of x which is less than or equal to xpt
     xib = mathx.find_last_nonzero(x <= xpt, axis)
     # Find index of xa, the first value of x which is greater than xpt
     xia = mathx.find_first_nonzero(x > xpt, axis)
     # If all elements of x are greater than xpt, use zeroth and first i.e.
     # linearly extrapolate
     inds = xib == -1
     xib[inds] = 0
     xia[inds] = 1
     # Likewise if all elements of x are smaller than or equal to xpt, use
     # second last and last elements
     inds = xia == lx
     xib[inds] = lx - 2
     xia[inds] = lx - 1
     # Get before and after values of x
     xb = mathx.take_broadcast(x, xib, axis)
     xa = mathx.take_broadcast(x, xia, axis)
     Dx = xa - xb
     if suppress_degeneracy_error:
         delta = mathx.divide0(xpt - xb, Dx)
     else:
         assert Dx.min() > 0
         delta = (xpt - xb) / Dx
     xixpt = xib + delta
     return xixpt
Exemple #5
0
 def __init__(self,
              lamb,
              n,
              zfun,
              rs_support,
              Er,
              gradxyE,
              rs_center=(0, 0),
              qs_center=(0, 0),
              polarizationxy=(1, 0)):
     z_center = zfun(*rs_center)
     Profile.__init__(self, lamb, n, z_center, rs_support, Er, gradxyE,
                      rs_center, qs_center, polarizationxy)
     self.zfun = zfun
     self.z = zfun(self.x, self.y)
     self.valid = np.isfinite(self.z)
     self.z[~self.valid] = self.z[self.valid].mean()
     assert np.allclose(Er[~self.valid], 0)
     sumIr = self.Ir.sum()
     self.mean_z = (self.z * self.Ir).sum() / sumIr
     app_propagator = mathx.expj(
         (self.mean_z - self.z) * mathx.divide0(self.Igradphi[2], self.Ir))
     app_propagator[np.isnan(self.z)] = 0
     Er_plane = Er * app_propagator
     gradxyE_plane = tuple(c * app_propagator for c in gradxyE)
     # Approximate plane profile.
     self.app = PlaneProfile(lamb, n, self.mean_z, rs_support, Er_plane,
                             gradxyE_plane, rs_center, qs_center)
Exemple #6
0
 def calc_normal(self, point):
     rho = (point[..., 0]**2 + point[..., 1]**2)**0.5
     dzdrho = self.calc_dzdrho(rho)
     factor = mathx.divide0(dzdrho, rho, 0)
     return v4hb.normalize(
         v4hb.stack_xyzw(-point[..., 0] * factor, -point[..., 1] * factor,
                         1, 0)
     )  # return v4hb.normalize(v4hb.stack_xyzw(-point[0]*factor, -point[1]*factor, 1, 0))
Exemple #7
0
def cart_to_polar(x, y, z, r_sign=1):
    """Convert from cartesian to polar coordinates.

    See polar_to_cart for defintions.
    """
    r = r_sign*(x**2 + y**2 + z**2)**0.5
    theta = np.arccos(mathx.divide0(z, r, 1))
    phi = np.arctan2(y*r_sign, x*r_sign)
    return r, theta, phi
Exemple #8
0
def calc_bessel(x, y, radius, gradr=False):
    rho = (x**2 + y**2)**0.5

    zero = special.jn_zeros(0, 1)

    # See RT2 p75 for derivation
    norm_fac = np.pi**0.5*special.jv(1, zero)*radius
    z = rho*zero/radius
    in_beam = rho <= radius
    E = pyfftw.byte_align(special.jv(0, z)*in_beam/norm_fac)

    if gradr:
        gradrhoE = special.jvp(0, z, 1)*in_beam/norm_fac*zero/radius
        gradxE = pyfftw.byte_align(mathx.divide0(gradrhoE*x, rho))
        gradyE = pyfftw.byte_align(mathx.divide0(gradrhoE*y, rho))
        return E, (gradxE, gradyE)
    else:
        return E
Exemple #9
0
 def interpolate(self, rs_support, num_pointss, rs_center=None):
     assert self.zfun is not None
     appi = self.app.interpolate(rs_support, num_pointss, rs_center)
     z = self.zfun(appi.x, appi.y)
     # Propagate from interpolated planar profile to resampled z.
     Ir = appi.Ir
     propagator = mathx.expj(
         (z - appi.z) * mathx.divide0(appi.Igradphi[2], Ir))
     Er = appi.Er * propagator
     gradxyE = tuple(c * propagator for c in appi.gradxyE)
     return type(self)(self.lamb, self.n, self.zfun, appi.rs_support, Er,
                       gradxyE, appi.rs_center, appi.qs_center)
Exemple #10
0
 def calc_all(self, z, rho):
     z = np.asarray(z)
     rho = np.asarray(rho)
     w = self.waist(z)
     psi = self.Gouy(z)
     R = self.roc(z)
     absE = self.w_0 / w * np.exp(-(rho / w)**2) * self.absE_w
     z_c = mathx.divide0(rho**2, 2 * R)
     # try:
     #     z_c=rho**2/(2*R)
     # except ZeroDivisionError:
     #     z_c=0
     phi = self.k * z_c - psi  # leave k out, retarded reference frame
     return {'w': w, 'psi': psi, 'absE': absE, 'phi': phi, 'R': R}
Exemple #11
0
def test_pbg_simple():
    waist = 100e-6
    n = 1
    lamb = 800e-9
    k = 2*np.pi*n/lamb
    # Totally arbitrary origin and axes.
    origin = np.asarray([1, 2, 3, 1])
    vector = v4hb.normalize(np.asarray([1, -2, -1, 0]))
    axis_x = v4hb.normalize(np.asarray([1, 1, 0, 0]))

    mode, (axis_x, axis_y) = pbg.Mode.make(otk.rt1._lines.Line(origin, vector), axis_x, lamb / n, waist)

    z_R = waist**2*k/2
    z = np.asarray([-10, 1, 0, 1, 10])[:, None]*z_R*0
    x = np.arange(3)[:, None, None]*waist
    y = np.arange(3)[:, None, None, None]*waist
    #z = np.asarray([[z_R]])
    #x = np.asarray([[waist]])
    #y = np.asarray([[waist]])
    rho = (x**2 + y**2)**0.5
    xhat = np.asarray([1, 0, 0, 0])
    yhat = np.asarray([0, 1, 0, 0])
    rhohat = mathx.divide0(xhat*x + yhat*y, rho)
    mode_matrix = np.stack([axis_x, axis_y, vector, origin], 0)
    points = v4hb.concatenate_xyzw(x, y, z, 1).dot(mode_matrix)

    field, phi_axis, grad_phi = mode.calc_field(k, points, calc_grad_phi=True)

    beam = beams.FundamentalGaussian(lamb/n, w_0=waist, flux=1)
    field_true = beam.E(z, rho)*mathx.expj(z*k)
    field_true_axis = beam.E(z, 0)*mathx.expj(z*k)
    grad_phi_true = (beam.dphidz(z, rho) + k)*vector + beam.dphidrho(z, rho)*rhohat.dot(mode_matrix)

    testing.assert_allclose(abs(field), abs(field_true))
    testing.assert_allclose(field, field_true)

    testing.assert_allclose(abs(mathx.wrap_to_pm(phi_axis - np.angle(field_true_axis), np.pi)), 0, atol=1e-6)

    testing.assert_allclose(grad_phi, grad_phi_true, atol=1e-6)
Exemple #12
0
def calc_gaussian_1d(k, r, waist0, z=0, r0=0, q0=0, carrier=True, gradr=False):
    """Calculate 1D Gaussian beam profile.

    The result is normalized to unity amplitude at the waist (not on the axis).

    Args:
        k: Wavenumber.
        r: Sampling points.
        waist0: Beam waist.
        z: Axial position relative to waist.
        r0: Real-space offset of waist.
        q0: Wavenumber-space offset of waist.
        carrier (bool): Whether to include kz carrier phase.
        gradr (bool): Whether to return partial derivative w.r.t r of the field.

    Returns:
        Er, or gradrEr.
    """
    z_R = waist0**2*k/2
    waist = waist0*(1 + (z/z_R)**2)**0.5
    roc = z + mathx.divide0(z_R**2, z, np.inf)
    # if z == 0:
    #    roc = np.inf
    # else:
    #    roc = z+z_R**2/z
    r1 = r0 + q0*z/k
    # In 1D,  Gouy phase shift is halved. See
    # Physical origin of the Gouy phase shift,  Feng and Winful,  Optics Letters Vol. 27 No. 8 2001.
    phi = (r - r1)**2*k/(2*roc) + q0*(r - r1) - 0.5*np.arctan(z/z_R) + q0**2*z/(2*k)
    if carrier:
        phi += k*z
    Er = (waist0/waist)**0.5*np.exp(-((r - r1)/waist)**2 + 1j*phi)
    if gradr:
        gradrphi = (r - r1)*k/roc + q0
        result = Er*(-2*(r - r1)/waist**2 + 1j*gradrphi)
    else:
        result = Er
    return pyfftw.byte_align(result)
Exemple #13
0
    def refract(self, normal, n, scale_Er=1, polarizationxy=(1, 0)):
        """

        Args:
            normal (3-tuple of 2D arrays): X, y and z components of normal vector, each sampled at (self.x, self.y).
            n:
            scale_Er: Multiplicative factor - must broadcast with self.Er.
            polarizationxy: Transverse components of polarization of the refracted beam.

        Returns:

        """
        k = 2 * np.pi / self.lamb * n

        normal = [nc * np.sign(normal[2]) for nc in normal]
        Igradphi_tangent = matseq.project_onto_plane(self.Igradphi, normal)[0]
        Igradphi_normal = np.maximum(
            (self.Ir * k)**2 - matseq.dot(Igradphi_tangent), 0)**0.5
        Igradphi = [
            tc + nc * Igradphi_normal
            for tc, nc in zip(Igradphi_tangent, normal)
        ]

        gradphi = [mathx.divide0(c, self.Ir) for c in Igradphi[:2]]
        gradxE, gradyE = self.recalc_gradxyE(gradphi)

        # Mean transverse wavenumber is intensity-weighted average of transverse gradient of phase.
        qs_center = np.asarray([component.sum() for component in Igradphi[:2]
                                ]) / self.Ir.sum()

        profile = self.change(n=n,
                              gradxyE=(gradxE * scale_Er, gradyE * scale_Er),
                              qs_center=qs_center,
                              Er=self.Er * scale_Er,
                              polarizationxy=polarizationxy)
        for _ in range(3):
            profile = profile.center_q()
        return profile
Exemple #14
0
    def reflect(self, normal, n, scale_Er=1, polarizationxy=(1, 0)):
        """

        Args:
            normal:
            n:
            scale_Er:
            polarizationxy:

        Returns:

        """
        Igradphi_tangent, Igradphi_normal = mathx.project_onto_plane(
            self.Igradphi, normal)
        Igradphi = [
            tc - nc * Igradphi_normal
            for tc, nc in zip(Igradphi_tangent, normal)
        ]
        gradphi = [mathx.divide0(c, self.Ir) for c in Igradphi[:2]]
        gradxE, gradyE = self.recalc_gradxyE(gradphi)

        # Mean transverse wavenumber is intensity-weighted average of transverse gradient of phase.
        qs_center = np.asarray([component.sum()
                                for component in Igradphi[:2]]) / Ir.sum()

        # Don't understand. What is the new coordinate system? Surely should
        zfun = lambda x, y: 2 * self.zfun(x, y) - self.zfun(*self.rs_center)

        profile = self.change(n=n,
                              gradxyE=(gradxE * scale_Er, gradyE * scale_Er),
                              qs_center=qs_center,
                              Er=self.Er * scale_Er,
                              polarizationxy=polarizationxy,
                              zfun=zfun)
        for _ in range(3):
            profile = profile.center_q()
        return profile
Exemple #15
0
def calc_plane_to_curved_spherical_factors(k,
                                           rs_support,
                                           num_pointss,
                                           z,
                                           ms,
                                           ri_centers=(0, 0),
                                           qs_center=(0, 0),
                                           ro_centers=(0, 0),
                                           kz_mode='local_xy'):
    """Calculate factors for 2D Sziklas-Siegman propagation (paraxial) from flat to curved surface(s).

    The magnifications must be the same for all output points, but can be different for the x and y planes.

    The zero-oth order phase (k_z) is (arbitrarily) included in QinvTPx.

    Compared to regular Sziklas-Siegman procedure, the factors are complicated by a transformation that allows nonzero
    real and angular space centers.  See derivation page 114 Dane's logbook 2.

    Possible optimizations: could probably shave 50% off by more aggressive use of numba to avoid calculation of intermediates.
    But typically the time is spent using (tensor contract) rather than calculating the factors, so it won't bring
    a great speedup.

    Args:
        k (scalar): Wavenumber.
        rs_support (scalar or pair): Real space support.
        num_pointss (scalar int or pair): Number of sampling points.
        z (array of size num_pointss): Propagation for distance.
        ms (scalar or pair): Magnification(s).
        ri_centers (pair of scalars): Center of initial real-space aperture.
        qs_center (pair of scalars): Center of angular space aperture.
        ro_centers (pair of scalars): Center of final real-space aperture. If not given, the shift from ri_centers is
            inferred from the propagation distance and qs_center.
        kz_mode (str): 'paraxial' or 'local_xy'.

    Returns:
        QinvTPx (3D array): Product propagator, inverse DFT and quadratic output factor along x. Axes are (xo, yo, kx).
        gradxQinvTPx (3D array): Derivative of above w.r.t output x. Note that z is constant - the derivative is along
            a transverse plane rather than the surface.
        QinvTPy (3D array): Product propagator, inverse DFT and quadratic output factor along x. Axes are (xo, yo, kx).
        gradyQinvTPy (3D array): Derivative of above w.r.t output x. As with gradxQinvTPx, z is constant.
        Tx (2D array): DFT factor. Axes are (kx, xi).
        Ty (2D array): DFT factor. Axes are (ky, yi).
        Qix (3D array): Input x quadratic phase factor vs x. Axes are (xo, yo, xi).
        Qiy (3D array): Input y quadratic phase factor. Axes are (xo, yo, yi).
    """
    assert kz_mode in ('local_xy', 'paraxial')
    num_pointss = sa.to_scalar_pair(num_pointss)
    ms = sa.to_scalar_pair(ms)
    if np.isclose(ms, 1).any():
        logger.debug('At least one magnification (%g, %g) close to 1.', ms)
    qs_center = sa.to_scalar_pair(qs_center)
    # TODO (maybe) factor out adjust_r.
    if kz_mode == 'paraxial':
        kz_center = k
    else:
        kz_center = (k**2 - (qs_center**2).sum())**0.5
    z_center = np.mean(z)
    if ro_centers is None:
        ro_centers = ri_centers + qs_center / kz_center * z_center

    # If local_xy mode is requested then propagation distances must be scaled. Calculate scaled propagation distances
    # and required kz component.
    if kz_mode == 'paraxial':
        zx = z
        zy = z
        delta_kz = k
    else:
        fx, fy, delta_kz, delta_gxkz, delta_gykz = calc_quadratic_kz_correction(
            k, *qs_center)
        zx = fx * z
        zy = fy * z

    ro_supports = rs_support * ms
    xi, yi = sa.calc_xy(rs_support, num_pointss, ri_centers)
    kxi, kyi = sa.calc_kxky(rs_support, num_pointss,
                            qs_center)  # +q_curvatures)
    roc_x = mathx.divide0(zx, ms[0] - 1, np.inf)
    roc_y = mathx.divide0(zy, ms[1] - 1, np.inf)
    # Quadratic phase is centered at origin. Numerically, this requires evaluation of complex exponentials of size N^3.
    # Significant fraction of total cost of this function, but unavoidable.
    Qix = calc_quadratic_phase_1d(-k, xi[:, 0] - ri_centers[0], roc_x[:, :,
                                                                      None])
    Qiy = calc_quadratic_phase_1d(-k, yi - ri_centers[1], roc_y[:, :, None])

    Tx = make_fft_matrix(num_pointss[0])
    if num_pointss[1] == num_pointss[0]:
        Ty = Tx
    else:
        Ty = make_fft_matrix(num_pointss[1])

    if 0:
        # This was an experiment - we calculate where a given pencil beam lands based on initial position and
        # momentum. It didn't improve the stability of the algorithm. In a ray picture, clip factor should be 0.5. But
        # I found that anything below 3 gave unacceptable artefacts.
        clip_factor = 3
        mean_rocs = z_centers / (ms - 1)
        xo_kx_xi = xi[:, 0] * ms[0] + (
            kxi - k * ri_centers[0] / mean_rocs[0]) * z_centers[0] / k
        yo_ky_yi = yi * ms[1] + (
            kyi[:, None] - k * ri_centers[1] / mean_rocs[1]) * z_centers[1] / k
        in_xo = abs(xo_kx_xi - ro_centers[0]) < ro_supports[0] * clip_factor
        in_yo = abs(yo_ky_yi - ro_centers[1]) < ro_supports[1] * clip_factor
        Txp = Tx * in_xo
        Typ = Ty * in_yo

    xo, yo = sa.calc_xy(ro_supports, num_pointss, ro_centers)
    roc_xo = roc_x + zx
    roc_yo = roc_y + zy

    # If in local_xy mode, then the factors which depend on xo and yo use transformed quantities, which we subsitute here.
    if kz_mode == 'local_xy':
        xo = xo + delta_gxkz * z
        yo = yo + delta_gykz * z

    # Effective kx and ky for propagation takes into account center of curvature.
    kxip = kxi[:, 0] - k * ri_centers[0] / roc_x[:, :, None]
    kyip = kyi - k * ri_centers[1] / roc_y[:, :, None]

    # Calculate product of propagator and inverse transform along x axis. The x axis (arbitrarily) includes delta_kz.
    # Could combine all exponents before exponentiation? Won't help much because the time will be dominated by the propagator
    # which is N^3 - the others are N^2.
    phi = delta_kz * z + k * (ri_centers[0]**2 /
                              (2 * roc_x) - ri_centers[0] * xo /
                              (roc_x * ms[0]))
    QinvTPx = (
        calc_propagator_quadratic_1d(k * ms[0], kxip, zx[:, :, None])
        *  # Propagation phase scaled by magnification.
        calc_quadratic_phase_1d(k, xo, roc_xo)[:, :, None]
        *  # Normal Sziklas-Siegman final quadratic phase.
        mathx.expj(phi[:, :, None]) * Tx.conj()[:, None, :] /  # Inverse DFT.
        abs(ms[0])**0.5)  # Magnification correction to amplitude.

    # Calculate product of propagator and inverse transform along x axis. Result depends on
    phi = k * (ri_centers[1]**2 / (2 * roc_y) - ri_centers[1] * yo /
               (roc_y * ms[1]))
    QinvTPy = (
        calc_propagator_quadratic_1d(k * ms[1], kyip, zy[:, :, None])
        *  # Propagation phase scaled by magnification.
        calc_quadratic_phase_1d(k, yo, roc_yo)[:, :, None]
        *  # Normal Sziklas-Siegman final quadratic phase.
        mathx.expj(phi[:, :, None]) * Ty.conj() /  # Inverse DFT.
        abs(ms[1])**0.5)  # Magnification correction to amplitude.

    # If local_xy mode, need translation correction factor. Could combine this with above to reduce number of N^3 expj
    # calls.
    if kz_mode == 'local_xy':
        QinvTPx *= mathx.expj(delta_gxkz * kxi[:, 0] / ms[0] * z[:, :, None])
        QinvTPy *= mathx.expj(delta_gykz * kyi / ms[1] * z[:, :, None])

    # Evaluate derivatives of the invTP factors with respect to output x and y (but constant z - not along the curved
    # surface).
    gradxQinvTPx = 1j * (k * (xo / roc_xo)[:, :, None] - ri_centers[0] * k /
                         (roc_x[:, :, None] * ms[0]) +
                         kxi[:, 0] / ms[0]) * QinvTPx
    gradyQinvTPy = 1j * (k * (yo / roc_yo)[:, :, None] - ri_centers[1] * k /
                         (roc_y[:, :, None] * ms[1]) + kyi / ms[1]) * QinvTPy

    # Won't use z gradients for now - will keep for future.
    # gradzPx=1j*calc_kz_paraxial_1d(k*ms[0], kxip)*Px

    factors = QinvTPx, gradxQinvTPx, QinvTPy, gradyQinvTPy, Tx, Ty, Qix, Qiy
    return factors
Exemple #16
0
 def roc(self, z):
     z = np.asarray(z)
     return z * (1 + mathx.divide0(self.z_R, z)**2)
Exemple #17
0
def line_two_points(x1, y1, x2, y2, x):
    return mathx.divide0(y1*(x2 - x) + y2*(x - x1), x2 - x1, float('nan'))
Exemple #18
0
def calc_beam_properties(rs_support, z, Er, Igradphi, rs_center=(0, 0)):
    """Calculate real space and angular centroids, and curvature.

    Assumes that propagation is to +z. Positive ROC means diverging i.e. center of curvature is to left.

    Args:
        k:
        x:
        y:
        Er:
        Igradphi (tuple): I times gradient of phase along the x, y and z.
        rs_center:
        Ir:

    Returns:

    """
    x, y = sa.calc_xy(rs_support, Er.shape, rs_center)
    Ir = mathx.abs_sqd(Er)
    sumIr = Ir.sum()

    # Calculate centroid. Improve the input estimate rs_center.
    rs_center[0], rs_center[1], varx, vary, _ = mathx.mean_and_variance2(
        x, y, Ir, sumIr)

    # Mean transverse wavenumber is intensity-weighted average of transverse gradient of phase.
    qs_center = np.asarray([component.sum()
                            for component in Igradphi[:2]]) / sumIr

    meanz = mathx.moment(z, Ir, 1, sumIr)

    # Correct spatial phase for surface curvature - approximate propagation from z to meanz.
    Er = Er * mathx.expj((meanz - z) * mathx.divide0(Igradphi[2], Ir))

    # Do this twice, since on the first pass we are using qs_center from Igradphi which is just an estimate.
    for _ in range(2):
        # Calculate phase of quadratic component at RMS distance from center. Proportional to A in Siegman IEE J. Quantum Electronics Vol. 27
        # 1991. Positive means diverging.
        phi_cx = 0.5 * ((x - rs_center[0]) *
                        (Igradphi[0] - Ir * qs_center[0])).sum() / sumIr
        phi_cy = 0.5 * ((y - rs_center[1]) *
                        (Igradphi[1] - Ir * qs_center[1])).sum() / sumIr

        # Fourier transform Er with quadratic phase removed.
        Ek = math.fft2(Er *
                       mathx.expj(-(x - rs_center[0])**2 * phi_cx / varx) *
                       mathx.expj(-(y - rs_center[1])**2 * phi_cy / vary))
        kx, ky = sa.calc_kxky(rs_support, Er.shape, qs_center)

        # Calculate mean square sizes of Fourier transform with quadratic phase removed.
        #qs_center[0], qs_center[1], varkxp, varkyp, _ = mathx.mean_and_variance2(kx, ky, mathx.abs_sqd(Ek))
        qs_center[0] = mathx.moment(kx, abs(Ek), 1)
        qs_center[1] = mathx.moment(ky, abs(Ek), 1)

        Ik = mathx.abs_sqd(Ek)
        sumIk = Ik.sum()
        varkxp = mathx.moment(kx - qs_center[0], Ik, 2, sumIk)
        varkyp = mathx.moment(ky - qs_center[1], Ik, 2, sumIk)

        # Calculate angular variance.
        varkx = bvar.infer_angular_variance_spherical(varx, phi_cx, varkxp)
        varky = bvar.infer_angular_variance_spherical(vary, phi_cy, varkyp)

    return meanz, rs_center, np.asarray((varx, vary)), qs_center, np.asarray(
        (varkx, varky)), np.asarray((phi_cx, phi_cy))
Exemple #19
0
    def __init__(self,
                 lamb: float,
                 n: float,
                 z_center: float,
                 rs_support,
                 Er,
                 gradxyE,
                 rs_center=(0, 0),
                 qs_center=(0, 0),
                 polarizationxy=(1, 0)):
        self.lamb = float(lamb)
        self.n = float(n)
        self.z_center = float(z_center)
        rs_support = sa.to_scalar_pair(rs_support)
        assert (rs_support > 0).all()
        self.rs_support = rs_support
        Er = np.asarray(Er).astype(complex)
        self.Er = Er
        assert not np.isnan(Er).any()
        assert len(gradxyE) == 2
        self.gradxyE = gradxyE
        self.rs_center = sa.to_scalar_pair(rs_center)
        self.qs_center = sa.to_scalar_pair(qs_center)

        self.Eq = math.fft2(Er)
        self.k = 2 * np.pi * self.n / self.lamb

        self.Ir = mathx.abs_sqd(Er)
        sumIr = self.Ir.sum()
        if np.isclose(sumIr, 0):
            raise NullProfileError()
        self.Igradphi = fsq.calc_Igradphi(self.k, Er, gradxyE, self.Ir)
        x, y = sa.calc_xy(rs_support, Er.shape, rs_center)
        self.x = x
        self.y = y
        self.r_center_indices = abs(x - rs_center[0]).argmin(), abs(
            y - rs_center[1]).argmin()
        self.delta_x, self.delta_y = self.rs_support / Er.shape
        self.power = sumIr * self.delta_x * self.delta_y
        mean_x = mathx.moment(x, self.Ir, 1, sumIr)
        mean_y = mathx.moment(y, self.Ir, 1, sumIr)
        self.centroid_rs = mean_x, mean_y
        var_x = mathx.moment(x - rs_center[0], self.Ir, 2, sumIr)
        var_y = mathx.moment(y - rs_center[1], self.Ir, 2, sumIr)
        self.var_rs = np.asarray((var_x, var_y))

        # Calculate phase of quadratic component at RMS distance from center. Proportional to A in Siegman IEE J. Quantum Electronics Vol. 27
        # 1991. Positive means diverging.
        phi_cx = 0.5 * (
            (x - rs_center[0]) *
            (self.Igradphi[0] - self.Ir * qs_center[0])).sum() / sumIr
        phi_cy = 0.5 * (
            (y - rs_center[1]) *
            (self.Igradphi[1] - self.Ir * qs_center[1])).sum() / sumIr
        self.phi_cs = np.asarray((phi_cx, phi_cy))
        self.rocs = mathx.divide0(self.k * self.var_rs, 2 * self.phi_cs,
                                  np.inf)

        xi, yi = np.unravel_index(self.Ir.argmax(), self.Ir.shape)
        self.peak_indices = xi, yi
        self.peak_Er = self.Er[xi, yi]
        self.peak_rs = np.asarray((self.x[xi], self.y[yi]))
        self.peak_qs = np.asarray([
            mathx.divide0((gradxE[xi, yi] * Er[xi, yi].conj()).imag,
                          self.Ir[xi, yi]) for gradxE in gradxyE
        ])

        self.kz_center = math.calc_kz(self.k, *self.qs_center)

        # Calculate 3D vectors.
        vector_center = v4hb.normalize(
            v4hb.stack_xyzw(*self.qs_center, self.kz_center, 0))
        polarizationz = -(polarizationxy *
                          vector_center[:2]).sum() / vector_center[2]
        origin_center = v4hb.stack_xyzw(*self.rs_center, self.z_center, 1)
        polarization = polarizationxy[0], polarizationxy[1], polarizationz, 0
        y = v4hb.cross(vector_center, polarization)
        self.frame = np.c_[polarization, y, vector_center, origin_center].T