예제 #1
0
파일: __init__.py 프로젝트: 10220/scri
    def speciality_index(self, **kwargs):
        """Computes the Baker-Campanelli speciality index (arXiv:gr-qc/0003031). NOTE: This quantity can only 
        determine algebraic speciality but can not determine the type! The rule of thumb given by Baker and
        Campanelli is that for an algebraically special spacetime the speciality index should differ from unity 
        by no more than a factor of two.

        """

        import spinsfast
        import spherical_functions as sf
        from spherical_functions import LM_index

        output_ell_max = kwargs.pop("output_ell_max") if "output_ell_max" in kwargs else self.ell_max
        working_ell_max = kwargs.pop("working_ell_max") if "working_ell_max" in kwargs else 2 * self.ell_max
        n_theta = n_phi = 2 * working_ell_max + 1

        # Transform to grid representation
        psi4 = np.empty((self.n_times, n_theta, n_phi), dtype=complex)
        psi3 = np.empty((self.n_times, n_theta, n_phi), dtype=complex)
        psi2 = np.empty((self.n_times, n_theta, n_phi), dtype=complex)
        psi1 = np.empty((self.n_times, n_theta, n_phi), dtype=complex)
        psi0 = np.empty((self.n_times, n_theta, n_phi), dtype=complex)

        for t_i in range(self.n_times):
            psi4[t_i, :, :] = spinsfast.salm2map(
                self.psi4.ndarray[t_i, :], self.psi4.spin_weight, lmax=self.ell_max, Ntheta=n_theta, Nphi=n_phi
            )
            psi3[t_i, :, :] = spinsfast.salm2map(
                self.psi3.ndarray[t_i, :], self.psi3.spin_weight, lmax=self.ell_max, Ntheta=n_theta, Nphi=n_phi
            )
            psi2[t_i, :, :] = spinsfast.salm2map(
                self.psi2.ndarray[t_i, :], self.psi2.spin_weight, lmax=self.ell_max, Ntheta=n_theta, Nphi=n_phi
            )
            psi1[t_i, :, :] = spinsfast.salm2map(
                self.psi1.ndarray[t_i, :], self.psi1.spin_weight, lmax=self.ell_max, Ntheta=n_theta, Nphi=n_phi
            )
            psi0[t_i, :, :] = spinsfast.salm2map(
                self.psi0.ndarray[t_i, :], self.psi0.spin_weight, lmax=self.ell_max, Ntheta=n_theta, Nphi=n_phi
            )

        curvature_invariant_I = psi4 * psi0 - 4 * psi3 * psi1 + 3 * psi2 ** 2
        curvature_invariant_J = (
            psi4 * (psi2 * psi0 - psi1 ** 2) - psi3 * (psi3 * psi0 - psi1 * psi2) + psi2 * (psi3 * psi1 - psi2 ** 2)
        )
        speciality_index = 27 * curvature_invariant_J ** 2 / curvature_invariant_I ** 3

        # Transform back to mode representation
        speciality_index_modes = np.empty((self.n_times, (working_ell_max) ** 2), dtype=complex)
        for t_i in range(self.n_times):
            speciality_index_modes[t_i, :] = spinsfast.map2salm(speciality_index[t_i, :], 0, lmax=working_ell_max - 1)

        # Convert product ndarray to a ModesTimeSeries object
        speciality_index_modes = speciality_index_modes[:, : LM_index(output_ell_max, output_ell_max, 0) + 1]
        speciality_index_modes = ModesTimeSeries(
            sf.SWSH_modes.Modes(
                speciality_index_modes, spin_weight=0, ell_min=0, ell_max=output_ell_max, multiplication_truncator=max
            ),
            time=self.t,
        )
        return speciality_index_modes
예제 #2
0
def silly_angular_momentum_flux(h, hdot=None):
    """Compute angular momentum flux from waveform explicitly

    This function uses very explicit (and slow) methods, but different as far as possible from the
    methods used in the main code.

    """
    import spinsfast
    hdot = hdot or h.data_dot
    zeros = np.zeros((hdot.shape[0], 4), dtype=hdot.dtype)
    data = np.concatenate((zeros, hdot), axis=1)  # Pad with zeros for spinsfast
    ell_min = 0
    ell_max = 2*h.ell_max  # Maximum ell value required to fully resolve Ji{h} * hdot
    n_theta = 2*ell_max + 1
    n_phi = n_theta
    hdot_map = spinsfast.salm2map(data, h.spin_weight, h.ell_max, n_theta, n_phi)

    jdot = np.zeros((h.n_times, 3), dtype=float)

    # Compute J_+ h
    for ell in range(h.ell_min, h.ell_max+1):
        i = h.index(ell, -ell) + 4
        data[:, i] = 0.0j
        for m in range(-ell, ell):
            i = h.index(ell, m+1) + 4
            j = h.index(ell, m)
            data[:, i] = 1.j * np.sqrt((ell-m)*(ell+m+1)) * h.data[:, j]
    jplus_h_map = spinsfast.salm2map(data, h.spin_weight, h.ell_max, n_theta, n_phi)

    # Compute J_- h
    for ell in range(h.ell_min, h.ell_max+1):
        for m in range(-ell+1, ell+1):
            i = h.index(ell, m-1) + 4
            j = h.index(ell, m)
            data[:, i] = 1.j * np.sqrt((ell+m)*(ell-m+1)) * h.data[:, j]
        i = h.index(ell, ell) + 4
        data[:, i] = 0.0j
    jminus_h_map = spinsfast.salm2map(data, h.spin_weight, h.ell_max, n_theta, n_phi)

    # Combine jplus and jminus to compute x-y components, then conjugate, multiply by hdot, and integrate
    jx_h_map = 0.5 * (jplus_h_map + jminus_h_map)
    jy_h_map = -0.5j * (jplus_h_map - jminus_h_map)
    jdot[:, 0] = -spinsfast.map2salm(jx_h_map.conjugate() * hdot_map, 0, 0)[..., 0].real * (2*np.sqrt(np.pi)) / (16*np.pi)
    jdot[:, 1] = -spinsfast.map2salm(jy_h_map.conjugate() * hdot_map, 0, 0)[..., 0].real * (2*np.sqrt(np.pi)) / (16*np.pi)

    # Compute J_z h, then conjugate, multiply by hdot, and integrate
    for ell in range(h.ell_min, h.ell_max+1):
        for m in range(-ell, ell+1):
            i = h.index(ell, m)
            data[:, i+4] = 1.j * m * h.data[:, i]
    jz_h_map = spinsfast.salm2map(data, h.spin_weight, h.ell_max, n_theta, n_phi)
    jdot[:, 2] = -spinsfast.map2salm(jz_h_map.conjugate() * hdot_map, 0, 0)[..., 0].real * (2*np.sqrt(np.pi)) / (16*np.pi)

    return jdot
