Subject: KeyError: 0 when Assigning Material Values in FenicsX Code for VCSEL Simulation

Dear FenicsX Community,

I am currently working on a simulation of a Vertical-Cavity Surface-Emitting Laser (VCSEL) using FenicsX, and I have encountered an issue that I hope the community can help me with. I’ve attached my Python code below. The code is designed to generate a 3D mesh for the VCSEL structure, tag different regions within the mesh, and assign material properties to each tagged region.

from dolfinx import fem, io, mesh
import ufl
import numpy as np
from mpi4py import MPI
from dolfinx.mesh import locate_entities, meshtags, locate_entities_boundary
import dolfinx

# Define the geometric parameters of the VCSEL
d = 4e-6  # Aperture diameter, 4 micrometers
# Introduce a new parameter D representing the lateral dimension of the entire VCSEL structure, assuming d is 2/3 of D
D = d * 3 / 2
lambda_0 = 850e-9  # Central wavelength, 850 nm
bottom_dbr_pairs = 29.5  # Number of bottom DBR pairs
top_dbr_pairs = 24  # Number of top DBR pairs
qw_thickness = 5e-9  # Quantum well thickness
oxide_gaas_thickness = 69.49e-9  # Thickness of the GaAs layer in the oxide window
oxide_algaas_top_thickness = 63.71e-9  # Thickness of the upper AlGaAs layer in the oxide window
oxide_complex_thickness = 15.93e-9  # Thickness of the composite layer in the oxide window
active_gaas_thickness = 136.49e-9  # Thickness of the GaAs layer in the active region
ni = 0.01  # Assumed ni value, can be modified according to the actual situation

# Refractive indices of materials
n_GaAs = 3.53
n_AlGaAs = 3.08
n_AlAs = 2.95
n_AlOx = 1.60

# Given thickness of each DBR layer in the original text
dbr_GaAs_thickness = 69.49e-9
dbr_AlGaAs_thickness = 79.63e-9

# Assume x is the 5 varying positions of the oxide aperture
x_positions = np.linspace(0, 63.71e-9, 5)

