Device Geometry

Overview

The DeviceGeometry class is the instance that will handle the device physical charachteristics. Currently it handles .gds files, only. It is capable of extending the 2D GDSII description of the device into a 3D structure that includes handling slanted walls defined in the LayerStack. It is also the piece of code that handles material meshing given a cloud of points.

It is the last step in the simulation setup prior to configuring the solver settings. It relies mainly on a LayerStack object, a .gds file and port detection settings.

Considerations

Following are a few considerations to take into account when configuring the device geometry.

  • The SetSettings is the main method for setting up the device geometry;

  • The SetAutoPortSettings is the method for setting up the auto port detection settings;

  • It currently support .gds files, only. But future support to .stl files is in order for a future release;

  • It relies on a single LayerStack and its internal MaterialLibrary to define the properties of the nodes in the simulation region.

Device and Port Buffers

When setting up a DeviceGeometry instance, the user should care with the device and port buffer definitions. Consider the following figures:

Top View
../_images/port_ext.svg
Cross Section
../_images/port_xsection.svg

The Top View displays the Mask Layout defined in the raw .gds file. The edges touching the GDSII Border can be detected as ports, for example, the one on the left. A port will consist of two elementes: a monitor and a source. The Port Monitor is place precisely at the GDSII Border, while the source for Mode Injection is place half way through the buffers dimension.

The buffers property specify how much empty space will be added/padded around the Mask Layout to avoid direct touching of the layout with the solver boundary conditions.

The Cross Section view, display a cross section of the port, it includes the other two buffers as Simulation Domain Buffers. To be flexible in terms of mode calculation, each port detection direction, can contain its own buffer (x, y) values. The Port Buffers are used to extend the port size the regular waveguide dimension, allowing that the calculated modes are zero at the perimeter defined by the Port Border. If the Port Border will never exceed the Simulation Domain Border.

You can check the complete device geometry example for more details on the Simulation Domain Buffer and the Port Buffer.

Auto Port Detection and Port Numbering

The DeviceGeometry uses an auxiliary GDS tool to detect ports automatically. This tool relies on finding two consecutive points that are touching the boundaries of the GDSII file. This detection relies on spifying the minimum and maximum width of the ports, as well as wich edges to detect (e.g., 'X', 'Y'). Once the ports are detected, they follow the numbering order provided in the follwoing diagram, plus, there is the addition of the the 'o' letter to it.

Port Numbering Rule
../_images/port_numbering.svg

Examples

The following examples will cover some specific settings of the DeviceGeometry and at the end it aggregates some of the example snipets into a more elaborate setup. That can be used prior instancing a specific EM solver. For more deatails please check the API documentation.

Initial Setup Without Port detection

By default there is no need to detect ports, for example, when using the VFD Mode Solver. So in this example we present how to instantiate a DeviceGeometry and then add a wg.gds GDSII file to specify the geometry type.

In this example we are padding the simulation in all directions by 1.5 microns, to add some empty space between the waveguide and the edges of the simulation region. The geometry_type="Fixed" indicates that the geometry speficiations is done via a static GDSII file (non-parametric structure). The layer_stack argument can be define as presented in the LayerStack examples.

from pyOptiShared.DeviceGeometry import DeviceGeometry

device_geometry = DeviceGeometry()
device_geometry.SetSettings(
    layer_stack=layer_stack,
    geometry_type="Fixed",
    gds_file=r"wg.gds",
    buffers={'x':1.5,'y':1.5,'z':1.5}
)

Uniform Grid from the Geometry Bounding Box

Considering the previous waveguide example, we can then extract the bounding box of the device geometry and construct an array of linearly spaced points that we can later identify to which materials they are defined from.

import numpy as np
from pyOptiShared.DeviceGeometry import DeviceGeometry

device_geometry = DeviceGeometry()
device_geometry.SetSettings(
    layer_stack=layer_stack,
    geometry_type="Fixed",
    gds_file=r"wg.gds",
    buffers={'x':1.5,'y':1.5,'z':1.5}
)