예제 #3
0
def test_eth_derivation(eth, spin_weight_of_eth):
    """Ensure that the various `eth` operators are derivations -- i.e., they obey the Leibniz product law

    Given two spin-weighted functions `f` and `g`, we need to test that

        eth(f * g) = eth(f) * g + f * eth(g)

    This test generates a set of random modes with equal power for `f` and `g`
    (though more realistic functions can be expected to have exponentially decaying
    mode amplitudes).  Because of the large power in high-ell modes, we need to
    double the number of modes in the representation of their product, which is why
    we use

        n_theta = n_phi = 4 * ell_max + 1

    These `f` and `g` functions must be transformed to the physical-space
    representation, multiplied there, the product transformed back to spectral
    space, the eth operator evaluated, and then transformed back again to physical
    space for comparison.

    We test both the Newman-Penrose and Geroch-Held-Penrose versions of eth, as
    well as their conjugates.

    """
    import spinsfast
    ell_max = 16
    n_modes = sf.LM_total_size(0, ell_max)
    n_theta = n_phi = 4 * ell_max + 1
    for s1 in range(-2, 2 + 1):
        for s2 in range(-s1, s1 + 1):
            np.random.seed(1234)
            ell_min1 = abs(s1)
            ell_min2 = abs(s2)
            f = np.random.rand(n_modes) + 1j * np.random.rand(n_modes)
            f[:sf.LM_total_size(0, ell_min1 - 1)] = 0j
            f_j_k = spinsfast.salm2map(f, s1, ell_max, n_theta, n_phi)
            g = np.random.rand(n_modes) + 1j * np.random.rand(n_modes)
            g[:sf.LM_total_size(0, ell_min2 - 1)] = 0j
            g_j_k = spinsfast.salm2map(g, s2, ell_max, n_theta, n_phi)
            fg_j_k = f_j_k * g_j_k
            fg = spinsfast.map2salm(fg_j_k, s1 + s2, 2 * ell_max)
            ethf = eth(f, s1, ell_min=0)
            ethg = eth(g, s2, ell_min=0)
            ethfg = eth(fg, s1 + s2, ell_min=0)
            ethf_j_k = spinsfast.salm2map(ethf, s1 + spin_weight_of_eth,
                                          ell_max, n_theta, n_phi)
            ethg_j_k = spinsfast.salm2map(ethg, s2 + spin_weight_of_eth,
                                          ell_max, n_theta, n_phi)
            ethfg_j_k = spinsfast.salm2map(ethfg, s1 + s2 + spin_weight_of_eth,
                                           2 * ell_max, n_theta, n_phi)
            assert np.allclose(ethfg_j_k,
                               ethf_j_k * g_j_k + f_j_k * ethg_j_k,
                               rtol=1e-10,
                               atol=1e-10)
예제 #4
0
def backward(salm, Ntheta, Nphi):
    """
    Returns a function on S^2 given it's spin coefficients in the form of an
    salm object.
    
    Parameters
    ----------
    salm : swsh.salm.salm
        a subclass of numpy.ndarray that stores the spin coefficients
    Ntheta : int
        an int giving the number of points discritising the theta variable of S^2
    Nphi : an int giving the number of points discritising the phi variable of S^2
    
    Returns
    -------
    numpy.ndarray : 
         a numpy.ndarray of shape (salm.spins.shape[[0], Ntheta, Nphi) 
         containing the values of the
         function on S^2, parameterised via the ecp discretisation, see
         section 2.3
    """
    spins = np.atleast_1d(salm.spins)
    Ntransform = spins.shape[0]
    lmax = salm.lmax
    if len(spins.shape) != 1:
        raise ValueError('spins must be an int or a one dimensional array of ints')
    data = np.asarray(salm)

    f = np.array([
        spinsfast.salm2map(data[i], spins[i], lmax, Ntheta, Nphi)
        for i in range(Ntransform)
    ])
    return f
