Ejemplo n.º 1
0
def NuFFT(image, coils, k_traj, w, n, osf, wg):    
    # pad image with zeros to the oversampling size
    pw = pad_width = int( n*(osf-1)/2 )
    image = pad(image, ([pw,pw], [pw,pw], [0,0]), 'constant' )

    # Kernel computation 
    kw = wg / osf
    kosf = floor(0.91 / (osf*1e-3))
    kwidth = osf*kw / 2
    beta = pi*sqrt((kw*(osf-0.5))**2-0.8)

    # compute kernel
    om = arange( floor(kosf*kwidth)+1 ) / floor(kosf*kwidth)
    p = besseli(0, beta*sqrt(1-om*om))
    p /= p[0]
    p[-1] = 0

    # deapodize
    x = arange(-osf*n/2, osf*n/2, dtype=complex) / n
    sqa = sqrt(pi*pi * kw*kw * x*x - beta*beta)
    dax = sin(sqa) / sqa
    dax /= dax[ int(osf*n/2) ]
    da = outer(dax.H, dax)
    image /= da[:,:,newaxis]
    
    m = np.zeros((len(k_traj), coils), dtype=complex) 
                      
    # convert k-space trajectory to matrix indices
    nx = n*osf/2 + osf*n*k_traj[:,0]
    ny = n*osf/2 + osf*n*k_traj[:,1]
    
    # Do the NuFFT for each coil
    for j in range(coils):
        # Compute FFT of each coil image
        kspace = fftc(image[:,:,j])

        # loop over samples in kernel at grid spacing
        for lx in arange(-kwidth, kwidth+1):
            for ly in arange(-kwidth, kwidth+1):                
                # find nearest samples
                nxt = around(nx+lx)
                nyt = around(ny+ly)
                
                # separable kernel value
                kkr = minimum(
                    np.round(kosf * sqrt(abs(nx-nxt)**2+abs(ny-nyt)**2)),
                    floor(kosf * kwidth)
                ).astype(int)
                kwr = p[kkr]

                # if data falls outside matrix, put it at the edge, zero out below
                nxt = nxt.clip(0,osf*n-1).astype(int)
                nyt = nyt.clip(0,osf*n-1).astype(int)
                vector = np.array([kspace[x,y] for x, y in zip(nxt, nyt)])
                
                # accumulate gridded data
                m[:,j] += vector * kwr
                
    return m * sqrt(w)
Ejemplo n.º 2
0
def NuFFT(image, coils, k_traj, w, n, osf, wg):
    # pad image with zeros to the oversampling size
    pw = pad_width = int(n * (osf - 1) / 2)
    image = pad(image, ([pw, pw], [pw, pw], [0, 0]), 'constant')

    # Kernel computation
    kw = wg / osf
    kosf = floor(0.91 / (osf * 1e-3))
    kwidth = osf * kw / 2
    beta = pi * sqrt((kw * (osf - 0.5))**2 - 0.8)

    # compute kernel
    om = arange(floor(kosf * kwidth) + 1) / floor(kosf * kwidth)
    p = besseli(0, beta * sqrt(1 - om * om))
    p /= p[0]
    p[-1] = 0

    # deapodize
    x = arange(-osf * n / 2, osf * n / 2, dtype=complex) / n
    sqa = sqrt(pi * pi * kw * kw * x * x - beta * beta)
    dax = sin(sqa) / sqa
    dax /= dax[int(osf * n / 2)]
    da = outer(dax.H, dax)
    image /= da[:, :, newaxis]

    m = np.zeros((len(k_traj), coils), dtype=complex)

    # convert k-space trajectory to matrix indices
    nx = n * osf / 2 + osf * n * k_traj[:, 0]
    ny = n * osf / 2 + osf * n * k_traj[:, 1]

    # Do the NuFFT for each coil
    for j in range(coils):
        # Compute FFT of each coil image
        kspace = fftc(image[:, :, j])

        # loop over samples in kernel at grid spacing
        for lx in arange(-kwidth, kwidth + 1):
            for ly in arange(-kwidth, kwidth + 1):
                # find nearest samples
                nxt = around(nx + lx)
                nyt = around(ny + ly)

                # separable kernel value
                kkr = minimum(
                    np.round(kosf * sqrt(abs(nx - nxt)**2 + abs(ny - nyt)**2)),
                    floor(kosf * kwidth)).astype(int)
                kwr = p[kkr]

                # if data falls outside matrix, put it at the edge, zero out below
                nxt = nxt.clip(0, osf * n - 1).astype(int)
                nyt = nyt.clip(0, osf * n - 1).astype(int)
                vector = np.array([kspace[x, y] for x, y in zip(nxt, nyt)])

                # accumulate gridded data
                m[:, j] += vector * kwr

    return m * sqrt(w)
