Exemple #1
0
def arc_reflection_model(wavelength,
                         thickness=125,
                         fraction_abraded=0,
                         porosity=0.3,
                         fraction_dust=0,
                         aoi=8,
                         n0=1.0003):
    """
    Return the reflection values for a model of an aged ARC. The reflection
    is a linear combination of reflection from a thin film of a variable
    thickness and the reflection from BK7 glass.

    Parameters
    ----------
    wavelength : ndarray

        wavelength in nm

    thickness

        thickness of ARC in nm.

    fraction_abraded

        fraction of coating loss. 1 corresponds to 100% of the coating area
        removed and only underlying glass is present, 0 corresponds to the
        coating covering the entire sample.

    fraction_dust

        fraction of module area covered by dust with reflectivity of 1. A
        value of 0 corresponds to no dust (clean sample), 1 to full dust
        coverage (reflectivity of 1).

    aoi

        angle of incidence in degrees.

    Returns
    -------

    reflectance : ndarray

        Reflectance of sample at the values of wavelength specified.

    """

    index_substrate = refractive_index_glass(wavelength)
    index_film = refractive_index_porous_silica(wavelength, porosity=porosity)
    glass_reflectance = single_interface_reflectance(
        n0=n0,
        n1=index_substrate,
        polarization='mixed',
        aoi=aoi)

    thin_film_R = thin_film_reflectance(index_film=index_film,
                                        index_substrate=index_substrate,
                                        film_thickness=thickness,
                                        aoi=aoi,
                                        wavelength=wavelength)

    reflectance = (1 - fraction_dust) * (
            fraction_abraded * glass_reflectance + (
            1 - fraction_abraded) * thin_film_R) + fraction_dust

    return reflectance