def test_eth_derivation(eth, spin_weight_of_eth):
    """Ensure that the various `eth` operators are derivations -- i.e., they obey the Leibniz product law

    Given two spin-weighted functions `f` and `g`, we need to test that

        eth(f * g) = eth(f) * g + f * eth(g)

    This test generates a set of random modes with equal power for `f` and `g` (though more realistic functions can
    be expected to have exponentially decaying mode amplitudes).  Because of the large power in high-ell modes,
    we need to double the number of modes in the representation of their product, which is why we use

        n_theta = n_phi = 4 * ell_max + 1

    These `f` and `g` functions must be transformed to the physical-space representation, multiplied there,
    the product transformed back to spectral space, the eth operator evaluated, and then transformed back again to
    physical space for comparison.

    We test both the Newman-Penrose and Geroch-Held-Penrose versions of eth, as well as their conjugates.

    """
    import spinsfast
    ell_max = 16
    n_modes = sf.LM_total_size(0, ell_max)
    n_theta = n_phi = 4 * ell_max + 1
    for s1 in range(-2, 2 + 1):
        for s2 in range(-s1, s1 + 1):
            np.random.seed(1234)
            ell_min1 = abs(s1)
            ell_min2 = abs(s2)
            f = np.random.rand(n_modes) + 1j * np.random.rand(n_modes)
            f[:sf.LM_total_size(0, ell_min1-1)] = 0j
            f_j_k = spinsfast.salm2map(f, s1, ell_max, n_theta, n_phi)
            g = np.random.rand(n_modes) + 1j * np.random.rand(n_modes)
            g[:sf.LM_total_size(0, ell_min2-1)] = 0j
            g_j_k = spinsfast.salm2map(g, s2, ell_max, n_theta, n_phi)
            fg_j_k = f_j_k * g_j_k
            fg = spinsfast.map2salm(fg_j_k, s1+s2, 2*ell_max)
            ethf = eth(f, s1, ell_min=0)
            ethg = eth(g, s2, ell_min=0)
            ethfg = eth(fg, s1+s2, ell_min=0)
            ethf_j_k = spinsfast.salm2map(ethf, s1+spin_weight_of_eth, ell_max, n_theta, n_phi)
            ethg_j_k = spinsfast.salm2map(ethg, s2+spin_weight_of_eth, ell_max, n_theta, n_phi)
            ethfg_j_k = spinsfast.salm2map(ethfg, s1+s2+spin_weight_of_eth, 2*ell_max, n_theta, n_phi)
            assert np.allclose(ethfg_j_k, ethf_j_k * g_j_k + f_j_k * ethg_j_k, rtol=1e-10, atol=1e-10)
예제 #6
0
def test_supertranslation_inverses():
    w1 = samples.random_waveform_proportional_to_time(rotating=False)
    ell_max = 4
    for ellpp, mpp in sf.LM_range(0, ell_max):
        supertranslation = np.zeros((sf.LM_total_size(0, ell_max), ),
                                    dtype=complex)
        if mpp == 0:
            supertranslation[sf.LM_index(ellpp, mpp, 0)] = 1.0
        elif mpp < 0:
            supertranslation[sf.LM_index(ellpp, mpp, 0)] = 1.0
            supertranslation[sf.LM_index(ellpp, -mpp, 0)] = (-1.0)**mpp
        elif mpp > 0:
            supertranslation[sf.LM_index(ellpp, mpp, 0)] = 1.0j
            supertranslation[sf.LM_index(ellpp, -mpp, 0)] = (-1.0)**mpp * -1.0j
        max_displacement = abs(
            spinsfast.salm2map(supertranslation, 0, ell_max, 4 * ell_max + 1,
                               4 * ell_max + 1)).max()
        w2 = copy.deepcopy(w1)
        w2 = w2.transform(supertranslation=supertranslation)
        w2 = w2.transform(supertranslation=-supertranslation)

        i1A = np.argmin(abs(w1.t - (w1.t[0] + 3 * max_displacement)))
        i1B = np.argmin(abs(w1.t - (w1.t[-1] - 3 * max_displacement)))
        i2A = np.argmin(abs(w2.t - w1.t[i1A]))
        i2B = np.argmin(abs(w2.t - w1.t[i1B]))
        try:
            assert np.allclose(w1.t[i1A:i1B + 1],
                               w2.t[i2A:i2B + 1],
                               rtol=0.0,
                               atol=1e-15), (
                                   w1.t[i1A],
                                   w2.t[i2A],
                                   w1.t[i1B],
                                   w2.t[i2B],
                                   w1.t[i1A:i1B + 1].shape,
                                   w2.t[i2A:i2B + 1].shape,
                               )
        except ValueError:
            print("Indices:\n\t", i1A, i1B, i2A, i2B)
            print("Times:\n\t", w1.t[i1A], w1.t[i1B], w2.t[i2A], w2.t[i2B])
            raise
        data1 = w1.data[i1A:i1B + 1]
        data2 = w2.data[i2A:i2B + 1]
        try:
            assert np.allclose(data1, data2, rtol=5e-10, atol=5e-14), [
                abs(data1 - data2).max(),
                data1.ravel()[np.argmax(abs(data1 - data2))],
                data2.ravel()[np.argmax(abs(data1 - data2))],
                np.unravel_index(np.argmax(abs(data1 - data2)), data1.shape),
            ]
            # list(sf.LM_range(0, ell_max)[np.unravel_index(np.argmax(abs(data1-data2)),
            #                                               data1.shape)[1]])])
        except:
            print("Indices:\n\t", i1A, i1B, i2A, i2B)
            print("Times:\n\t", w1.t[i1A], w1.t[i1B], w2.t[i2A], w2.t[i2B])
            raise