bbox = dev_geom.GetBoundingBox(zbWithBuffer=True)
(xmin, xmax), (ymin, ymax), (zmin, zmax) = bbox
xx = np.linspace(xmin, xmax, int(nx*kk))
yy = np.linspace(ymin, ymax, int(ny*kk))
zz = np.linspace(zmin, zmax, int(nz*kk))

XX, YY, ZZ = np.meshgrid(xx, yy, zz, indexing='ij')
points = np.asarray([XX.ravel(), YY.ravel(), ZZ.ravel()])

Obtaining a Material Mesh/Permittivity from a Cloud of points

Once the points are determined from device geometry bounding box, we can then retrieve the material mesh by calling the GetMaterialMesh method. This mesh contains an array of material numbers, that can be converted to their rescpective permittivity values at a particular wavelength using the GetPermittivityMesh method.

import numpy as np
from pyOptiShared.DeviceGeometry import DeviceGeometry

lamb = 1.55 # um

device_geometry = DeviceGeometry()
device_geometry.SetSettings(
    layer_stack=layer_stack,
    geometry_type="Fixed",
    gds_file=r"wg.gds",
    buffers={'x':1.5,'y':1.5,'z':1.5}
)

bbox = dev_geom.GetBoundingBox(zbWithBuffer=True)
(xmin, xmax), (ymin, ymax), (zmin, zmax) = bbox
xx = np.linspace(xmin, xmax, int(nx*kk))
yy = np.linspace(ymin, ymax, int(ny*kk))
zz = np.linspace(zmin, zmax, int(nz*kk))

# Crete the mesh arrays and linearize the points
XX, YY, ZZ = np.meshgrid(xx, yy, zz, indexing='ij')
points = np.asarray([XX.ravel(), YY.ravel(), ZZ.ravel()])

# Get the material and permittivity meshes
material_mesh = dev_geom.GetMaterialMesh(points)
eps = dev_geom.GetPermittivityMesh(material_mesh, lamb)

