Physics::PVD

Physical Vapor Deposition simulation framework in Perl

A comprehensive library for simulating PVD thin film growth using Kinetic Monte Carlo (kMC) for atomistic film formation and Direct Simulation Monte Carlo (DSMC) for vapor-phase transport. Includes optional interfaces to OpenFOAM, LAMMPS, and QuantumATK for multi-scale simulations.

Table of Contents

Features

Core Simulation Engines

| Engine | Description | |--------|-------------| | KMC | BKL rejection-free Kinetic Monte Carlo: adsorption, diffusion, desorption, Ehrlich-Schwoebel barriers, oblique deposition, multi-species | | DSMC | Direct Simulation Monte Carlo: Thompson energy distribution, cos^n angular emission, gas-phase collisions, Knudsen number characterization | | Hybrid | DSMC transport → KMC growth coupling (flux/energy/angle distributions) |

Film Analysis

External Tool Interfaces

| Tool | Capabilities | |------|-------------| | OpenFOAM | dsmcFoam+ case generation, mesh, BCs, particle injection, parallel execution | | LAMMPS | Deposition MD, sputtering cascades, post-deposition annealing, EAM/MEAM/Tersoff potentials, dump/log parsing | | QuantumATK | DFT/DFTB binding energies, sputter yield (BCA+MD), adatom diffusion MD, electronic transport |

Key Physics

Installation

From source (this repository)

cd Physics-PVD
perl Makefile.PL
make
make test
make install       # or: make install DESTDIR=~/perl5

Using local lib (no root)

perl Makefile.PL INSTALL_BASE=~/perl5
make && make test && make install
export PERL5LIB=~/perl5/lib/perl5:$PERL5LIB

With cpanm (once published)

cpanm Physics::PVD

Prerequisites

All core dependencies are included with standard Perl installations.

Optional Dependencies

These are only needed if you use the corresponding interface modules:

PDL (for visualization examples)

cpanm PDL PDL::Graphics::Gnuplot

OpenFOAM (for Physics::PVD::Interface::OpenFOAM)

# Ubuntu/Debian
sudo apt install openfoam
# Or from source: https://openfoam.org/download/

# Verify
which blockMesh && which dsmcFoam+

Required executables: blockMesh, dsmcInitialise, dsmcFoam+

LAMMPS (for Physics::PVD::Interface::LAMMPS)

# Ubuntu/Debian
sudo apt install lammps

# From source (recommended for custom packages):
git clone https://github.com/lammps/lammps.git
cd lammps/src
make yes-MANYBODY yes-MOLECULE   # EAM, MEAM potentials
make mpi                          # or: make serial

# Verify
which lmp

Required packages: MANYBODY (EAM/MEAM/Tersoff potentials)

QuantumATK (for Physics::PVD::Interface::QuantumATK)

QuantumATK requires a commercial license from Synopsys:

# Verify
which atkpython
atkpython -c "from QuantumATK import *; print('OK')"

Interatomic Potentials (for LAMMPS)

Download potentials from NIST Interatomic Potentials Repository:

Commonly needed for PVD:

Place in the potentials/ directory or set potential_dir parameter.

Quick Start

use Physics::PVD;

# Create simulation
my $pvd = Physics::PVD->new(temperature => 600, pressure => 5e-3);

# KMC film growth
my $kmc = $pvd->kmc(lattice_size => [100, 100, 50]);
$kmc->add_species(name => 'Ta', mass => 180.95, binding_energy => 8.1);
$kmc->deposit(flux => 1e14, time => 60);

# Results
my $film = $kmc->get_film;
printf "Thickness: %.1f nm, Roughness: %.2f nm\n",
       $film->thickness, $film->roughness;

API Reference

Physics::PVD (main module)

The top-level module provides factory methods and orchestrates simulations.

new(%options)

my $pvd = Physics::PVD->new(
    method      => 'kmc',       # 'kmc' | 'dsmc' | 'hybrid'
    temperature => 300,          # substrate temperature (K)
    pressure    => 1e-3,         # base pressure (Pa)
    verbose     => 0,            # print progress
    seed        => 12345,        # RNG seed for reproducibility
);