Exemple #2
0
def fit_arc_reflection_spectrum(wavelength,
                                reflectance,
                                x0=None,
                                aoi=8,
                                model='d',
                                fixed=None,
                                verbose=False,
                                method='basinhopping',
                                niter=20,
                                wavelength_min=450,
                                wavelength_max=1000):
    """
    This function fits an ARC model to the reflection spectrum. The ARC model
    is described in the function arc_reflection_model.

    Parameters
    ----------
    wavelength : ndarray

        Wavelength in nm

    reflectance : ndarray

        Fractional reflection between 0 and 1, unitless.

    x0 : dict

        Startpoint for fitting algorithm.

    aoi : float

        Angle of incidence of light

    model : str

        Different variation of the same model are available. The model is
        described under the function `arc_reflection_model`. The fixed values
        are specified in the input 'fixed'.

        'TP' - thickness and poristy are fit, fraction_abraded and fraction_dust
        set to fixed values.

        'TPA' - thickness, porosity and fraction_abraded are fit,
        fraction_dust is fixed.

        'TPAD' - thickness, porosity, fraction_abraded and fraction_dust are
        fit.

        'TAD' - thickness, fraction_abraded, fraction_dust are fit, porosity
        is a fixed value.

    fixed : dict

        Dictionary of parameters to be fixed. Default is:
           fixed = {'thickness': 125, 'fraction_abraded': 0, 'fraction_dust': 0,
             'porosity': 0.3}

     verbose : bool

        Whether to print output at each iteration.

    method : str

        Optimization method, can be 'minimize' or 'basinhopping'

    niter : int

        Number of basinhopping steps if method == 'basinhopping'

    wavelength_min : float

        Lower bound for wavelength values used in fit.

    wavelength_max : float

        Upper bound for wavelength values used in fit.

    Returns
    -------

    result : dict

        Dictionary of best fit values.

    ret

        Output of optimizer.

    """

    if np.mean(reflectance) > 1:
        print(
            'Warning: check that reflectance is a fractional value between 0 and 1.')

    if x0 == None:
        x0 = estimate_arc_reflection_model_params(wavelength, reflectance)

    fixed_default = {'thickness': 125,
                     'fraction_abraded': 0,
                     'fraction_dust': 0,
                     'porosity': 0.3}
    if fixed == None:
        fixed = fixed_default
    else:
        for p in fixed_default:
            if p not in fixed:
                fixed[p] = fixed_default[p]

    # print('x0: ', x0)

    scale = {'thickness': 0.01,
             'fraction_abraded': 1,
             'fraction_dust': 1000,
             'porosity': 1}

    if model == 'TPA':
        x0_list = [x0['thickness'] * scale['thickness'],
                   x0['fraction_abraded'] * scale['fraction_abraded'],
                   x0['porosity'] * scale['porosity']
                   ]
    elif model == 'TAD':
        x0_list = [x0['thickness'] * scale['thickness'],
                   x0['fraction_abraded'] * scale['fraction_abraded'],
                   x0['fraction_dust'] * scale['fraction_dust']
                   ]
    elif model == 'TPAD':
        x0_list = [x0['thickness'] * scale['thickness'],
                   x0['fraction_abraded'] * scale['fraction_abraded'],
                   x0['fraction_dust'] * scale['fraction_dust'],
                   x0['porosity'] * scale['porosity'],
                   ]
    elif model == 'TP':
        x0_list = [x0['thickness'] * scale['thickness'],
                   x0['porosity'] * scale['porosity'],
                   ]
    else:
        raise Exception('model options are "TP", "TPA", "TPAD" or "TAD"')

    # Increase by a factor of 100 to improve numeric accuracy.
    reflectance = reflectance * 100

    reflectance[reflectance < 0] = 0
    reflectance[reflectance > 100] = 100

    cax = np.logical_and(wavelength > wavelength_min,
                         wavelength < wavelength_max)

    wavelength_calc = wavelength.copy()

    index_substrate = refractive_index_glass(wavelength)

    glass_reflectance_calc = single_interface_reflectance(n0=1.0003,
                                                          n1=index_substrate,
                                                          aoi=aoi,
                                                          polarization='mixed',
                                                          )

    # # Get interpolator for correct
    # if not aoi == 8:
    #     raise Exception('aoi must be 8 degrees.')

    thickness_min = 50
    thickness_max = 200
    porosity_max = 0.499

    if model == 'TPA':
        bounds = [(thickness_min * scale['thickness'],
                   thickness_max * scale['thickness']),
                  (0, 1 * scale['fraction_abraded']),
                  (0, porosity_max * scale['porosity']),
                  ]
    elif model == 'TAD':
        bounds = [(thickness_min * scale['thickness'],
                   thickness_max * scale['thickness']),
                  (0, 1 * scale['fraction_abraded']),
                  (0, 1 * scale['fraction_dust']),
                  ]
    elif model == 'TPAD':
        bounds = [(thickness_min * scale['thickness'],
                   thickness_max * scale['thickness']),
                  (0, 1 * scale['fraction_abraded']),
                  (0, 1 * scale['fraction_dust']),
                  (0, porosity_max * scale['porosity']),
                  ]
    elif model == 'TP':
        bounds = [(thickness_min * scale['thickness'],
                   thickness_max * scale['thickness']),
                  (0, porosity_max * scale['porosity']),
                  ]

    def arc_model_c(wavelength, thickness, fraction_abraded, fraction_dust,
                    porosity):
        index_film = refractive_index_porous_silica(wavelength=wavelength,
                                         porosity=porosity)

        index_substrate = refractive_index_glass(wavelength=wavelength)
        thin_film_R = thin_film_reflectance(
            index_film=index_film,
            index_substrate=index_substrate,
            film_thickness=thickness,
            wavelength=wavelength,
            aoi=aoi
        )
        #
        # thin_film_R = thin_film_reflection_fast(
        #     wavelength=wavelength,
        #     thickness=thickness,
        #     aoi=aoi,
        #     porosity=porosity)

        #
        # index_film = index_porous_silica(wavelength=wavelength,
        #                                  porosity=porosity)
        # thin_film_reflectance = thin_film_reflection(
        #     polarization='mixed',
        #     wavelength=wavelength,
        #     d_list=[np.inf, thickness, np.inf],
        #     index_film=index_film,
        #     index_substrate=index_glass,
        #     aoi=aoi)

        glass_reflectance = np.interp(wavelength, wavelength_calc,
                                      glass_reflectance_calc)

        reflectance = (1 - fraction_dust) * (
                fraction_abraded * glass_reflectance + (
                1 - fraction_abraded) * thin_film_R) + fraction_dust

        return 100 * reflectance

    def arc_coating_error_function(x):

        # print('x: ',x)

        if model == 'TPA':
            thickness = x[0] / scale['thickness']
            fraction_abraded = x[1] / scale['fraction_abraded']
            porosity = x[2] / scale['porosity']
            reflectance_model = arc_model_c(wavelength, thickness,
                                            fraction_abraded=fraction_abraded,
                                            fraction_dust=fixed[
                                                'fraction_dust'],
                                            porosity=porosity)
        elif model == 'TAD':
            thickness = x[0] / scale['thickness']
            fraction_abraded = x[1] / scale['fraction_abraded']
            fraction_dust = x[2] / scale['fraction_dust']
            reflectance_model = arc_model_c(wavelength, thickness,
                                            fraction_abraded, fraction_dust,
                                            porosity=fixed['porosity'])
            # if verbose:
            #     print(
            #         'Thickness: {:03.2f}, Fraction Abraded: {:.1%}, Fraction dust: {:.1%}'.format(
            #             thickness,
            #             fraction_abraded,
            #             fraction_dust))
        elif model == 'TPAD':

            thickness = x[0] / scale['thickness']
            fraction_abraded = x[1] / scale['fraction_abraded']
            fraction_dust = x[2] / scale['fraction_dust']
            porosity = x[3] / scale['porosity']
            reflectance_model = arc_model_c(wavelength, thickness,
                                            fraction_abraded, fraction_dust,
                                            porosity)
        elif model == 'TP':

            thickness = x[0] / scale['thickness']
            porosity = x[1] / scale['porosity']
            reflectance_model = arc_model_c(wavelength, thickness,
                                            fraction_abraded=fixed[
                                                'fraction_abraded'],
                                            fraction_dust=fixed[
                                                'fraction_dust'],
                                            porosity=porosity)

        else:
            raise Exception('model type unknown')

        residual = np.mean(
            np.sqrt(np.abs(reflectance_model - reflectance) ** 2))

        return residual

    if method == 'minimize':
        res = minimize(arc_coating_error_function,
                       x0=x0_list,
                       options=dict(
                           # maxiter=100,
                           disp=verbose
                       ),
                       bounds=bounds
                       )
    elif method == 'basinhopping':

        def basinhopping_callback(x, f, accept):
            if model == 'TPA' and verbose:
                # print('x:', x)
                print(
                    '--\nThickness: {:03.2f}, Fraction Abraded: {:.1%}, Porosity: {:.1%}'.format(
                        x[0] / scale['thickness'],
                        x[1] / scale['fraction_abraded'],
                        x[2] / scale['porosity']
                    )
                )

        res = basinhopping(arc_coating_error_function,
                           x0=x0_list,
                           niter=niter,
                           minimizer_kwargs={'bounds': bounds},
                           disp=verbose,
                           callback=basinhopping_callback
                           )

    if model == 'TPA':
        result = {'thickness': res['x'][0] / scale['thickness'],
                  'fraction_abraded': res['x'][1] / scale['fraction_abraded'],
                  'fraction_dust': fixed['fraction_dust'],
                  'porosity': res['x'][2] / scale['porosity']}
    elif model == 'TAD':
        result = {'thickness': res['x'][0] / scale['thickness'],
                  'fraction_abraded': res['x'][1] / scale['fraction_abraded'],
                  'fraction_dust': res['x'][2] / scale['fraction_dust'],
                  'porosity': fixed['porosity']}
    elif model == 'TPAD':
        result = {'thickness': res['x'][0] / scale['thickness'],
                  'fraction_abraded': res['x'][1] / scale['fraction_abraded'],
                  'fraction_dust': res['x'][2] / scale['fraction_dust'],
                  'porosity': res['x'][3] / scale['porosity'],
                  }
    elif model == 'TP':
        result = {'thickness': res['x'][0] / scale['thickness'],
                  'fraction_abraded': fixed['fraction_abraded'],
                  'fraction_dust': fixed['fraction_dust'],
                  'porosity': res['x'][1] / scale['porosity'],
                  }

    return result, res
