Depth profile of an opaque sample

We consider an opaque material layer (300 nm thick, refractive index $\delta=10^{-5}$, $\beta=3\times10^{-6}$) sandwiched between two vacuum layers.

The standing wave pattern arises from superposition of incident and reflected beams. The field amplitude can reach 2, giving maximum intensity of 4.

2D intensity map: depth vs angle

The intensity distribution as a function of depth $z$ and incident angle $\alpha_i$.

The black region in the lower left shows where the material is opaque and radiation cannot penetrate (below the critical angle $\theta_c \approx 0.256°$ at large depths).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#!/usr/bin/env python3
"""
Depth profile of an opaque sample.
"""
import bornagain as ba
from bornagain import ba_plot as bp, deg, nm

#  beam data
ai_min = 0  # minimum incident angle
ai_max = 1*deg  # maximum incident angle
wl = 0.03*nm  # wavelength

#  depth position span
z_min = -400*nm
z_max = 100*nm


def get_sample():
    # Define materials
    material_vac = ba.RefractiveMaterial("Vac", 0, 0)
    material_A = ba.RefractiveMaterial("A", 1e-5, 3e-6)

    # Define layers
    layer_top = ba.Layer(material_vac)
    layer_1 = ba.Layer(material_A, 300*nm)
    layer_bot = ba.Layer(material_vac)

    # Define sample
    sample = ba.Sample()
    sample.addLayer(layer_top)
    sample.addLayer(layer_1)
    sample.addLayer(layer_bot)

    return sample


def get_simulation(sample):
    """
    Returns a depth-probe simulation.
    """
    nz = 500
    na = 10*nz

    scan = ba.AlphaScan(na, ai_min, ai_max)
    scan.setWavelength(wl)
    footprint = ba.FootprintSquare(0.01)
    scan.setFootprint(footprint)

    z_axis = ba.EquiDivision("z", nz, z_min, z_max)
    simulation = ba.DepthprobeSimulation(scan, sample, z_axis)

    return simulation


if __name__ == '__main__':
    sample = get_sample()
    simulation = get_simulation(sample)
    result = simulation.simulate()
    plotargs = {}
    plotargs['aspect'] = 'auto'
    plotargs['intensity_min'] = 2.5e-91
    plotargs['intensity_max'] = 4
    bp.plot_datafield(result, **plotargs)
    # Customize colorbar ticks
    import numpy as np
    fig = bp.plt.gcf()
    if len(fig.axes) > 1:
        cbar_ax = fig.axes[1]  # Colorbar axes
        ticks = [1, 1e-15, 1e-30, 1e-45, 1e-60, 1e-75, 1e-90]
        cbar_ax.yaxis.set_ticks(ticks)
    bp.plt.show()
auto/Examples/varia/Opaque2D.py

1D profile: intensity vs depth

A slice at fixed incident angle $\alpha_i = 0.0044°$ showing the standing wave oscillations through the material layers.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#!/usr/bin/env python3
"""
1D depth profile at fixed angle showing standing wave pattern.
"""
import bornagain as ba
from bornagain import ba_plot as bp, deg, nm

#  beam data
ai_fixed = 0.0044*deg  # fixed angle where maximum occurs
wl = 0.03*nm  # wavelength

#  depth position span
z_min = -350*nm
z_max = 250*nm


def get_sample():
    # Define materials
    material_vac = ba.RefractiveMaterial("Vac", 0, 0)
    material_A = ba.RefractiveMaterial("A", 1e-5, 3e-6)

    # Define layers
    layer_top = ba.Layer(material_vac)
    layer_1 = ba.Layer(material_A, 300*nm)
    layer_bot = ba.Layer(material_vac)

    # Define sample
    sample = ba.Sample()
    sample.addLayer(layer_top)
    sample.addLayer(layer_1)
    sample.addLayer(layer_bot)

    return sample


def get_simulation(sample):
    """
    Returns a depth-probe simulation at a single angle.
    """
    nz = 500
    na = 1  # Just one angle

    scan = ba.AlphaScan(na, ai_fixed, ai_fixed)
    scan.setWavelength(wl)
    footprint = ba.FootprintSquare(0.01)
    scan.setFootprint(footprint)

    z_axis = ba.EquiDivision("z", nz, z_min, z_max)
    simulation = ba.DepthprobeSimulation(scan, sample, z_axis)

    return simulation


