Exemple #1
0
def bdsnr(metric_set1, metric_set2, pchip=True):
    """
    BJONTEGAARD    Bjontegaard metric calculation
    Bjontegaard's metric allows to compute the average gain in psnr between two
    rate-distortion curves [1].
    rate1,psnr1 - RD points for curve 1
    rate2,psnr2 - RD points for curve 2

    returns the calculated Bjontegaard metric 'dsnr'

    code adapted from code written by : (c) 2010 Giuseppe Valenzise
    http://www.mathworks.com/matlabcentral/fileexchange/27798-bjontegaard-metric/content/bjontegaard.m
    """
    # pylint: disable=too-many-locals
    # numpy seems to do tricks with its exports.
    # pylint: disable=no-member
    # map() is recommended against.
    # pylint: disable=bad-builtin
    metric_set1 = preprocess(metric_set1, 0)
    metric_set2 = preprocess(metric_set2, 0)
    rate1 = [x[0] for x in metric_set1]
    psnr1 = [x[1] for x in metric_set1]
    rate2 = [x[0] for x in metric_set2]
    psnr2 = [x[1] for x in metric_set2]

    log_rate1 = list(map(math.log, rate1))
    log_rate2 = list(map(math.log, rate2))

    # Integration interval.
    min_int = max([min(log_rate1), min(log_rate2)])
    max_int = min([max(log_rate1), max(log_rate2)])

    if pchip:
        poly1 = PchipInterpolator(log_rate1, psnr1)
        poly2 = PchipInterpolator(log_rate2, psnr2)

        int1 = poly1.integrate(min_int, max_int)
        int2 = poly2.integrate(min_int, max_int)
    else:
        # Best cubic poly fit for graph represented by log_ratex, psrn_x.
        poly1 = np.polyfit(log_rate1, psnr1, 3)
        poly2 = np.polyfit(log_rate2, psnr2, 3)

        # Integrate poly1, and poly2.
        p_int1 = np.polyint(poly1)
        p_int2 = np.polyint(poly2)

        # Calculate the integrated value over the interval we care about.
        int1 = np.polyval(p_int1, max_int) - np.polyval(p_int1, min_int)
        int2 = np.polyval(p_int2, max_int) - np.polyval(p_int2, min_int)

    # Calculate the average improvement.
    if max_int != min_int:
        avg_diff = (int2 - int1) / (max_int - min_int)
    else:
        avg_diff = 0.0
    return avg_diff
