Beispiel #1
0
 def test_evenly_spaced(self):
     """Unevenly spaced input generates evenly spaced output."""
     x_uneven = np.logspace(1, 2, 100)
     x_even, _ = resample_evenly(
             x_uneven, np.zeros_like(x_uneven), len(x_uneven))
     dx = np.diff(x_even)
     np.testing.assert_allclose(dx, dx[0])
Beispiel #2
0
 def test_double_n_points(self):
     """Samples are correctly interpolated when n_points is ~doubled."""
     n_points = len(self.x) * 2 - 1
     x, y = resample_evenly(self.x, self.y, n_points)
     x_expected = np.linspace(self.x[0], self.x[-1], n_points)
     y_expected = self._signal(x_expected)
     np.testing.assert_array_equal(x, x_expected)
     np.testing.assert_allclose(y, y_expected)
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
Beispiel #4
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
Beispiel #6
0
 def test_default_n_points(self):
     """Returns 1+2**k samples if n_points isn't specified."""
     x_cant_romberg = np.arange(100)
     x_can_romberg, _ = resample_evenly(x_cant_romberg, x_cant_romberg)
     self.assertEqual(len(x_can_romberg), 129)
Beispiel #7
0
 def test_half_n_points(self):
     """Every other sample is dropped if (odd) n_points is ~halved."""
     n_points = len(self.x) // 2 + 1
     x, y = resample_evenly(self.x, self.y, n_points)
     np.testing.assert_array_equal(x, self.x[::2])
     np.testing.assert_allclose(y, self.y[::2])
Beispiel #8
0
 def test_same_n_points(self):
     """Samples are (roughly) unchanged if sampled at same rate."""
     n_points = len(self.x)
     x, y = resample_evenly(self.x, self.y, n_points)
     np.testing.assert_array_equal(x, self.x)
     np.testing.assert_allclose(y, self.y)
Beispiel #9
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