예제 #7
0
def grid(self, n_theta=None, n_phi=None, use_spinsfast=True, **kwargs):
    """Return values of function on an equi-angular grid

    This method converts mode weights of spin-weighted function to values on a
    grid.  The grid has `n_theta` evenly spaced points along the usual polar
    (colatitude) angle theta, and `n_phi` evenly spaced points along the usual
    azimuthal angle phi.  This grid corresponds to the one produced by
    `spherical.theta_phi`; see that function for specifics.

    The output array has one more dimension than this object; rather than the last
    axis giving the mode weights, the last two axes give the values on the
    two-dimensional grid.

    Parameters
    ----------
    n_theta : {None, int}, optional
        Number of points to use in theta direction.  None is equivalent to
        2*self.ell_max+1, which is the minimum number that can capture behavior up
        to and including ell_max.  If you need to multiply the result with some
        `other` spin-weighted function, you should use an n_theta value of 2 *
        (self.ell_max + other.ell_max) + 1 to avoid aliasing.
    n_phi : {None, int}, optional
        Number of points to use in the phi direction.  Here, None is equivalent to
        n_phi=n_theta, after calculation of the default value for n_theta.  Note
        that the same comments apply about avoiding aliasing.
    use_spinsfast : bool, optional
        If True, and `spinsfast` is accessible, use it to evaluate the function
        values; otherwise, use this module's `Wigner.evaluate` method.
    **kwargs : Any
        Additional keyword arguments are passed through to the Grid constructor on
        output

    """
    import copy
    import numpy as np
    import quaternionic
    from .. import Grid, theta_phi
    n_theta = n_theta or 2*self.ell_max+1
    n_phi = n_phi or n_theta
    metadata = copy.copy(self._metadata)
    metadata.pop('ell_max', None)
    metadata.update(**kwargs)
    try:
        import spinsfast
    except ImportError:
        use_spinsfast = False
    if use_spinsfast:
        return Grid(
            spinsfast.salm2map(self.view(np.ndarray), self.spin_weight, self.ell_max, n_theta, n_phi),
            **metadata
        )
    else:
        return Grid(
            self.evaluate(quaternionic.array.from_spherical_coordinates(theta_phi(n_theta, n_phi))),
            **metadata
        )
예제 #8
0
    def spinsfast_multiply(f, ellmin_f, ellmax_f, s_f, g, ellmin_g, ellmax_g, s_g):
        """Multiply functions pointwise

        This function takes the same arguments as the spherical_functions.multiply function and
        returns the same quantities.  However, this performs the multiplication simply by
        transforming the modes to function values on a (theta, phi) grid, multiplying those values
        at each point, and then transforming back to modes.

        """
        s_fg = s_f + s_g
        ellmin_fg = 0
        ellmax_fg = ellmax_f + ellmax_g
        n_theta = 2*ellmax_fg + 1
        n_phi = n_theta
        f_map = spinsfast.salm2map(f, s_f, ellmax_f, n_theta, n_phi)
        g_map = spinsfast.salm2map(g, s_g, ellmax_g, n_theta, n_phi)
        fg_map = f_map * g_map
        fg = spinsfast.map2salm(fg_map, s_fg, ellmax_fg)
        return fg, ellmin_fg, ellmax_fg, s_fg
예제 #9
0
파일: test_flux.py 프로젝트: hrueter/scri
def silly_momentum_flux(h):
    """Compute momentum flux from waveform with a silly but simple method

    This function evaluates the momentum-flux formula quite literally.  The formula is

         dp       R**2  | dh |**2 ^
    ---------- = ------ | -- |    n
    dOmega  dt   16  pi | dt |

    Here, p and nhat are vectors and R is the distance to the source.  The input h is differentiated
    numerically to find modes of dh/dt, which are then used to construct the values of dh/dt on a
    grid.  At each point of that grid, the value of |dh/dt|**2 nhat is computed, which is then
    integrated over the sphere to arrive at dp/dt.  Note that this integration is accomplished by
    way of a spherical-harmonic decomposition; the ell=m=0 mode is multiplied by 2*sqrt(pi) to
    arrive at the result that would be achieved by integrating over the sphere.

    """
    import spinsfast

    hdot = h.data_dot
    zeros = np.zeros((hdot.shape[0], 4), dtype=hdot.dtype)
    data = np.concatenate((zeros, hdot),
                          axis=1)  # Pad with zeros for spinsfast
    ell_min = 0
    ell_max = 2 * h.ell_max + 1  # Maximum ell value required for nhat*|hdot|^2
    n_theta = 2 * ell_max + 1
    n_phi = n_theta
    hdot_map = spinsfast.salm2map(data, h.spin_weight, h.ell_max, n_theta,
                                  n_phi)
    hdot_mag_squared_map = hdot_map * hdot_map.conjugate()

    theta = np.linspace(0.0, np.pi, num=n_theta, endpoint=True)
    phi = np.linspace(0.0, 2 * np.pi, num=n_phi, endpoint=False)
    x = np.outer(np.sin(theta), np.cos(phi))
    y = np.outer(np.sin(theta), np.sin(phi))
    z = np.outer(np.cos(theta), np.ones_like(phi))

    pdot = np.array([
        spinsfast.map2salm(hdot_mag_squared_map * n /
                           (16 * np.pi), 0, 0)[..., 0].real *
        (2 * np.sqrt(np.pi)) for n in [x, y, z]
    ]).T

    return pdot
