def _extract_theta_romb(fields: np.ndarray, ics: np.ndarray) -> np.ndarray:
    """Compute Eq. (5) of [1] using Romberg integration."""
    if not can_romberg(fields):
        fine_fields, fine_ics = resample_evenly(fields, ics)
    else:
        fine_fields, fine_ics = fields, ics
    step = abs(fine_fields[0] - fine_fields[1])

    theta = np.empty_like(fields)
    for i, (field, ic) in enumerate(zip(fields, ics)):
        # don't divide by zero
        denom = field**2 - fine_fields**2
        denom[denom == 0] = 1e-9

        theta[i] = field / np.pi * romb(
            (np.log(fine_ics) - np.log(ic)) / denom, step)
    return theta
示例#2
0
def produce_fraunhofer_fast(
        magnetic_field: np.ndarray,
        f2k: float,  # field-to-wavevector conversion factor
        cd: np.ndarray,  # current distribution
        xs: np.ndarray,
        ret_fourier: Optional[bool] = False) -> np.ndarray:
    """Generate Fraunhofer from current density using Romberg integration.

    If ret_fourier is True, return the Fourier transform instead of the
    critical current (useful for debugging).
    """
    g = np.empty_like(magnetic_field, dtype=complex)
    if not can_romberg(xs):
        xs, cd = resample_evenly(xs, cd)

    dx = abs(xs[0] - xs[1])
    for i, field in enumerate(magnetic_field):
        g[i] = romb(cd * np.exp(1j * f2k * field * xs), dx)

    return g if ret_fourier else np.abs(g)
def extract_current_distribution(
    fields: np.ndarray,
    ics: np.ndarray,
    f2k: float,
    jj_width: float,
    jj_points: int,
    debug: bool = False,
    theta: Optional[np.ndarray] = None,
) -> Tuple[np.ndarray, np.ndarray]:
    """Extract the current distribution from Ic(B).

    Parameters
    ----------
    fields : np.ndarray
        1D array of the magnetic field at which the critical current was measured.
    ics : np.ndarray
        1D array of the measured value of the critical current.
    f2k : float
        Field to wave-vector conversion factor. This can be estimated from the
        Fraunhofer periodicity.
    jj_width : float
        Size of the junction. The current distribution will be reconstructed on
        a larger region (2 * jj_width)
    jj_points : int
        Number of points used to describe the junction inside jj_width.
    theta : np.ndarray, optional
        Phase distribution to use in the current reconstruction. If None, it
        will be extracted from the given Fraunhofer pattern.

    Returns
    -------
    np.ndarray
        Positions at which the current density was calculated.
    np.ndarray
        Current density.

    """
    if not can_romberg(fields):
        fine_fields, fine_ics = resample_evenly(fields, ics)
    else:
        fine_fields, fine_ics = fields, ics

    fine_ics[np.less_equal(fine_ics, 1e-10)] = 1e-10

    if theta is None:
        theta = extract_theta(fine_fields, fine_ics, f2k, jj_width)

    # scale from B to beta
    fine_fields = f2k * fine_fields
    step = abs(fine_fields[0] - fine_fields[1])

    xs = np.linspace(-jj_width, jj_width, int(2 * jj_points))
    j = np.empty(xs.shape, dtype=complex)
    for i, x in enumerate(xs):
        j[i] = (1 / (2 * np.pi) *
                romb(fine_ics * np.exp(1j * (theta - fine_fields * x)), step))

    if debug:
        f, axes = plt.subplots(2, 1)
        axes[0].plot(f2k * fields, ics)
        axes[1].plot(xs, j.real)
        plt.show()

    return xs, j.real
示例#4
0
 def test_unevenly_spaced(self):
     """Unevenly spaced data return false."""
     x = np.arange(5)  # length is good
     self.assertTrue(can_romberg(x))
     x[-1] = 6  # spacing is bad
     self.assertFalse(can_romberg(x))
示例#5
0
 def test_length_less_than_2(self):
     """Lengths less than 2 return false."""
     self.assertFalse(can_romberg([]))
     self.assertFalse(can_romberg([1]))
示例#6
0
 def test_length_is_1_plus_power_of_2(self):
     """Lengths like 2**n + 1 return true."""
     result = [n for n in range(1026) if can_romberg(np.arange(n))]
     expected = [2, 3, 5, 9, 17, 33, 65, 129, 257, 513, 1025]
     self.assertEqual(result, expected)
示例#7
0
def extract_theta(
    fields: np.ndarray,
    ics: np.ndarray,
    f2k: float,  # field-to-k conversion factor (i.e. beta/B)
    jj_width: float,
    integ_method: Optional[Literal['romb', 'quad']] = 'romb'
) -> np.ndarray:
    """Compute the Ic Hilbert transform.

    Note the expression for θ(β) differs from Dynes and Fulton (1971) by a
    factor of 2, but gives the correct output. (The source of this discrepancy
    is as yet unknown.)

    Parameters
    ----------
    fields : np.ndarray
        Magnetic field at which the critical current was measured. For ND input
        the sweep should occur on the last axis.
    ics : np.ndarray
        Measured value of the critical current. For ND input the sweep should
        occur on the last axis.

    Returns
    -------
    np.ndarray
        Hilbert tranform of Ic to be used when rebuilding the current
        distribution.

    """
    # scale B to beta first; then forget about it
    fields = fields * f2k

    if integ_method == 'romb':
        if not can_romberg(fields):
            fine_fields, fine_ics = resample_evenly(fields, ics)
        else:
            fine_fields, fine_ics = fields, ics
        step = abs(fine_fields[0] - fine_fields[1])

        def integrand(beta, ic):
            denom = beta**2 - fine_fields**2
            denom[denom == 0] = 1e-9
            return (np.log(fine_ics) - np.log(ic)) / denom
    elif integ_method == 'quad':
        fine_ics = interp1d(fields, ics, 'cubic')
        def integrand(b, beta, ic):
            # quad will provide b when calling this
            denom = beta**2 - b**2
            if denom == 0:
                denom = 1e-9
            return (np.log(fine_ics(b)) - np.log(ic)) / denom
    else:
        raise ValueError(f"Integration method '{integ_method}' unsupported")

    theta = np.empty_like(fields)
    for i, (field, ic) in enumerate(zip(fields, ics)):
        if integ_method == 'romb':
            theta[i] = field / np.pi * romb(integrand(field, ic), step) \
                    - field * jj_width / 2
        elif integ_method == 'quad':
            theta[i] = (field / np.pi
                    * quad(integrand, np.min(fields), np.max(fields),
                        args=(field, ic))[0]
                    - field * jj_width / 2)
    return theta