configure(%params)

Update simulation parameters after construction.

$pvd->configure(temperature => 700, pressure => 2e-3);

kmc(%options) → Physics::PVD::KMC

Get or create the KMC engine instance.

my $kmc = $pvd->kmc(lattice_size => [100, 100, 50]);

dsmc(%options) → Physics::PVD::DSMC

Get or create the DSMC engine instance.

my $dsmc = $pvd->dsmc(n_particles => 10000);

film(%options) → Physics::PVD::Film

Get or create the Film analysis object.

my $film = $pvd->film;

interface($name, %options) → Interface object

Load and instantiate an external interface module.

my $lmp = $pvd->interface('lammps', executable => '/usr/bin/lmp');
my $foam = $pvd->interface('openfoam', case_dir => './my_case');
my $atk = $pvd->interface('quantumatk', python_path => 'atkpython');

run(%options) → results

Run a complete simulation using the configured method.

my $film = $pvd->run(method => 'kmc', steps => 50000, flux => 1e14);

available_methods() → list

Returns: ('kmc', 'dsmc', 'hybrid')

available_interfaces() → list

Returns: ('lammps', 'openfoam', 'quantumatk')

Physics::PVD::KMC

Kinetic Monte Carlo engine using the BKL rejection-free algorithm.

new(%options)

my $kmc = Physics::PVD::KMC->new(
    lattice_size      => [100, 100, 50],   # [nx, ny, nz] sites
    lattice_type      => 'fcc',             # 'fcc'|'bcc'|'hcp'|'simple_cubic'
    lattice_const     => 3.3,               # Angstrom
    temperature       => 300,               # K
    attempt_freq      => 1e13,              # Hz (Debye frequency)
    diffusion_barrier => 0.7,               # eV
    es_barrier        => 0.15,              # Ehrlich-Schwoebel barrier (eV)
    flux              => 1e14,              # atoms/cm²/s
    deposition_angle  => 0,                 # degrees from surface normal
    seed              => 42,
    verbose           => 1,
);

add_species(%spec)

$kmc->add_species(
    name              => 'Ta',
    mass              => 180.95,       # amu
    binding_energy    => 8.1,          # eV per atom
    diffusion_barrier => 0.7,          # eV (overrides global)
    desorption_energy => 8.1,          # eV
    sticking_coeff    => 1.0,          # 0–1
);

set_rates(%rates)

$kmc->set_rates(
    diffusion    => 0.65,   # eV barrier
    es_barrier   => 0.20,   # eV
    attempt_freq => 5e12,   # Hz
);

set_flux($flux)

$kmc->set_flux(2e14);  # atoms/cm²/s

set_angular_distribution($dist)

Feed a 2D flux map (from DSMC) to bias landing site selection.

$kmc->set_angular_distribution($dsmc->get_flux_distribution);

deposit(%options)

High-level deposition method. Calculates steps from flux × time.

$kmc->deposit(
    flux  => 1e14,   # atoms/cm²/s
    angle => 15,     # degrees (oblique deposition)
    time  => 120,    # seconds
);

run(steps => $n)

Low-level: run exactly N KMC steps.

$kmc->run(steps => 100000);

get_film() → Physics::PVD::Film

Extract current film state as a Film object.

my $film = $kmc->get_film;

get_surface() → arrayref

Returns the 2D height map $surface->[$x][$y] (in lattice layers).

coverage() → float

Fraction of substrate sites with at least one atom (0–1).

stats() → hashref

my $s = $kmc->stats;
# { time => 1.2e-3, steps => 50000, deposited => 1234,
#   coverage => 0.95, events => {adsorption=>1234, diffusion=>45000, desorption=>12} }

Physics::PVD::DSMC

Direct Simulation Monte Carlo for vapor transport.

new(%options)