예제 #10
0
def test_hyper_translation():
    """Compare code-transformed waveform to analytically transformed waveform"""
    print("")
    ell_max = 4
    for s in range(-2, 2+1):
        for ell in range(abs(s), ell_max+1):
            for m in range(-ell, ell+1):
                print("\tWorking on spin s =", s, ", ell =", ell, ", m =", m)
                for ellpp, mpp in sf.LM_range(2, ell_max):
                    supertranslation = np.zeros((sf.LM_total_size(0, ell_max),), dtype=complex)
                    if mpp == 0:
                        supertranslation[sf.LM_index(ellpp, mpp, 0)] = 1.0
                    elif mpp < 0:
                        supertranslation[sf.LM_index(ellpp, mpp, 0)] = 1.0
                        supertranslation[sf.LM_index(ellpp, -mpp, 0)] = (-1.0)**mpp
                    elif mpp > 0:
                        supertranslation[sf.LM_index(ellpp, mpp, 0)] = 1.0j
                        supertranslation[sf.LM_index(ellpp, -mpp, 0)] = (-1.0)**mpp * -1.0j
                    max_displacement = abs(spinsfast.salm2map(supertranslation, 0,
                                                              ell_max, 4*ell_max+1, 4*ell_max+1)).max()
                    w_m1 = (samples.single_mode_proportional_to_time(s=s, ell=ell, m=m)
                            .transform(supertranslation=supertranslation))
                    w_m2 = samples.single_mode_proportional_to_time_supertranslated(s=s, ell=ell, m=m,
                                                                                    supertranslation=supertranslation)
                    i1A = np.argmin(abs(w_m1.t-(w_m1.t[0]+2*max_displacement)))
                    i1B = np.argmin(abs(w_m1.t-(w_m1.t[-1]-2*max_displacement)))
                    i2A = np.argmin(abs(w_m2.t-w_m1.t[i1A]))
                    i2B = np.argmin(abs(w_m2.t-w_m1.t[i1B]))
                    assert np.allclose(w_m1.t[i1A:i1B+1], w_m2.t[i2A:i2B+1], rtol=0.0, atol=1e-16), \
                        (w_m1.t[i1A], w_m2.t[i2A], w_m1.t[i1B], w_m2.t[i2B],
                         w_m1.t[i1A:i1B+1].shape, w_m2.t[i2A:i2B+1].shape)
                    data1 = w_m1.data[i1A:i1B+1]
                    data2 = w_m2.data[i2A:i2B+1]
                    assert np.allclose(data1, data2, rtol=0.0, atol=5e-14), \
                        ([s, ell, m],
                         supertranslation,
                         [abs(data1-data2).max(),
                          data1.ravel()[np.argmax(abs(data1-data2))], data2.ravel()[np.argmax(abs(data1-data2))]],
                         [np.unravel_index(np.argmax(abs(data1-data2)), data1.shape)[0],
                          list(sf.LM_range(abs(s), ell_max)[np.unravel_index(np.argmax(abs(data1-data2)),
                                                                             data1.shape)[1]])])
예제 #11
0
def grid(self, n_theta=None, n_phi=None, **kwargs):
    """Return values of function on an equi-angular grid

    This method uses `spinsfast` to convert mode weights of spin-weighted function to values on a
    grid.  The grid has `n_theta` evenly spaced points along the usual polar (colatitude) angle
    theta, and `n_phi` evenly spaced points along the usual azimuthal angle phi.  This grid
    corresponds to the one produced by `spherical_functions.theta_phi`; see that function for
    specifics.

    The output array has one more dimension than this object; rather than the last axis giving the
    mode weights, the last two axes give the values on the two-dimensional grid.

    Parameters
    ==========
    n_theta: None or int [defaults to None]
        Number of points to use in theta direction.  None is equivalent to 2*self.ell_max+1, which
        is the minimum number that can capture behavior up to and including ell_max.  If you need to
        multiply the result with some `other` spin-weighted function, you should use an n_theta
        value of 2 * (self.ell_max + other.ell_max) + 1 to avoid aliasing.
    n_phi: None or int [defaults to None]
        Number of points to use in the phi direction.  Here, None is equivalent to n_phi=n_theta,
        after calculation of the default value for n_theta.  Note that the same comments apply about
        avoiding aliasing.
    **kwargs: any types
        Additional keyword arguments are passed through to the Grid constructor on output

    """
    import copy
    import numpy as np
    import spinsfast
    from .. import Grid
    n_theta = n_theta or 2 * self.ell_max + 1
    n_phi = n_phi or n_theta
    metadata = copy.copy(self._metadata)
    metadata.pop('ell_max', None)
    metadata.update(**kwargs)
    return Grid(
        spinsfast.salm2map(self.view(np.ndarray), self.s, self.ell_max,
                           n_theta, n_phi), **metadata)
예제 #12
0
Nlm = spinsfast.N_lm(lmax);
alm = zeros(Nlm,dtype=complex)


# Fill the alm with white noise
seed(3124432)
for l in range(abs(s),lmax+1) :
    for m in range(-l,l+1) :
        i = spinsfast.lm_ind(l,m,lmax)
        if (m==0) :
            alm[i] = normal()
        else :
            alm[i] = normal()/2 + 1j*normal()/2


f =  spinsfast.salm2map(alm,s,lmax,Ntheta,Nphi)
# In this pixelization, access the map with f[itheta,iphi]
# where 0 <= itheta <= Ntheta-1 and 0<= iphi <= Nphi-1
# and theta = pi*itheta/(Ntheta-1) phi = 2*pi*iphi/Nphi