Ejemplo n.º 3
0
def trapes_analytical(h):
    
    N=int(round(1.0/h))      # number of unknowns, assuming the RHS boundary value is known
    x=np.arange(N+1)*h   
#    x = np.linspace(0, 1,  N)
    z0 = 4*sqrt(2)
    z1 = 8
    g = sqrt(2)/8
    
    k1z0, i0z1 = besselk(1, z0), besseli(0, z1)
    k0z1, i1z0 = besselk(0, z1), besseli(1, z0)
    k0z0, i0z0 = besselk(0, z0), besseli(0, z0)
    
    J = k1z0*i0z1 + k0z1*i1z0 + g*(k0z0*i0z1 - k0z1*i0z0)
    A = (k1z0 + g*k0z0)/J
    B = (i1z0 - g*i0z0)/J
    
    z = sqrt(32.*(1 + x))
    theta = A* besseli(0, z) + B* besselk(0, z)
    dtheta = 16*(A*besseli(1, z) - B*besselk(1, z))/z
    return x, theta, dtheta 
Ejemplo n.º 4
0
    def set_qpabsb_eff(self, l_fin, h_fin, loverlap, l_TES, eff_absb=1.22e-4):
        ci = 2 * l_TES
        ri = ci / (2 * np.pi)

        a = (l_fin + l_TES) / 2
        b = l_fin

        co1 = 2 * l_TES + 2 * np.pi * l_fin
        h = ((a - b) / (a + b)) ** 2
        co = np.pi * (a + b) * (1 + 3 * h / (10 + np.sqrt(4 - 3 * h)))
        ro = co1 / (2 * np.pi)

        # -------- Relevant length scales
        # Diffusion length
        ld = 567 * h_fin  # µm

        # Surface impedance length
        la = 1 /eff_absb*h_fin**2/loverlap  # µm
        la_chk = (1e6 + 1600 / (900 ** 2) * 5) * (h_fin ** 2)  # µm

        # -------- Dimensionless Scales
        rhoi = ri / ld
        rhoo = ro / ld
        lambdaA = la / ld

        # QP collection coefficient
        """fQP = 2 * rhoi / (rhoo ** 2 - rhoi ** 2) * \
        (iv(1, rhoo) * kv(1, rhoi) - iv(1, rhoi) * kv(1, rhoo)) / \
        (iv(1, rhoo) * kv(0, rhoi) + lambdaA * kv(1, rhoi) + (iv(0, rhoi) - lambdaA * iv(1, rhoi)) * kv(1, rhoo))

        fQP = 2 * rhoi / (rhoo**2 - rhoi**2) * (iv(1, rhoo) * kv(1, rhoi) - iv(1, rhoi) * kv(1, rhoo)) / \
              (iv(1, rhoo) * (kv(0, rhoi) + lambdaA * kv(1, rhoi)) + (iv(0, rhoi) - lambdaA*iv(1, rhoi))*kv(1, rhoo))"""

        fQP = 2 * rhoi / (rhoo ** 2 - rhoi ** 2) \
        *(besseli(1, rhoo) * besselk(1, rhoi) - besseli(1, rhoi) * besselk(1, rhoo)) \
        / (besseli(1, rhoo) * (besselk(0, rhoi) + lambdaA * besselk(1, rhoi)) +
        (besseli(0, rhoi) - lambdaA * besseli(1, rhoi)) * besselk(1, rhoo))
        self._eQPabsb = fQP