my $dsmc = Physics::PVD::DSMC->new(
    domain          => [0.1, 0.1, 0.05],  # [W, D, H] meters
    n_cells         => [50, 50, 25],
    n_particles     => 10000,
    dt              => 1e-7,               # time step (s)

    # Gas
    gas_species     => 'Ar',
    gas_mass        => 39.948,             # amu
    pressure        => 1.0,                # Pa
    temperature     => 300,                # K

    # Target
    target_material => 'Ta',
    target_mass     => 180.95,
    target_diameter => 0.05,               # m
    surface_binding => 8.1,                # eV
    cosine_power    => 1,                  # cos^n distribution
    sputter_yield   => 1.0,

    # Geometry
    substrate_distance => 0.04,            # m

    seed    => 42,
    verbose => 1,
);

set_gas(%options)

$dsmc->set_gas(species => 'Kr', mass => 83.798, pressure => 3.0, temperature => 350);

set_target(%options)

$dsmc->set_target(material => 'Cu', mass => 63.546, surface_binding => 3.5, yield => 2.3);

set_substrate(%options)

$dsmc->set_substrate(distance => 0.06);  # 6 cm throw

run(timesteps => $n)

$dsmc->run(timesteps => 5000);

get_flux_distribution() → arrayref (2D)

Returns $flux->[$ix][$iy] — particle count per cell on substrate.

get_energy_distribution() → arrayref

List of arrival energies (eV) for all particles that reached the substrate.

get_angular_distribution() → arrayref

List of arrival angles (degrees from normal) for substrate-arriving particles.

mean_arrival_energy() → float (eV)

knudsen_number() → float

Ratio of mean free path to target-substrate distance.

stats() → hashref

my $s = $dsmc->stats;
# { total_particles => 10000, arrived => 7500, still_flying => 200,
#   mean_energy_eV => 3.2, knudsen_number => 2.5, time => 5e-4 }

Physics::PVD::Film

Film data structure with analysis methods.

new(%options)

my $film = Physics::PVD::Film->new(
    lattice_size  => [100, 100, 50],
    lattice_const => 3.3,     # Angstrom
);

add_atom(%atom)

Add atoms explicitly (alternative to lattice-from-KMC mode).

$film->add_atom(species => 'Ta', x => 5.0, y => 3.2, z => 1.1);

thickness() → float (nm)

Average film thickness.

roughness() → float (nm)

RMS surface roughness.

density() → float (0–1)

Occupied fraction of lattice sites below the maximum height.

porosity() → float (0–1)

1 - density()

composition_profile() → arrayref of hashrefs

my $prof = $film->composition_profile;
# [ {z_nm => 0.165, total_atoms => 980, Ta => 0.6, Cu => 0.4}, ... ]

export_xyz($filename)

Write film to XYZ format for visualization in OVITO, VMD, or ASE.

my $n = $film->export_xyz('my_film.xyz');  # returns atom count

export_lammps_data($filename)

Write film as LAMMPS data file for continued MD simulation.

$film->export_lammps_data('my_film.data');

summary() → hashref

my $s = $film->summary;
# { thickness_nm => 5.2, roughness_nm => 0.34, density => 0.92,
#   porosity => 0.08, n_atoms => 12500 }

Physics::PVD::Interface::OpenFOAM

Interface to OpenFOAM's dsmcFoam+ solver.

new(%options)

my $foam = Physics::PVD::Interface::OpenFOAM->new(
    case_dir        => './openfoam_pvd',
    executable      => 'dsmcFoam+',
    n_procs         => 4,
    domain_size     => [0.1, 0.1, 0.05],   # m
    n_cells         => [50, 50, 25],
    gas_species     => 'Ar',
    gas_pressure    => 1.0,                 # Pa
    gas_temperature => 300,                 # K
    target_species  => 'Ta',
    injection_rate  => 1e16,                # particles/s
    dt              => 1e-7,
    end_time        => 1e-3,
);

check_availability() → bool

Returns true if dsmcFoam+ is found in PATH.

setup_case()

Generates the complete OpenFOAM case directory:

my $dir = $foam->setup_case;

run(%options)

Runs blockMesh → dsmcInitialise → dsmcFoam+ (optionally parallel).

$foam->run(n_procs => 8);

import_results() → hashref

Parses the latest time directory and log files.