# Get back a 3D array of the linear points
shape = (xx.shape[0] // kk, kk,
                yy.shape[0] // kk, kk,
                zz.shape[0] // kk, kk)
eps = eps.reshape(shape)

Defining Auto Port Settings:

Auto port settings are used in auto port detection of ports in GDSII files. For that we use the SetAutoPortSettings method. Since it relies on a particular GDSII file, we have to call it after the SetSettings method.

There are for arguments:

  • direction that can be x, y or both. It indicates in which of the Top View bounds the ports should be detected.

  • min and max, the determine the minimun/maximun port widths to be detected. This can be either a single float (used for both x and y), a list[float, float] (x and y independently).

  • buffer, that determines how much empty space will be added around the port cross section to properly accomodate mode profiles.

    • It can be a single float, that will be used for both x and y ports extending by the same quantity both width and height;

    • It can be a list[float, float] that will be used for both x and y ports extending by the width and height independently; or

    • A more complex list[float, float] that controls independently width and height for each bounds.

Please, refer to SetAutoPortSettings for more details on the auto port detection settings.

Following is a more complete example, that will be used to detect ports on all sides, with idendependent width and height buffers. The GDSII file corresponds to a waveguide crossing (wgcrossing.gds).

import numpy as np
from pyOptiShared.LayerInfo import LayerStack
from pyOptiShared.Material import ConstMaterial
from pyOptiShared.DeviceGeometry import DeviceGeometry

##########################################
###         Material Settings          ###
##########################################
myindex1p45 = ConstMaterial(mat_name="myindex1p45", epsReal=1.45**2)
myindex3p5 = ConstMaterial(mat_name="myindex3p5", epsReal=3.5**2)

##########################################
###       Layer Stack Settings         ###
##########################################
layer_stack = LayerStack()
layer_stack.addLayer(name="L1", number=1, thickness=0.25, zmin=0.0,
                    material=myindex3p5)
layer_stack.addLayer(name="L2", number=2, thickness=0.25, zmin=0.25,
                    material=myindex3p5)
layer_stack.setBGandSub(background=myindex1p45, substrate=myindex1p45)

##########################################
###   Device Geometry/Port Settings    ###
##########################################
device_geometry = DeviceGeometry()
device_geometry.SetSettings(
    layer_stack=layer_stack,
    geometry_type="Fixed",
    gds_file=r"wgcrossing.gds",
    buffers={'x':1.5,'y':1.5,'z':1.5}
)
device_geometry.SetAutoPortSettings(
    direction="both",
    buffer=[[2, 1.5],[1.5, 1.25]], # [[x_width, x_height],[y_width, y_height]]
    min=[0.2, 0.21], # [x_min, y_min]
    max=[0.5, 0.49], # [x_max, y_max]
)

Visualizing the Geometry Setup

The following example shows a complete setup with visualization of thed evice geometry using the Show method. The GDSII file corresponds to a waveguide crossing (wgcrossing.gds)

from pyOptiShared.DeviceGeometry import DeviceGeometry
from pyOptiShared.LayerInfo import LayerStack
from pyOptiShared.Material import ConstMaterial

si02_mat = ConstMaterial(mat_name="SiO2", epsReal=1.45**2,color='lightgreen')
si_mat = ConstMaterial(mat_name="Si", epsReal=3.5**2,color='lightblue')
air_mat = ConstMaterial(mat_name="Air", epsReal=1**2,color='lightyellow')
layer_stack = LayerStack()

layer_stack.addLayer(name="L1", number=1, thickness=0.22, zmin=0.0,
                    material=si_mat, cladding=si02_mat,
                    sideWallAng=20)
layer_stack.addLayer(name="L2", number=2, thickness=0.22, zmin=0.0,
                    material=si02_mat, cladding=si02_mat,
                    sideWallAng=0)

layer_stack.setBGandSub(background=air_mat, substrate=si02_mat)

device_geometry = DeviceGeometry()
device_geometry.SetSettings(
    layer_stack=layer_stack,
    geometry_type="Fixed",
    gds_file='wgcrossing.gds',
    buffers={'x':1.5,'y':1.5,'z':1.5}
)
device_geometry.SetAutoPortSettings(
    direction="both",
    buffer=1.5,
)

device_geometry.Show()
../_images/dev_geometry_crossing_show_example.svg

A Complete Device Geometry Setup

This example uses the information provided in the previous examples to construct a full instantiation and setup of a LayerStack. This is example is usefull for simulations using the pyFDTD Solver or the VFD Mode Solver. It starts by configuring some materials, creating the layer stack and finaly setting up the DeviceGeometry. The device under charachterization is a dielectric waveguide (wg.gds).

import numpy as np
from pyOptiShared.LayerInfo import LayerStack
from pyOptiShared.Material import ConstMaterial
from pyOptiShared.DeviceGeometry import DeviceGeometry

##########################################
###         Material Settings          ###
##########################################
myindex1p45 = ConstMaterial(mat_name="myindex1p45", epsReal=1.45**2)
myindex3p5 = ConstMaterial(mat_name="myindex3p5", epsReal=3.5**2)

##########################################
###       Layer Stack Settings         ###
##########################################
layer_stack = LayerStack()
layer_stack.addLayer(name="L1", number=1, thickness=0.25, zmin=0.0,
                    material=myindex3p5)
layer_stack.addLayer(name="L2", number=2, thickness=0.25, zmin=0.25,
                    material=myindex3p5)
layer_stack.setBGandSub(background=myindex1p45, substrate=myindex1p45)

##########################################
###   Device Geometry/Port Settings    ###
##########################################
device_geometry = DeviceGeometry()
device_geometry.SetSettings(
    layer_stack=layer_stack,
    geometry_type="Fixed",
    gds_file=r"wg.gds",
    buffers={'x':1.5,'y':1.5,'z':1.5}
)
device_geometry.SetAutoPortSettings(
    direction="x",
    buffer=1.0,
    min=0.2,
    max=0.5,
)

API Documentation

pyOptiShared.DeviceGeometry.DeviceGeometry()

This class handles the geometry of the device under simulation.