Ansys Lumerical MODE - EigenMode Expansion (EME)

When you setup an electromagnetic simulation using Ansys Lumerical MODE (using the EME solver) from within IPKISS, you go through the following steps:

  1. Define the geometry that represents your component.

  2. Specify the simulation job, consisting of the geometry, the expected outputs and simulation settings

  3. Inspect the exported geometry

  4. Retrieve the simulation results

1. Define the geometry

We’ve already covered step 1 by creating a SimulationGeometry for an MMI. The next step is to create an object that will handle the simulations.

2. Define the simulation

To create a simulation using the geometry we’ve just defined, we’ll instantiate a i3.device_sim.LumericalEMESimulation object:

simulation = i3.device_sim.LumericalEMESimulation(
    geometry=sim_geom,
    outputs=[
        i3.device_sim.SMatrixOutput(
            name='smatrix',
            wavelength_range=(1.5, 1.6, 60)
       )
    ],
    setup_macros=[
        i3.device_sim.lumerical_macros.eme_setup(
            group_spans=[transition_length, mmi_length, transition_length],
            cells=[40, 1, 40]
        ),
        i3.device_sim.lumerical_macros.eme_profile_xy('out1')
    ]
)

Apart from the simulation geometry we have to specify an output. By using SMatrixOutput, the simulator will perform an S-parameter sweep and return an SMatrix (i3.circuit_sim.Smatrix1DSweep).

We have also added two macros:

  • eme_setup allows to control the discretization of the component in Lumerical EME. In this case the component is split into three parts: the input taper, the MMI and the output taper. The input and output tapers are discretised in the x-direction (the direction of propagation) into 40 cells, while the MMI only consists of one cell because it has a constant width.

  • eme_profile_xy lets us place a monitor on a port and visualize the results (like the electric field) after the simulation is done.

Now everything is set up to start Ansys Lumerical EME and do our first simulation.

3. Inspect the simulation job

To see what it looks like, you can use the inspect method:

simulation.inspect()

The Lumerical EME Solver graphical user interface will open twice. First, the GUI will be opened to build up the geometry and settings in an automated way. Then, the GUI will close and re-open for inspection. This is for technical implementation reasons.

After execution, the Lumerical EME GUI will open:

../../_images/eme_inspect.png

You can now explore the geometry, ports, outputs and other settings as exported by IPKISS. You can make changes and modify settings from here. See Export additional settings below on how to make those settings available for future projects.

Note

The EME solver will be set up with 10 cells in the propagation direction to allow a fast initial setup. In most cases the number of cells and cell groups needs to be setup by the user using the macro eme_setup to arrive at a meaningful simulation. This can be done after initial inspection of the project.

4. Retrieve and plot simulation results

When you’ve confirmed that the simulation setup is correct, you can continue with running the simulation and retrieving the results from the outputs. By default you don’t have to launch the simulation explicitly; when you request the results, IPKISS will run the simulation when required.

import numpy as np
smatrix = simulation.get_result(name='smatrix')
transmission = np.abs(smatrix['out1', 'in1'])**2

Note

By default, the S-matrix that is returned after the simulation will have its sweep units in micrometer. This is in contrast to manually importing the S-matrix touchstone file using import_touchstone_smatrix, in which case the default sweep units are in GHz.

Since not all tools use the same physics conventions (e.g. \(\exp(-j\omega t)\) vs \(\exp(j \omega t)\)), IPKISS will ensure that the S-parameters are converted from the tool conventions into IPKISS conventions (\(\exp(-j\omega t)\)).

You can now plot these S-parameters with matplotlib, or they can be stored to disk (S-parameter data handling). Lumerical EME will also create a touchstone file named ‘smatrix.s3p’ that can be imported using import_touchstone_smatrix.

smatrix = simjob.get_result(name="smatrix")
smatrix.visualize(
    scale="dB",
    term_pairs=[
        ("in1", "out1"),
        ("out1", "out2"),
        ("in1", "in1"),
    ],
    yrange=[-60, 0.1],
)
../../_images/eme_sweep.png

Note

These simulation results are artificial results as they’re based on fictional technology and components. These only serve demonstration purposes.

Only the transmission to the ‘out1’ port is plotted because the MMI is symmetric. We see that less around half of the power is transmitted to each output port, but the MMI can be optimized further.

The electric field at the output port can be visualized thanks to the monitor we placed earlier:

../../_images/eme_electric_field.png

5. Advanced simulation settings

Monitors

Just as is the case with the geometry, IPKISS sets reasonable defaults for the port monitors. When you don’t specify any ports, defaults will automatically be added. The default position, direction are directly taken from the OpticalPorts of the layout, the dimensions of the Ports are calculated with a heuristic. Specifically, the width of the port is taken from the core_width attribute of the trace_template of the IPKISS OpticalPort. Any waveguide template that is a WindowWaveguideTemplate will have this attribute. For the majority of waveguide templates this is the case. The heuristic will add a fixed margin of 1um to this core_width. The height of the port is derived from the Material Stack used to virtually fabricate the layer of the core of the Waveguide, searching for the highest refractive index region but excluding the bottom, top, left and right boundary materials.

You can override these defaults by using the monitors argument of the simulator class:

simulation = i3.device_sim.LumericalEMESimulation(
    geometry=sim_geom,
    monitors=[
        i3.device_sim.Port(
            name='in1',
            # we make the box of the port quite large for demonstration purposes
            box_size=(5., 5.)
        ),
        i3.device_sim.Port(name='out1'),
        i3.device_sim.Port(name='out2')
    ]
)

Note that you can not change the x-position of the monitors in Lumerical EME. When you inspect the simulation object you’ll see something similar to the picture below:

../../_images/eme_monitors.png

Multimode waveguides

By default, waveguide ports will be simulated with a single mode (the fundamental mode). You can override this in order to take multiple modes into account:

simjob = i3.device_sim.LumericalEMESimulation(
   geometry=sim_geom,
   monitors=[i3.device_sim.Port(name="in1", n_modes=2),
             i3.device_sim.Port(name="out1", n_modes=2),
             i3.device_sim.Port(name="out2", n_modes=2)],
   outputs=[
      i3.device_sim.SMatrixOutput(
         name='smatrix',
         wavelength_range=(1.5, 1.6, 60)
      )
   ],
   setup_macros=[
      i3.device_sim.lumerical_macros.eme_setup(
         group_spans=(transition_length, mmi_length, transition_length),
         cells=[40, 1, 40],
         display_cells=False
      )]
)

The simulation results will then contain the S-parameters for each port-mode combination:

smatrix = simjob.get_result(name="smatrix")
smatrix.visualize(
  scale="dB",
  term_pairs=[
      ("in1:0", "out1:0"),
      ("in1:1", "out1:1"),
  ],
)
../../_images/eme_multimode.png

6. Tool-specific settings

In addition to generic settings which can be applied to multiple solvers, the IPKISS device simulation interface also allows to use the full power of the solver tool. Tool-specific materials can be used and tool-specific macros can be defined.

Using materials defined by the simulation tool

By default, IPKISS exports materials for each material used in the process flow definition.

You can also reuse materials which are already defined by the device solvers by specifying a dictionary solver_material_map to the simulation object (LumericalEMESimulation). It maps IPKISS materials onto materials defined by the tool (name-based).

For example:

simjob = i3.device_sim.LumericalEMESimulation(
    ....
    solver_material_map={
        i3.TECH.MATERIALS.SILICON: 'Si (Silicon) - Palik',
        i3.TECH.MATERIALS.SILICON_OXIDE: 'SiO2 (Glass) - Palik'
    }
)

This will map the TECH.MATERIALS.SILICON, and SILICON_OXIDE, onto materials defined by the electromagnetic solver. The material name should be know by the solver. Please check the tool documentation to find out which materials are available.

Macros

Macros allow you to add tool-specific commands using their API. We have already demonstrated this above by using the eme_setup macro to change the discretization.

IPKISS provides some premade macros which are available under the i3.device_sim.lumerical_macros namespace. Visit the documentation for more information.

Note

If you plan on writing your own macro, make sure to use the port names defined by Lumerical EME (port_1, port_2, …) and not the IPKISS port names.

You can also use macros to execute tool-specific code to generate output files and retrieve those with simjob.get_result. In the following example we use it to generate a file with the numbers 0 to 5.

simjob = i3.device_sim.LumericalEMESimulation(
    geometry=geometry,
    outputs=[
        i3.device_sim.MacroOutput(
            name='macro-output',
            # filepath must correspond with the file used in the commands.
            filepath='test.txt',
            commands=[
                'a=linspace(0, 4, 5);',
                'write("test.txt",num2str(a), "overwrite");'
            ]

        )
    ],
)

# get_result will execute the code and take care of copying the file when required.
fpath = simjob.get_result('macro-ouput')
print(open(fpath).read())
# expected output:
# 0
# 1
# 2
# 3
# 4

Warning

When your lumerical script contains a for loop, you should pass your commands using a multi-line string:

# the following will fail:
output_numbers = i3.device_sim.MacroOutput(
  name="sweep-numbers",
  filepath="numbers.txt",
  commands=[
    'numbers=linspace(0, 4, 5);',
    'for(num=numbers){',
    '  write("numbers.txt", num2str(num));',
    '}'
  ]
)


# instead use a multiline string:
output_numbers = i3.device_sim.MacroOutput(
  name="sweep-numbers",
  filepath="numbers.txt",
  commands=["""
    numbers=linspace(0, 4, 5);
    for(num=numbers){
      write("numbers.txt", num2str(num));
    }
    """
  ]
)

Export additional settings from Lumerical EME to IPKISS

Often you will want to tweak certain settings (simulation settings, materials, …) using very tool-specific commands or actions. Since it is not feasible to abstract everything, IPKISS provides a way to store and apply tool-specific settings.

When inspecting the simulation project from the Lumerical EME GUI, one can easily tweak any desired settings from the GUI. Almost anything can be modified, really. Consult the Lumerical manual for information.

As an example, we modify the energy conservation setting as follows:

../../_images/eme_set_settings.png

Now, we can store all the additional settings which were made back into the IPKISS model. These will then automatically be re-applied when you run the same simulation next time, also when you make changes to the device geometry, virtual fabrication process or simulation settings!

Save the settings to IPKISS by copying your commands and storing them into a file.

../../_images/eme_import_settings.png

7. Building models based on simulation results

Finally, when you are satisfied with the S-matrix you obtained, you can store it in the Touchstone format. Then, you can use it to build a CircuitModel for your PCell, see Physical device simulation guide and Circuit simulation with scatter matrix files.