Exemple #3
0
"""Example for printing a list of the refractive index of BK7 glass, useful
for performing a light reference in the spectrometer software.

"""

import numpy as np
from pvarc.materials import refractive_index_glass
from pvarc import single_interface_reflectance
wavelength = np.arange(190, 1125, 10)
index_glass = refractive_index_glass(wavelength, type='BK7')

reflectance = single_interface_reflectance(n0=1.0003,
                                           n1=index_glass,
                                           aoi=8.0,
                                           polarization='mixed')

print('Glass reflectance')
for k in range(len(wavelength)):
    print('{}\t{:.5f}'.format(wavelength[k], reflectance[k]))
                                     model='TPA',
                                     aoi=8,
                                     wavelength_min=450,
                                     wavelength_max=1000,
                                     method='basinhopping',
                                     verbose=True)
wavelength_extend = np.linspace(300, 1250, 1000)
reflection_fit = arc_reflection_model(wavelength_extend, **x)

# Calculate solar weighted photon reflection (SWPR) using fit
swpr = solar_weighted_photon_reflectance(wavelength_extend, reflection_fit)

# Calculate SWPR for glass reference
index_glass = refractive_index_glass(wavelength_extend)
reflection_BK7 = single_interface_reflectance(n0=1.0003,
                                       n1=index_glass,
                                       aoi=8)