# Process each position in a loop
for i, x in enumerate(x_positions):
    # Calculate the lengths of each part
    top_dbr_length = top_dbr_pairs * (dbr_GaAs_thickness + dbr_AlGaAs_thickness)
    oxide_window_length = oxide_gaas_thickness + (oxide_algaas_top_thickness - x) + oxide_complex_thickness + x
    active_layer_length = 2 * active_gaas_thickness + qw_thickness
    bottom_dbr_length = bottom_dbr_pairs * (dbr_GaAs_thickness + dbr_AlGaAs_thickness)
    total_length = top_dbr_length + oxide_window_length + active_layer_length + bottom_dbr_length

    # Generate a 3D mesh, increase the number of meshes in the longitudinal direction for better resolution of each layer
    nx = 20
    ny = 20
    nz = 1000
    domain = mesh.create_box(MPI.COMM_WORLD, [np.array([-D / 2, -D / 2, 0]), np.array([D / 2, D / 2, total_length])],
                             (nx, ny, nz), cell_type=mesh.CellType.hexahedron)

    # Define the tagging functions for different regions
    def region_func(z_start, z_end, r_cond=lambda r: True):
        return lambda x_coords: np.logical_and(
            np.logical_and(x_coords[2] >= z_start, x_coords[2] < z_end),
            r_cond(np.sqrt(x_coords[0] ** 2 + x_coords[1] ** 2))
        )

    regions = {
        'top_dbr': region_func(0, top_dbr_length),
        'oxide_gaas': region_func(top_dbr_length, top_dbr_length + oxide_gaas_thickness),
        'oxide_algaas_top': region_func(
            top_dbr_length + oxide_gaas_thickness,
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x)
        ),
        'oxide_complex_center': region_func(
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x),
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x) + oxide_complex_thickness,
            lambda r: r < d / 2
        ),
        'oxide_complex_outer': region_func(
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x),
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x) + oxide_complex_thickness,
            lambda r: r >= d / 2
        ),
        'oxide_algaas_bottom': region_func(
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x) + oxide_complex_thickness,
            top_dbr_length + oxide_gaas_thickness + (oxide_algaas_top_thickness - x) + oxide_complex_thickness + x
        ),
        'active_gaas_top': region_func(
            top_dbr_length + oxide_window_length,
            top_dbr_length + oxide_window_length + active_gaas_thickness
        ),
        'quantum_well_center': region_func(
            top_dbr_length + oxide_window_length + active_gaas_thickness,
            top_dbr_length + oxide_window_length + active_gaas_thickness + qw_thickness,
            lambda r: r < d / 2
        ),
        'quantum_well_outer': region_func(
            top_dbr_length + oxide_window_length + active_gaas_thickness,
            top_dbr_length + oxide_window_length + active_gaas_thickness + qw_thickness,
            lambda r: r >= d / 2
        ),
        'active_gaas_bottom': region_func(
            top_dbr_length + oxide_window_length + active_gaas_thickness + qw_thickness,
            top_dbr_length + oxide_window_length + 2 * active_gaas_thickness + qw_thickness
        ),
        'bottom_dbr': region_func(
            top_dbr_length + oxide_window_length + active_layer_length,
            top_dbr_length + oxide_window_length + active_layer_length + bottom_dbr_length
        ),
       'substrate': region_func(
            top_dbr_length + oxide_window_length + active_layer_length + bottom_dbr_length,
            total_length
        )
    }

    # Initialize the tagging array (the same size as the number of mesh cells)
    tdim = domain.topology.dim
    index_map = domain.topology.index_map(tdim)
    num_local_cells = index_map.size_local
    cell_indices = np.arange(num_local_cells, dtype=np.int32)
    global_cell_indices = index_map.local_to_global(cell_indices)
    values = np.zeros(num_local_cells, dtype=np.int32)

    # Define the tagging values
    TAGS = {
        'top_dbr_GaAs': 1, 'top_dbr_AlGaAs': 2,
        'oxide_gaas': 3, 'oxide_algaas_top': 4, 'oxide_complex_center': 5, 'oxide_complex_outer': 6, 'oxide_algaas_bottom': 7,
        'active_gaas_top': 8, 'quantum_well_center': 9, 'quantum_well_outer': 10, 'active_gaas_bottom': 11,
        'bottom_dbr_GaAs': 12, 'bottom_dbr_AlGaAs': 13,
       'substrate': 14
    }

    # Tag the top DBR layer
    z = top_dbr_length
    for j in range(int(top_dbr_pairs)):
        z -= dbr_AlGaAs_thickness + dbr_GaAs_thickness
        gaas_region = region_func(z, z + dbr_GaAs_thickness)
        gaas_cells = locate_entities(domain, tdim, gaas_region)
        local_gaas_cells = np.where(np.isin(global_cell_indices, gaas_cells))[0]
        values[local_gaas_cells] = TAGS['top_dbr_GaAs']
        z += dbr_GaAs_thickness
        algaas_region = region_func(z, z + dbr_AlGaAs_thickness)
        algaas_cells = locate_entities(domain, tdim, algaas_region)
        local_algaas_cells = np.where(np.isin(global_cell_indices, algaas_cells))[0]
        values[local_algaas_cells] = TAGS['top_dbr_AlGaAs']
        z += dbr_AlGaAs_thickness

    # Tag the oxide window layer
    for region_name, func in [
        ('oxide_gaas', regions['oxide_gaas']),
        ('oxide_algaas_top', regions['oxide_algaas_top']),
        ('oxide_algaas_bottom', regions['oxide_algaas_bottom'])
    ]:
        cells = locate_entities(domain, tdim, func)
        local_cells = np.where(np.isin(global_cell_indices, cells))[0]
        values[local_cells] = TAGS[region_name]

    # Tag the oxide composite layer, distinguish between inner and outer parts
    cells = locate_entities(domain, tdim, regions['oxide_complex_center'])
    local_cells = np.where(np.isin(global_cell_indices, cells))[0]
    values[local_cells] = TAGS['oxide_complex_center']

    cells = locate_entities(domain, tdim, regions['oxide_complex_outer'])
    local_cells = np.where(np.isin(global_cell_indices, cells))[0]
    values[local_cells] = TAGS['oxide_complex_outer']

    # Tag the active region
    for region_name, func in [
        ('active_gaas_top', regions['active_gaas_top']),
        ('active_gaas_bottom', regions['active_gaas_bottom'])
    ]:
        cells = locate_entities(domain, tdim, func)
        local_cells = np.where(np.isin(global_cell_indices, cells))[0]
        values[local_cells] = TAGS[region_name]

    # Tag the quantum well layer, distinguish between inner and outer parts
    cells = locate_entities(domain, tdim, regions['quantum_well_center'])
    local_cells = np.where(np.isin(global_cell_indices, cells))[0]
    values[local_cells] = TAGS['quantum_well_center']

    cells = locate_entities(domain, tdim, regions['quantum_well_outer'])
    local_cells = np.where(np.isin(global_cell_indices, cells))[0]
    values[local_cells] = TAGS['quantum_well_outer']

    # Tag the bottom DBR layer
    z = top_dbr_length + oxide_window_length + active_layer_length
    for j in range(int(bottom_dbr_pairs)):
        z += dbr_AlGaAs_thickness + dbr_GaAs_thickness
        algaas_region = region_func(z - dbr_AlGaAs_thickness, z)
        algaas_cells = locate_entities(domain, tdim, algaas_region)
        local_algaas_cells = np.where(np.isin(global_cell_indices, algaas_cells))[0]
        values[local_algaas_cells] = TAGS['bottom_dbr_AlGaAs']
        z -= dbr_AlGaAs_thickness
        gaas_region = region_func(z - dbr_GaAs_thickness, z)
        gaas_cells = locate_entities(domain, tdim, gaas_region)
        local_gaas_cells = np.where(np.isin(global_cell_indices, gaas_cells))[0]
        values[local_gaas_cells] = TAGS['bottom_dbr_GaAs']
        z -= dbr_GaAs_thickness

    # Tag the substrate
    substrate_cells = locate_entities(domain, tdim, regions['substrate'])
    local_substrate_cells = np.where(np.isin(global_cell_indices, substrate_cells))[0]
    values[local_substrate_cells] = TAGS['substrate']

    # Correctly initialize MeshTags
    marker = meshtags(domain, tdim, global_cell_indices, values)

    # # Save the tagging information (optional)
    # with open(f'vcsel_mesh_tags_x_{i}.txt', 'w') as f:
    #     f.write("Cell index, Tag\n")
    #     for cell_idx, tag in zip(global_cell_indices, values):
    #         f.write(f"{cell_idx}, {tag}\n")