alm2 = spinsfast.map2salm(f,s,lmax)

diff_max = max((alm-alm2))
print("max(alm2-alm) = "+str(diff_max))

figure()
imshow(f.real,interpolation='nearest')
colorbar()
title("Real part of f")
xlabel("iphi")
예제 #13
0
# Set up the parameters of the transformation.  These Ntheta and Nphi
# are usually overkill for plotting purposes, but don't slow things
# down noticeably.
s = 0  # Spin weight
lmax = 7
Ntheta = 2**9 + 1
Nphi = 2**9
Nlm = spinsfast.N_lm(lmax)

# These are the physical coordinates to be used below.
theta = linspace(0, pi, num=Ntheta)
phi = linspace(0, 2 * pi, num=Nphi)

# Here, we transform the data, take its absolute value, and then
# construct a colormap from its normalized values.
f = spinsfast.salm2map(alm, s, lmax, Ntheta, Nphi)
absf = abs(f)

# This is for 2-d plots.
pcolormesh(phi, theta, absf)
xlim((0, 2 * pi))
ylim((0, pi))
xlabel(r'$\phi$')
ylabel(r'$\theta$')
tight_layout()
#savefig('Waveform.png', dpi=200, transparent=True)
#show()

# This is for 3-d plots.  This is by far the slowest part of the
# process.  Increase rstride and cstride to make this go faster at the
# cost of resolution.
def test_SWSH_multiplication_formula(multiplication_function):
    """Test formula for multiplication of SWSHs

    Much of the analysis is based on the formula

    s1Yl1m1 * s2Yl2m2 = sum([
        s3Yl3m3 * (-1)**(l1+l2+l3+s3+m3) * sqrt((2*l1+1)*(2*l2+1)*(2*l3+1)/(4*pi))
            * Wigner3j(l1, l2, l3, s1, s2, -s3) * Wigner3j(l1, l2, l3, m1, m2, -m3)
        for s3 in [s1+s2]
        for l3 in range(abs(l1-l2), l1+l2+1)
        for m3 in [m1+m2]
    ])

    This test evaluates each side of this formula, and compares the values at all collocation points
    on the sphere.  This is tested for each possible value of (s1, l1, m1, s2, l2, m2) up to l1=4
    and l2=4 [the number of items to test scales very rapidly with ell], and tested again for each
    (0, 1, m1, s2, l2, m2) up to l2=8.

    """
    atol = 2e-15
    rtol = 2e-15
    ell_max = 4
    for ell1 in range(ell_max + 1):
        for s1 in range(-ell1, ell1 + 1):
            for m1 in range(-ell1, ell1 + 1):
                for ell2 in range(ell_max + 1):
                    for s2 in range(-ell2, ell2 + 1):
                        for m2 in range(-ell2, ell2 + 1):
                            swsh1 = np.zeros(sf.LM_total_size(0, ell_max),
                                             dtype=np.complex)
                            swsh1[sf.LM_index(ell1, m1, 0)] = 1.0
                            swsh2 = np.zeros(sf.LM_total_size(0, ell_max),
                                             dtype=np.complex)
                            swsh2[sf.LM_index(ell2, m2, 0)] = 1.0
                            swsh3, ell_min_3, ell_max_3, s3 = multiplication_function(
                                swsh1, 0, ell_max, s1, swsh2, 0, ell_max, s2)
                            assert s3 == s1 + s2
                            assert ell_min_3 == 0
                            assert ell_max_3 == 2 * ell_max
                            n_theta = 2 * ell_max_3 + 1
                            n_phi = n_theta
                            swsh3_map = spinsfast.salm2map(
                                swsh3, s3, ell_max_3, n_theta, n_phi)
                            swsh4_map = np.zeros_like(swsh3_map)
                            for ell4 in range(abs(ell1 - ell2),
                                              ell1 + ell2 + 1):
                                for s4 in [s1 + s2]:
                                    for m4 in [m1 + m2]:
                                        swsh4_k = np.zeros_like(swsh3)
                                        swsh4_k[sf.LM_index(ell4, m4, 0)] = (
                                            (-1)
                                            **(ell1 + ell2 + ell4 + s4 + m4) *
                                            np.sqrt(
                                                (2 * ell1 + 1) *
                                                (2 * ell2 + 1) *
                                                (2 * ell4 + 1) / (4 * np.pi)) *
                                            sf.Wigner3j(
                                                ell1, ell2, ell4, s1, s2, -s4)
                                            * sf.Wigner3j(
                                                ell1, ell2, ell4, m1, m2, -m4))
                                        swsh4_map[:] += spinsfast.salm2map(
                                            swsh4_k, s4, ell_max_3, n_theta,
                                            n_phi)
                            assert np.allclose(swsh3_map,
                                               swsh4_map,
                                               atol=atol,
                                               rtol=rtol)

    atol = 8e-15
    rtol = 8e-15
    ell_max = 8
    for ell1 in [1]:
        for s1 in [0]:
            for m1 in [-1, 0, 1]:
                for ell2 in range(ell_max + 1):
                    for s2 in range(-ell2, ell2 + 1):
                        for m2 in range(-ell2, ell2 + 1):
                            swsh1 = np.zeros(sf.LM_total_size(0, ell_max),
                                             dtype=np.complex)
                            swsh1[sf.LM_index(ell1, m1, 0)] = 1.0
                            swsh2 = np.zeros(sf.LM_total_size(0, ell_max),
                                             dtype=np.complex)
                            swsh2[sf.LM_index(ell2, m2, 0)] = 1.0
                            swsh3, ell_min_3, ell_max_3, s3 = multiplication_function(
                                swsh1, 0, ell_max, s1, swsh2, 0, ell_max, s2)
                            assert s3 == s1 + s2
                            assert ell_min_3 == 0
                            assert ell_max_3 == 2 * ell_max
                            n_theta = 2 * ell_max_3 + 1
                            n_phi = n_theta
                            swsh3_map = spinsfast.salm2map(
                                swsh3, s3, ell_max_3, n_theta, n_phi)
                            swsh4_map = np.zeros_like(swsh3_map)
                            for ell4 in [ell2 - 1, ell2, ell2 + 1]:
                                if ell4 < 0:
                                    continue
                                swsh4_k = np.zeros_like(swsh3)
                                # swsh4_k[sf.LM_index(ell4, m1+m2, 0)] = (
                                #     (-1)**(1+ell2+ell4+s2+m1+m2)
                                #     * np.sqrt(3*(2*ell2+1)*(2*ell4+1)/(4*np.pi))
                                #     * sf.Wigner3j(1, ell2, ell4, 0, s2, -s2)
                                #     * sf.Wigner3j(1, ell2, ell4, m1, m2, -m1-m2)
                                # )
                                # swsh4_map[:] += (
                                #     spinsfast.salm2map(swsh4_k, s2, ell_max_3, n_theta, n_phi)
                                # )
                                swsh4_k[sf.LM_index(ell4, m1 + m2, 0)] = (
                                    (-1)**(ell2 + ell4 + m1) * np.sqrt(
                                        (2 * ell4 + 1)) *
                                    sf.Wigner3j(1, ell2, ell4, 0, s2, -s2) *
                                    sf.Wigner3j(1, ell2, ell4, m1, m2,
                                                -m1 - m2))
                                swsh4_map[:] += (
                                    (-1)**(s2 + m2 + 1) *
                                    np.sqrt(3 * (2 * ell2 + 1) / (4 * np.pi)) *
                                    spinsfast.salm2map(swsh4_k, s2, ell_max_3,
                                                       n_theta, n_phi))
                            assert np.allclose(swsh3_map,
                                               swsh4_map,
                                               atol=atol,
                                               rtol=rtol), np.max(
                                                   np.abs(swsh3_map -
                                                          swsh4_map))
