DUV Tutorial Series

Step-by-Step Tutorials for Monte Carlo Setup, PSF Modeling, and Analysis

Available Tutorials

Monte Carlo Simulation
Learn how to set up and run Monte Carlo particle simulations for energy deposition modeling.
Duration: 30 minutes
Double Gaussian PSF
Master double Gaussian point spread function calculation with FFT convolution.
Duration: 25 minutes
Partial Coherence
Understand partial coherence effects and NA/illumination optimization.
Duration: 20 minutes
Flare Modeling
Model flare effects with third Gaussian and long-tail characteristics.
Duration: 15 minutes
Swing Curves
Calculate swing curves and analyze contrast/NILS vs pitch relationships.
Duration: 35 minutes
Model Comparison
Compare Monte Carlo vs Double Gaussian models with statistical validation.
Duration: 40 minutes

Monte Carlo Simulation Tutorial

1
Installation and Setup
Setting up the DUV lithography simulation environment requires proper installation of dependencies and configuration of simulation parameters. This tutorial will guide you through the complete setup process.

System Requirements:

  • Python 3.8+ with NumPy, SciPy, and Matplotlib
  • FFT Libraries: FFTW or Intel MKL for high-performance convolution
  • Memory: Minimum 8GB RAM for large simulations
  • GPU Support: Optional CUDA for Monte Carlo acceleration

Installation Steps:

  1. Create a virtual environment to avoid dependency conflicts
  2. Install the core simulation package and dependencies
  3. Verify installation with test simulations
  4. Configure simulation parameters for your specific use case
# Install DUV simulation package
pip install duv-simulator

# Import required modules
import duv_simulator as duv
import numpy as np
import matplotlib.pyplot as plt

# Initialize simulator
simulator = duv.DUVSimulator()
⚠️ Important: Always test your installation with a simple simulation to ensure all dependencies are working correctly before proceeding with complex analyses.
2
Basic Monte Carlo Simulation
Run your first Monte Carlo simulation with default parameters.
# Basic Monte Carlo simulation
mc_results = simulator.run_monte_carlo(
    num_particles=100000,
    wavelength=193,  # nm
    na=0.85,
    feature_size=50.0,  # nm
    dose=30.0  # mJ/cm²
)

# Display results
print(f"Total energy deposited: {mc_results.statistics.total_energy:.2e} J")
print(f"Peak intensity: {mc_results.statistics.peak_intensity:.2e} W/cm²")
print(f"Simulation time: {mc_results.statistics.simulation_time:.2f} s")
3
Visualization and Analysis
Visualize simulation results and perform statistical analysis.
# Create energy deposition heatmap
plt.figure(figsize=(10, 8))
plt.imshow(mc_results.energy_deposition, cmap='viridis')
plt.colorbar(label='Energy Density')
plt.title('Monte Carlo Energy Deposition Profile')
plt.xlabel('X Position (nm)')
plt.ylabel('Y Position (nm)')
plt.show()

# Plot particle distribution
plt.figure(figsize=(10, 6))
plt.scatter(mc_results.particle_positions[:, 0], 
           mc_results.particle_positions[:, 1],
           c=mc_results.particle_energies, 
           cmap='viridis', s=1)
plt.colorbar(label='Particle Energy')
plt.title('Particle Distribution')
plt.xlabel('X Position (nm)')
plt.ylabel('Y Position (nm)')
plt.show()

# Statistical analysis
statistics = mc_results.get_statistics()
print(f"Mean energy per particle: {statistics.mean_energy:.2e} J")
print(f"Energy standard deviation: {statistics.std_energy:.2e} J")
print(f"Energy range: {statistics.min_energy:.2e} - {statistics.max_energy:.2e} J")
4
Parameter Optimization
Optimize simulation parameters for better performance and accuracy.
# Parameter optimization
def optimize_simulation():
    # Test different particle counts
    particle_counts = [10000, 50000, 100000, 500000, 1000000]
    results = []
    
    for num_particles in particle_counts:
        result = simulator.run_monte_carlo(
            num_particles=num_particles,
            wavelength=193,
            na=0.85,
            feature_size=50.0,
            dose=30.0,
            simulation_speed="fast"
        )
        results.append({
            'particles': num_particles,
            'time': result.statistics.simulation_time,
            'energy': result.statistics.total_energy,
            'peak': result.statistics.peak_intensity
        })
    
    return results