swpr_bk7 = solar_weighted_photon_reflectance(wavelength_extend, reflection_BK7)

# Calculate power enhancement due to coating.
power_enchancement = swpr_bk7 - swpr

# Compare fit vs simulated value.
print('--\nComparison of true values vs. best fit')
for p in ['thickness', 'porosity', 'fraction_abraded']:
    print('{}.\t True: {:.2f}, Fit: {:.2f}, '.format(p, param_true[p], x[p]))

# Plot theory.
plt.plot(wavelength_extend,
         100 * reflection_fit,
         label='Fit',
                                     fixed={'fraction_abraded': 0},
                                     method='minimize')

print('Time for fit: {:.2f}s'.format(time() - start_time))

# Get the reflectance for the fitted model
wavelength_extend = np.linspace(300, 1250, 1000)
reflectance_fit = arc_reflection_model(wavelength_extend, **x)

# Calculate solar weighted photon reflection (SWPR) using fit
swpr = solar_weighted_photon_reflectance(wavelength_extend, reflectance_fit)

# Calculate SWPR for glass reference
index_substrate = refractive_index_glass(wavelength_extend)
reflection_glass = single_interface_reflectance(n0=1.0003,
                                                n1=index_substrate,
                                                aoi=8,
                                                polarization='mixed')

swpr_glass = solar_weighted_photon_reflectance(wavelength_extend,
                                               reflection_glass)

# Calculate power enhancement due to coating.
power_enchancement = swpr_glass - swpr

# Plot theory.
plt.plot(
    wavelength_extend,
    100 * reflectance_fit,
    label='Fit',
    linewidth=3,
    color=[1, 0.5, 0, 0.5],