/* * WMM-Application: * directional coupler with varying gap width, coupled mode theory */ /* * WMM * Wave-matching method for mode analysis of dielectric waveguides * Manfred Hammer * (2002) */ #include #include #include #include"wmminc.h" /* coupler parameters (lengths in micrometers) */ #define Wgps 2.00 // straight waveguide: core width #define Wgpv 0.14 // straight waveguide: core thickness #define Wgpng 1.98 // straight waveguide: core refractive index #define Wgpnb 1.45 // substrate/straight waveguide, background refr. index #define Wgpw 2.75 // ring waveguide: core width #define Wgpt 1.0 // ring waveguide: core thickness #define Wgpnr 1.596 // ring waveguide: core refractive index #define Wgpna 1.0 // cover refractive index double Wgpb = 1.0; // straight waveguide: burying depth double Wgpg = 0.5; // straight waveguide: center displacement from ring rim double Wgpp = 0.0; // position of ring waveguide; 0: closest approach #define Wgpl 1.55 // vacuum wavelength #define WgpR 100.0 // ring radius, outer rim #define WgpZ 40.0 // half length of the directional couplers, // cores are considered to be decoupled for |z| > Z #define Numz 40 // number of discretization steps in the numerical // integration of the coupled mode equations Polarization POL=QTE; // QTE or QTM (semivectorial simulation) #define CalcBasisModes 1 // 1: calculate the basis fields, save them to files // 0: read them from previously stored files /* display window, mode profile plots; x in [x0, x1], y in [y0, y1], format: Disp(x0, y0, x1, y1), note the axis orientation! */ Rect Disp(-3.5, -4.9, 2.0, 3.9); /* the straight waveguide */ Waveguide swgdef() { Waveguide g(1, 1); g.hx(0) = 0.0; g.hx(1) = Wgpv; g.hy(0) = -Wgps/2.0; g.hy(1) = Wgps/2.0; g.n(0,0) = Wgpnb; g.n(0,1) = Wgpnb; g.n(0,2) = Wgpnb; g.n(1,0) = Wgpnb; g.n(1,1) = Wgpng; g.n(1,2) = Wgpnb; g.n(2,0) = Wgpnb; g.n(2,1) = Wgpnb; g.n(2,2) = Wgpnb; g.lambda = Wgpl; return g; } /* the ring waveguide */ Waveguide rwgdef() { Waveguide g(1, 1); g.hx(0) = 0.0; g.hx(1) = Wgpt; g.hy(0) = -Wgpw/2.0; g.hy(1) = Wgpw/2.0; g.n(0,0) = Wgpnb; g.n(0,1) = Wgpnb; g.n(0,2) = Wgpnb; g.n(1,0) = Wgpna; g.n(1,1) = Wgpnr; g.n(1,2) = Wgpna; g.n(2,0) = Wgpna; g.n(2,1) = Wgpna; g.n(2,2) = Wgpna; g.lambda = Wgpl; return g; } /* the entire coupler structure */ #define Ltol 1.0e-8 Waveguide cpwgdef() { if(Wgpnb <= Ltol) { fprintf(stderr, "mres.c: cpwgdef: Wgpb<=0 not implemented.\n"); exit(1); } Dvector hytmp(4); hytmp(0) = -Wgpp-Wgpw; hytmp(1) = -Wgpp; hytmp(2) = Wgpg-Wgps/2.0; hytmp(3) = Wgpg+Wgps/2.0; hytmp.sort(0); Dvector hy(4); hy(0) = hytmp(0); int ny=1; for(int j=1; j<=3; ++j) { if(fabs(hy(ny-1) - hytmp(j)) >= Ltol) { hy(ny) = hytmp(j); ++ny; } } --ny; Waveguide wg(3, ny); wg.hx(0) = -Wgpb-Wgpv; wg.hx(1) = -Wgpb; wg.hx(2) = 0.0; wg.hx(3) = Wgpt; for(int i=0; i<=ny; ++i) wg.hy(i) = hy(i); for(int m=0; m<=ny+1; ++m) { double y; Rect r; r = wg.rectbounds(1, m); y = (r.y0+r.y1)/2.0; wg.n(0,m) = Wgpnb; if(y < Wgpg-Wgps/2.0 || y > Wgpg+Wgps/2.0) wg.n(1, m) = Wgpnb; else wg.n(1, m) = Wgpng; wg.n(2,m) = Wgpnb; if(y < -Wgpp-Wgpw || y > -Wgpp) wg.n(3, m) = Wgpna; else wg.n(3, m) = Wgpnr; wg.n(4,m) = Wgpna; } wg.lambda = Wgpl; return wg; } /* mode solver parameters */ WMM_Parameters pardef() { WMM_Parameters p; p.vform = HXHY; p.vnorm = NRMMH; p.ccomp = CCALL; p.ini_d_alpha = 0.01; p.ini_N_alpha = 10; p.ini_alpha_max = 2.0; p.ini_steps = 20; p.ref_num = 5; p.ref_exp = 4.0; p.ref_sdf = 0.5; p.fin_d_alpha = 0.01; p.fin_N_alpha = 30; p.fin_alpha_max = 3.0; p.btol = 1.0e-7; p.mshift = 1.0e-8; return p; } /* coupled mode theory simulation */ int main() { WMM_Parameters par = pardef(); WMM_ModeArray ma; WMM_Mode m, stmo, rimo; Waveguide wg; double bs, ba, lc; int n; // for field profile plots: select the interesting field component Fcomp fcp = principalcomp(POL); if(CalcBasisModes == 1) { // define the straight waveguide wg = swgdef(); // find its fundamental mode WMM_findfundmode(wg, POL, SYM, 0.0, 0.0, par, 's', 't', ma); // save it to a file stmo = ma(0); stmo.write_def('s', 't'); ma.clear(); // define the ring waveguide wg = rwgdef(); // find its fundamental mode WMM_findfundmode(wg, POL, SYM, 0.0, 0.0, par, 'r', 'i', ma); // save it to a file rimo = ma(0); rimo.write_def('r', 'i'); ma.clear(); } else { // read both basis modes from files stmo.read_def(POL, SYM, 's', 't'); rimo.read_def(POL, SYM, 'r', 'i'); } // look at the position of closest approach, z=0: // shift the basic modes to the positions of the two waveguides, // compose an array of basis modes m = stmo; m.translate(-Wgpb-Wgpv, Wgpg); ma.add(m); m = rimo; m.translate(0.0, -(Wgpp+Wgpw/2.0)); ma.add(m); n = 2; // the coupler structure Wgpp = 0.0; wg = cpwgdef(); // control output: basis waveguides and composite structures, geometry fprintf(stderr, "\n Straight waveguide (WG1):\n"); ma(0).wg.write(stderr); fprintf(stderr, "\n Ring waveguide (WG2):\n"); ma(1).wg.write(stderr); fprintf(stderr, "\n Composite coupler structure:\n"); wg.write(stderr); // plots of the basis fields, shifted fprintf(stderr, "\nBasis mode profiles:\n"); ma(0).mfile(fcp, ORG, Disp, 80, 50, 's', 't', 'S'); ma(0).mfile(fcp, MOD, Disp, 120, 120, 's', 't', 'I'); ma(1).mfile(fcp, ORG, Disp, 80, 50, 'r', 'i', 'S'); ma(1).mfile(fcp, MOD, Disp, 120, 120, 'r', 'i', 'I'); fprintf(stderr, "\n"); // initiate coupled mode theory, // compute supermode propagation constants and amplitude vectors Dmatrix sigma(n,n); Dvector sbeta(n); Dmatrix samp(n,n); Dmatrix S(n,n); Dmatrix BK(n,n); CMTmats(ma, wg, S, BK); // control output ... // fprintf(stderr, "[S]\n"); // S.write(stderr); // fprintf(stderr, "[B+K]\n"); // BK.write(stderr); // supermodes related to the z-invariant structure: CMTsetup(ma, wg, sigma, sbeta, samp); bs = sbeta(0); ba = sbeta(1); // supermode propagation constants fprintf(stderr, "\nSupermodes in z=0:\n"); fprintf(stderr, "beta_s: %g mum^(-1), vec(%g, %g)\n", bs, samp(0,0), samp(1, 0)); fprintf(stderr, "beta_a: %g mum^(-1), vec(%g, %g)\n", ba, samp(0,1), samp(1, 1)); // the coupling length lc = PI/fabs(bs-ba); fprintf(stderr, " L_c: %g mum\n", lc); fprintf(stderr, "\n"); // compose the supermode profiles WMM_ModeArray smode; CMTsupermodes(ma, wg, sbeta, samp, smode); // plots of the supermode profiles fprintf(stderr, "\nSupermode profiles:\n"); smode(0).mfile(fcp, ORG, Disp, 80, 50, 's', '0', 'S'); smode(1).mfile(fcp, ORG, Disp, 80, 50, 'a', '0', 'S'); smode(0).mfile(fcp, MOD, Disp, 120, 120, 's', '0', 'I'); smode(1).mfile(fcp, MOD, Disp, 120, 120, 'a', '0', 'I'); ma.clear(); // ring/straight waveguide coupling, longitudinally varying structure // memory for output curves Dvector pAB(Numz); Dvector pAb(Numz); Dvector paB(Numz); Dvector pab(Numz); Dvector len(Numz); len = Dvector(Numz); // an average propagation constant double betaq = 0.5*(stmo.beta + rimo.beta); // matrices involved in the Runge-Kutta integration Cmatrix M0(2,2); Cmatrix Mp(2,2); Cmatrix M1(2,2); Cmatrix N1(2,2); Cmatrix N2(2,2); Cmatrix N3(2,2); Cmatrix N4(2,2); Cmatrix One; One.unit(2); // initial mode amplitudes // input in coupler port A Cvector inA(2); inA(0) = CC1; inA(1) = CC0; // input in coupler port a Cvector ina(2); ina(0) = CC0; ina(1) = CC1; // local mode amplitudes Cvector out; // stepsize for the numerical integration double h = WgpZ*2.0/((double)Numz); // coupler transfer matrix, contribution from an integration step Cmatrix Tp(2,2); // cummulative coupler transfer matrix Cmatrix T(2,2); fprintf(stderr, "\n<< CMT: "); // start with T=1 at z=-Z T.unit(2); double z = -WgpZ; Wgpp = WgpR-sqrt(WgpR*WgpR-z*z); ma.clear(); // for properly shifted cores ... m = stmo; m.translate(-Wgpb-Wgpv, Wgpg); ma.add(m); m = rimo; m.translate(0.0, -(Wgpp+Wgpw/2.0)); ma.add(m); wg = cpwgdef(); // ... evaluate the CMT matrices ... CMTmats(ma, wg, S, BK); S.inverse(); // ... for amplitudes with the explicit average phase evolution M0 = mult(add(mult(One, betaq), mult(mult(S, BK), -1.0)), CCI); for(int zi=0; zi<=Numz-1; ++zi) { // half a stepsize advanced z += h/2.0; Wgpp = WgpR-sqrt(WgpR*WgpR-z*z); ma.clear(); m = stmo; m.translate(-Wgpb-Wgpv, Wgpg); ma.add(m); m = rimo; m.translate(0.0, -(Wgpp+Wgpw/2.0)); ma.add(m); wg = cpwgdef(); CMTmats(ma, wg, S, BK); S.inverse(); Mp = mult(add(mult(One, betaq), mult(mult(S, BK), -1.0)), CCI); // another half stepsize advanced z += h/2.0; Wgpp = WgpR-sqrt(WgpR*WgpR-z*z); ma.clear(); m = stmo; m.translate(-Wgpb-Wgpv, Wgpg); ma.add(m); m = rimo; m.translate(0.0, -(Wgpp+Wgpw/2.0)); ma.add(m); wg = cpwgdef(); CMTmats(ma, wg, S, BK); S.inverse(); M1 = mult(add(mult(One, betaq), mult(mult(S, BK), -1.0)), CCI); // apply the Runge-Kutta recipe N1 = mult(M0, h); N2 = mult(mult(Mp, add(One, mult(N1, 0.5))), h); N3 = mult(mult(Mp, add(One, mult(N2, 0.5))), h); N4 = mult(mult(M1, add(One, N3)), h); Tp = One; Tp = add(Tp, mult(N1, 1.0/6.0)); Tp = add(Tp, mult(N2, 1.0/3.0)); Tp = add(Tp, mult(N3, 1.0/3.0)); Tp = add(Tp, mult(N4, 1.0/6.0)); T = mult(Tp, T); // evaluate the local amplitudes: remove the average phase Tp = mult(T, expi(-betaq*(z+WgpZ))); // store the local amplitudes len(zi) = z; out = mult(Tp, inA); pAB(zi) = out(0).sqabs(); pAb(zi) = out(1).sqabs(); out = mult(Tp, ina); paB(zi) = out(0).sqabs(); pab(zi) = out(1).sqabs(); M0 = M1; fprintf(stderr, "."); } fprintf(stderr, " Ok. >>\n"); // the scattering matrix of the entire coupler T = mult(T, expi(-betaq*(2.0*WgpZ))); fprintf(stderr, "\nCoupler scattering matrix T:\n\n"); T.write(stderr); // coupling constant: both offdiagonal elements should be equal fprintf(stderr, "|kappa|^2: %g\n", T(1, 0).sqabs()); fprintf(stderr, " %g\n", T(0, 1).sqabs()); double kappa = 0.5*(T(0, 1).sqabs()+T(1, 0).sqabs()); fprintf(stderr, " average: %g\n\n", kappa); // transfer constant: both diagonal elements should have equal // absolute values double tau = T(0, 0).sqabs(); double rho = T(1, 1).sqabs(); fprintf(stderr, "|tau|^2: %g\n", tau); fprintf(stderr, "|rho|^2: %g\n", rho); tau = 0.5*(tau+rho); fprintf(stderr, "average: %g\n\n", tau); // power conservation: |tau|^2+|kappa|^2 = 1 ? fprintf(stderr, "|tau|^2+|kappa|^2: %g\n\n", tau+kappa); // save amplitude evolution to files fprintf(stderr, "Amplitude evolution, excitation in A:\n"); toxyf("pAB", len, pAB); toxyf("pAb", len, pAb); toxyf("pAB+pAb", len, add(pAB, pAb)); fprintf(stderr, "Amplitude evolution, excitation in a:\n"); toxyf("paB", len, paB); toxyf("pab", len, pab); toxyf("paB+pab", len, add(paB, pab)); fprintf(stderr, "\nOk.\n\n"); ma.clear(); return 0; }