PLaSK library
Loading...
Searching...
No Matches
efm.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2# This file is part of PLaSK (https://plask.app) by Photonics Group at TUL
3# Copyright (c) 2022 Lodz University of Technology
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation, version 3.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13
14import unittest
15
16from numpy import *
17
18from plask import *
19from plask import material, geometry, mesh
20from optical import effective
21
22
24
25 def setUp(self):
28 self.manager.load('''
29 <plask loglevel="debug">
30 <materials>
31 <material name="GaAs" base="semiconductor">
32 <nr>3.53</nr>
33 <absp>0.</absp>
34 </material>
35 <material name="AlGaAs" base="semiconductor">
36 <nr>3.08</nr>
37 <absp>0.</absp>
38 </material>
39 <material name="AlAs" base="semiconductor">
40 <nr>2.95</nr>
41 <absp>0.</absp>
42 </material>
43 <material name="AlOx" base="semiconductor">
44 <nr>1.53</nr>
45 <absp>0.</absp>
46 </material>
47 <material name="InGaAs" base="semiconductor">
48 <nr>3.53</nr>
49 <absp>0.</absp>
50 </material>
51 </materials>
52 <geometry>
53 <cylindrical axes="rz" name="vcsel" outer="extend" bottom="GaAs">
54 <stack name="layers">
55 <block dr="10" dz="0.06949" material="GaAs"/>
56 <stack name="top-dbr" repeat="24">
57 <block dr="10" dz="0.07955" material="AlGaAs"/>
58 <block dr="10" dz="0.06949" material="GaAs"/>
59 </stack>
60 <block name="x1" dr="10" dz="0.06371" material="AlGaAs"/>
61 <shelf name="oxide-layer">
62 <block dr="4" dz="0.01593" material="AlAs"/><block dr="6" dz="0.01593" material="AlOx"/>
63 </shelf>
64 <block name="x" dr="10" dz="0.00000" material="AlGaAs"/>
65 <block dr="10" dz="0.13649" material="GaAs"/>
66 <shelf name="QW">
67 <block name="active" role="gain" dr="4" dz="0.00500" material="InGaAs"/><block dr="6" dz="0.00500" material="InGaAs"/>
68 </shelf>
69 <zero/>
70 <block dr="10" dz="0.13649" material="GaAs"/>
71 <stack name="bottom-dbr" repeat="29">
72 <block dr="10" dz="0.07955" material="AlGaAs"/>
73 <block dr="10" dz="0.06949" material="GaAs"/>
74 </stack>
75 <block dr="10" dz="0.07955" material="AlGaAs"/>
76 </stack>
77 </cylindrical>
78 </geometry>
79 <solvers>
80 <optical name="efm" lib="effective" solver="EffectiveFrequencyCyl">
81 <geometry ref="vcsel"/>
82 </optical>
83 </solvers>
84 </plask>''')
86 self.solver.lam0 = 980.
87 self.profile = StepProfile(self.solver.geometry)
88 self.solver.inGain = self.profile.outGain
89
90 def testField(self):
91 axis0 = plask.mesh.Regular(0.001, 39.999, 100000)
92 #axis0 = linspace(0.01, 3.99, 200)
93 #axis1 = [ self.manager.geo.layers.bbox.lower.z-1e-6, 0.0025, self.manager.geo.layers.bbox.upper.z+-1e-6 ]
95 dr = axis0[1]-axis0[0]
96 msh = mesh.Rectangular2D(axis0, axis1)
97 self.solver.find_mode(980.1, 0)
98 self.solver.modes[0].power = 2000.
99 field = self.solver.outLightMagnitude(0,msh).array[:,-1]
100 integral = 2e-12 * pi * sum(field * msh.axis0) * dr
101 self.assertAlmostEqual(integral, 2., 4)
102
105 msh = self.solver.mesh.elements.mesh
106 geo = self.solver.geometry
107 refr = [geo.get_material(point).Nr(980., 300.) for point in msh]
108 self.assertEqual(
109 ["%.8g" % nr.real for nr in self.solver.outRefractiveIndex(msh)],
110 ["%.8g" % r.real for r in refr]
111 )
112
114 m = self.solver.find_mode(980.1)
115 self.assertEqual(m, 0)
116 self.assertEqual(len(self.solver.modes), 1)
117 self.assertAlmostEqual(self.solver.modes[m].lam, 979.702-0.021j, 3)
118
119 def testThreshold(self):
120 try:
121 from scipy.optimize import brentq
122 except ImportError:
123 pass
124 else:
125 def fun(g):
126 self.profile[self.manager.geo['active']] = g
127 m = self.solver.find_mode(980.1)
128 return imag(self.solver.modes[m].lam)
129 threshold = brentq(fun, 0., 2000., xtol=1e-6)
130 self.assertAlmostEqual(threshold, 1181.7, 1)
131
133 self.profile[self.manager.geo['active']] = 1181.6834
134 m = self.solver.find_mode(980.1)
135 self.solver.modes[m].power = 2.0
136 box = self.solver.geometry.item.bbox
137 field = self.solver.outLightMagnitude(m, mesh.Rectangular2D(mesh.Ordered([0.]), mesh.Ordered([box.lower.z, box.upper.z])))
138 total_power = self.solver.modes[m].power * (1. + 3.53 * field[0] / field[1])
139 self.assertAlmostEqual(-self.solver.get_total_absorption(m), total_power, 2)
140
142 self.profile[self.manager.geo['active']] = 1181.6834
143 m = self.solver.find_mode(980.1)
144 self.solver.modes[m].power = 2.0
145 box = self.solver.geometry.get_object_bboxes(self.manager.geo['active'])[0]
146 msh = mesh.Rectangular2D(mesh.Regular(0., 10., 1000), [0.5 * (box.lower.z + box.upper.z)])
147 heat = self.solver.outHeat(msh).array[:,0]
148 # 1e-15: µm³->m³ W->mW
149 integral = 2e-15 * pi * sum(heat * msh.axis0) * (box.upper.z - box.lower.z) * (msh.axis0[1] - msh.axis0[0])
150 self.assertAlmostEqual(integral, self.solver.get_total_absorption(m), 2)
151
152 def testNeffs(self):
153 rr = array([0.0, 2.0, 6.0])
154 neffs = self.solver.get_nng(rr)
155 self.assertAlmostEqual(neffs[0], 3.3174, 3)
156 self.assertAlmostEqual(neffs[1], 3.3174, 3)
157 self.assertAlmostEqual(neffs[2], 3.2816, 3)
158
159 def testDeltaNeffs(self):
160 rr = array([0.0, 2.0, 6.0])
161 neffs = self.solver.get_delta_neff(rr)
162 self.assertAlmostEqual(neffs[0], 0.0244635-0.0095423j, 6)
163 self.assertAlmostEqual(neffs[1], 0.0244635-0.0095423j, 6)
164 self.assertAlmostEqual(neffs[2], 0.0006568-0.3719099j, 6)