my $results = $foam->import_results;

Physics::PVD::Interface::LAMMPS

Interface to LAMMPS molecular dynamics.

new(%options)

my $lmp = Physics::PVD::Interface::LAMMPS->new(
    executable          => 'lmp',
    n_procs             => 4,
    work_dir            => './lammps_pvd',
    potential_type      => 'eam/alloy',
    potential_file      => 'potentials/CuTa.eam.alloy',
    substrate_material  => 'Cu',
    substrate_size      => [10, 10, 5],     # unit cells
    deposit_species     => 'Ta',
    deposit_energy      => 5.0,             # eV
    deposit_interval    => 1000,            # timesteps between deposits
    timestep            => 1.0,             # fs
    temperature         => 300,             # K
);

check_availability() → bool

generate_input(%options) → filename

Generate a LAMMPS input script from a template.

Templates: 'deposition', 'sputtering', 'annealing'

my $file = $lmp->generate_input(
    template => 'deposition',
    filename => 'my_run/in.deposit',
    params   => {
        n_deposits  => 200,
        run_between => 2000,
        total_steps => 200000,
    },
);

run(%options)

$lmp->run(input_file => 'in.pvd', n_procs => 8);

parse_dump($filename) → arrayref of frames

my $frames = $lmp->parse_dump('dump.lammpstrj');
# [ {timestep => 0, n_atoms => 500, atoms => [{id,type,x,y,z}, ...]}, ... ]

parse_log($filename) → arrayref of hashrefs

my $thermo = $lmp->parse_log;
# [ {Step => 0, Temp => 300, PotEng => -3.5, ...}, ... ]

Physics::PVD::Interface::QuantumATK

Interface to QuantumATK for first-principles calculations.

new(%options)

my $atk = Physics::PVD::Interface::QuantumATK->new(
    python_path   => 'atkpython',
    work_dir      => './quantumatk_pvd',
    calculator    => 'DFTB',          # 'DFT'|'DFTB'|'ForceField'
    xc_functional => 'GGA-PBE',
    basis_set     => 'DZP',
    k_points      => [4, 4, 1],
    material      => 'Ta',
    surface       => [1, 1, 0],
    slab_layers   => 6,
    vacuum        => 15,              # Angstrom
);

check_availability() → bool

calculate_binding_energy(%options) → filename

Generates a Python script for DFT/DFTB binding energy calculation.

my $script = $atk->calculate_binding_energy(
    adatom => 'Cu',
    site   => 'hollow',    # 'hollow'|'bridge'|'atop'
);

setup_sputtering(%options) → filename

Generates a BCA + MD sputtering yield simulation script.

my $script = $atk->setup_sputtering(
    ion        => 'Ar',
    ion_energy => 500,     # eV
    angle      => 0,       # normal incidence
    n_ions     => 200,
);

run_md(%options) → filename

Generates an adatom diffusion MD script.

my $script = $atk->run_md(
    temperature => 600,
    steps       => 100000,
);

run_script($filename)

Execute the generated Python script with atkpython.

$atk->run_script($script);

get_results() → hashref

Parse output log for computed quantities.

my $r = $atk->get_results;
# { binding_energy_eV => 5.3, sputter_yield => 0.8, diffusion_coeff => 1.2e-7 }

Examples

1. Basic KMC Film Growth

use Physics::PVD;

my $pvd = Physics::PVD->new(temperature => 600);
my $kmc = $pvd->kmc(lattice_size => [50, 50, 30]);
$kmc->add_species(name => 'Ta', mass => 180.95, binding_energy => 8.1);
$kmc->deposit(flux => 1e14, time => 30);

my $film = $kmc->get_film;
printf "Thickness: %.1f nm\n", $film->thickness;
$film->export_xyz('ta_film.xyz');

See: examples/kmc_basic.pl

2. DSMC Vapor Transport

use Physics::PVD;

my $pvd = Physics::PVD->new(pressure => 2.0);
my $dsmc = $pvd->dsmc(n_particles => 5000, target_material => 'Ta');
$dsmc->run(timesteps => 3000);