Ejemplo n.º 5
0
def pdf(d, x=None, tol=1e-4, n_bins=1e4):
    n_bins = int(n_bins)

    x0 = x
    xmax = chi2.ppf(1 - tol, 1) * sum(d)
    dx = xmax / n_bins
    x = np.array(range(n_bins - 20)) * dx
    nu = len(d)

    if nu < 3:
        # Exact solution for 1 or 2 degrees of freedom
        if x0 is not None:
            x = x0
        if nu == 1:
            p = chi2.pdf(x / d[0], 1) / d[0]
        else:
            a = d[0]
            b = d[1]
            p = 1 / (2 * np.sqrt(a * b)) * np.exp(-(a + b) * x / (4 * a * b)) * besseli(0, (a - b) * x / (4 * a * b))
    else:
        # Numerical solution for arbitrary degrees of freedom
        du = 2 * np.pi / xmax
        u = np.array(range(n_bins)) * du
        ug, dg = np.meshgrid(u, d)
        phi = 1 / np.sqrt(1 - 2 * 1j * ug * dg)
        phi = np.prod(phi, axis=0)
        p = (du / np.pi) * np.real(fft.fft(phi))
        p = p[:(n_bins - 20)] - min(p)
        if x0 is not None:
            p = np.interp(x0, x, p)
            x = x0

    if x0 is None:
        return p, x
    else:
        return p
Ejemplo n.º 6
0
def NuFFT_adj(data, coils, k_traj, w, n, osf, wg):
    # width of the kernel on the original grid
    kw = wg / osf
    
    # preweight
    dw = data * sqrt(w)
    
    # compute kernel, assume e1 is 0.001, assuming nearest neighbor
    kosf = floor(0.91/(osf*1e-3))
    
    # half width in oversampled grid units
    kwidth = osf * kw / 2
    
    # beta from the Beatty paper
    beta = pi * sqrt((kw*(osf-0.5))**2 - 0.8)
    
    # compute kernel
    om = arange(floor(kosf*kwidth)+1, dtype=complex) / (kosf*kwidth)
    p = besseli(0, beta*sqrt(1-om*om))
    p /= p[0]
    p[-1] = 0

    # convert k-space trajectory to matrix indices
    nx = n*osf/2 + osf*n*k_traj[:,0]
    ny = n*osf/2 + osf*n*k_traj[:,1]
    
    im = zeros((osf*n, osf*n, coils), dtype=complex)
  
    for j in range(coils):    
        ksp = np.zeros_like(im[:,:,j])

        for lx in arange(-kwidth, kwidth+1):
            for ly in arange(-kwidth, kwidth+1):
                # find nearest samples
                nxt = around(nx + lx)
                nyt = around(ny + ly)
                
                # seperable kernel value
                kkr = minimum(
                        around(kosf * sqrt(abs(nx-nxt)**2 + abs(ny-nyt)**2)),
                        floor(kosf * kwidth)
                    ).astype(int)
                kwr = p[kkr]
                
                # if data falls outside matrix, put it at the edge, zero out below
                nxt = nxt.round().clip(0,osf*n-1).astype(int)
                nyt = nyt.round().clip(0,osf*n-1).astype(int)

                # accumulate gridded data
                ksp += coo_matrix((dw[:,j]*kwr.H, (nxt, nyt)), shape=(osf*n,osf*n))
                    
        # zero out data at edges, which is probably due to data outside mtx
        ksp[:,0] = ksp[:,-1] = ksp[0,:] = ksp[-1,:] = 0       
    
        # do the FFT and deapodize
        im[:,:,j] = ifftc( ksp )

    # deapodize
    x = arange(-osf*n/2, osf*n/2, dtype=complex) / n
    sqa = sqrt(pi*pi * kw*kw * x*x - beta**2)
    dax = sin(sqa) / sqa
    dax /= dax[ int(osf*n/2) ]
    da = outer(dax.H, dax)
    im /= da[:,:,newaxis] 
    
    # trim the images
    q = int((osf-1)*n/2)
    return im[q:n+q, q:n+q]     