# Run optimization
opt_results = optimize_simulation()

# Plot optimization results
plt.figure(figsize=(12, 4))

plt.subplot(1, 3, 1)
plt.plot([r['particles'] for r in opt_results], 
         [r['time'] for r in opt_results], 'o-')
plt.xlabel('Number of Particles')
plt.ylabel('Simulation Time (s)')
plt.title('Simulation Time vs Particles')

plt.subplot(1, 3, 2)
plt.plot([r['particles'] for r in opt_results], 
         [r['energy'] for r in opt_results], 'o-')
plt.xlabel('Number of Particles')
plt.ylabel('Total Energy (J)')
plt.title('Energy vs Particles')

plt.subplot(1, 3, 3)
plt.plot([r['particles'] for r in opt_results], 
         [r['peak'] for r in opt_results], 'o-')
plt.xlabel('Number of Particles')
plt.ylabel('Peak Intensity (W/cm²)')
plt.title('Peak Intensity vs Particles')

plt.tight_layout()
plt.show()

Double Gaussian PSF Tutorial

1
PSF Calculation
Calculate double Gaussian point spread function with custom parameters.
# Calculate double Gaussian PSF
psf_results = simulator.calculate_double_gaussian(
    sigma1=30.0,      # nm - narrow Gaussian
    sigma2=100.0,     # nm - wide Gaussian
    amplitude1=0.8,   # amplitude of narrow Gaussian
    amplitude2=0.2,   # amplitude of wide Gaussian
    grid_size=128,    # grid resolution
    feature_size=200.0  # nm - simulation area
)

# Display PSF metrics
print(f"FWHM1: {psf_results.metrics.fwhm1:.1f} nm")
print(f"FWHM2: {psf_results.metrics.fwhm2:.1f} nm")
print(f"Peak intensity: {psf_results.metrics.peak_intensity:.3f}")
print(f"Total energy: {psf_results.metrics.total_energy:.3f}")
2
PSF Visualization
Visualize 2D and 1D PSF profiles with component analysis.
# Visualize 2D PSF
plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.imshow(psf_results.psf_2d, cmap='viridis')
plt.colorbar(label='PSF Value')
plt.title('Double Gaussian PSF (2D)')
plt.xlabel('X Position (nm)')
plt.ylabel('Y Position (nm)')

# Visualize 1D cross-section
plt.subplot(1, 2, 2)
x_positions = psf_results.psf_1d['x']
psf_values = psf_results.psf_1d['y']
plt.plot(x_positions, psf_values, 'b-', linewidth=2, label='Total PSF')

# Plot individual components
narrow_component = psf_results.narrow_component
wide_component = psf_results.wide_component
plt.plot(x_positions, narrow_component, 'r--', label='Narrow Gaussian')
plt.plot(x_positions, wide_component, 'g--', label='Wide Gaussian')

plt.xlabel('Position (nm)')
plt.ylabel('PSF Value')
plt.title('PSF Components (1D Cross-Section)')
plt.legend()
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()
3
Aerial Image Calculation
Calculate aerial image using FFT convolution with pattern.
# Create test pattern
pattern = duv.create_line_pattern(
    width=50.0,  # nm
    length=200.0,  # nm
    grid_size=128
)

# Calculate aerial image
aerial_image = simulator.calculate_aerial_image(
    pattern=pattern,
    psf=psf_results.psf_2d
)

# Visualize results
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
plt.imshow(pattern, cmap='gray')
plt.title('Test Pattern')
plt.xlabel('X Position (nm)')
plt.ylabel('Y Position (nm)')

plt.subplot(1, 3, 2)
plt.imshow(psf_results.psf_2d, cmap='viridis')
plt.title('Double Gaussian PSF')
plt.xlabel('X Position (nm)')
plt.ylabel('Y Position (nm)')

plt.subplot(1, 3, 3)
plt.imshow(aerial_image, cmap='viridis')
plt.title('Aerial Image')
plt.xlabel('X Position (nm)')
plt.ylabel('Y Position (nm)')

plt.tight_layout()
plt.show()