printf "Knudsen: %.2f\n", $dsmc->knudsen_number;
printf "Mean energy: %.2f eV\n", $dsmc->mean_arrival_energy;

See: examples/dsmc_transport.pl

3. Hybrid DSMC→KMC

use Physics::PVD;

my $pvd = Physics::PVD->new(method => 'hybrid', temperature => 400, pressure => 1.5);
my $film = $pvd->run(steps => 50000, timesteps => 2000, flux => 5e13);
printf "Film: %.1f nm, roughness: %.2f nm\n", $film->thickness, $film->roughness;

See: examples/hybrid_dsmc_kmc.pl

4. LAMMPS Deposition MD

use Physics::PVD;

my $pvd = Physics::PVD->new;
my $lmp = $pvd->interface('lammps',
    substrate_material => 'Cu',
    deposit_species    => 'Ta',
    potential_file     => 'CuTa.eam.alloy',
);

$lmp->generate_input(template => 'deposition', params => {n_deposits => 100});
$lmp->run;
my $frames = $lmp->parse_dump;

See: examples/lammps_pvd.pl

5. OpenFOAM DSMC

use Physics::PVD;

my $pvd = Physics::PVD->new;
my $foam = $pvd->interface('openfoam', gas_pressure => 5.0, target_species => 'Cu');
$foam->setup_case;
$foam->run(n_procs => 4);
my $results = $foam->import_results;

6. QuantumATK Binding Energy

use Physics::PVD;

my $pvd = Physics::PVD->new;
my $atk = $pvd->interface('quantumatk', material => 'Ta', surface => [1,1,0]);
my $script = $atk->calculate_binding_energy(adatom => 'Cu', site => 'hollow');
$atk->run_script($script);
my $r = $atk->get_results;
printf "Binding energy: %.2f eV\n", $r->{binding_energy_eV};

Physical Models

Kinetic Monte Carlo (KMC)

The BKL (Bortz-Kalos-Lebowitz, 1975) rejection-free algorithm:

  1. Rate catalog: All possible events enumerated with Arrhenius rates
    k = ν₀ × exp(-Ea / kBT)

  2. Event selection: Random event chosen proportional to its rate
    P(event_i) = k_i / Σk_j

  3. Time advancement: Physical time incremented by
    Δt = -ln(u) / R_total where u ∈ (0,1)

Events:

Direct Simulation Monte Carlo (DSMC)

Bird's method (1994) for rarefied gas dynamics:

  1. Particle emission: Thompson energy P(E) ∝ E/(E+Eb)³, cosine^n angular
  2. Free flight: Ballistic motion for time step dt
  3. Collision: Null-collision method with VHS cross-section
    P_coll = n_gas × σ × v_rel × dt
  4. Energy transfer: Hard-sphere scattering in COM frame

Knudsen Number Regimes

| Kn | Regime | Transport | |----|--------|-----------| | > 10 | Free-molecular | Ballistic, line-of-sight | | 0.1–10 | Transitional | Partial thermalization | | < 0.1 | Continuum | Fully diffusive |

Directory Structure

Physics-PVD/
├── README.md
├── Makefile.PL
├── lib/
│   └── Physics/
│       ├── PVD.pm                    # Main module
│       └── PVD/
│           ├── KMC.pm                # Kinetic Monte Carlo engine
│           ├── DSMC.pm               # Direct Simulation Monte Carlo
│           ├── Film.pm               # Film analysis & export
│           └── Interface/
│               ├── OpenFOAM.pm       # dsmcFoam+ interface
│               ├── LAMMPS.pm         # LAMMPS MD interface
│               └── QuantumATK.pm     # QuantumATK DFT/DFTB
├── t/
│   └── basic.t                       # Test suite
└── examples/
    ├── kmc_basic.pl                  # Simple KMC deposition
    ├── dsmc_transport.pl             # Vapor transport analysis
    ├── hybrid_dsmc_kmc.pl            # Coupled DSMC→KMC
    └── lammps_pvd.pl                 # LAMMPS MD deposition

License

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself (Artistic License 2.0 / GPL v1+).