Ejemplo n.º 7
0
def NuFFT_adj(data, coils, k_traj, w, n, osf, wg):
    # width of the kernel on the original grid
    kw = wg / osf

    # preweight
    dw = data * sqrt(w)

    # compute kernel, assume e1 is 0.001, assuming nearest neighbor
    kosf = floor(0.91 / (osf * 1e-3))

    # half width in oversampled grid units
    kwidth = osf * kw / 2

    # beta from the Beatty paper
    beta = pi * sqrt((kw * (osf - 0.5))**2 - 0.8)

    # compute kernel
    om = arange(floor(kosf * kwidth) + 1, dtype=complex) / (kosf * kwidth)
    p = besseli(0, beta * sqrt(1 - om * om))
    p /= p[0]
    p[-1] = 0

    # convert k-space trajectory to matrix indices
    nx = n * osf / 2 + osf * n * k_traj[:, 0]
    ny = n * osf / 2 + osf * n * k_traj[:, 1]

    im = zeros((osf * n, osf * n, coils), dtype=complex)

    for j in range(coils):
        ksp = np.zeros_like(im[:, :, j])

        for lx in arange(-kwidth, kwidth + 1):
            for ly in arange(-kwidth, kwidth + 1):
                # find nearest samples
                nxt = around(nx + lx)
                nyt = around(ny + ly)

                # seperable kernel value
                kkr = minimum(
                    around(kosf * sqrt(abs(nx - nxt)**2 + abs(ny - nyt)**2)),
                    floor(kosf * kwidth)).astype(int)
                kwr = p[kkr]

                # if data falls outside matrix, put it at the edge, zero out below
                nxt = nxt.round().clip(0, osf * n - 1).astype(int)
                nyt = nyt.round().clip(0, osf * n - 1).astype(int)

                # accumulate gridded data
                ksp += coo_matrix((dw[:, j] * kwr.H, (nxt, nyt)),
                                  shape=(osf * n, osf * n))

        # zero out data at edges, which is probably due to data outside mtx
        ksp[:, 0] = ksp[:, -1] = ksp[0, :] = ksp[-1, :] = 0

        # do the FFT and deapodize
        im[:, :, j] = ifftc(ksp)

    # deapodize
    x = arange(-osf * n / 2, osf * n / 2, dtype=complex) / n
    sqa = sqrt(pi * pi * kw * kw * x * x - beta**2)
    dax = sin(sqa) / sqa
    dax /= dax[int(osf * n / 2)]
    da = outer(dax.H, dax)
    im /= da[:, :, newaxis]

    # trim the images
    q = int((osf - 1) * n / 2)
    return im[q:n + q, q:n + q]
Ejemplo n.º 8
0
def calculateEigenPair(a, c0, f, azimuthal_m, radial_n, mode_type, sign=1):
    if mode_type not in {"Kelvin", "Poincare"}:
        raise ValueError("Unknown mode_type: '" + mode_type + "'")

    if sign not in {-1, 1}:
        raise ValueError("Invalid sign: '" + str(sign) + "'")

    if mode_type == 'Kelvin' and sign == 1:
        raise ValueError(
            "Invalid parameter selection. Kelvin modes with clockwise phase propagation do not exist"
        )

    bracket_factor = 2e-2
    eps = 1e-7

    PoincareEqn = lambda sig: (a / np.sqrt(c0**2 / (sig**2 - f**2))) * (
        (1 / azimuthal_m) * besselj(azimuthal_m - 1, a / np.sqrt(c0**2 / (
            sig**2 - f**2))) /
        (a / np.sqrt(c0**2 / (sig**2 - f**2))) * besselj(
            azimuthal_m, a / np.sqrt(c0**2 / (sig**2 - f**2)))) - 1 + (f / sig)

    KelvinEqn = lambda sig: (a / np.sqrt(c0**2 / (f**2 - sig**2))) * (
        (1 / azimuthal_m) * besseli(azimuthal_m - 1, a / np.sqrt(c0**2 / (
            f**2 - sig**2))) /
        (a / np.sqrt(c0**2 / (f**2 - sig**2))) * besseli(
            azimuthal_m, a / np.sqrt(c0**2 / (f**2 - sig**2)))) - 1 + (f / sig)

    # Calculate sigma
    if mode_type == 'Poincare' and sign == 1:
        sig = f
        for n in range(0, radial_n):
            rt = root(PoincareEqn,
                      method="bisect",
                      bracket=[(1 + eps) * sig, (1 + bracket_factor) * sig])
            if rt.root is None:
                print(
                    "Did not find an eigenvalue in expected range for Poincare mode with n="
                    + str(n))
            sig = rt.root
    elif mode_type == 'Poincare' and sign == -1:
        sig = -f
        for n in range(0, radial_n):
            rt = root(PoincareEqn,
                      method="bisect",
                      bracket=[(1 + bracket_factor) * sig, (1 + eps) * sig])
            if rt.root is None:
                print(
                    "Did not find an eigenvalue in expected range for Poincare mode with n="
                    + str(n))
            sig = rt.root
    else:
        sig = -f
        for n in range(
                0, radial_n
        ):  # Can also restrict n's on kelvin mode using Csanady's restriction on S.
            rt = root(KelvinEqn,
                      method="bisect",
                      bracket=[(1 - eps) * sig, -eps])
            if rt.root is None:
                print(
                    "Did not find an eigenvalue in expected range for Kelvin mode with n="
                    + str(n))
            sig = rt.root

    Theta = lambda theta: np.exp(1j * azimuthal_m * theta)
    if mode_type == "Kelvin":
        R = lambda r: besseli(azimuthal_m,
                              np.sqrt((f**2 - sig**2) / c0**2) * r)
    else:
        #Poincare
        R = lambda r: besselj(azimuthal_m,
                              np.sqrt((sig**2 - f**2) / c0**2) * r)

    eigenfunction = lambda r, theta: R(r) * Theta(theta)
    return (sig, (lambda r, theta: eigenfunction(r, theta)))