# Define the material properties
    materials = {
        1: n_GaAs,
        2: n_AlGaAs,
        3: n_GaAs,
        4: n_AlGaAs,
        5: n_AlOx,
        6: n_AlAs,
        7: n_AlGaAs,
        8: n_GaAs,
        9: n_GaAs + 1j * ni,
        10: n_GaAs,
        11: n_GaAs,
        12: n_GaAs,
        13: n_AlGaAs,
        14: n_GaAs
    }

    material_values = np.array([materials[tag] for tag in values], dtype=np.complex128)
    material = fem.Function(domain, ufl.FunctionSpace(domain, ("DG", 0)))
    material.x.array[:] = material_values

When I run this code, I receive the following error:

Traceback (most recent call last):
  File "/home/xxx_77/program/xxx.py", line 223, in <module>
    material_values = np.array([materials[tag] for tag in values], dtype=np.complex128)
                                ~~~~~~~~~^^^^^
KeyError: 0

I’m noob at python,please help me! I would greatly appreciate any help, advice, or insights from the community.

Thank you in advance!

Please do not post a new topic on one that has been closed for being off topic: Help with KeyError: 0 in Material Assignment Using FenicsX - #3

This forum is served by volunteers in academia and industry who have an interest in FEniCS and its use. As I said previously, this is unrelated to FEniCS and you would be best served by following Python forums and/or tutorials.

The reason you have 0 in your values is a result of your implementation. A simple MWE would be

a = {1: 0.1, 2: 0.3}
a[0] #raises key error, only 1 or 2 is set