def test_SWSH_multiplication_formula(multiplication_function):
    """Test formula for multiplication of SWSHs

    Much of the analysis is based on the formula

    s1Yl1m1 * s2Yl2m2 = sum([
        s3Yl3m3 * (-1)**(l1+l2+l3+s3+m3) * sqrt((2*l1+1)*(2*l2+1)*(2*l3+1)/(4*pi))
            * Wigner3j(l1, l2, l3, s1, s2, -s3) * Wigner3j(l1, l2, l3, m1, m2, -m3)
        for s3 in [s1+s2]
        for l3 in range(abs(l1-l2), l1+l2+1)
        for m3 in [m1+m2]
    ])

    This test evaluates each side of this formula, and compares the values at all collocation points
    on the sphere.  This is tested for each possible value of (s1, l1, m1, s2, l2, m2) up to l1=4
    and l2=4 [the number of items to test scales very rapidly with ell], and tested again for each
    (0, 1, m1, s2, l2, m2) up to l2=8.

    """
    atol=2e-15
    rtol=2e-15
    ell_max = 4
    for ell1 in range(ell_max+1):
        for s1 in range(-ell1, ell1+1):
            for m1 in range(-ell1, ell1+1):
                for ell2 in range(ell_max+1):
                    for s2 in range(-ell2, ell2+1):
                        for m2 in range(-ell2, ell2+1):
                            swsh1 = np.zeros(sf.LM_total_size(0, ell_max), dtype=np.complex)
                            swsh1[sf.LM_index(ell1, m1, 0)] = 1.0
                            swsh2 = np.zeros(sf.LM_total_size(0, ell_max), dtype=np.complex)
                            swsh2[sf.LM_index(ell2, m2, 0)] = 1.0
                            swsh3, ell_min_3, ell_max_3, s3 = multiplication_function(swsh1, 0, ell_max, s1, swsh2, 0, ell_max, s2)
                            assert s3 == s1 + s2
                            assert ell_min_3 == 0
                            assert ell_max_3 == 2*ell_max
                            n_theta = 2*ell_max_3 + 1
                            n_phi = n_theta
                            swsh3_map = spinsfast.salm2map(swsh3, s3, ell_max_3, n_theta, n_phi)
                            swsh4_map = np.zeros_like(swsh3_map)
                            for ell4 in range(abs(ell1-ell2), ell1+ell2+1):
                                for s4 in [s1+s2]:
                                    for m4 in [m1+m2]:
                                        swsh4_k = np.zeros_like(swsh3)
                                        swsh4_k[sf.LM_index(ell4, m4, 0)] = (
                                            (-1)**(ell1+ell2+ell4+s4+m4)
                                            * np.sqrt((2*ell1+1)*(2*ell2+1)*(2*ell4+1)/(4*np.pi))
                                            * sf.Wigner3j(ell1, ell2, ell4, s1, s2, -s4)
                                            * sf.Wigner3j(ell1, ell2, ell4, m1, m2, -m4)
                                        )
                                        swsh4_map[:] += spinsfast.salm2map(swsh4_k, s4, ell_max_3, n_theta, n_phi)
                            assert np.allclose(swsh3_map, swsh4_map, atol=atol, rtol=rtol)

    atol=8e-15
    rtol=8e-15
    ell_max = 8
    for ell1 in [1]:
        for s1 in [0]:
            for m1 in [-1, 0, 1]:
                for ell2 in range(ell_max+1):
                    for s2 in range(-ell2, ell2+1):
                        for m2 in range(-ell2, ell2+1):
                            swsh1 = np.zeros(sf.LM_total_size(0, ell_max), dtype=np.complex)
                            swsh1[sf.LM_index(ell1, m1, 0)] = 1.0
                            swsh2 = np.zeros(sf.LM_total_size(0, ell_max), dtype=np.complex)
                            swsh2[sf.LM_index(ell2, m2, 0)] = 1.0
                            swsh3, ell_min_3, ell_max_3, s3 = multiplication_function(swsh1, 0, ell_max, s1, swsh2, 0, ell_max, s2)
                            assert s3 == s1 + s2
                            assert ell_min_3 == 0
                            assert ell_max_3 == 2*ell_max
                            n_theta = 2*ell_max_3 + 1
                            n_phi = n_theta
                            swsh3_map = spinsfast.salm2map(swsh3, s3, ell_max_3, n_theta, n_phi)
                            swsh4_map = np.zeros_like(swsh3_map)
                            for ell4 in [ell2-1, ell2, ell2+1]:
                                if ell4 < 0:
                                    continue
                                swsh4_k = np.zeros_like(swsh3)
                                # swsh4_k[sf.LM_index(ell4, m1+m2, 0)] = (
                                #     (-1)**(1+ell2+ell4+s2+m1+m2)
                                #     * np.sqrt(3*(2*ell2+1)*(2*ell4+1)/(4*np.pi))
                                #     * sf.Wigner3j(1, ell2, ell4, 0, s2, -s2)
                                #     * sf.Wigner3j(1, ell2, ell4, m1, m2, -m1-m2)
                                # )
                                # swsh4_map[:] += (
                                #     spinsfast.salm2map(swsh4_k, s2, ell_max_3, n_theta, n_phi)
                                # )
                                swsh4_k[sf.LM_index(ell4, m1+m2, 0)] = (
                                    (-1)**(ell2+ell4+m1)
                                    * np.sqrt((2*ell4+1))
                                    * sf.Wigner3j(1, ell2, ell4, 0, s2, -s2)
                                    * sf.Wigner3j(1, ell2, ell4, m1, m2, -m1-m2)
                                )
                                swsh4_map[:] += (
                                    (-1)**(s2+m2+1)
                                    * np.sqrt(3*(2*ell2+1)/(4*np.pi))
                                    * spinsfast.salm2map(swsh4_k, s2, ell_max_3, n_theta, n_phi)
                                )
                            assert np.allclose(swsh3_map, swsh4_map, atol=atol, rtol=rtol), np.max(np.abs(swsh3_map-swsh4_map))
