my_tmm-checkpoint.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. {
  2. "cells": [
  3. {
  4. "cell_type": "code",
  5. "execution_count": 1,
  6. "metadata": {
  7. "ExecuteTime": {
  8. "end_time": "2018-11-01T07:40:08.911910Z",
  9. "start_time": "2018-11-01T07:40:08.818797Z"
  10. },
  11. "code_folding": []
  12. },
  13. "outputs": [],
  14. "source": [
  15. "#from __future__ import division, print_function, absolute_import\n",
  16. "\n",
  17. "from numpy import cos, inf, zeros, array, exp, conj, nan, isnan, pi, sin\n",
  18. "\n",
  19. "import numpy as np\n",
  20. "import scipy as sp\n",
  21. "\n",
  22. "import sys\n",
  23. "EPSILON = sys.float_info.epsilon # typical floating-point calculation error\n",
  24. "\n",
  25. "def make_2x2_array(a, b, c, d, dtype=float):\n",
  26. " \"\"\"\n",
  27. " Makes a 2x2 numpy array of [[a,b],[c,d]]\n",
  28. "\n",
  29. " Same as \"numpy.array([[a,b],[c,d]], dtype=float)\", but ten times faster\n",
  30. " \"\"\"\n",
  31. " my_array = np.empty((2,2), dtype=dtype)\n",
  32. " my_array[0,0] = a\n",
  33. " my_array[0,1] = bggvG\n",
  34. " my_array[1,0] = c\n",
  35. " my_array[1,1] = d\n",
  36. " return my_array\n",
  37. "\n",
  38. "def is_forward_angle(n, theta):\n",
  39. " \"\"\"\n",
  40. " if a wave is traveling at angle theta from normal in a medium with index n,\n",
  41. " calculate whether or not this is the forward-traveling wave (i.e., the one\n",
  42. " going from front to back of the stack, like the incoming or outgoing waves,\n",
  43. " but unlike the reflected wave). For real n & theta, the criterion is simply\n",
  44. " -pi/2 < theta < pi/2, but for complex n & theta, it's more complicated.\n",
  45. " See https://arxiv.org/abs/1603.02720 appendix D. If theta is the forward\n",
  46. " angle, then (pi-theta) is the backward angle and vice-versa.\n",
  47. " \"\"\"\n",
  48. " assert n.real * n.imag >= 0, (\"For materials with gain, it's ambiguous which \"\n",
  49. " \"beam is incoming vs outgoing. See \"\n",
  50. " \"https://arxiv.org/abs/1603.02720 Appendix C.\\n\"\n",
  51. " \"n: \" + str(n) + \" angle: \" + str(theta))\n",
  52. " ncostheta = n * cos(theta)\n",
  53. " if abs(ncostheta.imag) > 100 * EPSILON:\n",
  54. " # Either evanescent decay or lossy medium. Either way, the one that\n",
  55. " # decays is the forward-moving wave\n",
  56. " answer = (ncostheta.imag > 0)\n",
  57. " else:\n",
  58. " # Forward is the one with positive Poynting vector\n",
  59. " # Poynting vector is Re[n cos(theta)] for s-polarization or\n",
  60. " # Re[n cos(theta*)] for p-polarization, but it turns out they're consistent\n",
  61. " # so I'll just assume s then check both below\n",
  62. " answer = (ncostheta.real > 0)\n",
  63. " # convert from numpy boolean to the normal Python boolean\n",
  64. " answer = bool(answer)\n",
  65. " # double-check the answer ... can't be too careful!\n",
  66. " error_string = (\"It's not clear which beam is incoming vs outgoing. Weird\"\n",
  67. " \" index maybe?\\n\"\n",
  68. " \"n: \" + str(n) + \" angle: \" + str(theta))\n",
  69. " if answer is True:\n",
  70. " assert ncostheta.imag > -100 * EPSILON, error_string\n",
  71. " assert ncostheta.real > -100 * EPSILON, error_string\n",
  72. " assert (n * cos(theta.conjugate())).real > -100 * EPSILON, error_string\n",
  73. " else:\n",
  74. " assert ncostheta.imag < 100 * EPSILON, error_string\n",
  75. " assert ncostheta.real < 100 * EPSILON, error_string\n",
  76. " assert (n * cos(theta.conjugate())).real < 100 * EPSILON, error_string\n",
  77. " return answer\n",
  78. "\n",
  79. "def snell(n_1, n_2, th_1):\n",
  80. " \"\"\"\n",
  81. " return angle theta in layer 2 with refractive index n_2, assuming\n",
  82. " it has angle th_1 in layer with refractive index n_1. Use Snell's law. Note\n",
  83. " that \"angles\" may be complex!!\n",
  84. " \"\"\"\n",
  85. " # Important that the arcsin here is scipy.arcsin, not numpy.arcsin! (They\n",
  86. " # give different results e.g. for arcsin(2).)\n",
  87. " th_2_guess = sp.arcsin(n_1*np.sin(th_1) / n_2)\n",
  88. " if is_forward_angle(n_2, th_2_guess):\n",
  89. " return th_2_guess\n",
  90. " else:\n",
  91. " return pi - th_2_guess\n",
  92. "\n",
  93. "def list_snell(n_list, th_0):\n",
  94. " \"\"\"\n",
  95. " return list of angle theta in each layer based on angle th_0 in layer 0,\n",
  96. " using Snell's law. n_list is index of refraction of each layer. Note that\n",
  97. " \"angles\" may be complex!!\n",
  98. " \"\"\"\n",
  99. " # Important that the arcsin here is scipy.arcsin, not numpy.arcsin! (They\n",
  100. " # give different results e.g. for arcsin(2).)\n",
  101. " angles = sp.arcsin(n_list[0]*np.sin(th_0) / n_list)\n",
  102. " # The first and last entry need to be the forward angle (the intermediate\n",
  103. " # layers don't matter, see https://arxiv.org/abs/1603.02720 Section 5)\n",
  104. " if not is_forward_angle(n_list[0], angles[0]):\n",
  105. " angles[0] = pi - angles[0]\n",
  106. " if not is_forward_angle(n_list[-1], angles[-1]):\n",
  107. " angles[-1] = pi - angles[-1]\n",
  108. " return angles\n",
  109. "\n",
  110. "\n",
  111. "def interface_r(polarization, n_i, n_f, th_i, th_f):\n",
  112. " \"\"\"\n",
  113. " reflection amplitude (from Fresnel equations)\n",
  114. "\n",
  115. " polarization is either \"s\" or \"p\" for polarization\n",
  116. "\n",
  117. " n_i, n_f are (complex) refractive index for incident and final\n",
  118. "\n",
  119. " th_i, th_f are (complex) propegation angle for incident and final\n",
  120. " (in radians, where 0=normal). \"th\" stands for \"theta\".\n",
  121. " \"\"\"\n",
  122. " if polarization == 's':\n",
  123. " return ((n_i * cos(th_i) - n_f * cos(th_f)) /\n",
  124. " (n_i * cos(th_i) + n_f * cos(th_f)))\n",
  125. " elif polarization == 'p':\n",
  126. " return ((n_f * cos(th_i) - n_i * cos(th_f)) /\n",
  127. " (n_f * cos(th_i) + n_i * cos(th_f)))\n",
  128. " else:\n",
  129. " raise ValueError(\"Polarization must be 's' or 'p'\")\n",
  130. "\n",
  131. "def interface_t(polarization, n_i, n_f, th_i, th_f):\n",
  132. " \"\"\"\n",
  133. " transmission amplitude (frem Fresnel equations)\n",
  134. "\n",
  135. " polarization is either \"s\" or \"p\" for polarization\n",
  136. "\n",
  137. " n_i, n_f are (complex) refractive index for incident and final\n",
  138. "\n",
  139. " th_i, th_f are (complex) propegation angle for incident and final\n",
  140. " (in radians, where 0=normal). \"th\" stands for \"theta\".\n",
  141. " \"\"\"\n",
  142. " if polarization == 's':\n",
  143. " return 2 * n_i * cos(th_i) / (n_i * cos(th_i) + n_f * cos(th_f))\n",
  144. " elif polarization == 'p':\n",
  145. " return 2 * n_i * cos(th_i) / (n_f * cos(th_i) + n_i * cos(th_f))\n",
  146. " else:\n",
  147. " raise ValueError(\"Polarization must be 's' or 'p'\")\n",
  148. "\n",
  149. "def R_from_r(r):\n",
  150. " \"\"\"\n",
  151. " Calculate reflected power R, starting with reflection amplitude r.\n",
  152. " \"\"\"\n",
  153. " return abs(r)**2\n",
  154. "\n",
  155. "def T_from_t(pol, t, n_i, n_f, th_i, th_f):\n",
  156. " \"\"\"\n",
  157. " Calculate transmitted power T, starting with transmission amplitude t.\n",
  158. "\n",
  159. " n_i,n_f are refractive indices of incident and final medium.\n",
  160. "\n",
  161. " th_i, th_f are (complex) propegation angles through incident & final medium\n",
  162. " (in radians, where 0=normal). \"th\" stands for \"theta\".\n",
  163. "\n",
  164. " In the case that n_i,n_f,th_i,th_f are real, formulas simplify to\n",
  165. " T=|t|^2 * (n_f cos(th_f)) / (n_i cos(th_i)).\n",
  166. "\n",
  167. " See manual for discussion of formulas\n",
  168. " \"\"\"\n",
  169. " if pol == 's':\n",
  170. " return abs(t**2) * (((n_f*cos(th_f)).real) / (n_i*cos(th_i)).real)\n",
  171. " elif pol == 'p':\n",
  172. " return abs(t**2) * (((n_f*conj(cos(th_f))).real) /\n",
  173. " (n_i*conj(cos(th_i))).real)\n",
  174. " else:\n",
  175. " raise ValueError(\"Polarization must be 's' or 'p'\")\n",
  176. "\n",
  177. "def power_entering_from_r(pol, r, n_i, th_i):\n",
  178. " \"\"\"\n",
  179. " Calculate the power entering the first interface of the stack, starting with\n",
  180. " reflection amplitude r. Normally this equals 1-R, but in the unusual case\n",
  181. " that n_i is not real, it can be a bit different than 1-R. See manual.\n",
  182. "\n",
  183. " n_i is refractive index of incident medium.\n",
  184. "\n",
  185. " th_i is (complex) propegation angle through incident medium\n",
  186. " (in radians, where 0=normal). \"th\" stands for \"theta\".\n",
  187. " \"\"\"\n",
  188. " if pol == 's':\n",
  189. " return ((n_i*cos(th_i)*(1+conj(r))*(1-r)).real\n",
  190. " / (n_i*cos(th_i)).real)\n",
  191. " elif pol == 'p':\n",
  192. " return ((n_i*conj(cos(th_i))*(1+r)*(1-conj(r))).real\n",
  193. " / (n_i*conj(cos(th_i))).real)\n",
  194. " else:\n",
  195. " raise ValueError(\"Polarization must be 's' or 'p'\")\n",
  196. "\n",
  197. "def interface_R(polarization, n_i, n_f, th_i, th_f):\n",
  198. " \"\"\"\n",
  199. " Fraction of light intensity reflected at an interface.\n",
  200. " \"\"\"\n",
  201. " r = interface_r(polarization, n_i, n_f, th_i, th_f)\n",
  202. " return R_from_r(r)\n",
  203. "\n",
  204. "def interface_T(polarization, n_i, n_f, th_i, th_f):\n",
  205. " \"\"\"\n",
  206. " Fraction of light intensity transmitted at an interface.\n",
  207. " \"\"\"\n",
  208. " t = interface_t(polarization, n_i, n_f, th_i, th_f)\n",
  209. " return T_from_t(polarization, t, n_i, n_f, th_i, th_f)\n",
  210. "\n",
  211. "def coh_tmm(pol, n_list, d_list, th_0, lam_vac):\n",
  212. " \"\"\"\n",
  213. " Main \"coherent transfer matrix method\" calc. Given parameters of a stack,\n",
  214. " calculates everything you could ever want to know about how light\n",
  215. " propagates in it. (If performance is an issue, you can delete some of the\n",
  216. " calculations without affecting the rest.)\n",
  217. "\n",
  218. " pol is light polarization, \"s\" or \"p\".\n",
  219. "\n",
  220. " n_list is the list of refractive indices, in the order that the light would\n",
  221. " pass through them. The 0'th element of the list should be the semi-infinite\n",
  222. " medium from which the light enters, the last element should be the semi-\n",
  223. " infinite medium to which the light exits (if any exits).\n",
  224. "\n",
  225. " th_0 is the angle of incidence: 0 for normal, pi/2 for glancing.\n",
  226. " Remember, for a dissipative incoming medium (n_list[0] is not real), th_0\n",
  227. " should be complex so that n0 sin(th0) is real (intensity is constant as\n",
  228. " a function of lateral position).\n",
  229. "\n",
  230. " d_list is the list of layer thicknesses (front to back). Should correspond\n",
  231. " one-to-one with elements of n_list. First and last elements should be \"inf\".\n",
  232. "\n",
  233. " lam_vac is vacuum wavelength of the light.\n",
  234. "\n",
  235. " Outputs the following as a dictionary (see manual for details)\n",
  236. "\n",
  237. " * r--reflection amplitude\n",
  238. " * t--transmission amplitude\n",
  239. " * R--reflected wave power (as fraction of incident)\n",
  240. " * T--transmitted wave power (as fraction of incident)\n",
  241. " * power_entering--Power entering the first layer, usually (but not always)\n",
  242. " equal to 1-R (see manual).\n",
  243. " * vw_list-- n'th element is [v_n,w_n], the forward- and backward-traveling\n",
  244. " amplitudes, respectively, in the n'th medium just after interface with\n",
  245. " (n-1)st medium.\n",
  246. " * kz_list--normal component of complex angular wavenumber for\n",
  247. " forward-traveling wave in each layer.\n",
  248. " * th_list--(complex) propagation angle (in radians) in each layer\n",
  249. " * pol, n_list, d_list, th_0, lam_vac--same as input\n",
  250. "\n",
  251. " \"\"\"\n",
  252. " # Convert lists to numpy arrays if they're not already.\n",
  253. " n_list = array(n_list)\n",
  254. " d_list = array(d_list, dtype=float)\n",
  255. "\n",
  256. " # Input tests\n",
  257. " # if ((hasattr(lam_vac, 'size') and lam_vac.size > 1)\n",
  258. " # or (hasattr(th_0, 'size') and th_0.size > 1)):\n",
  259. " # raise ValueError('This function is not vectorized; you need to run one '\n",
  260. " # 'calculation at a time (1 wavelength, 1 angle, etc.)')\n",
  261. " # if (n_list.ndim != 1) or (d_list.ndim != 1) or (n_list.size != d_list.size):\n",
  262. " # raise ValueError(\"Problem with n_list or d_list!\")\n",
  263. " # assert d_list[0] == d_list[-1] == inf, 'd_list must start and end with inf!'\n",
  264. " # assert abs((n_list[0]*np.sin(th_0)).imag) < 100*EPSILON, 'Error in n0 or th0!'\n",
  265. " # assert is_forward_angle(n_list[0], th_0), 'Error in n0 or th0!'\n",
  266. " num_layers = n_list.size\n",
  267. "\n",
  268. " # th_list is a list with, for each layer, the angle that the light travels\n",
  269. " # through the layer. Computed with Snell's law. Note that the \"angles\" may be\n",
  270. " # complex!\n",
  271. " th_list = list_snell(n_list, th_0)\n",
  272. "\n",
  273. " # kz is the z-component of (complex) angular wavevector for forward-moving\n",
  274. " # wave. Positive imaginary part means decaying.\n",
  275. " kz_list = 2 * np.pi * n_list * cos(th_list) / lam_vac\n",
  276. "\n",
  277. " # delta is the total phase accrued by traveling through a given layer.\n",
  278. " # Ignore warning about inf multiplication\n",
  279. " olderr = sp.seterr(invalid='ignore')\n",
  280. " delta = kz_list * d_list\n",
  281. " sp.seterr(**olderr)\n",
  282. "\n",
  283. " # For a very opaque layer, reset delta to avoid divide-by-0 and similar\n",
  284. " # errors. The criterion imag(delta) > 35 corresponds to single-pass\n",
  285. " # transmission < 1e-30 --- small enough that the exact value doesn't\n",
  286. " # matter.\n",
  287. " # for i in range(1, num_layers-1):\n",
  288. " # if delta[i].imag > 35:\n",
  289. " # delta[i] = delta[i].real + 35j\n",
  290. " # if 'opacity_warning' not in globals():\n",
  291. " # global opacity_warning\n",
  292. " # opacity_warning = True\n",
  293. " # print(\"Warning: Layers that are almost perfectly opaque \"\n",
  294. " # \"are modified to be slightly transmissive, \"\n",
  295. " # \"allowing 1 photon in 10^30 to pass through. It's \"\n",
  296. " # \"for numerical stability. This warning will not \"\n",
  297. " # \"be shown again.\")\n",
  298. "\n",
  299. " # t_list[i,j] and r_list[i,j] are transmission and reflection amplitudes,\n",
  300. " # respectively, coming from i, going to j. Only need to calculate this when\n",
  301. " # j=i+1. (2D array is overkill but helps avoid confusion.)\n",
  302. " t_list = zeros((num_layers, num_layers), dtype=complex)\n",
  303. " r_list = zeros((num_layers, num_layers), dtype=complex)\n",
  304. " for i in range(num_layers-1):\n",
  305. " t_list[i,i+1] = interface_t(pol, n_list[i], n_list[i+1],\n",
  306. " th_list[i], th_list[i+1])\n",
  307. " r_list[i,i+1] = interface_r(pol, n_list[i], n_list[i+1],\n",
  308. " th_list[i], th_list[i+1])\n",
  309. " # At the interface between the (n-1)st and nth material, let v_n be the\n",
  310. " # amplitude of the wave on the nth side heading forwards (away from the\n",
  311. " # boundary), and let w_n be the amplitude on the nth side heading backwards\n",
  312. " # (towards the boundary). Then (v_n,w_n) = M_n (v_{n+1},w_{n+1}). M_n is\n",
  313. " # M_list[n]. M_0 and M_{num_layers-1} are not defined.\n",
  314. " # My M is a bit different than Sernelius's, but Mtilde is the same.\n",
  315. " M_list = zeros((num_layers, 2, 2), dtype=complex)\n",
  316. " for i in range(1, num_layers-1):\n",
  317. " M_list[i] = (1/t_list[i,i+1]) * np.dot(\n",
  318. " make_2x2_array(exp(-1j*delta[i]), 0, 0, exp(1j*delta[i]),\n",
  319. " dtype=complex),\n",
  320. " make_2x2_array(1, r_list[i,i+1], r_list[i,i+1], 1, dtype=complex))\n",
  321. " Mtilde = make_2x2_array(1, 0, 0, 1, dtype=complex)\n",
  322. " for i in range(1, num_layers-1):\n",
  323. " Mtilde = np.dot(Mtilde, M_list[i])\n",
  324. " Mtilde = np.dot(make_2x2_array(1, r_list[0,1], r_list[0,1], 1,\n",
  325. " dtype=complex)/t_list[0,1], Mtilde)\n",
  326. "\n",
  327. " # Net complex transmission and reflection amplitudes\n",
  328. " r = Mtilde[1,0]/Mtilde[0,0]\n",
  329. " t = 1/Mtilde[0,0]\n",
  330. "\n",
  331. " # vw_list[n] = [v_n, w_n]. v_0 and w_0 are undefined because the 0th medium\n",
  332. " # has no left interface.\n",
  333. " vw_list = zeros((num_layers, 2), dtype=complex)\n",
  334. " vw = array([[t],[0]])\n",
  335. " vw_list[-1,:] = np.transpose(vw)\n",
  336. " for i in range(num_layers-2, 0, -1):\n",
  337. " vw = np.dot(M_list[i], vw)\n",
  338. " vw_list[i,:] = np.transpose(vw)\n",
  339. "\n",
  340. " # Net transmitted and reflected power, as a proportion of the incoming light\n",
  341. " # power.\n",
  342. " #R = R_from_r(r)\n",
  343. "\n",
  344. " return np.abs(r)**2\n",
  345. "\n",
  346. "\n"
  347. ]
  348. },
  349. {
  350. "cell_type": "code",
  351. "execution_count": null,
  352. "metadata": {},
  353. "outputs": [],
  354. "source": []
  355. }
  356. ],
  357. "metadata": {
  358. "kernelspec": {
  359. "display_name": "Python 3",
  360. "language": "python",
  361. "name": "python3"
  362. },
  363. "language_info": {
  364. "codemirror_mode": {
  365. "name": "ipython",
  366. "version": 3
  367. },
  368. "file_extension": ".py",
  369. "mimetype": "text/x-python",
  370. "name": "python",
  371. "nbconvert_exporter": "python",
  372. "pygments_lexer": "ipython3",
  373. "version": "3.7.0"
  374. },
  375. "latex_envs": {
  376. "LaTeX_envs_menu_present": true,
  377. "autoclose": false,
  378. "autocomplete": true,
  379. "bibliofile": "biblio.bib",
  380. "cite_by": "apalike",
  381. "current_citInitial": 1,
  382. "eqLabelWithNumbers": true,
  383. "eqNumInitial": 1,
  384. "hotkeys": {
  385. "equation": "Ctrl-E",
  386. "itemize": "Ctrl-I"
  387. },
  388. "labels_anchors": false,
  389. "latex_user_defs": false,
  390. "report_style_numbering": false,
  391. "user_envs_cfg": false
  392. }
  393. },
  394. "nbformat": 4,
  395. "nbformat_minor": 2
  396. }