Exemple #2
0
def complex_conductivity(stacks, frequencies, temperatures=None,
                         norm=True, update=True, squeeze=True):
    """
    Compute the (normalized) complex conductivity at each z grid point
    in a stack.

    Args:
        stacks: iterable of superconductivity.multilayer.Stack
            Stack classes defining the geometry of the multilayer. There
            should be one for each temperature. If a single stack is
            given, the temperatures keyword argument can be used to
            broadcast the geometry to more than one temperature. If a
            stack is given outside of an iterable or the broadcasting is
            used, the stack is copied first. Otherwise, the stack is updated
            inplace.
        frequencies: iterable of floats
            The frequencies in Hz at which to evaluate the conductivity.
            All inputs are immediately coerced into numpy arrays.
        temperatures: iterable of floats
            If only one stack is provided, this keyword argument sets
            the temperatures at which to evaluate the conductivity. It
            defaults to None, and the temperatures are determined by the
            temperatures of the stacks.
        norm: boolean
            The default is True and the conductivity is reported
            normalized to the normal state conductivity. If False, the
            complex conductivity is reported in units of Ohms^-1 m^-1.
        update: boolean
            The default is True, and the density of states will be
            computed in this function. To skip this computation, set
            this keyword argument to False.
        squeeze: boolean
            The default is True and singleton dimensions in the output
            array are removed.

    Returns:
        sigma: numpy.ndarray
            The complex conductivity of the geometry. The axes
            correspond to (temperature, frequency, position). If any
            dimension has size 1 and the keyword argument 'squeeze' is
            True, it is removed.
    """
    log.info("Computing the complex conductivity.")
    # Coerce the inputs into the right form.
    if isinstance(stacks, Stack):
        stacks = [copy.deepcopy(stacks)]

    if len(stacks) == 1 and temperatures is None:
        temperatures = np.array([stacks[0].t[0]])
    elif len(stacks) == 1 and temperatures is not None:
        temperatures = np.asarray(temperatures).ravel()
        stacks = [copy.deepcopy(stacks[0]) for i in range(temperatures.size)]
        for i, stack in enumerate(stacks):
            stack.t = temperatures[i]
    elif temperatures is not None:
        raise ValueError("The 'temperatures' keyword argument can only be "
                         "used if only one stack is provided.")
    else:
        temperatures = np.array([stack.t[0] for stack in stacks])

    frequencies = np.asarray(frequencies).ravel()

    # Check to see if we've computed this value recently and return that value
    # instead. This is useful for fitting codes which may query the same
    # values multiple times.
    key = (repr(stacks), frequencies.tostring(), temperatures.tostring(),
           norm, update, squeeze)
    if key in _precomputed_values.keys():
        return _precomputed_values[key]

    # Check that there are the right number of stacks
    if len(stacks) != temperatures.size:
        raise ValueError("'stacks' must be a single stack or a list of "
                         "stacks of the same length as 'temperatures'.")

    for i, stack in enumerate(stacks):
        # If we are updating the stack, also use a better energy grid.
        if update:
            prefactor = BCS / np.pi * stack.scale
            hf = h * frequencies.max() / prefactor
            stack.e = prefactor * np.linspace(0.0, 64.0 + hf, 20000)
        # Check that all stacks have the same energy grid.
        if (stack.e != stacks[0].e).any():
            raise ValueError("The given 'stacks' do not have consistent "
                             "energy grids.")
        # Check that all stacks have the same position grid.
        if (stack.z != stacks[0].z).any():
            raise ValueError("The given 'stacks' do not have consistent "
                             "position grids.")
        # Check that all stacks have the right temperature.
        if (stack.t != temperatures[i]).any():
            raise ValueError("The given 'stacks' do not have consistent "
                             "temperatures.")

    # Create the output array.
    sigma = np.empty((temperatures.size, frequencies.size, stacks[0].z.size),
                     dtype=complex)

    # Get the maximum energy and frequency for the calculation
    max_e = stacks[0].e.max()
    max_hf = h * frequencies.max()

    # Extract the energy vector making it extend to negative energies.
    e = np.hstack([-stacks[0].e[:0:-1], stacks[0].e])

    # Create a fermi function that returns the right shape.
    def f(en, temp):
        return fermi(en, temp, units='J')[:, np.newaxis]

    # Loop over the given temperatures.
    for it, t in enumerate(temperatures):
        # Compute the density of states at this temperature.
        if update:
            stacks[it].update_dos()

        # Loop over the layers.
        z_stop = 0
        for layer in stacks[it].layers:
            z_start = z_stop
            z_stop = z_start + layer.z.size
            # Get the complex density of states and density of pairs
            # data at all energies.
            dos_r = np.concatenate([np.cos(layer.theta[:0:-1, :]).real,
                                    np.cos(layer.theta).real], axis=0)
            dos_i = np.concatenate([-np.cos(layer.theta[:0:-1, :]).imag,
                                    np.cos(layer.theta).imag], axis=0)
            dop_r = np.concatenate([(1j * np.sin(layer.theta[:0:-1, :])).real,
                                    (-1j * np.sin(layer.theta)).real], axis=0)
            dop_i = np.concatenate([(-1j * np.sin(layer.theta[:0:-1, :])).imag,
                                    (-1j * np.sin(layer.theta)).imag], axis=0)

            # Create PCHIP functions from the data arrays.
            with np.errstate(invalid="ignore"):
                dos_r = PchipInterpolator(e, dos_r, extrapolate=True, axis=0)
                dos_i = PchipInterpolator(e, dos_i, extrapolate=True, axis=0)
                dop_r = PchipInterpolator(e, dop_r, extrapolate=True, axis=0)
                dop_i = PchipInterpolator(e, dop_i, extrapolate=True, axis=0)

            # Loop over the given frequencies.
            for iv, v in enumerate(h * frequencies):
                # Create the integrand arrays.
                # Herman et al. Phys. Rev. B, 96, 1, 2017.
                integrand1 = (f(e, t) - f(e + v, t)) * (
                    dos_r(e) * dos_r(e + v) + dop_r(e) * dop_r(e + v)) / v
                integrand2 = -(1 - 2 * f(e, t)) * (
                    dos_r(e) * dos_i(e + v) + dop_r(e) * dop_i(e + v)) / v

                # Turn them into functions by PCHIP interpolation.
                with np.errstate(invalid="ignore"):
                    integrand1 = PchipInterpolator(e, integrand1,
                                                   extrapolate=True, axis=0)
                    integrand2 = PchipInterpolator(e, integrand2,
                                                   extrapolate=True, axis=0)

                # Compute the complex conductivity at this frequency and
                # temperature for this layer in the stack. We choose the
                # largest valid value for infinity. All of the interesting
                # information in the integrand should be close to the gap
                # energy anyways.
                pos_inf = max_e - max_hf
                neg_inf = -max_e - max_hf
                sigma1 = integrand1.integrate(neg_inf, pos_inf)
                sigma2 = integrand2.integrate(neg_inf, pos_inf)

                # Fill the output array.
                sigma[it, iv, z_start:z_stop] = sigma1 - 1j * sigma2

                # Give sigma units if we aren't normalizing it.
                if not norm:
                    sigma[it, iv, z_start:z_stop] /= layer.rho

    # Remove the extra dimensions.
    if squeeze:
        sigma = sigma.squeeze()

    # Save the result so that we can skip the computation if we want the
    # same values again.
    _precomputed_values[key] = sigma

    return sigma