if __name__ == '__main__':
    sample = get_sample()
    simulation = get_simulation(sample)
    result = simulation.simulate()
    import numpy as np

    # Extract 1D data
    data = result.dataArray()
    intensity = data.flatten()
    z_axis = result.axis(1)
    z_values = np.linspace(z_axis.min(), z_axis.max(), z_axis.size()) / nm

    # Custom plot for 1D profile
    bp.plt.figure(figsize=(10, 6))
    ax = bp.plt.gca()
    ax.plot(z_values, intensity, 'b-', linewidth=2)
    ax.axvline(x=0, color='gray', linestyle=':', linewidth=1)
    ax.axvline(x=-300, color='gray', linestyle=':', linewidth=1)

    # Shade regions
    ax.axvspan(z_min/nm, -300, alpha=0.1, color='blue', label='Bottom vacuum')
    ax.axvspan(-300, 0, alpha=0.1, color='orange', label='Material A (300nm)')
    ax.axvspan(0, z_max/nm, alpha=0.1, color='blue', label='Top vacuum')

    ax.set_xlabel('Depth z (nm)', fontsize=14)
    ax.set_ylabel('Intensity', fontsize=14)
    title = f'Depth Profile at α = {ai_fixed/deg:.6f}°'
    ax.set_title(title, fontsize=16)
    ax.grid(True, alpha=0.3)
    ax.legend(fontsize=10)
    ax.set_ylim([0, max(intensity)*1.1])

    bp.plt.tight_layout()
    bp.plt.show()
auto/Examples/varia/OpaqueVsDepth.py

1D profile: intensity vs angle

A slice at fixed depth $z = 100$ nm (in the upper vacuum layer) showing how intensity varies with incident angle.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env python3
"""
Intensity vs angle at fixed depth z=100nm (upper vacuum boundary).
"""
import bornagain as ba
from bornagain import ba_plot as bp, deg, nm

#  beam data
ai_min = 0*deg
ai_max = 1*deg
wl = 0.03*nm  # wavelength

#  fixed depth at upper boundary
z_fixed = 100*nm


def get_sample():
    # Define materials
    material_vac = ba.RefractiveMaterial("Vac", 0, 0)
    material_A = ba.RefractiveMaterial("A", 1e-5, 3e-6)

    # Define layers
    layer_top = ba.Layer(material_vac)
    layer_1 = ba.Layer(material_A, 300*nm)
    layer_bot = ba.Layer(material_vac)

    # Define sample
    sample = ba.Sample()
    sample.addLayer(layer_top)
    sample.addLayer(layer_1)
    sample.addLayer(layer_bot)

    return sample


def get_simulation(sample):
    """
    Returns a depth-probe simulation scanning over angles at fixed depth.
    """
    nz = 1  # Single depth point
    na = 500

    scan = ba.AlphaScan(na, ai_min, ai_max)
    scan.setWavelength(wl)
    footprint = ba.FootprintSquare(0.01)
    scan.setFootprint(footprint)

    z_axis = ba.EquiDivision("z", nz, z_fixed, z_fixed)
    simulation = ba.DepthprobeSimulation(scan, sample, z_axis)

    return simulation


if __name__ == '__main__':
    sample = get_sample()
    simulation = get_simulation(sample)
    result = simulation.simulate()
    import numpy as np

    # Extract 1D data
    data = result.dataArray()
    intensity = data.flatten()
    alpha_axis = result.axis(0)
    alpha_values = np.linspace(alpha_axis.min(), alpha_axis.max(),
                               alpha_axis.size()) / deg

    # Custom plot for intensity vs angle
    bp.plt.figure(figsize=(10, 6))
    ax = bp.plt.gca()
    ax.plot(alpha_values, intensity, 'b-', linewidth=2,
            label=f'z = {z_fixed/nm:.0f} nm')
    ax.axhline(y=1, color='r', linestyle='--', linewidth=1,
               label='Expected vacuum intensity')

    # Calculate and mark critical angle
    delta = 1e-5
    theta_c = np.sqrt(2*delta) * 180/np.pi  # in degrees
    ax.axvline(x=theta_c, color='orange', linestyle=':', linewidth=1.5,
               label=f'Critical angle θc = {theta_c:.3f}°')

    ax.set_xlabel('Incident angle αi (°)', fontsize=14)
    ax.set_ylabel('Intensity', fontsize=14)
    title = f'Intensity vs Angle at z = {z_fixed/nm:.0f} nm (upper boundary)'
    ax.set_title(title, fontsize=16)
    ax.grid(True, alpha=0.3)
    ax.legend(fontsize=10)
    ax.set_ylim([0, max(intensity)*1.1])

    bp.plt.tight_layout()
    bp.plt.show()
auto/Examples/varia/OpaqueVsAlpha.py