Note
Click here to download the full example code
Make a Rectangular AWG¶
This simple example illustrates how to make a rectangular AWG and how to run a Caphe simulation on it. For this example, we use the IPKISS AWG Designer module.
Getting started¶
We start by importing the technology with other required modules:
from technologies import silicon_photonics
from ipkiss3 import all as i3
import numpy, pylab as plt
import awg_designer.all as awg
Next, create a template for the free propagation region. This defines the layers, slab modes, etc.
slab_t = awg.SlabTemplate()
slab_t.Layout(slab_layers=[i3.PPLayer(i3.TECH.PROCESS.WG, i3.TECH.PURPOSE.DF_AREA)])
slab_t.SlabModes(modes=[awg.SimpleSlabMode(name="TE0", n_eff=2.8, n_g=3.2, polarization="TE")])
N = 44 # number of arms
R = 150.0 # radius of the star couplers
W = 2.0 # aperture width
M = 8 # outputs
The aperture¶
Make a virtual aperture
ap = awg.OpenWireWgAperture(slab_template=slab_t)
ap_lo = ap.Layout(aperture_core_width=W,
aperture_edge_width=1.0)
ap_sm = ap.FieldModelFromCamfr()
ap_cm = ap.CircuitModel(simulation_wavelengths=[1.55])
The input star coupler¶
Make a multi-aperture for the arms consisting of N apertures like these, arranged in a circle and get the transformations of the individual apertures.
angle_step = i3.RAD2DEG * (W + 0.2) / R
angles_arms = numpy.linspace(-angle_step * (N - 1) / 2.0, angle_step * (N - 1) / 2.0, N)
ap_arms_in, _, trans_arms_in, trans_ports_in = awg.get_star_coupler_apertures(
apertures_arms=[ap] * N,
apertures_ports=[ap],
angles_arms=angles_arms,
angles_ports=[0],
radius=R,
mounting='confocal',
input=True
)
Make the input star coupler
sc_in = awg.StarCoupler(aperture_in=ap,
aperture_out=ap_arms_in)
sc_in_lo = sc_in.Layout(
contour=awg.get_star_coupler_extended_contour(
apertures_in=[ap],
apertures_out=[ap] * N,
trans_in=trans_ports_in,
trans_out=trans_arms_in,
radius_in=R,
radius_out=R,
extension_angles=(10, 5)
)
)
sc_in_lo.visualize()

<Figure size 640x480 with 1 Axes>
The output star coupler¶
Make the multi-apertures for the outputs and get the transformations for the individual apertures.
angle_step = i3.RAD2DEG * (4.7) / R
angles_ports = numpy.linspace(angle_step * (M - 1) / 2.0, -angle_step * (M - 1) / 2.0, M)
ap_arms_out, ap_out, trans_arms_out, trans_ports_out = awg.get_star_coupler_apertures(
apertures_arms=[ap] * N,
apertures_ports=[ap] * M,
angles_arms=angles_arms,
angles_ports=angles_ports,
radius=R,
mounting='rowland',
input=False
)
Make the output star coupler
sc_out = awg.StarCoupler(aperture_in=ap_arms_out,
aperture_out=ap_out)
sc_out_lo = sc_out.Layout(contour=awg.get_star_coupler_extended_contour(apertures_in=[ap] * N,
apertures_out=[ap] * M,
trans_in=trans_arms_out,
trans_out=trans_ports_out,
radius_in=R,
radius_out=R / 2,
extension_angles=(10, 10)))
sc_out_lo.visualize()

<Figure size 640x480 with 1 Axes>
The rectangular waveguide array¶
Make the RectangularWaveguideArray
<RectangularWaveguideArray.Layout view 'RectangularWaveguideArray_1:layout'>
The Arrayed Waveguide Grating¶
Make an AWG with the 3 building blocks
rect_awg = awg.ArrayedWaveguideGrating(star_coupler_in=sc_in,
star_coupler_out=sc_out,
waveguide_array=waveguide_array)
awg_lo = rect_awg.Layout()
awg_lo.visualize()

<Figure size 640x480 with 1 Axes>
Running the Caphe simulation¶
print("Running Caphe simulation (with wavelength-independent star couplers)")
sc_in.CircuitModel(simulation_wavelengths=[1.55])
sc_out.CircuitModel(simulation_wavelengths=[1.55])
awg_cm = rect_awg.CircuitModel()
wavelengths = numpy.linspace(1.52, 1.58, 401)
import time
t0 = time.time()
S = awg_cm.get_smatrix(wavelengths)
print("Calculation time:", time.time() - t0)
for i in range(1, M+1):
plt.plot(wavelengths, 10 * numpy.log10(numpy.abs(S["in1", "out{}".format(i)])**2), label="out{}".format(i))
plt.xlabel("Wavelength")
plt.ylabel("Transmission [dB]")
plt.legend()
plt.show()

Running Caphe simulation (with wavelength-independent star couplers)
Calculation time: 19.572583436965942
Using SpectrumAnalyzer to analyze the results¶
We finally use i3.SpectrumAnalyzer
to
detect the peaks and calculate the crosstalk.

<Figure size 640x480 with 1 Axes>
Let’s calculate the passbands for a cutoff of 15 dB:
bands = sa.cutoff_passbands(-15)
And then calculate the nearest neighbor crosstalk for each channel. The value reported for out1 is the crosstalk caused by the neirest channels out8 and out2:
print(sa.near_crosstalk(bands))
OrderedDict([('out1', -45.37150120024613), ('out2', -41.879428210337686), ('out3', -44.506661009205665), ('out4', -45.28224001354202), ('out5', -45.015670090851685), ('out6', -43.536530565621334), ('out7', -43.243615804376354), ('out8', -46.42733115539546)])
We also calculate the far neighbor crosstalk, which ignores nearest neighbors:
print(sa.far_crosstalk(bands))
OrderedDict([('out1', -51.86543979109562), ('out2', -50.032806858759386), ('out3', -50.32748205773943), ('out4', -50.68732612620517), ('out5', -50.923112582810646), ('out6', -50.52006972636316), ('out7', -49.96463909246463), ('out8', -49.71220208939775)])