Exemple #3
0
def bdrate(metric_set1, metric_set2, pchip=True):
    """
    BJONTEGAARD    Bjontegaard metric calculation
    Bjontegaard's metric allows to compute the average % saving in bitrate
    between two rate-distortion curves [1].

    rate1,psnr1 - RD points for curve 1
    rate2,psnr2 - RD points for curve 2

    adapted from code from: (c) 2010 Giuseppe Valenzise

    """
    # numpy plays games with its exported functions.
    # pylint: disable=no-member
    # pylint: disable=too-many-locals
    # pylint: disable=bad-builtin
    metric_set1 = preprocess(metric_set1, 1)
    metric_set2 = preprocess(metric_set2, 1)
    rate1 = [x[0] for x in metric_set1]
    psnr1 = [x[1] for x in metric_set1]
    rate2 = [x[0] for x in metric_set2]
    psnr2 = [x[1] for x in metric_set2]

    log_rate1 = list(map(math.log, rate1))
    log_rate2 = list(map(math.log, rate2))

    # Integration interval.
    min_int = max([min(psnr1), min(psnr2)])
    max_int = min([max(psnr1), max(psnr2)])

    if pchip:
        poly1 = PchipInterpolator(psnr1, log_rate1)
        poly2 = PchipInterpolator(psnr2, log_rate2)

        int1 = poly1.integrate(min_int, max_int)
        int2 = poly2.integrate(min_int, max_int)
    else:
        # Best cubic poly fit for graph represented by log_ratex, psrn_x.
        poly1 = np.polyfit(psnr1, log_rate1, 3)
        poly2 = np.polyfit(psnr2, log_rate2, 3)

        # find integral
        p_int1 = np.polyint(poly1)
        p_int2 = np.polyint(poly2)

        # Calculate the integrated value over the interval we care about.
        int1 = np.polyval(p_int1, max_int) - np.polyval(p_int1, min_int)
        int2 = np.polyval(p_int2, max_int) - np.polyval(p_int2, min_int)

    # Calculate the average improvement.
    avg_exp_diff = (int2 - int1) / (max_int - min_int)

    # In really bad formed data the exponent can grow too large.
    # clamp it.
    if avg_exp_diff > 200:
        avg_exp_diff = 200

    # Convert to a percentage.
    avg_diff = (math.exp(avg_exp_diff) - 1) * 100

    return avg_diff