Warning. The cost of running this notebook is larger than 50 FC. The cost of running the FDTD portion of the notebook is about 60 FlexCredits. The cost of running the EME portion is less than 5.
EME is a frequency-domain technique used for propagating electromagnetic fields and calculating the complete bidirectional scattering matrix. It is particularly useful for simulating long waveguides, as it can be significantly faster and more cost-effective than FDTD. In the EME solver, the waveguide is divided into cells, and the bidirectional scattering matrix is calculated at its boundaries. Therefore, the method is suitable for waveguides with adiabatic variations, where the relative error compared to a full-wave FDTD simulation can be small.
Additionally, the EME solver can be used to simulate bent waveguides, either by concatenating circular sections with different bend radii or by creating a grid to capture the variations in the local radius for an arbitrary bend. However, the workflow might be counterintuitive, as the EME simulation consists of a straight geometry, with each EME cell representing a local curvature. This is depicted in the above picture, where a waveguide formed by a combination of circular arcs in FDTD is modeled as a straight waveguide in EME, with each cell accounting for the curvature radius of each section.
In this notebook, we will demonstrate the use of the EME solver for analyzing bent structures through the following examples:
S-shape bend composed of two semi-circles with identical bend radii
Coupling arms formed by several semi-circles
S-shape Euler bend composed of two clothoid curves
Also, we will compare the results obtained with the EME solver with the ones obtained with an FDTD simulation.
More information about the EME solver can be found in this example notebook.
# import libraries# suppress warnings during calculationsimport warningsimport matplotlib.pyplot as pltimport numpy as npimport tidy3d as tdfrom tidy3d import webwarnings.filterwarnings("ignore")# emit each unique Tidy3D warning only oncetd.log.warn_once =True
Circular Bends
The EME cells will be defined with the EMEExplicitGrid object, which allows us to position EME grid cells in specific positions. The first and last cells will simulate straight sections, which are the input and output waveguides of the device. The intermediate cells are a combination of circular arcs. The S-bend is formed by two circular arcs with a radius of curvature of equal magnitude but different signs, which represent a counter-clockwise and clockwise curvature. The curvature radius is defined in the EMEModeSpec object.
For all examples, the structures will consist of Silicon Nitride waveguides buried in \(\text{SiO}_2\).
Firstly, we will define a function for creating the EME simulation. For defining the bend region, we will input a list of tuples, where each tuple will have the information of the length and bend radius of a given cell.
def get_bend_eme( bends=[(1, 1)], wvgIn=2, wvgOut=2, width=0.6, thickness=0.4, plane_size=(6, 6), num_modes=160, target_neff=1.5, core_index=2, precision="double", store_port_modes=False, constraint=None,):""" Creates and returns an EME simulation for a bent waveguide structure. Parameters: bends (list of tuple): A list of tuples where each tuple represents a bend in the waveguide. Each tuple consists of two values: length and bend radius. Default is [(1, 1)]. wvgIn (float): The length of the straight waveguide section at the input. Default is 2. wvgOut (float): The length of the straight waveguide section at the output. Default is 2. width (float): The width of the waveguide. Default is 0.6. thickness (float): The thickness of the waveguide core. Default is 0.4. plane_size (tuple): The size of the simulation plane (height, width). Default is (6, 6). num_modes (int): The number of modes to compute for the simulation. Default is 160. target_neff (float): The target effective index for the modes. Default is 1.5. core_index (float): The refractive index of the core material. Default is 2. precision (str): The numerical precision for the simulation, either "single" or "double". Default is "double". store_port_modes (bool): Whether to store port modes. Default is False. """# default mode spec mode_spec = td.EMEModeSpec( num_modes=num_modes, bend_radius=None, bend_axis=1, num_pml=(12, 12), target_neff=target_neff, precision=precision, )# size of the EME simulation. Start with 2 to compensate the port offsets size =2# add straight sectionsif wvgIn >0: bends = [(wvgIn, None)] + bendsif wvgOut >0: bends = bends + [(wvgOut, None)] structures = [] mode_specs = []# iterate through each section and create each mode specfor i, bend inenumerate(bends): length, radius = bend mode_specs.append(mode_spec.updated_copy(bend_radius=radius))if i ==0: boundaries = [length]else: boundaries.append(boundaries[-1] + length) size += length eme_grid_spec = td.EMEExplicitGrid(boundaries=boundaries[:-1], mode_specs=mode_specs)# monitor for visualizing fields eme_field_mon = td.EMEFieldMonitor(name="field", size=(td.inf, 0, td.inf), num_modes=1)# creating structures sio2 = td.material_library["SiO2"]["Palik_LowLoss"] core = td.Medium(permittivity=core_index**2)# creating waveguide structure structures = [ td.Structure( attrs={}, geometry=td.PolySlab( axis=2, sidewall_angle=0.0, slab_bounds=(-thickness /2, thickness /2), vertices=np.array( [ [-1e15, -width /2], [-1e15, width /2], [1e15, width /2], [1e15, -width /2], ] ), ), medium=core, ) ]# creating the simulation object sim = td.EMESimulation( size=(size, plane_size[0], plane_size[1]), medium=sio2, center=(size /2-1, 0, 0), structures=structures, axis=0, freqs=[td.C_0 /1.55], eme_grid_spec=eme_grid_spec, grid_spec=td.GridSpec.auto(min_steps_per_wvl=20), monitors=[eme_field_mon], port_offsets=(1, 1), symmetry=(0, 0, 1), store_port_modes=store_port_modes, constraint=constraint, )return sim
For each EME simulation, we will also create an equivalent FDTD simulation to explicitly simulate the curvature. For convenience, we will define auxiliary functions for creating a list of x and y points for a given EME simulation object, that can later be used to generate the correct structure for the FDTD simulation:
# function for creating points for an arc sectiondef arc(length, radius, center=(0, 0), theta0=0): theta = theta0 + np.linspace(0, length, 1001) / radius x = radius * np.cos(theta - np.pi /2) y = radius * np.sin(theta - np.pi /2) x = x + (center[0] - x[0]) y = y + (center[1] - y[0])return x, y, theta# function for creating points for a straight section at a given angledef line(length, theta, center=(0, 0)): x = [center[0], center[0] + length * np.cos(theta)] y = [center[1], center[1] + length * np.sin(theta)]return x, y# function for creating a list of points representing the bend waveguide from an EME simulation objectdef eme2Curve(sim): mode_specs = sim.eme_grid_spec.mode_specs boundaries = sim.eme_grid_spec.boundaries boundaries = np.append(0, boundaries) boundaries = np.append(boundaries, sim.size[0] -sum(sim.port_offsets)) lengths = np.diff(boundaries) radius = [i.bend_radius for i in mode_specs] x, y = [], [] cx, cy =0, 0 theta =0for i, r inenumerate(radius):if r: a, b, t = arc(lengths[i], r, (cx, cy), theta0=theta) theta = t[-1]else: a, b = line(lengths[i], theta, (cx, cy)) x = np.append(x, a) y = np.append(y, b) cx = x[-1] cy = y[-1]return x, y
Next, we will create a function to return an FDTD simulation object, given as input a list of x and y coordinates of the waveguide structure. To analyze the transmission, a ModeMonitor is placed at the end of the last straight section, in the same position as the last cell of the EME grid. For analyzing modes that are not parallel with the x or y axis, we will input the correct angle_theta parameter in the ModeSpec object.
def get_fdtd(x, y, sim, width=0.6, thickness=0.4):# output angle theta = np.arctan2(y[-1] - y[-2], x[-1] - x[-2])# simulation size sx =max(x) -min(x) sy =max(y) -min(y)# simulation center centerX =min(x) + sx /2 centerY =min(y) + sy /2# adding extra size so the structures will not start or end at the PML boundaries x = np.append([-5, 0], x) y = np.append([0, 0], y) cx = x[-1] cy = y[-1] a, b = line(5, theta, (cx, cy)) x = np.append(x, a) y = np.append(y, b)# creating the structure with gdstkimport gdstk cell = gdstk.Cell("bends") cell.add(gdstk.FlexPath(x +1j* y, width, layer=1, datatype=0)) geo = td.PolySlab.from_gds( cell, gds_layer=1, axis=2, slab_bounds=(-thickness /2, thickness /2), )[0]# simulation size size = (sx +4, sy +5, 3)# structure and source fdtd_structure = td.Structure(geometry=geo, medium=sim.structures[0].medium) source = td.ModeSource( center=(0, 0, 0), size=(0, 3, 3), direction="+", mode_spec=td.ModeSpec(num_modes=1), source_time=td.GaussianPulse(freq0=td.C_0 /1.55, fwidth=0.1* td.C_0 /1.55), )while theta >=2* np.pi: theta -=2* np.pi thetaDeg =180* theta / np.pi# adjusting monitor size for the output angleif (thetaDeg >-45) and (thetaDeg <=45): mon_size = (0, 3, 3)elif (thetaDeg >45) and (thetaDeg <=135): mon_size = (3, 0, 3)elif (thetaDeg >135) and (thetaDeg <=225): mon_size = (0, 3, 3)else: mon_size = (3, 0, 3)# monitors fieldMon = td.FieldMonitor( name="field", center=(0, 0, 0), size=(td.inf, td.inf, 0), freqs=[td.C_0 /1.55] )# adjusting the theta angle of the output monitorifabs(theta - np.pi /2) <0.05orabs(theta - np.pi) <0.05: modeMon = td.ModeMonitor( name="modeMon", size=mon_size, center=(cx, cy, 0), freqs=[td.C_0 /1.55], mode_spec=source.mode_spec, )else:# adjusting the correct angle for each quadrantif thetaDeg <=90: t = thetaif (thetaDeg >90) and (thetaDeg <=180): t =-(theta - np.pi /2)if (thetaDeg >180) and (thetaDeg <=270): t = (theta - np.pi) + theta - np.pi /2if (thetaDeg >270) and (thetaDeg <=360): t =-(theta -3* np.pi /2) modeMon = td.ModeMonitor( center=(cx, cy, 0), size=mon_size, freqs=[td.C_0 /1.55], mode_spec=td.ModeSpec( num_modes=5, angle_theta=t, angle_phi=0, ), name="modeMon", ) sim_fdtd = td.Simulation( size=size, center=(centerX, centerY, sim.center[2]), medium=sim.medium, grid_spec=sim.grid_spec, sources=[source], monitors=[fieldMon, modeMon], run_time=10e-12, structures=[fdtd_structure], boundary_spec=td.BoundarySpec( x=td.Boundary.absorber(), y=td.Boundary.absorber(), z=td.Boundary.pml() ), symmetry=(0, 0, 1), )return sim_fdtd
Plane Size and Mode Index
As a sanity check, let’s start with a simple 90-degree bend:
02:55:47 UTC WARNING: A large number (160) of modes requested in monitor
'mode_spec'. This can lead to solver slow-down and increased cost.
Consider decreasing the number of modes and using
'ModeSpec.target_neff' to target the modes of interest. This may
become a hard limit in future Tidy3D versions.
# plotting the EME simulationeme_sim.plot(z=0)plt.show()
# plotting the FDTD simulationfdtd_sim.plot(z=0)plt.show()
02:55:48 UTC Created task 'eme' with resource_id
'eme-f0bde3d2-e5ec-4f6f-83a7-a6b87c968e51' and task_type 'EME'.
Tidy3D's EME solver is currently in the beta stage. Cost of EME
simulations is subject to change in the future.
↑ simulation.hdf5.gz ━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 1.8/1.8 kB • ? • 0:00:00
Estimated FlexCredit cost: 0.196. For this solver type, the
estimate is the final billed cost.
status = queued
To cancel the simulation, use 'web.abort(task_id)' or
'web.delete(task_id)' or abort/delete the task in the web UI.
Terminating the Python script will not stop the job running on the
cloud. 02:55:58 UTC starting up solver
running solver
solver progress ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
02:56:25 UTC status = success
↓ simulation_data.hdf5.gz ━━━━━━━━━━━ 100.0% • 3.9/3.9 MB • 444.4 MB/s • 0:00:00
02:56:27 UTC Loading results from simulation_data.hdf5
02:56:28 UTC WARNING: Warning messages were found in the solver log. For more
information, check 'SimulationData.log' or use
'web.download_log(task_id)'.
# analyzing the transmittancet =float(eme_data.smatrix.S21.isel(mode_index_in=0, mode_index_out=0, f=0).abs.item() **2)print(f"Transmittance: {t}")
Transmittance: 9.919098373240908e-26
# plotting the fieldseme_data.plot_field("field", "Hz", "real", mode_index=0, eme_port_index=0)plt.show()
We can note that the results are not correct, and there is a warning message in the simulation log.
As discussed in this notebook, the plane size for a bend mode solver simulation must be sufficiently large to yield correct values for the losses. Small plane sizes can lead to negative values, which are disregarded by the EME solver, and hence the results will not be correct.
x, y = eme2Curve(eme_sim)fdtd_sim = get_fdtd(x, y, eme_sim)# run the EME and FDTD simulations together in a single batcheme_data, fdtd_data = web.run([eme_sim, fdtd_sim], verbose=True)
02:56:30 UTC WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_0'
has a large number (1.45e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries.
WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_1'
has a large number (1.45e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries.
WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_2'
has a large number (1.45e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries.
Uploading data for 2 tasks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
Started working on Batch containing 2 tasks.
Maximum FlexCredit cost: 11.131 for the whole batch.
Use 'Batch.real_cost()' to get the billed FlexCredit cost after
completion. 02:56:48 UTC Batch complete.
22a96833e81222a0d0b6f194a9d... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:18
eccc2c086dffaa94b1b0db06809... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:11
As we can see, with a plane size large enough, the results are now correct and very close to those obtained with an FDTD simulation. Hence, starting with an analysis of the required plane size for a given simulation is always a good approach.
We can also plot the fields for both simulations. Although the EME simulation correctly captures the field decay, the meaning of the field plot for bent waveguides is not a true representation of the electric field, as it is in the FDTD simulation.
Now, we can simulate a S-bend formed by two circular arcs, with the same radius and opposite sign, and compare with FDTD results. The s-bends are parametrized by their length and offset, hence, we will define an auxiliary function to retrieve the correct angle and curvature radius for a given bend.
Next, we will vary the length of the S-bend and compare the results obtained from the EME and FDTD simulations.
lengths = [20, 40, 60, 80, 100]eme_sims = []fdtd_sims = []for length in lengths: r, theta = get_theta(length, 20) eme_sim = get_bend_eme( wvgIn=2, wvgOut=2, bends=[(theta * r, r), (theta * r, -r)], plane_size=(17, 17), num_modes=160, ) x, y = eme2Curve(eme_sim) fdtd_sim = get_fdtd(x, y, eme_sim) eme_sims.append(eme_sim) fdtd_sims.append(fdtd_sim)eme_data, fdtd_data = web.run([eme_sims, fdtd_sims], verbose=True)transmittance_eme = [float(d.smatrix.S21.isel(mode_index_in=0, mode_index_out=0, f=0).abs.item() **2) for d in eme_data]transmittance_fdtd = [float(d["modeMon"].amps.sel(direction="+", mode_index=0).abs.item() **2) for d in fdtd_data]
WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_0'
has a large number (1.06e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries.
02:56:56 UTC WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_3'
has a large number (1.06e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries.
WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_1'
has a large number (1.06e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries.
WARNING: Internal mode solver monitor '_eme_mode_solver_monitor_2'
has a large number (1.06e+05) of grid points. This can lead to
solver slow-down and increased cost. Consider making the size of
the component smaller, as long as the modes of interest decay by
the plane boundaries. Uploading data for 10 tasks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
02:56:58 UTC Started working on Batch containing 10 tasks.
Maximum FlexCredit cost: 34.947 for the whole batch.
Use 'Batch.real_cost()' to get the billed FlexCredit cost after
completion. 02:57:33 UTC Batch complete.
ec8a859d5e95ed06ea486a40c96... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:34
2d47f43e62744cc943c58afb74b... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:04
1d750be11a119f4086916f29a61... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:04
0a8a074eed1a699fad038830127... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:04
3ae188cfd4d436b3acc6d1c73d4... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:03
386815aa375594b97a057784705... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:02
1cb96af36cb4d115cd556181faa... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:08
5f3286f94e4242cc79e08c1b1ab... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:16
cb38c427d4cf3e4bb57c80af70b... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:34
3d17857a128c50d55416d174977... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:07
Plotting the results, we can see that there is a good match between FDTD and EME transmittance.
For a bend, the EME result depends on how well the modal basis captures both the local bend mode and the radiation shed at the transitions between cells.
The mode solver returns the num_modes modes closest to target_neff. The guided mode of the straight waveguide sits at \(n_\text{eff} \approx 1.5456\). Targeting that value biases the basis toward the guided mode and overestimates the transmittance. Setting target_neff slightly lower (here 1.5) keeps the guided mode in the basis while also including the lower-index radiation modes that account for the junction loss.
The required number of modes grows with the number of connected bends, since radiation accumulates across the cell interfaces. We check convergence on a device of two connected bends (r = 20 µm) by sweeping the number of modes with EMEModeSweep in a single run. We also set constraint=None: the default passive constraint regularizes the interface matching but becomes expensive above ~50 modes, so it is disabled here for the large mode counts.
# convergence with the number of modes, on two connected bendstheta = np.pi /2r =20conv_bends = [(theta * r, r), (theta * r, -r)]mode_counts =list(range(40, 201, 10))conv_sim = get_bend_eme( wvgIn=2, wvgOut=2, bends=conv_bends, plane_size=(15, 15), num_modes=max(mode_counts), constraint=None,)conv_sim = conv_sim.updated_copy(monitors=[], sweep_spec=td.EMEModeSweep(num_modes=mode_counts))conv_data = web.run(conv_sim, task_name="mode_convergence", verbose=True)transmittance_conv = ( np.abs(conv_data.smatrix.S21.isel(mode_index_in=0, mode_index_out=0, f=0).values.flatten()) **2)fig, ax = plt.subplots()ax.plot(mode_counts, transmittance_conv, "o-")ax.axvline(160, color="k", ls="--", label="160 modes")ax.set_xlabel("number of modes")ax.set_ylabel("transmittance")ax.legend()plt.show()
02:57:35 UTC WARNING: A large number (200) of modes requested in monitor
'mode_spec'. This can lead to solver slow-down and increased cost.
Consider decreasing the number of modes and using
'ModeSpec.target_neff' to target the modes of interest. This may
become a hard limit in future Tidy3D versions.
Created task 'mode_convergence' with resource_id
'eme-0d9bac96-da6d-40d6-9ba2-23d52bac4800' and task_type 'EME'.
Tidy3D's EME solver is currently in the beta stage. Cost of EME
simulations is subject to change in the future.
↑ simulation.hdf5.gz ━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 1.8/1.8 kB • ? • 0:00:00
Estimated FlexCredit cost: 1.648. For this solver type, the
estimate is the final billed cost.
status = queued
To cancel the simulation, use 'web.abort(task_id)' or
'web.delete(task_id)' or abort/delete the task in the web UI.
Terminating the Python script will not stop the job running on the
cloud. 02:57:41 UTC starting up solver
running solver
solver progress ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
02:59:19 UTC status = success
↓ simulation_data.hdf5.gz ━━━━━━━━━━ 100.0% • 17.1/17.1 • 469.2 MB/s • 0:00:00
MB
02:59:21 UTC Loading results from simulation_data.hdf5
The transmittance converges once about 160 modes are included (it is flat to within ~0.002 between 160 and 200 modes). We therefore use num_modes = 160 with constraint=None for the simulations in this notebook.
We can also work with a combination of s-bends, that can work as arms for interferometers. In this case, we will have a very large FDTD simulation, that costs around 10 FC, while the EME simulation will cost less then 1.
Uploading data for 2 tasks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
02:59:22 UTC Started working on Batch containing 2 tasks.
Maximum FlexCredit cost: 33.014 for the whole batch.
Use 'Batch.real_cost()' to get the billed FlexCredit cost after
completion. 03:04:35 UTC Batch complete.
7f8a434c44f1062c60168a9539e... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:05:12
8e00f320835da94dc0ae2639783... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:05:12
As we can see, the results are very close, but the FC cost for the EME simulation is much smaller.
We can use a similar approach as in the previous examples to simulate an arbitrary curve that is not formed by circular arcs, such as an S-bend formed with Euler curves, similar to this example notebook.
As discussed in this example, for an adiabatically varying waveguide, the EME simulation can be a good approximation of the real structure. In this case, we will approximate the real structure using an EME grid accounting for the local radius at different sections.
First, we will define a function to create the Euler bend:
def euler_curve(A=2.4, L=10, num_points=1000):from scipy import integrate Ls = np.linspace(0, L, num_points) # L at (x1,y1) x1 = np.zeros(len(Ls)) # x coordinate of the clothoid curve y1 = np.zeros(len(Ls)) # y coordinate of the clothoid curve# compute x1 and y1 using the above integral equations y1 = np.array([integrate.quad(lambda theta: A * np.sin(theta**2/2), 0, L / A)[0] for L in Ls]) x1 = np.array([integrate.quad(lambda theta: A * np.cos(theta**2/2), 0, L / A)[0] for L in Ls])return x1, y1def euler_bend(offset, length, num_points=1000):from scipy.optimize import fsolvedef get_params(params): A, L = params x1, y1 = euler_curve(A=A, num_points=num_points, L=L)return (abs(x1[-1] - length /2) **2, abs(y1[-1] - offset /2) **2) A, L = fsolve(get_params, (1, 1)) x1, y1 = euler_curve(A=A, num_points=num_points, L=L) x = np.append(x1, np.flip(-x1 +2* x1[-1])) y = np.append(y1, np.flip(-y1 +2* y1[-1]))return x, y
Next, we define a function for calculating the local curvature radius, as \(R = \frac{(1 + y'^2)^{\frac{3}{2}}}{y''}\):
As we can note, an uniformly spaced grid might not be the most efficient approach, as the local radius varies much more in some sections than in others. We can visualize this by looking at its derivative:
One possible approach is to divide the curve into sections based on its derivative value. This way, sections with greater variation will have a higher grid density, better capturing the waveguide radius change.
We will create a function get_bends that will divide the curve into tree sections, according with the local radius derivative values, and create even spaced grid cells into these sections.
# function to create evenly distributed grid cells into one particular sectiondef get_positions(points, n_grid):if n_grid >=len(points):return np.arange(0, len(points), 1) indices = np.linspace(0, len(points) -1, n_grid, dtype=int)return indicesdef get_bends(x_euler, y_euler, grid_per_section=5, plot=False):# local radius and its derivatives local_radius = get_local_radius(x_euler, y_euler) dR = np.gradient(local_radius, x_euler, edge_order=2)# slice the data in chunks based on mean and standard deviation mean = np.nanmean(abs(dR)) std = np.nanstd(abs(dR)) section1 =abs(dR) >= mean + std section2 = (abs(dR) < mean + std) & (abs(dR) >= mean) section3 =abs(dR) < mean section1_points = get_positions(x_euler[section1], grid_per_section) section2_points = get_positions(x_euler[section2], grid_per_section) section3_points = get_positions(x_euler[section3], grid_per_section)# discontinuity points in the derivative that are continuous in the local radius curve discontinuity = x_euler[np.isnan(dR)][~np.isnan(local_radius[np.isnan(dR)])] discontinuity_y = y_euler[np.isnan(dR)][~np.isnan(local_radius[np.isnan(dR)])] discontinuity_radius = local_radius[np.isnan(dR)][~np.isnan(local_radius[np.isnan(dR)])]# retrieving the x position and local radius of the EME grid x_positions = np.concatenate( [ x_euler[section1][section1_points], x_euler[section2][section2_points], x_euler[section3][section3_points], discontinuity, [x_euler[-1]], ] ) sort = np.argsort(x_positions) x_positions = x_positions[sort] radius = np.concatenate( [ local_radius[section1][section1_points], local_radius[section2][section2_points], local_radius[section3][section3_points], discontinuity_radius, [local_radius[-1]], ] ) radius = radius[sort][1:]# transform positions in length length = np.diff(x_positions)# creating the list of tuples bends =list(zip(length, radius))# plot the grid pointsif plot: fig, ax = plt.subplots() ax.plot(x_euler, y_euler) ax.plot( x_euler[section1][section1_points], y_euler[section1][section1_points],"o", label="section 1", ) ax.plot( x_euler[section2][section2_points], y_euler[section2][section2_points],"o", label="section 2", ) ax.plot( x_euler[section3][section3_points], y_euler[section3][section3_points],"o", label="section 3", ) ax.plot(discontinuity, discontinuity_y, "*", label="discontinuity") ax.legend() plt.show()return bends
Now, let’s visualize an example. As we can note, the grid points are densely packed at the edges of the S-bend, where the local radius experiences more variation.
Uploading data for 2 tasks ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00
03:14:52 UTC Started working on Batch containing 2 tasks.
Maximum FlexCredit cost: 38.559 for the whole batch.
Use 'Batch.real_cost()' to get the billed FlexCredit cost after
completion. 03:23:45 UTC Batch complete.
034ea8b2dc73b5e40032cc93909... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:08:52
f5aeb4ea7c8cfc8abf5524e040e... → success ━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:08:52
As we can see, there is only a small deviation compared to the FDTD simulation, although the EME simulation costs around 10 times less.
As we can see, the EME solver can be a good choice instead of FDTD when calculating very long bend waveguides. For waveguides formed only by circular arcs, the simulation setup is straightforward, and the error compared with an FDTD simulation is minimal.
On the other hand, for bends of arbitrary shapes, the simulation setup is more demanding, and the EME simulation becomes an approximation, as the structure is divided into cells and not all local curvature points are considered. Nevertheless, for a sufficient number of cells, the results deviate from an FDTD simulation by less than 3%, making it a suitable tool for simulating very large structures with minimal runtime and computational cost.
Additionally, it is worth emphasizing that the EME method will only yield low error for systems with slow variations, and where waveguides propagate along the same axis. For example, a system with two waveguides propagating with different curvatures would not be well-defined in an EME simulation.
Below, we summarize the pros and cons of each method:
EME Solver
FDTD Solver
Easy setup for circular arc sections
Can be demanding for a complex combination of circular arcs
Setup is demanding for bends of arbitrary shapes
For shapes based on mathematical equations, the geometry setup is straightforward
Low number of unknowns. Ideal for long structures
Number of grid points can be very large for long waveguides
We use necessary cookies to run this website. With your permission, we
also use analytics cookies to understand site usage and marketing
cookies for advertising, retargeting, and HubSpot tracking.
Learn more about our cookie policy.
Privacy choices
Choose which optional cookies Flexcompute may use. Necessary cookies
are always on because they support core website behavior, security, and
saving your consent record.
Your browser is sending a Global Privacy Control signal, so marketing
cookies are disabled.
Subscribe
Thanks for subscribing
Publish Your Notebook
Thank you for publishingA confirmation email has been sent to your inbox. Your notebook will be available within the next 48 hours.
Community Library
TERMS & CONDITIONS
EFFECTIVE DATE: January 1, 2025
Terms and Conditions for User-Submitted Content
By submitting content to Flexcompute, you agree to the following terms:
1. Ownership and Copyright
All content, including but not limited to text, data, images, and other materials submitted by users,
remains the sole property of the original creator. Flexcompute does not claim ownership of the submitted
content or any intellectual property rights associated with it.
Users affirm that they own the copyright or have obtained all necessary permissions for the submitted
content and are fully responsible for ensuring that their submissions do not infringe on any third-party
rights.
2. Responsibility for Content
Users are solely responsible for the content they submit. Flexcompute does not endorse, guarantee,
or verify the accuracy, legality, or appropriateness of any submitted content. Users are responsible
for ensuring that their content complies with all applicable laws and regulations.
Users declare that they have obtained authorization from all co-authors and collaborators associated
with the submitted content, granting them the right to submit and sign on behalf of all contributors.
Users agree not to submit content that is illegal, defamatory, obscene, or violates the rights
of others, including privacy and intellectual property rights.
3. Modification and Presentation
Flexcompute reserves the right to review, edit, or modify submitted content to improve clarity,
formatting, and presentation while maintaining the original intent and message.
These modifications are made to enhance the overall quality and readability of the published material.
Users acknowledge that Flexcompute may format or present the content in a way
that aligns with our editorial and visual standards.
4. Liability Disclaimer
Flexcompute shall not be held liable for any disputes arising from user-submitted content, including
but not limited to copyright claims, inaccuracies, or damages resulting from the publication of user content.
Users agree to indemnify Flexcompute against any claims or legal actions resulting from their submitted content.
5. Content Use Rights
By submitting content, users grant Flexcompute a non-exclusive, royalty-free, worldwide license to publish, distribute,
and promote the content for the purposes of showcasing user contributions and marketing our services.
This license does not transfer ownership of the content or any copyright to Flexcompute.
Users retain the right to withdraw their submissions at any time.
Upon request, Flexcompute will remove the content from its platform.
6. Privacy and Confidentiality
Users acknowledge that submitted content may be publicly accessible and therefore waive any
rights to confidentiality or privacy regarding the published material.
Flexcompute will not share personal information of users without
their consent, in accordance with our privacy policy.
Community Library
How the process works:
You submit your notebook.
You will get a permanent URL to promote your work.
Cite the URL as a reference for others in your upcoming papers to enhance the impact.
When someone clicks your URL, your notebook will be displayed.
Enter your email address below to receive the presentation slides. In the future, we’ll share you very few emails when we have new tutorial release, development updates, valuable toolkits and technical guidance . You can unsubscribe at any time by clicking the link at the bottom of every email. We’ll never share your information.