123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- // 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<vec> myrealeigenvectors = eig.geteigenvectorrealpart();
- std::vector<double> myrealeigenvalues = eig.geteigenvaluerealpart();
- std::vector<double> 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;
- }
|