Ejemplo n.º 9
0
    def set_qpabsb_eff_matt(self):
        """
        Calculate the QP collection efficiciency using Matt's method. Sets class atribute
        slef.eQPabsb
        
        Parameters:
        -----------
        eff_absb : float, optional
            W/Al transmition/trapping probability
                
        """
        # From Effqp_2D_moffatt.m in Matt's dropbox
        # Here we are using Robert Moffatt's full QP model. There are some pretty big assumptions:
        # 1) Diffusion length scales with Al thickness (trapping surface dominated and diffusion thickness limited)
        # 2) Absorption length scales with Al thickness**2/l_overlap
        # Future: scale boundary impedance with W thickness
        # INPUTS:
        #    1) fin length [um]
        #    2) fin height [um]
        #    3) W/Al overlap [um]
        #    4) TES length [um]
        #    5) W/Al transmition/trapping probability
        # OUTPUTS:
        #    1) Quasi-Particle Collection Efficiency
        #    2) Diffusion Length
        #    3) W/Al Surface Absorption Length
        #    4) W/Al Transmission Probability
        # https://www.stanford.edu/~rmoffatt/Papers/Diffusion%20and%20Absorption%20with%201D%20and%202D%20Solutions.pdf
        # DOI: 10.1007/s10909-015-1406-7
        # -------------------------------------------------------------------------------------------------------------

        l_tes = self.TES.l * 1e6  #convert to [um]
        l_fin = self.l_fin * 1e6  #convert to [um]
        h_fin = self.h_fin * 1e6  #convert to [um]
        l_overlap = self.TES.l_overlap * 1e6  #convert to [um]
        n_fin = self.TES.n_fin
        eff_absb = self.eff_absb
        # We assume pie shaped QP collection fins
        ci = 2 * l_tes  # inner circle circumferance
        ri = ci / (2 * np.pi)  # inner radius

        # Outer circumferance of "very simplified" ellipse
        co1 = 2 * l_tes + 2 * np.pi * l_fin

        # Another approximation...
        a = (l_fin + l_tes) / 2
        b = l_fin

        # https://www.mathsisfun.com/geometry/ellipse-perimeter.html
        h = ((a - b) / (a + b))**2
        co = np.pi * (a + b) * (1 + 3 * h / (10 + np.sqrt(4 - 3 * h)))
        ro = co1 / (2 * np.pi)

        # -------- Relevant length scales ------------
        # Diffusion length
        ld = 567 * h_fin  # [µm] this is the fit in Jeff's published LTD 16 data
        self.ld = ld
        # Surface impedance length
        la = (1 / eff_absb) * (h_fin**2 / l_overlap
                               )  # [µm] these match the values used by Noah
        la_chk = (1e6 + 1600 / (900**2) * 5) * (h_fin**2)  # µm
        self.la = la

        # -------- Dimensionless Scales -------
        rhoi = ri / ld
        rhoo = ro / ld
        lambdaA = la / ld

        # QP collection coefficient

        fQP = (2 * rhoi / (rhoo ** 2 - rhoi ** 2)) \
        *(besseli(1, rhoo) * besselk(1, rhoi) - besseli(1, rhoi) * besselk(1, rhoo)) \
        / (besseli(1, rhoo) * (besselk(0, rhoi) + lambdaA * besselk(1, rhoi)) +
        (besseli(0, rhoi) - lambdaA * besseli(1, rhoi)) * besselk(1, rhoo))
        self.eQPabsb = fQP