# 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 - **Data loading** — simple whitespace-delimited files and native J.A. Woollam VASE instrument format (auto-detected), with automatic eV ↔ nm conversion - **Transfer Matrix Method** (`VASE::TMM`) — physics sign convention (*e*+2*iβ*), Verdet Fresnel coefficients, arbitrary layer counts - **Dispersion models** (`VASE::Dispersion`) — Cauchy, Sellmeier, Tauc-Lorentz, Drude, General Oscillator - **EMA mixing rules** (`VASE::EMA`) — Linear, Bruggeman, Maxwell-Garnett - **Material file loader** (`VASE::Materials`) — point-by-point `.mat` files with automatic eV/nm unit handling and interpolation - **Circular Delta residuals** — proper handling of the 0°/360° wrap in the Levenberg-Marquardt objective function - **Parameter bounds & vary/fix** (`VASE::Parameter`) — logit-transformed bounded optimisation, fixed-parameter support - **Global optimiser** (`VASE::Optimizer`) — Differential Evolution (DE/rand/1/bin) and grid search for initial parameter estimation - **Weighted fitting** — uses measured uncertainties (sigma) from Woollam files - **Numerical Jacobian** — configurable finite-difference step with minimum absolute floor - **Plotting** — multi-angle colour-coded overlays via PDL::Graphics::Gnuplot ## Installation From source: ```bash perl Makefile.PL make make test make install ``` ## Quick Start ```perl 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 ```perl 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. ```perl 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 ```perl 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 ```perl 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 ```perl 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 ```perl 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 ```perl 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 - [PDL](https://metacpan.org/pod/PDL) ≥ 2.0 - [PDL::Fit::LM](https://metacpan.org/pod/PDL::Fit::LM) - [PDL::NiceSlice](https://metacpan.org/pod/PDL::NiceSlice) - [PDL::Graphics::Gnuplot](https://metacpan.org/pod/PDL::Graphics::Gnuplot) (optional, for plotting) ## Author Jovan Trujillo 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.