Physics::Ellipsometry::VASE

Variable Angle Spectroscopic Ellipsometry analysis for Perl / PDL.

Description

Physics::Ellipsometry::VASE is a complete framework for spectroscopic ellipsometry data analysis. It includes built-in optical models, a transfer matrix method, and global optimisation — so you can go from raw VASE data to fitted film parameters in a few lines of code.

Core features

Installation

From source:

perl Makefile.PL
make
make test
make install

Quick Start

use PDL;
use PDL::NiceSlice;
use Physics::Ellipsometry::VASE;
use Physics::Ellipsometry::VASE::TMM qw(psi_delta);
use Physics::Ellipsometry::VASE::Dispersion qw(cauchy_nk);
use Physics::Ellipsometry::VASE::Materials qw(load_material interpolate_material);

# Load data and substrate material
my $vase = Physics::Ellipsometry::VASE->new(
    layers         => 2,
    circular_delta => 1,       # circular Delta residuals
    deriv_step     => 1e-3,
);
$vase->load_data('measurement.dat');

my $substrate = load_material('si_jaw.mat');

# Model: Air / Cauchy film / Si substrate
sub my_model {
    my ($params, $x) = @_;
    my $lambda = $x->(:,0);
    my $theta  = $x->(:,1);

    my ($n_film, $k_film) = cauchy_nk($lambda, $params->at(0), $params->at(1), 0);
    my $N_film = $n_film + i() * $k_film;

    my ($n_sub, $k_sub) = interpolate_material($substrate, $lambda);
    my $N_sub = $n_sub + i() * $k_sub;

    my $N_air = pdl(1.0) + i() * pdl(0.0);
    my $d_nm  = $params->at(2);

    my ($psi, $delta) = psi_delta(
        $lambda, $theta,
        [$N_air, $N_film, $N_sub],
        [$d_nm],
    );
    return $psi->append($delta);
}

$vase->set_model(\&my_model);
my $fitted = $vase->fit(pdl [2.0, 0.01, 100.0]);
my $mse    = $vase->mse($fitted, nparams => 3);
printf "MSE = %.4f, thickness = %.1f nm\n", $mse, $fitted->at(2);

$vase->plot($fitted, output => 'fit.png');

Submodules

VASE::TMM — Transfer Matrix Method

use Physics::Ellipsometry::VASE::TMM qw(psi_delta);

my ($psi, $delta) = psi_delta(
    $lambda_nm, $theta_deg,
    [$N_ambient, $N_film1, $N_film2, $N_substrate],  # complex N piddles
    [$d_film1_nm, $d_film2_nm],                       # layer thicknesses
    delta_ref => $measured_delta,                      # optional alignment
);

VASE::Dispersion — Optical dispersion models

All functions use direct-call syntax (no closures) to avoid PDL::NiceSlice source-filter conflicts.

use Physics::Ellipsometry::VASE::Dispersion qw(cauchy_nk sellmeier_nk
    tauc_lorentz_nk drude_nk genosc_nk);

my ($n, $k) = cauchy_nk($lambda, $A, $B, $C);
my ($n, $k) = sellmeier_nk($lambda, [1.28, 0.01], [0.01, 100]);
my ($n, $k) = tauc_lorentz_nk($lambda, $A, $E0, $Gamma, $Eg, $eps_inf);
my ($n, $k) = drude_nk($lambda, $eps_inf, $omega_p, $gamma);
my ($n, $k) = genosc_nk($lambda, [[100, 4.0, 1.0], [50, 6.0, 2.0]], 1.0);

VASE::EMA — Effective Medium Approximation

use Physics::Ellipsometry::VASE::EMA qw(ema_linear ema_bruggeman ema_maxwell_garnett);

my $eps_eff = ema_bruggeman($eps_host, $eps_inclusion, $volume_fraction);
my $N_eff   = sqrt($eps_eff);

VASE::Materials — Optical constants loader

use Physics::Ellipsometry::VASE::Materials qw(load_material interpolate_material);

my $mat = load_material('ta_pbp.mat');   # auto eV→nm, Windows CR stripping
my ($n, $k) = interpolate_material($mat, $lambda_grid);

VASE::Parameter — Bounds & vary/fix control

use Physics::Ellipsometry::VASE::Parameter qw(param params_to_pdl make_fit_model);

my $params = [
    param(name => 'thickness', value => 200, min => 50, max => 350),
    param(name => 'offset',    value => 0.5, vary => 0),  # fixed
];
my $fit_model = make_fit_model($params, \&full_model);
my $init      = params_to_pdl($params);

VASE::Optimizer — Global optimisation

use Physics::Ellipsometry::VASE::Optimizer qw(differential_evolution grid_search);

my ($best, $cost) = differential_evolution(
    objective => sub { ... return $chi2 },
    bounds    => [[1.8, 2.5], [0, 0.05], [50, 350]],
    verbose   => 1,
);

my ($best, $cost) = grid_search(
    objective   => $objective,
    base_params => $initial_pdl,
    grid        => [{ index => 0, min => 100, max => 300, steps => 50 }],
);

Data Formats

Simple format

# Wavelength(nm)  Angle(deg)  Psi(deg)  Delta(deg)
400  70  45.0  120.0
410  70  44.5  121.0

Woollam VASE format

Recognised automatically when line 2 begins with VASEmethod[. Header metadata is stored as object attributes; sigma columns are extracted and used as fit weights.

w1_11012006
VASEmethod[EllipsometerType=4, Isotropic, ...]
Original[w1_11012006.dat]
nm
245.732520	64.999634	14.355849	149.18707	0.04032	0.132363

Material files (.mat)

Woollam-style 3-line header (name, units, count) followed by wavelength / n / k columns. Units can be nm or eV (auto-converted).

Model Function Contract

sub model {
    my ($params, $x) = @_;
    # $params — PDL piddle of fit parameters
    # $x      — (npts, 2) piddle: col 0 = wavelength (nm), col 1 = angle (deg)
    #
    # Must return: $psi->append($delta)
    #   — flat piddle of length 2*npts (all psi then all delta)
}

Examples

The examples/ directory contains working scripts:

| Script | Description | |--------|-------------| | fit_linear.pl | Minimal linear dispersion model | | vase_test_fit.pl | Cauchy thin-film model (Ta₂O₅ on Si) with Fresnel equations | | vase_tauc_lorentz_fit.pl | Tauc-Lorentz oscillator with numerical Kramers-Kronig | | Cap_01242007/fit_vase.pl | Full 4-medium Ta₂O₅/Ta metal analysis using all v1.00 modules | | Cap_01242007/fit_refellips.py | Same analysis in Python/refellips for comparison |

Dependencies

Author

Jovan Trujillo jtrujil1@asu.edu
Advanced Electronics and Photonics Core, Arizona State University

License

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.