# # Simple Waveguide (MODE FDE)
#
# A simple example using MODE.
# Waveguide (FDE): https://optics.ansys.com/hc/en-us/articles/360042800453-Waveguide-FDE
#
# The Finite Difference Eigenmode (FDE) solver in MODE is used to characterize a straight waveguide.
#
# In Part 1, we build the structure and set the FDE simulation region.
# In Part 2, we calculate the supported mode profiles of the waveguide.
#
# Prerequisites:
# Valid MODE license is required.

# Perform required imports

from collections import OrderedDict

import matplotlib.pyplot as plt
import numpy as np

import ansys.lumerical.core as lumapi

# ## Part 1: Set up structures and simulation objects

# +
# Set hide = True to hide the Lumerical GUI.
mode = lumapi.MODE(hide=False)

# Set key parameters
wavelength = 1.55e-6  # Center wavelength
# Set the waveguide cross-section and material
wg_width = 0.5e-6
wg_height = 0.22e-6
wg_material = "Si (Silicon) - Palik"
# Set substrate and cladding cross-section and material
sub_width = 10e-6
sub_height = 5e-6
sub_material = "SiO2 (Glass) - Palik"
clad_width = 10e-6
clad_height = 5e-6
clad_material = "SiO2 (Glass) - Palik"
# Set FDE region
fde_x_span = 3e-6
fde_y_span = 3e-6
fde_y_center = wg_height / 2
fde_z = 0e-6

z_span = 1.0e-6  # Sets z span for all structures, but note FDE solver utilizes a cross section

# Build substrate and cladding
mode.addrect(name="substrate", x=0, x_span=sub_width, y_min=-sub_height, y_max=0, z=0, z_span=z_span, material=sub_material)
mode.addrect(name="clad", x=0, x_span=clad_width, y_min=0, y_max=clad_height, z=0, z_span=z_span, material=clad_material)
# -


# Build waveguide
# Use mesh order override to ensure waveguide object is prioritized over substrate and cladding
wg_props = OrderedDict(
    [
        ("name", "waveguide"),
        ("x", 0),
        ("x span", wg_width),
        ("y min", 0),
        ("y max", wg_height),
        ("z", 0),
        ("z span", z_span),
        ("material", wg_material),
        ("override mesh order from material database", True),
        ("mesh order", 1),
    ]
)
mode.addrect(properties=wg_props)


# Add FDE solver region
fde_props = OrderedDict([("x", 0), ("x span", fde_x_span), ("y", fde_y_center), ("y span", fde_y_span), ("z", fde_z)])
mode.addfde(properties=fde_props)

# Add mesh override region
mesh_props = OrderedDict(
    [
        ("set maximum mesh step", True),
        ("override x mesh", True),
        ("override y mesh", True),
        ("dx", 0.01e-6),
        ("dy", 0.01e-6),
        ("based on a structure", True),
        ("structure", "waveguide"),
    ]
)
mode.addmesh(properties=mesh_props)

# ## Part 2: Calculate the supported modes of the waveguide
# The analysis_props are equivalent to the settings in the Eigensolver Analysis window in the GUI.

mode.setanalysis("wavelength", wavelength)
mode.setanalysis("number of trial modes", 10)
mode.setanalysis("search", "near n")
mode.setanalysis("use max index", True)

mode.findmodes()


# +
# Select and plot the fundamental mode
selected_mode_number = 1
selected_mode = "mode" + str(selected_mode_number)
Efield = mode.getresult("FDE::data::" + selected_mode, "E")

# Plot in Lumerical GUI
mode.visualize((Efield))
# -

# <img src="images/waveguide_mode_visualizer.png" width="600">

# +
# Plot in Python - requires matplotlib
# Note that Lumerical uses an unstructured mesh, so the spacing between points may be non-constant.
# Therefore, it is preferable to collect x, y data from the monitor and plot using contourf.
x, y = Efield["x"], Efield["y"]
Ex, Ey, Ez = Efield["E"][:, :, 0, 0, 0], Efield["E"][:, :, 0, 0, 1], Efield["E"][:, :, 0, 0, 2]
E_mag = np.abs(Ex) ** 2 + np.abs(Ey) ** 2 + np.abs(Ez) ** 2
X, Y = np.meshgrid(x, y)  # Create meshgrid for plotting
plt.figure()
plt.contourf(X, Y, np.transpose(E_mag))
plt.show()
# -

# <img src="images/waveguide_mode_matplotlib.png" width="600">