예제 #16
0
    def grid_multiply(self, mts, **kwargs):
        """Compute mode weights of the product of two functions

        This will compute the values of `self` and `mts` on a grid, multiply the grid
        values together, and then return the mode coefficients of the product.  This
        takes less time and memory compared to the `SWSH_modes.Modes.multiply()`
        function, at the risk of introducing aliasing effects if `working_ell_max` is
        too small.

        Parameters
        ----------
        self: ModesTimeSeries
            One of the quantities to multiply.
        mts: ModesTimeSeries
            The quantity to multiply with 'self'.
        working_ell_max: int, optional
            The value of ell_max to be used to define the computation grid. The
            number of theta points and the number of phi points are set to
            2*working_ell_max+1. Defaults to (self.ell_max + mts.ell_max).
        output_ell_max: int, optional
            The value of ell_max in the output mts object. Defaults to self.ell_max.

        """
        import spinsfast
        from spherical_functions import LM_index

        output_ell_max = kwargs.pop("output_ell_max", self.ell_max)
        working_ell_max = kwargs.pop("working_ell_max",
                                     self.ell_max + mts.ell_max)
        n_theta = n_phi = 2 * working_ell_max + 1

        if self.n_times != mts.n_times or not np.equal(self.t, mts.t).all():
            raise ValueError(
                "The time series of objects to be multiplied must be the same."
            )

        # Transform to grid representation
        self_grid = spinsfast.salm2map(self.ndarray,
                                       self.spin_weight,
                                       lmax=self.ell_max,
                                       Ntheta=n_theta,
                                       Nphi=n_phi)
        mts_grid = spinsfast.salm2map(mts.ndarray,
                                      mts.spin_weight,
                                      lmax=mts.ell_max,
                                      Ntheta=n_theta,
                                      Nphi=n_phi)

        product_grid = self_grid * mts_grid
        product_spin_weight = self.spin_weight + mts.spin_weight

        # Transform back to mode representation
        product = spinsfast.map2salm(product_grid,
                                     product_spin_weight,
                                     lmax=working_ell_max)

        # Convert product ndarray to a ModesTimeSeries object
        product = product[:, :LM_index(output_ell_max, output_ell_max, 0) + 1]
        product = ModesTimeSeries(
            spherical_functions.SWSH_modes.Modes(
                product,
                spin_weight=product_spin_weight,
                ell_min=0,
                ell_max=output_ell_max,
                multiplication_truncator=max,
            ),
            time=self.t,
        )
        return product