Physics-PVD
view release on metacpan or search on metacpan
lib/Physics/PVD/Interface/QuantumATK.pm view on Meta::CPAN
return $filename;
}
# Generate script for sputtering simulation (BCA + MD)
sub setup_sputtering {
my ($self, %opts) = @_;
my $ion = $opts{ion} // 'Ar';
my $ion_energy = $opts{ion_energy} // 500; # eV
my $angle = $opts{angle} // 0; # degrees
my $n_ions = $opts{n_ions} // 100;
my $filename = $opts{filename} // "$self->{work_dir}/sputtering.py";
my $material = $self->{material};
my $script = <<PYTHON;
# QuantumATK: Sputtering yield simulation (MD + BCA)
# Generated by Physics::PVD::Interface::QuantumATK
from QuantumATK import *
import numpy as np
# Build target surface
bulk = BulkConfiguration(
bravais_lattice=BodyCenteredCubic(3.3058*Angstrom),
elements=[$material],
fractional_coordinates=[[0.0, 0.0, 0.0]]
)
target = SlabConfiguration(bulk, surface_normal=[1,1,0],
number_of_layers=12, vacuum=30*Angstrom)
# MD Calculator (Tersoff or EAM potential)
calculator = TersoffCalculator(target)
target.setCalculator(calculator)
# Ion bombardment parameters
ion_energy = $ion_energy * eV
ion_angle = $angle * np.pi / 180.0
n_ions = $n_ions
sputtered_atoms = 0
for i in range(n_ions):
# Random impact point
x = np.random.uniform(0, target.bravaisLattice().a())
y = np.random.uniform(0, target.bravaisLattice().b())
z_top = max(target.cartesianCoordinates()[:, 2]) + 5.0*Angstrom
# Ion velocity
mass_ion = {'Ar': 39.948, 'Ne': 20.18, 'Xe': 131.29}['$ion'] * amu
speed = np.sqrt(2 * ion_energy / mass_ion)
vx = speed * np.sin(ion_angle)
vy = 0.0
vz = -speed * np.cos(ion_angle)
# Add ion and run MD
config = target.copy()
config.addAtoms(elements=['$ion'], cartesian_coordinates=[[x, y, z_top]])
config.setVelocities([vx, vy, vz], indices=[-1])
md = MolecularDynamics(
configuration=config,
trajectory_filename='$self->{work_dir}/cascade_{}.hdf5'.format(i),
steps=2000,
time_step=0.5*fs,
log_interval=100
)
md.run()
# Count sputtered atoms (z > z_top)
final = md.finalConfiguration()
z_final = final.cartesianCoordinates()[:, 2]
sputtered_atoms += np.sum(z_final > z_top)
sputter_yield = sputtered_atoms / n_ions
print(f"Sputter yield ({ion} -> {material}, E={ion_energy} eV, θ={angle}°): {sputter_yield:.3f}")
PYTHON
open my $fh, '>', $filename or croak "Cannot write $filename: $!";
print $fh $script;
close $fh;
return $filename;
}
# Generate script for adatom diffusion MD
sub run_md {
my ($self, %opts) = @_;
my $temperature = $opts{temperature} // 600; # K
my $steps = $opts{steps} // 50000;
my $filename = $opts{filename} // "$self->{work_dir}/adatom_md.py";
my $material = $self->{material};
my $calc = $self->{calculator};
my $script = <<PYTHON;
# QuantumATK: Adatom diffusion MD on $material surface
# Generated by Physics::PVD::Interface::QuantumATK
from QuantumATK import *
import numpy as np
# Build surface + adatom
bulk = BulkConfiguration(
bravais_lattice=BodyCenteredCubic(3.3058*Angstrom),
elements=[$material],
fractional_coordinates=[[0.0, 0.0, 0.0]]
)
slab = SlabConfiguration(bulk, surface_normal=[1,1,0],
number_of_layers=$self->{slab_layers}, vacuum=20*Angstrom)
# Place adatom
top_z = max(slab.cartesianCoordinates()[:, 2])
center_xy = np.mean(slab.cartesianCoordinates()[:, :2], axis=0)
slab.addAtoms(elements=['$material'],
cartesian_coordinates=[[center_xy[0], center_xy[1], top_z + 2.5]])
# Calculator
calculator = ${calc}Calculator(slab)
slab.setCalculator(calculator)
# NVT MD
md = MolecularDynamics(
( run in 1.219 second using v1.01-cache-2.11-cpan-e7c6538aa59 )