// This code simulates the eigenmodes of two nearby rectangular SiN dielectric photonic // waveguides buried in a SiO2 clad. To do so the 2D cross sectional geometry is // considered and the propagation is assumed to be along the z axis. // To solve this eigenvalue problem in 2D while taking into account the z component // the electric field is split into a transverse and a longitudinal part. // Since the searched modes are confined in and around the waveguide, a perfect // conductor boundary condition is valid, as long as the domain is large compared // to the waveguide dimensions. // // More details can be found in 'NASA technical paper 3485'. // // // The waveguides are 500 nm wide and 250 nm high. Their spacing is 100 nm. // _____________________________________________________ // | | // | SiO2 ___________ ___________ | // | | | | | | // | | SiN | | SiN | | // | |___________| |___________| | // | | // |_____________________________________________________| // // Credits: R. Haouari #include "sparselizardbase.h" using namespace mathop; void sparselizard(void) { // Give names to the physical region numbers : int wavg1 = 1, wavg2 = 2, clad = 3, all = 4, bound = 5; mesh mymesh; mymesh.regionskin(bound, all); mymesh.load("optical_waveguide.msh"); int waveguides = regionunion({wavg1, wavg2}); // Waveguide boundary plotting: expression(1).write(regionintersection({waveguides, clad}), "skin.vtu"); wallclock clk; // Edge shape functions 'hcurl' for the tranverse electric field Et. // Nodal shape functions 'h1' for the longitudinal electric field El. field Et("hcurl"), El("h1"); // Use interpolation order 2 on the whole domain: Et.setorder(all, 2); El.setorder(all, 2); // Material properties definition parameter n, epsr, mur; n|clad = 1.4; n|wavg1 = 2.0; n|wavg2 = 2.0; epsr|all = n*n; mur|all = 1.0; // Light property double lambda = 680e-9, c = 299792458, f0 = c/lambda, k0 = 2.0*getpi()/lambda; std::cout << std::endl << "lambda = " << lambda*1e9 <<" nm, f0 = " << f0 << " Hz, k0 = " << k0 << " 1/m" << std::endl << std::endl; // Perfect conductor boundary condition: El.setconstraint(bound); Et.setconstraint(bound); // Weak formulation for the eigenvalue problem for confined propagation of an EM wave in a waveguide: // // 1/mur*curl(Et)*curl(Et') - k0^2*epsr*Et*Et' = -bt^2/mur*( grad(El)*Et') + Et*Et' ) // // bt^2/mur*( grad(El)*grad(El') + Et*grad(El') ) = k0^2*bt^2*epsr* El*El' // // where curl() and grad() have a special definition in the transverse (xy) plane. // // The bt constant coming from the space derivation of exp(i*bt*z) is replaced by a time derivative dt(). // Operators grad() and curl() in the transverse plane: expression dtdtgradEl(3,1,{dtdt(dx(dof(El))), dtdt(dy(dof(El))), 0}); expression gradtfEl(3,1,{dx(tf(El)), dy(tf(El)), 0}); formulation mode; mode += integral(all, curl(dof(Et))*curl(tf(Et))- k0*k0*mur*epsr*(dof(Et))*tf(Et)); mode += integral(all, dtdtgradEl*tf(Et)+ dtdt(dof(Et))*tf(Et)); mode += integral(all, dtdtgradEl*gradtfEl+dtdt(dof(Et))*gradtfEl); mode += integral(all, -k0*k0*mur*epsr*dtdt(dof(El))*tf(El)); mode.generate(); // Get the stiffness matrix K and mass matrix M: mat K = mode.K(); mat M = mode.M(); // Remove the rows and columns corresponding to 0 constraints: K.removeconstraints(); M.removeconstraints(); // Create the object to solve the eigenvalue problem: eigenvalue eig(K, M); // Compute the 5 eigenvalues closest to the target magnitude bt^2 (propagation constant^2). // We are looking for modes around neff_target, the target effective refractive index. double neff_target = 1.6, bt = k0*neff_target, bt2 = std::pow(bt,2.0); std::cout << "Target is neff = " << neff_target << ", beta = " << bt << " rad/m" << std::endl << std::endl; // Propagation mode eigenvalue is purely imaginary (i*bt), we thus target -bt^2: eig.compute(5, -bt2); // Get all eigenvectors and eigenvalues found: std::vector myrealeigenvectors = eig.geteigenvectorrealpart(); std::vector myrealeigenvalues = eig.geteigenvaluerealpart(); std::vector neffcs; expression Etotal = array3x1(compx(Et), compy(Et), El); // Loop on all eigenvalues found: int index = 1; for (int i = 0; i < myrealeigenvalues.size(); i++) { if (myrealeigenvalues[i] < 0) { // Transfer the data from the ith eigenvector to fields Et and El: Et.setdata(all, myrealeigenvectors[i]); El.setdata(all, myrealeigenvectors[i]); // Compute the propagation constant and the effective refractive index: double btc = std::sqrt(std::abs(myrealeigenvalues[i])); double neffc = btc/k0; // We need to write separately on the clad and waveguide regions to visualize the discontinuity: Etotal.write(clad, "Eclad_"+ std::to_string(index) +".vtu", 2); Etotal.write(regionunion({wavg1,wavg2}), "Ewav_"+ std::to_string(index) +".vtu", 2); // Display mode information: std::cout << "Mode " << index << ": beta = " << btc << " rad/m, neff = " << neffc << std::endl; neffcs.push_back(neffc); index++; } } // Write a .pvd file to visualize all the modes together: grouptimesteps("Eclad.pvd", "Eclad_", 1, neffcs); grouptimesteps("Ewav.pvd", "Ewav_", 1, neffcs); std::cout << std::endl; clk.print("Total time elapsed:"); // Code validation line. Can be removed. std::cout << (neffcs[0] < 1.65780 && neffcs[0] > 1.65778); } int main(void) { SlepcInitialize(0,{},0,0); sparselizard(); SlepcFinalize(); return 0; }