├── README.md ├── SMatrix.cpp ├── SMatrix.hpp ├── dos_sim.cpp ├── fieldFunctions.cpp ├── fieldFunctions.hpp ├── heat_transfer_sim.cpp ├── materials.cpp ├── materials.hpp ├── mlgeo.cpp ├── mlgeo.hpp └── numericalIntegration.cpp /README.md: -------------------------------------------------------------------------------- 1 | # MultilayerEM 2 | 3 | [Owen D. Miller](http://math.mit.edu/~odmiller) 4 | 5 | A set of c/c++ (more like c + classes) codes for a variety of electromagnetics computations for multilayer stacks of metals/dielectrics. The code uses a stable scattering matrix computation, in contrast to the more typically used (and numerically unstable) transmission matrix formalism. For a stack composed of arbitrarily many layers, one can compute: 6 | 7 | * Scattering matrices 8 | * Reflection / Transmission (field and power coefficients) 9 | * Density of States (electric DOS) at any point in the stack 10 | * Radiative heat transfer, between either a single point or a whole layer and 11 | a user-specified set of flux planes. 12 | 13 | The heat transfer is computed through the fluctuation-dissipation theorem, which provides a simple expression for the stochastic sources in media at a giventemperature. 14 | 15 | 16 | A reference for both the scattering matrix approach and the heat transfer formulation is (note I have implemented one or two things differently to speed up the algorithm): 17 | 18 | Francoeur et. al., "Solution of near-field thermal radiation in one-dimensional layered media using dyadic Green's functions and the scattering matrix method," Journal of Quantitative Spectroscopy and Radiative Transfer **110**, 2002 (2009) [[dx.doi.org/10.1016/j.jqsrt.2009.05.010](http://dx.doi.org/10.1016/j.jqsrt.2009.05.010)] 19 | 20 | The codes are written in standard non-dimensional units. Simple conversion factors are commented around the corresponding functions. 21 | 22 | ## Codes 23 | * *heat\_transfer\_sim.cpp*: a working example of how to compute emission rates in a large complex multilayer system. 24 | 25 | * *dos\_sim.cpp*: similarly, a working example of how to compute the density of states. 26 | 27 | * *SMatrix.cpp*: a class to hold scattering matrices of a structure at a given frequency (w) and parallel wavevector (kp) 28 | 29 | * *mlgeo.cpp*: a multilayer geometry class, encapsulating the permittivity and thickness of each layer (including the embedding half-spaces) 30 | 31 | * *fieldFunctions.cpp*: contains the functions to compute flux rates, the density of states, and the reflection/transmission coefficients. Also contains 32 | auxiliary functions to compute blackbody flux rates and the density of states 33 | in vacuum (in non-dimensional units, again). 34 | 35 | * *materials.cpp*: (NOT REQUIRED) Note that this is an auxiliary code I have provided. It is a code I use with functions for common material models (in the infrared). Users can specify permittivities however they see fit. 36 | 37 | * *numericalIntegration.cpp*: (NOT REQUIRED) This file provides functions to perform numerical integrations over wavevector and possible frequency. It uses the [Cubature](http://ab-initio.mit.edu/wiki/index.php/Cubature) package 38 | written by [Steven G. Johnson](http://math.mit.edu/~stevenj) 39 | 40 | * Many of the above have corresponding header files 41 | 42 | Note that as currently written, heat\_transfer\_sim.cpp and dos\_sim.cpp do each use a lambda, so I compile them with the -std=c++11 flag. However, this is not at all required (it is only to comply with my own material function definitions) and no other aspect of the code depends on c++11. 43 | 44 | On Windows, you may have to compile with the flag -D\_USE\_MATH\_DEFINES (for M_PI) 45 | 46 | The codes here have been verified a number of ways, including reproducing results from the following papers (in addition to the reference above) 47 | 48 | * Karl Joulain, Remi Carminati, Jean-Philippe Mulet, and Jean-Jacques Greffet, "Definition and measurement of the local density of electromagnetic states close to an interface," Physical Review B **68**, 245405 (2003) [[dx.doi.org/10.1103/PhysRevB.68.245405] (http://dx.doi.org/10.1103/PhysRevB.68.245405)] 49 | * Philippe Ben-Abdallah, Karl Joulain, Jeremie Dervillon, and Gilberto Domingues, "Near-field heat transfer mediated by surface wave hybridization between two films," Journal of Applied Physics **106**, 044306 (2009) [[dx.doi.org/10.1063/1.3204481](http://dx.doi.org/10.1063/1.3204481)] 50 | * Yu Guo, Christian L. Cortes, Sean Molesky, and Zubin Jacob, "Broadband super-Planckian thermal emission from hyperbolic metamaterial," Applied Physics Letters **101**, 131106 (2012) [[dx.doi.org/10.1063/1.4754616](http://dx.doi.org/10.1063/1.4754616)] 51 | -------------------------------------------------------------------------------- /SMatrix.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "mlgeo.hpp" 5 | #include "SMatrix.hpp" 6 | 7 | typedef std::complex cdouble; 8 | typedef cdouble (*epsfn)(double); 9 | const cdouble II(0.0,1.0); 10 | 11 | // Fresnel coefficients (Sipe's notation) 12 | // NOTE: kp should be normalized by k0! pass kp/k0 13 | void reflTrans(cdouble eps0, cdouble eps1, double kp, int pol, 14 | cdouble &r, cdouble &t) { 15 | cdouble w0, w1; 16 | w0 = sqrt( eps0 - kp*kp ); 17 | w1 = sqrt( eps1 - kp*kp ); 18 | if(pol==TM) { 19 | r = (w0*eps1 - w1*eps0) / (w0*eps1 + w1*eps0); 20 | t = 2.*sqrt(eps0)*sqrt(eps1)*w0 / (w0*eps1 + w1*eps0); 21 | } else { 22 | r = (w0-w1) / (w0+w1); 23 | t = 2.*w0 / (w0+w1); 24 | } 25 | } 26 | 27 | // fwd decl's of some utility fxn's 28 | void eyeS(cdouble *S, int i); 29 | int ind11(int a, int b); 30 | int ind12(int a, int b); 31 | int ind21(int a, int b); 32 | int ind22(int a, int b); 33 | 34 | SMatrix::SMatrix(const mlgeo &g, double k0In, double kpIn) 35 | : kz(new cdouble[g.N+1]), k(new cdouble[g.N+1]), kp(kpIn), k0(k0In), N(g.N), 36 | sTE(new cdouble[4*(2*g.N+1)]), sTM(new cdouble[4*(2*g.N+1)]) // member init. list 37 | { 38 | initMatrix(g); 39 | } 40 | 41 | // units of k0, kp, only matter relative to d (only m*d products ever used) 42 | SMatrix::SMatrix(const cdouble *eps, const double *d, int NIn, double k0In, double kpIn) 43 | : kz(new cdouble[N+1]), k(new cdouble[N+1]), kp(kpIn), k0(k0In), N(NIn), 44 | sTE(new cdouble[4*(2*N+1)]), sTM(new cdouble[4*(2*N+1)]) // member init. list 45 | { 46 | mlgeo g = mlgeo(eps, d, N); 47 | initMatrix(g); 48 | } 49 | 50 | // simple destructor 51 | SMatrix::~SMatrix() { 52 | delete[] kz; 53 | delete[] k; 54 | delete[] sTE; 55 | delete[] sTM; 56 | } 57 | 58 | void SMatrix::initMatrix(const mlgeo &g) { 59 | double dl; 60 | cdouble exp1, exp2, r, t, *s; 61 | int m, n; // going to set S(0,n) and S(m,N), m=0:N,n=0:N 62 | 63 | for(int pol=0; pol<2; ++pol) { 64 | s = (pol==TE) ? sTE : sTM; 65 | 66 | // S(0,n) for n=0:N 67 | m = 0; 68 | n = 0; 69 | eyeS(s,ind11(m,n)); 70 | k[0] = sqrt( g.eps(0) ) * k0; 71 | kz[0] = sqrt( k[0]*k[0] - kp * kp ); 72 | for(n=1; n<=N; ++n) { 73 | dl = (n>1) ? g.d(n-1) : 0; 74 | k[n] = sqrt( g.eps(n) ) * k0; 75 | kz[n] = sqrt(k[n] * k[n] - kp * kp); 76 | exp1 = exp(II * kz[n-1] * dl); 77 | exp2 = exp(2. * II * kz[n-1] * dl); 78 | reflTrans( g.eps(n-1), g.eps(n), kp/k0, pol, r, t ); 79 | 80 | //std::cout << "[SMatrix]: " << m << " " << n << " " << r << " " << t 81 | //<< exp1 << " " << exp2 << " " << s[ind11(m,n-1)] << " " 82 | //<< s[ind12(m,n-1)] << std::endl; 83 | s[ind11(m,n)] = ( s[ind11(m,n-1)] * t * exp1 ) 84 | / ( 1. - s[ind12(m,n-1)] * r * exp2 ); 85 | s[ind12(m,n)] = ( s[ind12(m,n-1)] * exp2 - r ) 86 | / ( 1. - s[ind12(m,n-1)] * r * exp2 ); 87 | s[ind21(m,n)] = s[ind11(m,n)] * s[ind22(m,n-1)] * r * exp1 / t + s[ind21(m,n-1)]; 88 | s[ind22(m,n)] = s[ind22(m,n-1)] * ( r * s[ind12(m,n)] + 1. ) * exp1 / t; 89 | } 90 | 91 | // S(m,N) for m=N:0 (backwards recurrence relations) 92 | m = N; 93 | n = N; 94 | eyeS(s,ind11(m,n)); 95 | for(m=N; m>1; --m) { // m-1 is the new layer, from m 96 | dl = (m>1) ? g.d(m-1) : 0; 97 | exp1 = exp(II*kz[m-1]*dl); 98 | exp2 = exp(2.*II*kz[m-1]*dl); 99 | reflTrans( g.eps(m), g.eps(m-1), kp/k0, pol, r, t ); 100 | 101 | s[ind11(m-1,n)] = s[ind11(m,n)] * exp1 * (1. - r*r) /( t*(1.-r*s[ind21(m,n)]) ); 102 | s[ind12(m-1,n)] = s[ind12(m,n)] + r * s[ind11(m,n)] * s[ind22(m,n)] 103 | / ( 1. - r*s[ind21(m,n)] ); 104 | s[ind21(m-1,n)] = exp2 * (s[ind21(m,n)] - r) / (1. - r*s[ind21(m,n)]); 105 | s[ind22(m-1,n)] = t * exp1 * s[ind22(m,n)] / ( 1. - r*s[ind21(m,n)] ); 106 | } 107 | } 108 | } 109 | 110 | cdouble SMatrix::S11(int a, int b, int pol) const { 111 | if(pol==TM) 112 | return sTM[ind11(a,b)]; 113 | else 114 | return sTE[ind11(a,b)]; 115 | } 116 | 117 | cdouble SMatrix::S12(int a, int b, int pol) const { 118 | if(pol==TM) 119 | return sTM[ind12(a,b)]; 120 | else 121 | return sTE[ind12(a,b)]; 122 | } 123 | 124 | cdouble SMatrix::S21(int a, int b, int pol) const { 125 | if(pol==TM) 126 | return sTM[ind21(a,b)]; 127 | else 128 | return sTE[ind21(a,b)]; 129 | } 130 | 131 | cdouble SMatrix::S22(int a, int b, int pol) const { 132 | if(pol==TM) 133 | return sTM[ind22(a,b)]; 134 | else 135 | return sTE[ind22(a,b)]; 136 | } 137 | 138 | void SMatrix::print() { 139 | int ind0, ind1; 140 | for(int pol=0; pol<=1; ++pol) { 141 | std::cout << "-------" << std::endl; 142 | if(pol==TE) 143 | std::cout << "pol: " << "TE" << std::endl; 144 | else 145 | std::cout << "pol: " << "TM" << std::endl; 146 | std::cout << "-------" << std::endl; 147 | for(int ind=0; ind<=1; ++ind) { 148 | for(int i=0; i<=N; ++i) { 149 | ind0 = (ind==0) ? 0 : i; 150 | ind1 = (ind==0) ? i : N; 151 | std::cout << "layer: " << i << std::endl; 152 | std::cout << " s11(" << ind0 << "," << ind1 << "): " 153 | << S11(ind0,ind1,pol) << std::endl; 154 | std::cout << " s12(" << ind0 << "," << ind1 << "): " 155 | << S12(ind0,ind1,pol) << std::endl; 156 | std::cout << " s21(" << ind0 << "," << ind1 << "): " 157 | << S21(ind0,ind1,pol) << std::endl; 158 | std::cout << " s22(" << ind0 << "," << ind1 << "): " 159 | << S22(ind0,ind1,pol) << std::endl; 160 | } 161 | } 162 | } 163 | } 164 | 165 | void eyeS(cdouble *S, int i) { // insert identity matrix 166 | S[i+0] = 1; S[i+1] = 0; 167 | S[i+2] = 0; S[i+3] = 1; 168 | } 169 | int ind11(int a, int b) { 170 | return 4*(a+b); 171 | } 172 | int ind12(int a, int b) { 173 | return 4*(a+b)+1; 174 | } 175 | int ind21(int a, int b) { 176 | return 4*(a+b)+2; 177 | } 178 | int ind22(int a, int b) { 179 | return 4*(a+b)+3; 180 | } 181 | -------------------------------------------------------------------------------- /SMatrix.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef SMATRIX_H 3 | #define SMATRIX_H 4 | 5 | #include 6 | #include "mlgeo.hpp" 7 | 8 | const int TE = 1; 9 | const int TM = 0; 10 | 11 | // SMatrix contains the scattering matrices (and indexing functions) 12 | // for a multilayer stack with permittivity eps and thicknesses t 13 | // N = number of interfaces (i.e. N+1 = number of layers) 14 | class SMatrix { 15 | public: 16 | SMatrix(const std::complex *eps, const double *d, 17 | int NIn, double k0In, double kpIn); 18 | SMatrix(const mlgeo &g, double k0In, double kpIn); 19 | ~SMatrix(); 20 | std::complex S11(int a, int b, int pol) const; 21 | std::complex S12(int a, int b, int pol) const; 22 | std::complex S21(int a, int b, int pol) const; 23 | std::complex S22(int a, int b, int pol) const; 24 | void print(); 25 | std::complex *kz; // kz = sqrt( eps*k0^2 - kp^2 ) 26 | std::complex *k; // sqrt(eps)*k0 27 | double kp, k0; // par wavevector, k0 28 | int N; 29 | 30 | private: 31 | void initMatrix(const mlgeo &g); 32 | std::complex *sTE; 33 | std::complex *sTM; 34 | }; 35 | 36 | // Fresnel coefficiencts at an interface 37 | void reflTrans(std::complex eps0, std::complex eps1, 38 | double kp, int pol, std::complex &r, std::complex &t); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /dos_sim.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "fieldFunctions.hpp" 5 | #include "SMatrix.hpp" 6 | #include "mlgeo.hpp" 7 | #include "materials.hpp" 8 | 9 | typedef std::complex cdouble; 10 | typedef cdouble (*epsfn)(double); 11 | 12 | double *dosKpInt(const mlgeo &g, const int *l, const double *zl, int Nl, double k0); 13 | 14 | int main() { 15 | // frequency and wavevector 16 | double w1 = 1.3e14; 17 | double w2 = 2.0e14; 18 | int Nw = 400; 19 | 20 | double k1 = 0.01; 21 | double k2 = 45; 22 | int Nk = 150; 23 | 24 | const double zl = -0.002; 25 | 26 | const bool omegaK = false; 27 | const bool print_eps = false; 28 | 29 | const double lscale = 1e-6; // um 30 | 31 | // parameters for a single multilayer stack 32 | const epsfn eps0 = epsVac; 33 | const epsfn eps1 = epsSiC; 34 | const epsfn eps2 = [=] (double w) { return epsConst(w,3.9); }; 35 | const epsfn epsN = epsVac; 36 | const int numLayersPerMat = 1; 37 | const double ff = 0.2; 38 | const double totalD = 0.1; 39 | const double d1 = ff * totalD; // thickness of eps1 40 | const double d2 = (1.-ff) * totalD; 41 | const int numLayers = numLayersPerMat + 2; 42 | 43 | epsfn *eps = new epsfn[numLayers]; 44 | cdouble *epsV = new cdouble[numLayers]; 45 | double *d = new double[numLayers-2]; 46 | 47 | // setup stack, eps & d values 48 | // single stack 49 | eps[0] = eps0; 50 | eps[numLayers-1] = epsN; 51 | for (int i = 1; i <= numLayersPerMat; ++i) { 52 | eps[i] = (i%2==1) ? eps1 : eps2; 53 | d[i-1] = (i%2==1) ? d1 : d2; 54 | } 55 | 56 | // alternative, simple specification for small stack 57 | /*const int numLayers = 4; 58 | const epsfn eps2 = [=] (double w) { return epsConst(w,3.9); }; 59 | epsfn eps[numLayers] = {epsVac, epsCr, epsAu, epsVac}; 60 | cdouble epsV[numLayers]; 61 | double d[numLayers-2] = {0.005, 0.005}; 62 | */ 63 | 64 | if(print_eps) { 65 | for(int i=0; i 3 | #include 4 | #include "SMatrix.hpp" 5 | #include "mlgeo.hpp" 6 | #include "fieldFunctions.hpp" 7 | 8 | typedef std::complex cdouble; 9 | const cdouble II(0.,1.); 10 | using std::conj; 11 | using std::real; 12 | using std::imag; 13 | 14 | const double hbar = 6.62606957e-34/2./M_PI; 15 | const double kB = 1.3806488e-23; 16 | const double c0 = 299792458.; 17 | const double Z0 = 119.9169832 * M_PI; 18 | 19 | // Flux from s to zl for given geometry (g) and kp, k0 20 | // Integrated over emitter evenly distributed in s 21 | // if don't want integral, gfFlux's below provide simple interface 22 | // nHat = 1. or -1. 23 | // in Francoeur et. al., zl --> zc and zs --> zprime 24 | // units of kp/k0 matter only relative to zl/zs 25 | // zl, zs are RELATIVE positions, starting from left boundary of resp. layers 26 | // not yet permitted: zl<0 if l==0, zl>0 if l==N (bndry condtions change) 27 | // 28 | // Specify all emitting layers in s array. First version computes/discards the 29 | // scattering matrix for you, second accepts precomputed S. nHat is assumed 30 | // to be the same for each emittering layer 31 | double flux(const mlgeo &g, double k0, double kp, int l, double zl, 32 | const int *s, int Ns, double nHat) { 33 | SMatrix S = SMatrix(g, k0, kp); 34 | return flux(g, S, l, zl, s, Ns, nHat); 35 | } 36 | double flux(const mlgeo &g, const SMatrix &S, int l, double zl, 37 | const int *s, int Ns, double nHat) { 38 | pwaves pTE, pTM; 39 | double f = 0; 40 | for (int sind = 0, si = s[sind]; sind < Ns; ++sind, ++si) { 41 | if (imag(g.eps(si)) == 0) 42 | continue; 43 | pWavesL(S, l, si, TE, &pTE); 44 | pWavesL(S, l, si, TM, &pTM); 45 | f -= nHat * S.k0 * S.k0 / (M_PI * M_PI) * imag(g.eps(si)) 46 | * imag( gfFluxTE(S, pTE, l, si, zl, g.d(si), nHat) 47 | + gfFluxTM(S, pTM, l, si, zl, g.d(si), nHat) ); 48 | } 49 | return f; 50 | } 51 | 52 | // flux of a blackbody (/ dist^2 / freq), not / wavevector! 53 | // multiply by (1/a)^2 to get 1/m^2 54 | double flux_bb(double k0) { 55 | return k0 * k0 / (4. * M_PI * M_PI); 56 | } 57 | 58 | // Integrated flux for blackbody (in vacuum) 59 | // Given by Stefan-Boltzmann (or integral of mean_energy * dos_bb) 60 | // multiply by hbar*c^2/lscale^4 to get W/m^2 61 | double flux_bb_int(double lscale, double T) { 62 | return pow(lscale * kB * T / (hbar * c0), 4) * M_PI * M_PI / 60.; 63 | } 64 | 65 | // density of states, DOS (technically electric DOS, i.e. DOS of an electric dipole) 66 | // cf. e.g. Joulain et. al. PRB 68, 245405 (2003) 67 | double dos(const mlgeo &g, double k0, double kp, int l, double zl) { 68 | SMatrix S = SMatrix(g, k0, kp); 69 | return dos(S, l, zl); 70 | } 71 | 72 | // same as above but with S precomputed (e.g. if multiple zl's desired) 73 | double dos(const SMatrix &S, int l, double zl) { 74 | pwaves pTE, pTM; 75 | pWavesL(S, l, l, TE, &pTE); // source layer = emitter layer 76 | pWavesL(S, l, l, TM, &pTM); 77 | 78 | cdouble kzl = S.kz[l]; // kzs = kzl 79 | cdouble kl = S.k[l]; 80 | cdouble kp = S.kp; 81 | cdouble xl = II * kzl * zl; // xs = xl 82 | 83 | cdouble Ae = pTE.Al; 84 | cdouble Be = pTE.Bl * exp(-2.*xl); 85 | cdouble Ce = pTE.Cl * exp(2.*xl); 86 | cdouble De = pTE.Dl; 87 | cdouble Am = pTM.Al; 88 | cdouble Bm = pTM.Bl * exp(-2.*xl); 89 | cdouble Cm = pTM.Cl * exp(2.*xl); 90 | cdouble Dm = pTM.Dl; 91 | 92 | // extra factors of 1 are extra source term when l==s 93 | cdouble Epp = II * kzl * kp / (kl * kl) 94 | * (Am - Bm - Cm + Dm + 1.); 95 | cdouble Ett = II * kp / kzl 96 | * (Ae + Be + Ce + De + 1.); 97 | cdouble Ezz = II * kp * kp * kp / (kzl * kl * kl) 98 | * (Am + Bm + Cm + Dm + 1.); 99 | return S.k0 * S.k0 * imag(Epp + Ett + Ezz) / (2. * M_PI * M_PI); 100 | } 101 | 102 | double dos_vacuum(double k0) { 103 | return k0 * k0 / (2. * M_PI * M_PI); 104 | } 105 | 106 | // multiply by hbar*c/lscale to get J 107 | double mean_energy(double k0, double lscale, double T) { 108 | return k0 / (exp(hbar * c0 * k0 / (lscale * kB * T)) - 1); // Boltzmann "Theta" 109 | } 110 | 111 | void reflTrans(const mlgeo &g, double k0, double theta, int pol, 112 | cdouble *r, double *R, cdouble *t, double *T) { 113 | SMatrix S = new SMatrix(g, k0, k0 * sin(theta)); 114 | *r = S->S21(0, g.N, pol); 115 | *t = S->S11(0, g.N, pol); 116 | *R = (*r) * conj(*r); 117 | *T = real(sqrt(g.eps(N))) / real(sqrt(g.eps(0))) * (*t) * conj(*t); 118 | } 119 | 120 | // compute just the partial waves in layer l 121 | // Splus and Sminus taken out, inserted in flux eqn. (as in Francoeur) 122 | void pWavesL(const SMatrix &S, int l, int s, int pol, pwaves *p) { 123 | int N = S.N; 124 | if (s==0) { // emitting half-space 125 | p->Cl = 0; // no waves emitted in the backward direction 126 | p->Dl = 0; 127 | if (l==0) { 128 | p->Al = 0; 129 | p->Bl = S.S21(l,N,pol); 130 | } else { 131 | p->Al = S.S11(0,N,pol) / S.S11(l,N,pol); 132 | p->Bl = S.S21(l,N,pol) * p->Al; 133 | } 134 | } else if (s==N) { 135 | p->Al = 0; // no waves in forward dir 136 | p->Bl = 0; 137 | if (l==N) { 138 | p->Dl = 0; 139 | p->Cl = S.S12(0,l,pol); 140 | } else { 141 | p->Dl = S.S22(0,N,pol) / S.S22(0,l,pol); 142 | p->Cl = S.S12(0,l,pol) * p->Dl; 143 | } 144 | } else { 145 | // partial waves in s,0,N layers (A0=BN=C0=DN=0) 146 | cdouble Bs = S.S21(s,N,pol) / (1. - S.S12(0,s,pol)*S.S21(s,N,pol)) ; 147 | cdouble As = S.S12(0,s,pol) * Bs; 148 | cdouble B0 = S.S22(0,s,pol) * Bs; 149 | cdouble AN = S.S11(s,N,pol) * (As + 1.); 150 | cdouble Cs = S.S12(0,s,pol) / (1. - S.S12(0,s,pol)*S.S21(s,N,pol)); 151 | cdouble Ds = S.S21(s,N,pol) * Cs; 152 | cdouble CN = S.S11(s,N,pol) * Cs; 153 | cdouble D0 = S.S22(0,s,pol) * (Ds + 1.); 154 | if (l==s) { // flux, emitter in same layer 155 | p->Al = As; 156 | p->Bl = Bs; 157 | p->Cl = Cs; 158 | p->Dl = Ds; 159 | } else if (lBl = B0 / S.S22(0,l,pol); 161 | p->Al = S.S12(0,l,pol) * p->Bl; 162 | p->Dl = D0 / S.S22(0,l,pol); 163 | p->Cl = S.S12(0,l,pol) * p->Dl; 164 | } else { // flux to the right of emitter 165 | p->Al = AN / S.S11(l,N,pol); 166 | p->Bl = S.S21(l,N,pol) * p->Al; 167 | p->Cl = CN / S.S11(l,N,pol); 168 | p->Dl = S.S21(l,N,pol) * p->Cl; 169 | } 170 | } 171 | } 172 | 173 | // integral of product of exponential terms arising in Green's function 174 | // computations (i.e. integral of term in zsProd() from 0 to ds) 175 | cdouble zsInt(int s1, int s2, cdouble kzs, double ds) { 176 | if(s1==1 && s2==1) 177 | return (exp(2. * II * real(kzs) * ds) - 1.) / (2. * II * real(kzs)); 178 | else if(s1==1 && s2==-1) 179 | return (1. - exp(-2. * imag(kzs) * ds)) / (2. * imag(kzs)); 180 | else if(s1==-1 && s2==1) 181 | return (exp(2. * imag(kzs) * ds) - 1.) / (2. * imag(kzs)); 182 | else if(s1==-1 && s2==-1) 183 | return (1. - exp(-2. * II * real(kzs) * ds)) / (2. * II * real(kzs)); 184 | return -1; // shouldn't get here 185 | } 186 | 187 | // product of exponential terms arising in Green's function computations 188 | cdouble zsProd(int s1, int s2, cdouble kzs, double zs) { 189 | return exp(II * zs * (double(s1) * kzs + double(s2) * conj(kzs))); 190 | } 191 | 192 | // TE flux from Green's functions 193 | // extra factor of kp (elsewhere in the integrand) to make dimensionless 194 | // if integrate==true (default), then integral over emitter layer 195 | // done analytically. In this case xs = thickness of layer s 196 | // if integrate==false, then xs is the location of the emitter 197 | cdouble gfFluxTE(const SMatrix &S, const pwaves &pTE, 198 | int l, int s, double zl, double xs, bool integrate) { 199 | cdouble kzl = S.kz[l]; 200 | cdouble kzs = S.kz[s]; 201 | cdouble kp = S.kp; 202 | cdouble xl = II * kzl * zl; 203 | cdouble A = pTE.Al * exp(xl); 204 | cdouble B = pTE.Bl * exp(-xl); 205 | cdouble C = pTE.Cl * exp(xl); 206 | cdouble D = pTE.Dl * exp(-xl); 207 | 208 | cdouble fTE = 0; 209 | int gES[4] = {-1,-1,+1,+1}; // signs in exponent terms 210 | int gHS[4] = {+1,+1,-1,-1}; 211 | 212 | cdouble (*spaceFx)(int, int, cdouble, double); 213 | if (integrate) 214 | spaceFx = zsInt; 215 | else 216 | spaceFx = zsProd; 217 | 218 | // Note the overall neg. sign below 219 | cdouble prefac = II * kp / (4. * kzs) * conj(kzl / kzs); 220 | cdouble gEtt[4] = {A, B, C, D}; 221 | cdouble gHpt[4] = {A, -B, C, -D}; 222 | for (int i=0; i<4; ++i) 223 | for (int j=0; j<4; ++j) 224 | fTE -= prefac * gEtt[i] * conj(gHpt[j]) * spaceFx(gES[i], gHS[j], kzs, xs); 225 | 226 | return fTE; 227 | } 228 | 229 | // TM flux from Green's functions 230 | cdouble gfFluxTM(const SMatrix &S, const pwaves &pTM, 231 | int l, int s, double zl, double xs, bool integrate) { 232 | cdouble kzl = S.kz[l]; 233 | cdouble kzs = S.kz[s]; 234 | cdouble kl = S.k[l]; 235 | cdouble ks = S.k[s]; 236 | cdouble kp = S.kp; 237 | cdouble xl = II * kzl * zl; 238 | cdouble A = pTM.Al * exp(xl); 239 | cdouble B = pTM.Bl * exp(-xl); 240 | cdouble C = pTM.Cl * exp(xl); 241 | cdouble D = pTM.Dl * exp(-xl); 242 | 243 | cdouble fTM = 0; 244 | int gES[4] = {-1,-1,+1,+1}; // signs in exponent terms 245 | int gHS[4] = {+1,+1,-1,-1}; 246 | 247 | cdouble (*spaceFx)(int, int, cdouble, double); 248 | if (integrate) 249 | spaceFx = zsInt; 250 | else 251 | spaceFx = zsProd; 252 | 253 | // TM1 254 | cdouble prefac = II * kzl * kp / (4. * ks * kl) * conj(kl / ks); 255 | cdouble gEpp[4] = {A, -B, -C, D}; 256 | cdouble gHtp[4] = {-A, -B, C, D}; 257 | for(int i=0; i<4; ++i) 258 | for(int j=0; j<4; ++j) 259 | fTM += prefac * gEpp[i] * conj(gHtp[j]) * spaceFx(gES[i], gHS[j], kzs, xs); 260 | 261 | // TM2 262 | prefac *= kp * conj(kp) / (kzs * conj(kzs)); 263 | cdouble gEpz[4] = {-A, B, -C, D}; 264 | cdouble gHtz[4] = {A, B, C, D}; 265 | for(int i=0; i<4; ++i) 266 | for(int j=0; j<4; ++j) 267 | fTM += prefac * gEpz[i] * conj(gHtz[j]) * spaceFx(gES[i], gHS[j], kzs, xs); 268 | 269 | return fTM; 270 | } 271 | -------------------------------------------------------------------------------- /fieldFunctions.hpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "SMatrix.hpp" 4 | 5 | /***** primary functions ********/ 6 | 7 | // flux from emitters in s to zl in layer l 8 | // at free-space wavevector k0 and parallel wavevector k0 9 | // (number / distance^2 / wavevector / freq) 10 | double flux(const mlgeo &g, double k0, double kp, int l, double zl, 11 | const int *s, int Ns, double nHat); 12 | double flux(const mlgeo &g, const SMatrix &S, int l, double zl, 13 | const int *s, int Ns, double nHat); 14 | 15 | double flux_bb(double k0); 16 | double flux_bb_int(double lscale, double T); // integrated over w at temp. T 17 | 18 | // density of states (/ wavevector / distance^3 / second / freq) 19 | double dos(const mlgeo &g, double k0, double kp, int l, double zl); 20 | double dos(const SMatrix &S, int l, double zl); 21 | 22 | double dos_vacuum(double k0); 23 | 24 | // mean energy of Planck oscillator at freq w, temp. T 25 | double mean_energy(double k0, double lscale, double T); 26 | 27 | // r, t = reflection / transmission coefficients 28 | // R, T = reflected / transmitted power (divided by inc. power) 29 | // at angle theta, for given polarization 30 | void reflTrans(const mlgeo &g, double k0, double theta, int pol, 31 | std::complex *r, double *R, std::complex *t, double *T); 32 | 33 | // normalization for flux: 1/a^2 (i.e. a^2 * flux is in SI units) 34 | // normalization for dos: 1/a^2/c 35 | // where zl = zl_SI / a; 36 | // mean_energy is already in J 37 | // 38 | /***** potentially useful structs / functions, innards ********/ 39 | 40 | struct pwaves { 41 | std::complex Al, Bl, Cl, Dl; 42 | }; 43 | 44 | // compute partial waves l relative to a source in layer s 45 | void pWavesL(const SMatrix &S, int l, int s, int pol, pwaves *p); 46 | 47 | // TE/TM flux rates at l from emitter in s (either all of s or a single point) 48 | // Use these functions if don't want integral over emitter layer, 49 | // or want polarization specificity 50 | std::complex gfFluxTE(const SMatrix &S, const pwaves &pTE, 51 | int l, int s, double zl, double xs, bool integrate=true); 52 | 53 | std::complex gfFluxTM(const SMatrix &S, const pwaves &pTM, 54 | int l, int s, double zl, double xs, bool integrate=true); 55 | -------------------------------------------------------------------------------- /heat_transfer_sim.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "fieldFunctions.hpp" 6 | #include "SMatrix.hpp" 7 | #include "mlgeo.hpp" 8 | #include "materials.hpp" 9 | 10 | typedef std::complex cdouble; 11 | typedef cdouble (*epsfn)(double); 12 | 13 | double fluxKpInt(const mlgeo &g, const int *l, const double *zl, const double *nHat, 14 | const int *s, int Nl, int Ns, double k0); 15 | double fluxKpWInt(const epsfn *eps, const double *d, const int *l, const double *zl, 16 | const double *nHat, const int *s, int numLayers, int Nl, int Ns, 17 | double lscale, double temp); 18 | 19 | const double c0 = 299798452; 20 | 21 | int main() { 22 | // frequency and wavevector 23 | double w1 = 1.0e14; 24 | double w2 = 1.0e15; 25 | int Nw = 200; 26 | 27 | double k1 = 0.01; 28 | double k2 = 45; 29 | int Nk = 150; 30 | 31 | const bool omegaK = false; 32 | const bool omega = true; 33 | const bool print_eps = true; 34 | //const bool boltzmann = true; 35 | const double temp = 1000.; 36 | 37 | const double lscale = 1e-6; // nm 38 | 39 | // pararmeters for two multilayer stacks 40 | // of alternating materials/thicknesses, with some thickness 41 | // separating them. Vacuum on either side 42 | // eps0 || ... || eps2,d2 || eps1,d1 || epsInt,di || eps1,d1 || eps2,d2 || ... || epsN 43 | const int numLayersPerMat = 2; 44 | const epsfn eps0 = epsVac; 45 | const epsfn eps1 = epsCr; 46 | const epsfn eps2 = [=] (double w) { return epsConst(w,3.9); }; // SiO2=3.9 47 | const epsfn epsN = epsVac; 48 | const epsfn epsInt = epsVac; // spacer layer 49 | const double ff = 0.2; 50 | const double totalD = 0.1; 51 | const double d1 = ff * totalD; // thickness of eps1 (um) 52 | const double d2 = (1.-ff) * totalD; 53 | const double di = 0.5; // distance between stacks 54 | const int numLayers = numLayersPerMat*2 + 3; 55 | const int intLayer = numLayersPerMat + 1; 56 | const int lastLayer = 2 * intLayer; 57 | 58 | epsfn *eps = new epsfn[numLayers]; 59 | cdouble *epsV = new cdouble[numLayers]; 60 | double *d = new double[numLayers-2]; 61 | const int numEmitters = numLayersPerMat; 62 | 63 | // arrays with eps functions & layer thicknesses 64 | eps[0] = eps0; 65 | eps[intLayer] = epsInt; 66 | eps[lastLayer] = epsN; 67 | d[intLayer-1] = di; 68 | for(int i=0; i // for printEps 3 | #include 4 | 5 | typedef std::complex cdouble; 6 | typedef cdouble (*epsfn)(double); 7 | const cdouble II(0.0,1.0); 8 | 9 | double c0 = 299792458; 10 | double q = 1.602176565e-19; 11 | double eps0 = 8.85418782e-12; 12 | double mu0 = M_PI*4e-7; 13 | double h = 6.62606957e-34; 14 | double hbar = h / (2.*M_PI); 15 | double wToEV = hbar / q; 16 | 17 | // add logspace option 18 | void printEps(epsfn fx, double w1, double w2, int Nw) { 19 | double dw = (Nw>1) ? (w2 - w1) / (Nw - 1) : 2*(w2-w1); 20 | for(double w=w1; w<=w2; w += dw) 21 | std::cout << w << " " << fx(w) << std::endl; 22 | } 23 | 24 | cdouble epsAu(double w) { 25 | double epsInf = 1., wp = 1.3594122e16, g = 1.0495462e14; 26 | return epsInf - wp*wp / (w*w + II*g*w); 27 | } 28 | 29 | // for now, just a simple Drude model 30 | // could also have a multi-oscillator model 31 | cdouble epsAg(double w) { 32 | double epsInf = 1., wp = 1.3689e16, g = 2.7347e13; 33 | return epsInf - wp*wp / (w*w + II*g*w); 34 | } 35 | 36 | // ref: Joulain et. al. PRB 68, 245405 (2003) 37 | cdouble epsAl(double w) { 38 | double epsInf = 1., wp = 1.747e16, g = 7.596e13; 39 | return epsInf - wp*wp / (w*w + II*g*w); 40 | } 41 | 42 | cdouble epsCr(double w) { 43 | double epsInf = 1., wp = 7.1942e14, g = 7.6921e13; 44 | return epsInf - wp*wp / (w*w + II*g*w); 45 | } 46 | 47 | cdouble epsW(double w) { 48 | double epsInf = 1., wp = 8.8118e15, g = 7.5963e13; 49 | return epsInf - wp*wp / (w*w + II*g*w); 50 | } 51 | 52 | // SiC, from Ben-Abdallah et. al. J. Appl. Phys. 106, 044306 (2009) 53 | cdouble epsSiC(double w) { 54 | double epsInf, wLO, wTO, g; 55 | epsInf = 6.7; 56 | wLO = 18.253e13; 57 | wTO = 14.937e13; 58 | g = 8.966e11; 59 | return epsInf * (1. + (wLO*wLO - wTO*wTO) / (wTO*wTO - w*w - II*g*w)); // same as in epsCBN 60 | } 61 | 62 | cdouble epsSiDoped(double w, double N) { 63 | double epsf = 1.0035; 64 | double eps0Si = 11.87; 65 | double w0 = 6.6e15; 66 | double epsSi = epsf + (eps0Si - epsf) * w0*w0 / (w0*w0 - w*w); 67 | if(N==0) 68 | return epsSi; 69 | N = N * 1e6; // convert to /m^3 70 | double q = 1.602176565e-19; 71 | double rho = 1.1e-4; 72 | double me = 9.10938215e-31; 73 | double m = 0.34*me; 74 | double eps0 = 8.85418782e-12; 75 | double wp = sqrt(N * q * q/(eps0 * m)); 76 | double g = N * q * q * rho / m; 77 | return epsSi - wp*wp / (w*w + II*g*w); 78 | } 79 | 80 | // c-BN, from Francoeur et. al. JQSRT 110, 2002 (2009) 81 | cdouble epsCBN(double w) { 82 | double epsInf, wLO, wTO, g; 83 | epsInf = 4.46; 84 | wLO = 2.451e14; 85 | wTO = 1.985e14; 86 | g = 9.934e11; 87 | return epsInf * (w*w - wLO*wLO + II*g*w) / (w*w - wTO*wTO + II*g*w); 88 | } 89 | 90 | cdouble epsVac(double w) { 91 | return cdouble(1.,0.); 92 | } 93 | 94 | // for next three methods: 95 | // with c++11, can use lambda expression to change signature 96 | // e.g. auto f = [=] (double d) { return epsCont(d,1); }; 97 | cdouble epsConst(double w, cdouble eps) { 98 | return eps; 99 | } 100 | 101 | cdouble epsDrude(double w, double wp, double g) { 102 | return cdouble( 1 - wp*wp /( w*w + g*g ), g*wp*wp /( w*w*w + g*g*w ) ); 103 | } 104 | 105 | cdouble epsDrude(double w, double wp, double g, double epsInf) { 106 | return cdouble( epsInf - wp*wp /( w*w + g*g ), g*wp*wp /( w*w*w + g*g*w ) ); 107 | } 108 | -------------------------------------------------------------------------------- /materials.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MATERIALS_H 3 | #define MATERIALS_H 4 | 5 | #include 6 | 7 | void printEps(std::complex (*fx)(double), double w1, double w2, int Nw); 8 | std::complex epsAu(double w); 9 | std::complex epsAg(double w); 10 | std::complex epsAl(double w); 11 | std::complex epsCr(double w); 12 | std::complex epsW(double w); 13 | std::complex epsSiC(double w); 14 | std::complex epsSiDoped(double w, double N=0.); 15 | std::complex epsCBN(double w); // c-BN 16 | std::complex epsVac(double w); 17 | std::complex epsConst(double w, std::complex eps); 18 | std::complex epsDrude(double w, double wp, double gamma); // epsInf=1 19 | std::complex epsDrude(double w, double wp, double gamma, double epsInf); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /mlgeo.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "mlgeo.hpp" 6 | 7 | typedef std::complex cdouble; 8 | 9 | mlgeo::mlgeo(const cdouble *epsIn, const double *dIn, int NIn) 10 | : N(NIn), eps_(new cdouble[NIn+1]), d_(new double[NIn-1]), z_(new double[NIn]) { 11 | z_[0] = 0.; // set first interface at zero 12 | for(int i=0; i<=N; ++i) { 13 | eps_[i] = epsIn[i]; 14 | if(i0 && i0 && i0); 38 | return z_[i-1]; 39 | } 40 | 41 | void mlgeo::print() const { 42 | std::cout << "-------------------------" << std::endl; 43 | std::cout << "Number of interfaces: " << N << std::endl; 44 | std::cout << "Number of layers: " << N + 1 << std::endl; 45 | for(int i=0; i<=N; ++i) { 46 | std::cout << "layer: " << i << std::endl; 47 | std::cout << " eps: " << eps(i) << std::endl; 48 | if(i>0 && i0) 51 | std::cout << " z: " << z(i) << std::endl; 52 | } 53 | std::cout << "-------------------------" << std::endl; 54 | } 55 | 56 | 57 | -------------------------------------------------------------------------------- /mlgeo.hpp: -------------------------------------------------------------------------------- 1 | 2 | // MULTI-LAYER GEOMETRY CLASS 3 | 4 | #ifndef MLGEO_H 5 | #define MLGEO_H 6 | 7 | #include 8 | 9 | class mlgeo { 10 | public: 11 | mlgeo(const std::complex *epsIn,const double *dIn, int NIn); 12 | ~mlgeo(); 13 | std::complex eps(int i) const; 14 | double d(int i) const; 15 | double z(int i) const; 16 | void print() const; 17 | int N; 18 | private: 19 | std::complex *eps_; 20 | double *d_; 21 | double *z_; 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /numericalIntegration.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "cubature.h" 5 | #include "fieldFunctions.hpp" 6 | #include "SMatrix.hpp" 7 | 8 | typedef std::complex cdouble; 9 | typedef cdouble (*epsfn)(double); 10 | const double c0 = 299798452; 11 | 12 | struct fluxData { 13 | const mlgeo &g; 14 | const int *s; 15 | const int *l; 16 | const double *zl; 17 | const double *nHat; 18 | int Ns, Nl; 19 | double k0; 20 | }; 21 | 22 | struct fluxData2 { 23 | const epsfn *eps; 24 | const double *d; 25 | const int *s; 26 | const int *l; 27 | const double *zl; 28 | const double *nHat; 29 | double lscale, temp; // temp = temperature 30 | int numLayers, Ns, Nl; 31 | }; 32 | 33 | struct dosData { 34 | const mlgeo &g; 35 | const int *l; 36 | const double *zl; 37 | double k0; 38 | }; 39 | 40 | int fluxFx(unsigned ndim, const double *x, void *fdata, unsigned fdim, double *fval) { 41 | double t0 = x[0]; 42 | double kp = t0 / (1. - t0); 43 | fluxData *f = (fluxData *) fdata; 44 | SMatrix S = SMatrix(f->g, f->k0, kp); 45 | //std::cout << f->k0 << " " << kp << std::endl; 46 | fval[0] = 0; 47 | for (int il = 0; il < f->Nl; ++il) 48 | fval[0] += flux(f->g, S, f->l[il], f->zl[il], f->s, f->Ns, f->nHat[il]); 49 | if(fval[0] != fval[0]) // isnan 50 | fval[0] = 0; 51 | else 52 | fval[0] /= pow(1. - t0, 2); 53 | return 0; 54 | } 55 | 56 | // x = [k0; kp] 57 | int fluxFx2(unsigned ndim, const double *x, void *fdata, unsigned fdim, double *fval) { 58 | double t0 = x[0]; 59 | double k0 = t0 / (1. - t0); 60 | double t1 = x[1]; 61 | double kp = t1 / (1. - t1); 62 | 63 | fluxData2 *f = (fluxData2 *) fdata; 64 | double w = k0 * c0 / f->lscale; 65 | cdouble *epsV = new cdouble[f->numLayers]; 66 | for (int i = 0; i < f->numLayers; ++i) 67 | epsV[i] = f->eps[i](w); 68 | mlgeo g = mlgeo(epsV, f->d, f->numLayers - 1); 69 | SMatrix S = SMatrix(g, k0, kp); 70 | fval[0] = 0; 71 | for (int il = 0; il < f->Nl; ++il) 72 | fval[0] += flux(g, S, f->l[il], f->zl[il], f->s, f->Ns, f->nHat[il]); 73 | if( fval[0] != fval[0] ) // isnan 74 | fval[0] = 0.; 75 | else 76 | fval[0] *= mean_energy(k0, f->lscale, f->temp) / pow((1. - t0) * (1. - t1), 2); 77 | //std::cout << t0 << " " << t1 << " " << k0 << " " << kp/k0 << " " << *fval 78 | //<< " " << mean_energy(k0, f->lscale, f->temp) << std::endl; 79 | return 0; 80 | } 81 | 82 | int dosFx(unsigned ndim, const double *x, void *fdata, unsigned fdim, double *fval) { 83 | double t0 = x[0]; 84 | double kp = t0 / (1. - t0); 85 | dosData *f = (dosData *) fdata; 86 | SMatrix S = SMatrix(f->g, f->k0, kp); 87 | for (unsigned il = 0; il < fdim; ++il) { 88 | fval[il] = dos(S, f->l[il], f->zl[il]) / pow(1. - t0, 2); 89 | if(fval[il] != fval[il]) 90 | fval[il] = 0.; 91 | } 92 | //std::cout << "k0: " << f->k0 << " kp: " << kp << " " << *fval << std::endl; 93 | return 0; 94 | } 95 | 96 | // integrate over kp and w (integration over emitter layer assumed) 97 | // This allows Nl flux planes (each with a corresponding zl and nHat) given by l 98 | double fluxKpWInt(const epsfn *eps, const double *d, const int *l, const double *zl, 99 | const double *nHat, const int *s, int numLayers, int Nl, int Ns, 100 | double lscale, double temp) { 101 | unsigned fdim = 1, dim = 2; 102 | double xmin[2] = {0,0}; 103 | double xmax[2] = {1,1.}; 104 | double val, err; 105 | double absError = 0, relError = 1e-6; 106 | size_t maxEval = 1e5; 107 | fluxData2 fdata = {eps, d, s, l, zl, nHat, lscale, temp, numLayers, Ns, Nl}; 108 | int res = hcubature(fdim, fluxFx2, &fdata, dim, xmin, xmax, maxEval, 109 | absError, relError, ERROR_INDIVIDUAL, &val, &err); 110 | if (res != 0) 111 | std::cout << "Return value: " << res << std::endl; 112 | return val; // Note that the integral is over normalized kp/k0 113 | } 114 | 115 | // integrate over kp (integration over emitter layer assumed) 116 | // This allows Nl flux planes (each with a corresponding zl and nHat) given by l 117 | double fluxKpInt(const mlgeo &g, const int *l, const double *zl, const double *nHat, 118 | const int *s, int Nl, int Ns, double k0) { 119 | unsigned fdim = 1, dim = 1; 120 | double xmin = 0, xmax = 1, val, err; // transform later 121 | double absError = 0, relError = 1e-4; 122 | size_t maxEval = 1e5; 123 | fluxData fdata = {g, s, l, zl, nHat, Ns, Nl, k0}; 124 | int res = hcubature(fdim, fluxFx, &fdata, dim, &xmin, &xmax, maxEval, 125 | absError, relError, ERROR_INDIVIDUAL, &val, &err); 126 | if (res != 0) 127 | std::cout << "k0: " << k0 << " return value: " << res << std::endl; 128 | return val; 129 | } 130 | 131 | double *dosKpInt(const mlgeo &g, const int *l, const double *zl, int Nl, double k0) { 132 | int res; 133 | unsigned fdim = Nl, dim = 1; 134 | double xmin = 0, xmax = 1; 135 | double *val = new double[fdim]; 136 | double *err = new double[fdim]; // transform later 137 | double absError = 0, relError = 1e-4; 138 | size_t maxEval = 1e5; 139 | dosData fdata = {g, l, zl, k0}; 140 | res = hcubature(fdim, dosFx, &fdata, dim, &xmin, &xmax, maxEval, 141 | absError, relError, ERROR_INDIVIDUAL, val, err); 142 | if (res != 0) 143 | std::cout << "k0: " << k0 << " return value: " << res << std::endl; 144 | delete[] err; 145 | return val; 146 | } 147 | 148 | --------------------------------------------------------------------------------