ndispers 0.3.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ndispers/__init__.py +7 -0
- ndispers/_baseclass.py +302 -0
- ndispers/groups/__init__.py +6 -0
- ndispers/groups/_uniax_neg_32.py +162 -0
- ndispers/groups/_uniax_neg_3m.py +172 -0
- ndispers/helper.py +35 -0
- ndispers/media/__init__.py +6 -0
- ndispers/media/crystals/_CLBO.py +173 -0
- ndispers/media/crystals/_KBBF_Li2016.py +176 -0
- ndispers/media/crystals/_KDP.py +148 -0
- ndispers/media/crystals/_KTP.py +430 -0
- ndispers/media/crystals/_LB4.py +180 -0
- ndispers/media/crystals/_LBO_Castech.py +455 -0
- ndispers/media/crystals/_LBO_EKSMA.py +455 -0
- ndispers/media/crystals/_LBO_Ghosh1995.py +460 -0
- ndispers/media/crystals/_LBO_KK1994.py +462 -0
- ndispers/media/crystals/_LBO_KK2018.py +447 -0
- ndispers/media/crystals/_LBO_Newlight.py +444 -0
- ndispers/media/crystals/_SLN_MgO_doped.py +173 -0
- ndispers/media/crystals/_SLT_MgO_doped.py +197 -0
- ndispers/media/crystals/__init__.py +33 -0
- ndispers/media/crystals/_alphaBBO.py +142 -0
- ndispers/media/crystals/_betaBBO_Eimerl1987.py +181 -0
- ndispers/media/crystals/_betaBBO_Ghosh1995.py +196 -0
- ndispers/media/crystals/_betaBBO_KK2010.py +209 -0
- ndispers/media/crystals/_betaBBO_Tamosauskas2018.py +201 -0
- ndispers/media/crystals/_calcite.py +158 -0
- ndispers/media/glasses/__init__.py +7 -0
- ndispers/media/glasses/_caf2.py +106 -0
- ndispers/media/glasses/_fusedsilica.py +104 -0
- ndispers/tests/test_LBO.py +63 -0
- ndispers/tests/test_betaBBO.py +57 -0
- ndispers-0.3.0.dist-info/METADATA +124 -0
- ndispers-0.3.0.dist-info/RECORD +36 -0
- ndispers-0.3.0.dist-info/WHEEL +5 -0
- ndispers-0.3.0.dist-info/licenses/LICENSE +21 -0
ndispers/__init__.py
ADDED
ndispers/_baseclass.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""
|
|
2
|
+
base class for medium object - _baseclass.py
|
|
3
|
+
"""
|
|
4
|
+
import sympy
|
|
5
|
+
from sympy.utilities import lambdify
|
|
6
|
+
wl = sympy.Symbol('lambda')
|
|
7
|
+
phi = sympy.Symbol('phi')
|
|
8
|
+
theta = sympy.Symbol('theta')
|
|
9
|
+
T = sympy.Symbol('T')
|
|
10
|
+
|
|
11
|
+
from math import pi
|
|
12
|
+
c_ms = 2.99792458e8 #(m/s) speed of light in vacuum
|
|
13
|
+
c_umfs = c_ms * 1e-9 #(µm/fs)
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
|
|
18
|
+
from .helper import (returnShape, arg_signchange)
|
|
19
|
+
|
|
20
|
+
class Medium:
|
|
21
|
+
"""
|
|
22
|
+
Medium object base class. Vacuum with n=1.0.
|
|
23
|
+
"""
|
|
24
|
+
__slots__ = ["__plane", "__theta_rad", "__phi_rad", "_cached_func_dict"]
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
self.__plane = 'arb'
|
|
28
|
+
self.__theta_rad = 'arb'
|
|
29
|
+
self.__phi_rad = 'arb'
|
|
30
|
+
self._cached_func_dict = defaultdict(dict)
|
|
31
|
+
self._cached_func_dict['n_expr'] = {'o': 0, 'e': 0}
|
|
32
|
+
self._cached_func_dict['dn_wl_expr'] = {'o': 0, 'e': 0}
|
|
33
|
+
self._cached_func_dict['d2n_wl_expr'] = {'o': 0, 'e': 0}
|
|
34
|
+
self._cached_func_dict['d3n_wl_expr'] = {'o': 0, 'e': 0}
|
|
35
|
+
self._cached_func_dict['GD_expr'] = {'o': 0, 'e': 0}
|
|
36
|
+
self._cached_func_dict['GV_expr'] = {'o': 0, 'e': 0}
|
|
37
|
+
self._cached_func_dict['ng_expr'] = {'o': 0, 'e': 0}
|
|
38
|
+
self._cached_func_dict['GD_expr'] = {'o': 0, 'e': 0}
|
|
39
|
+
self._cached_func_dict['GVD_expr'] = {'o': 0, 'e': 0}
|
|
40
|
+
self._cached_func_dict['TOD_expr'] = {'o': 0, 'e': 0}
|
|
41
|
+
self._cached_func_dict['woa_theta_expr'] = {'o': 0, 'e': 0}
|
|
42
|
+
self._cached_func_dict['woa_phi_expr'] = {'o': 0, 'e': 0}
|
|
43
|
+
self._cached_func_dict['dndT_expr'] = {'o': 0, 'e': 0}
|
|
44
|
+
|
|
45
|
+
def clear(self):
|
|
46
|
+
"""clear cached functions"""
|
|
47
|
+
self.__init__()
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def help(self):
|
|
51
|
+
print(self.__doc__)
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def plane(self):
|
|
55
|
+
return self.__plane
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def theta_rad(self):
|
|
59
|
+
return self.__theta_rad
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def phi_rad(self):
|
|
63
|
+
return self.__phi_rad
|
|
64
|
+
|
|
65
|
+
def __repr__(self):
|
|
66
|
+
return f"{self.__class__}\n plane: {self.plane}\n theta_rad: {self.theta_rad}\n phi_rad: {self.phi_rad}"
|
|
67
|
+
|
|
68
|
+
def n_expr(self, pol):
|
|
69
|
+
return 1.0
|
|
70
|
+
|
|
71
|
+
""" Derivative expressions """
|
|
72
|
+
def dn_wl_expr(self, pol):
|
|
73
|
+
""" Sympy expression for first derivative of n with respect to wl """
|
|
74
|
+
return sympy.diff(self.n_expr(pol), wl)
|
|
75
|
+
|
|
76
|
+
def d2n_wl_expr(self, pol):
|
|
77
|
+
""" Sympy expression for second derivative of n with respect to wl """
|
|
78
|
+
return sympy.diff(self.dn_wl_expr(pol), wl)
|
|
79
|
+
|
|
80
|
+
def d3n_wl_expr(self, pol):
|
|
81
|
+
""" Sympy expression for third derivative of n with respect to wl """
|
|
82
|
+
return sympy.diff(self.d2n_wl_expr(pol), wl)
|
|
83
|
+
|
|
84
|
+
def GD_expr(self, pol):
|
|
85
|
+
""" Sympy expression for group delay """
|
|
86
|
+
return (self.n_expr(pol) - wl * self.dn_wl_expr(pol)) * 1e3 / c_umfs
|
|
87
|
+
|
|
88
|
+
def GV_expr(self, pol):
|
|
89
|
+
""" Sympy expression for group velocity """
|
|
90
|
+
return (c_umfs/self.n_expr(pol)) / (1 - (wl/self.n_expr(pol)) * self.dn_wl_expr(pol))
|
|
91
|
+
|
|
92
|
+
def ng_expr(self, pol):
|
|
93
|
+
""" Sympy expression for group index """
|
|
94
|
+
n_expr = self.n_expr(pol)
|
|
95
|
+
return n_expr * (1 - wl/n_expr * self.dn_wl_expr(pol))
|
|
96
|
+
|
|
97
|
+
def GVD_expr(self, pol):
|
|
98
|
+
""" Sympy expression for Group Delay Dispersion """
|
|
99
|
+
return wl**3/(2*pi*c_umfs**2) * self.d2n_wl_expr(pol) * 1e3
|
|
100
|
+
|
|
101
|
+
def TOD_expr(self, pol):
|
|
102
|
+
""" Sympy expression for Third Order Dispersion """
|
|
103
|
+
return - wl**4/(4*pi**2*c_umfs**3) * (3*self.d2n_wl_expr(pol) + wl * self.d3n_wl_expr(pol)) * 1e3
|
|
104
|
+
|
|
105
|
+
def woa_theta_expr(self, pol):
|
|
106
|
+
""" Sympy expression for polar walkoff angle """
|
|
107
|
+
return sympy.atan(- sympy.diff(self.n_expr(pol), theta) / self.n_expr(pol))
|
|
108
|
+
|
|
109
|
+
def woa_phi_expr(self, pol):
|
|
110
|
+
""" Sympy expression for azimuthal walkoff angle """
|
|
111
|
+
return sympy.atan(- sympy.diff(self.n_expr(pol), phi) / self.n_expr(pol))
|
|
112
|
+
|
|
113
|
+
def dndT_expr(self, pol):
|
|
114
|
+
""" Sympy expression for dn/dT """
|
|
115
|
+
return sympy.diff(self.n_expr(pol), T)
|
|
116
|
+
|
|
117
|
+
def dndT2_expr(self, pol):
|
|
118
|
+
""" Sympy expression for d^2n/dT^2 """
|
|
119
|
+
return sympy.diff(self.dndT_expr(pol), T)
|
|
120
|
+
|
|
121
|
+
""" lambdified functions """
|
|
122
|
+
def _func(self, expr, *args, pol='o'):
|
|
123
|
+
array_args = map(np.asarray, args)
|
|
124
|
+
func = self._cached_func_dict[expr.__name__][pol]
|
|
125
|
+
if func:
|
|
126
|
+
return np.resize(func(*args), returnShape(*array_args))
|
|
127
|
+
else:
|
|
128
|
+
func = lambdify(self.symbols, expr(pol), 'numpy')
|
|
129
|
+
self._cached_func_dict[expr.__name__][pol] = func
|
|
130
|
+
return np.resize(func(*args), returnShape(*array_args))
|
|
131
|
+
|
|
132
|
+
def n(self, *args, pol='o'):
|
|
133
|
+
return self._func(self.n_expr, *args, pol=pol)
|
|
134
|
+
|
|
135
|
+
def dn_wl(self, *args, pol='o'):
|
|
136
|
+
return self._func(self.dn_wl_expr, *args, pol=pol)
|
|
137
|
+
|
|
138
|
+
def d2n_wl(self, *args, pol='o'):
|
|
139
|
+
return self._func(self.d2n_wl_expr, *args, pol=pol)
|
|
140
|
+
|
|
141
|
+
def d3n_wl(self, *args, pol='o'):
|
|
142
|
+
return self._func(self.d3n_wl_expr, *args, pol=pol)
|
|
143
|
+
|
|
144
|
+
def GD(self, *args, pol='o'):
|
|
145
|
+
return self._func(self.GD_expr, *args, pol=pol)
|
|
146
|
+
|
|
147
|
+
def GV(self, *args, pol='o'):
|
|
148
|
+
return self._func(self.GV_expr, *args, pol=pol)
|
|
149
|
+
|
|
150
|
+
def ng(self, *args, pol='o'):
|
|
151
|
+
return self._func(self.ng_expr, *args, pol=pol)
|
|
152
|
+
|
|
153
|
+
def GVD(self, *args, pol='o'):
|
|
154
|
+
return self._func(self.GVD_expr, *args, pol=pol)
|
|
155
|
+
|
|
156
|
+
def TOD(self, *args, pol='o'):
|
|
157
|
+
return self._func(self.TOD_expr, *args, pol=pol)
|
|
158
|
+
|
|
159
|
+
def woa_theta(self, *args, pol='e'):
|
|
160
|
+
""" Polar walk-off angle (rad) """
|
|
161
|
+
return self._func(self.woa_theta_expr, *args, pol=pol)
|
|
162
|
+
|
|
163
|
+
def woa_phi(self, *args, pol='e'):
|
|
164
|
+
""" Azimuthal walk-off angle (rad) """
|
|
165
|
+
return self._func(self.woa_phi_expr, *args, pol=pol)
|
|
166
|
+
|
|
167
|
+
def dndT(self, *args, pol='o'):
|
|
168
|
+
"""
|
|
169
|
+
dn/dT function for given arguments (angle, temperature and polarization)
|
|
170
|
+
|
|
171
|
+
NOTE
|
|
172
|
+
----
|
|
173
|
+
Here, self.dndT_expr is given by sympy.diff(self.n_expr(pol)), so there is no need to give dndT_expr explicitly.
|
|
174
|
+
"""
|
|
175
|
+
return self._func(self.dndT_expr, *args, pol=pol)
|
|
176
|
+
|
|
177
|
+
def dndT2(self, *args, pol='o'):
|
|
178
|
+
""" d^2n/dT^2 """
|
|
179
|
+
return self._func(self.dndT2_expr, *args, pol=pol)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
Methods for three-wave interactions
|
|
184
|
+
"""
|
|
185
|
+
def dk_sfg(self, wl1, wl2, angle_rad, T_degC, pol1, pol2, pol3):
|
|
186
|
+
"""
|
|
187
|
+
Wavevector mismatch for sum-frequency generation (SFG).
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
wl1 : float or array_like
|
|
192
|
+
1st pump wavelength in µm.
|
|
193
|
+
wl2 : float or array_like
|
|
194
|
+
2nd pump wavelength in µm.
|
|
195
|
+
angle_rad : float or array_like
|
|
196
|
+
theta or phi angles in radians.
|
|
197
|
+
T_degC : float or array_like
|
|
198
|
+
Crystal temperature in degC.
|
|
199
|
+
pol1: {'o', 'e'}
|
|
200
|
+
Polarization of 1st pump wave.
|
|
201
|
+
pol2: {'o', 'e'}
|
|
202
|
+
Polarization of 2nd pump wave.
|
|
203
|
+
pol3: {'o', 'e'}
|
|
204
|
+
Polarization of sum-frequency wave.
|
|
205
|
+
|
|
206
|
+
Return
|
|
207
|
+
------
|
|
208
|
+
float or array_like
|
|
209
|
+
Wavevector mismatch for SFG (in rad/µm)
|
|
210
|
+
"""
|
|
211
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
212
|
+
n1 = self.n(wl1, angle_rad, T_degC, pol=pol1)
|
|
213
|
+
n2 = self.n(wl2, angle_rad, T_degC, pol=pol2)
|
|
214
|
+
n3 = self.n(wl3, angle_rad, T_degC, pol=pol3)
|
|
215
|
+
dk_sfg = 2*pi * (n3/wl3 - n2/wl2 - n1/wl1)
|
|
216
|
+
return dk_sfg
|
|
217
|
+
|
|
218
|
+
def pmAngles_sfg(self, wl1, wl2, T_degC, tol_deg=0.005, deg=False):
|
|
219
|
+
"""
|
|
220
|
+
Phase-matching (PM) angles for sum-frequency generation (SFG) and sum-frequency wavelength.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
wl1 : float or array_like
|
|
225
|
+
1st pump wavelength in µm.
|
|
226
|
+
wl2 : float or array_like
|
|
227
|
+
2nd pump wavelength in µm.
|
|
228
|
+
tol_deg : float, defalut=0.005
|
|
229
|
+
Tolerance error of angle in degree.
|
|
230
|
+
deg : bool, default=False
|
|
231
|
+
If returned angles are expressed in radians (False) or degrees (True).
|
|
232
|
+
|
|
233
|
+
Return
|
|
234
|
+
------
|
|
235
|
+
dict,
|
|
236
|
+
'wl3' : wavelength of SFG
|
|
237
|
+
{'ooe'} : PM angle for negative type-I
|
|
238
|
+
{'eeo'} : PM angle for positive type-I
|
|
239
|
+
{'oee', 'eoe'} : PM angles for negative type-II
|
|
240
|
+
{'oee', 'eoe'} : PM angles for positive type-II
|
|
241
|
+
"""
|
|
242
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
243
|
+
|
|
244
|
+
def pmAngle_for_pol(pol1, pol2, pol3):
|
|
245
|
+
angle_ar = np.arange(0, 90, tol_deg) * pi/180
|
|
246
|
+
angle_pm = angle_ar[arg_signchange(self.dk_sfg(wl1, wl2, angle_ar, T_degC, pol1, pol2, pol3))]
|
|
247
|
+
if deg:
|
|
248
|
+
angle_pm *= 180/pi
|
|
249
|
+
pm_angles = dict()
|
|
250
|
+
if self.theta_rad == 'var':
|
|
251
|
+
pm_angles['theta'] = angle_pm.tolist()
|
|
252
|
+
pm_angles['phi'] = None
|
|
253
|
+
elif self.phi_rad == 'var':
|
|
254
|
+
pm_angles['phi'] = angle_pm.tolist()
|
|
255
|
+
pm_angles['theta'] = None
|
|
256
|
+
return pm_angles
|
|
257
|
+
|
|
258
|
+
d = dict()
|
|
259
|
+
d['wl3'] = wl3
|
|
260
|
+
# Type-I interaction
|
|
261
|
+
d['ooe'] = pmAngle_for_pol('o', 'o', 'e') #negative
|
|
262
|
+
d['eeo'] = pmAngle_for_pol('e', 'e', 'o') #positive
|
|
263
|
+
# Type-II interaction
|
|
264
|
+
d['oee'] = pmAngle_for_pol('o', 'e', 'e') #nega1
|
|
265
|
+
d['eoe'] = pmAngle_for_pol('e', 'o', 'e') #nega2
|
|
266
|
+
d['eoo'] = pmAngle_for_pol('e', 'o', 'o') #posi1
|
|
267
|
+
d['oeo'] = pmAngle_for_pol('o', 'e', 'o') #posi2
|
|
268
|
+
return d
|
|
269
|
+
|
|
270
|
+
def pmFactor_sfg(self, wl1, wl2, angle_rad, T_degC, pol1, pol2, pol3, L_mm):
|
|
271
|
+
"""
|
|
272
|
+
Phase-matching factor, sin^2((0.5*dk*L)/(0.5*dk*L)), for sum-frequency generation (SFG).
|
|
273
|
+
|
|
274
|
+
Parameters
|
|
275
|
+
----------
|
|
276
|
+
wl1 : float or array_like
|
|
277
|
+
1st pump wavelength in µm.
|
|
278
|
+
wl2 : float or array_like
|
|
279
|
+
2nd pump wavelength in µm.
|
|
280
|
+
angle_rad : float or array_like
|
|
281
|
+
theta or phi angles in radians.
|
|
282
|
+
T_degC : float or array_like
|
|
283
|
+
Crystal temperature in degC.
|
|
284
|
+
pol1: {'o', 'e'}
|
|
285
|
+
Polarization of 1st pump wave.
|
|
286
|
+
pol2: {'o', 'e'}
|
|
287
|
+
Polarization of 2nd pump wave.
|
|
288
|
+
pol3: {'o', 'e'}
|
|
289
|
+
Polarization of sum-frequency wave.
|
|
290
|
+
L_mm : float
|
|
291
|
+
Crystal length in mm.
|
|
292
|
+
|
|
293
|
+
Return
|
|
294
|
+
------
|
|
295
|
+
float or array_like
|
|
296
|
+
Phase-matching factor for SFG.
|
|
297
|
+
"""
|
|
298
|
+
L_um = L_mm * 1e3
|
|
299
|
+
t = 0.5 * self.dk_sfg(wl1, wl2, angle_rad, T_degC, pol1, pol2, pol3) * L_um
|
|
300
|
+
return (np.sin(t)/t)**2
|
|
301
|
+
|
|
302
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from ndispers._baseclass import Medium
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
class Uniax_neg_32(Medium):
|
|
5
|
+
"""
|
|
6
|
+
Class of negative uniaxial crystals of point group 32.
|
|
7
|
+
|
|
8
|
+
d = [[ d11, -d11, 0, d14, 0, 0 ],
|
|
9
|
+
[ 0, 0, 0, 0, -d14, -d11 ],
|
|
10
|
+
[ 0, 0, 0, 0, 0, 0 ]]
|
|
11
|
+
= [d_il]
|
|
12
|
+
i = 1,2,3 (1=x, 2=y, 3=z)
|
|
13
|
+
l = 1,2,...,6 (1=xx, 2=yy, 3=zz, 4=yz, 5=zx, 6=xy)
|
|
14
|
+
|
|
15
|
+
Under the Kleinman symmetry,
|
|
16
|
+
dxyz = dyzx, or d14 = d25.
|
|
17
|
+
|
|
18
|
+
With d25 = - d14, we get d14 = d25 = -d14. Therefore, d14 = 0.
|
|
19
|
+
Nonzero element is only d11.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self):
|
|
24
|
+
super().__init__()
|
|
25
|
+
|
|
26
|
+
def delta11(self, d11_known, wl1, wl2, T_degC):
|
|
27
|
+
"""
|
|
28
|
+
Miller' delta for d11 coefficient (ijk = xxx).
|
|
29
|
+
|
|
30
|
+
Parameters
|
|
31
|
+
----------
|
|
32
|
+
d11_known : float, second-order nonlinear coefficient, d11, known from literature.
|
|
33
|
+
wl1 : float, first pump wavelength in µm for the d11
|
|
34
|
+
wl2 : float, second pump wavelength in µm for the d11
|
|
35
|
+
T_degC : float, crystal temperature in degC.
|
|
36
|
+
|
|
37
|
+
return
|
|
38
|
+
------
|
|
39
|
+
float, Miller's delta for 11 (ijk = xxx) component.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
43
|
+
theta_rad = 0 # This is an arbitrary value since n of o-wave does not depends on theta.
|
|
44
|
+
# chi : linear susceptibility
|
|
45
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
46
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
47
|
+
chi_wl3o = self.n(wl3, theta_rad, T_degC, pol='o')**2 - 1
|
|
48
|
+
return d11_known / (chi_wl3o * chi_wl2o * chi_wl1o)
|
|
49
|
+
|
|
50
|
+
def d11_sfg(self, wl1, wl2, T_degC, delta11=0):
|
|
51
|
+
"""
|
|
52
|
+
d11 coefficient as a function of two pump wavelengths for SFG.
|
|
53
|
+
|
|
54
|
+
Parameters
|
|
55
|
+
----------
|
|
56
|
+
wl1 : float, first pump wavelength in µm.
|
|
57
|
+
wl2 : float, second pump wavelength in µm.
|
|
58
|
+
T_degC : crystal temperature in degC.
|
|
59
|
+
|
|
60
|
+
return
|
|
61
|
+
------
|
|
62
|
+
float, d11 coefficient (pm/V).
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
67
|
+
theta_rad = 0 # This is an arbitrary value since n of o-wave does not depends on theta.
|
|
68
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
69
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
70
|
+
chi_wl3o = self.n(wl3, theta_rad, T_degC, pol='o')**2 - 1
|
|
71
|
+
return delta11 * chi_wl3o * chi_wl2o * chi_wl1o
|
|
72
|
+
|
|
73
|
+
def delta14(self, d14_known, wl1, wl2, T_degC):
|
|
74
|
+
"""
|
|
75
|
+
Miller' delta for d14 coefficient (ijk = xyz).
|
|
76
|
+
|
|
77
|
+
Parameters
|
|
78
|
+
----------
|
|
79
|
+
d14_known : float, second-order nonlinear coefficient, d14, known from literature.
|
|
80
|
+
wl1 : float, first pump wavelength in µm for the d14
|
|
81
|
+
wl2 : float, second pump wavelength in µm for the d14
|
|
82
|
+
T_degC : float, crystal temperature in degC.
|
|
83
|
+
|
|
84
|
+
return
|
|
85
|
+
------
|
|
86
|
+
float, Miller's delta for 14 (ijk = xyz) component.
|
|
87
|
+
|
|
88
|
+
"""
|
|
89
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
90
|
+
theta_rad = 0 # This is an arbitrary value since n of o-wave does not depends on theta.
|
|
91
|
+
# chi : linear susceptibility
|
|
92
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
93
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
94
|
+
chi_wl3o = self.n(wl3, theta_rad, T_degC, pol='o')**2 - 1
|
|
95
|
+
return d14_known / (chi_wl3o * chi_wl2o * chi_wl1o)
|
|
96
|
+
|
|
97
|
+
def d14_sfg(self, wl1, wl2, T_degC, delta14=0):
|
|
98
|
+
"""
|
|
99
|
+
d11 coefficient as a function of two pump wavelengths for SFG.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
wl1 : float, first pump wavelength in µm.
|
|
104
|
+
wl2 : float, second pump wavelength in µm.
|
|
105
|
+
T_degC : crystal temperature in degC.
|
|
106
|
+
|
|
107
|
+
return
|
|
108
|
+
------
|
|
109
|
+
float, d14 coefficient (pm/V).
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
114
|
+
theta_rad = 0 # This is an arbitrary value since n of o-wave does not depends on theta.
|
|
115
|
+
chi_wl1e = self.n(wl1, theta_rad, T_degC, pol='e')**2 - 1
|
|
116
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
117
|
+
chi_wl3e = self.n(wl3, theta_rad, T_degC, pol='e')**2 - 1
|
|
118
|
+
return delta14 * chi_wl3e * chi_wl2o * chi_wl1e
|
|
119
|
+
|
|
120
|
+
def deff_sfg(self, wl1, wl2, theta_rad, phi_rad, T_degC, pol1, pol2, pol3):
|
|
121
|
+
"""
|
|
122
|
+
Effective second-order nonlinear coefficient, d_eff, of SFG.
|
|
123
|
+
Wavelength dependence is estimated from Miller's rule.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
wl1 : float, first pump wavelength in µm.
|
|
128
|
+
wl2 : float, second pump wavelength in µm.
|
|
129
|
+
T_degC : float, crystal temperature in degC.
|
|
130
|
+
pol1 : str, {'o', 'e'}, polarization of the 1st pump wave.
|
|
131
|
+
pol2 : str, {'o', 'e'}, polarization of the 2nd pump wave.
|
|
132
|
+
pol3 : str, {'o', 'e'}, polarization of the sum-frequency wave.
|
|
133
|
+
|
|
134
|
+
return
|
|
135
|
+
------
|
|
136
|
+
float, Effective d coefficient (pm/V).
|
|
137
|
+
|
|
138
|
+
"""
|
|
139
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
140
|
+
_d11 = self.d11_sfg(wl1, wl2, T_degC)
|
|
141
|
+
_d14 = self.d14_sfg(wl1, wl2, T_degC)
|
|
142
|
+
rho3 = self.woa_theta(wl3, theta_rad, T_degC, pol=pol3)
|
|
143
|
+
# For negative uniaxial type-I (ooe),
|
|
144
|
+
if pol1 == 'o' and pol2 == 'o' and pol3 == 'e':
|
|
145
|
+
deff = _d11 * np.cos(3*phi_rad) * np.cos(theta_rad + rho3)
|
|
146
|
+
|
|
147
|
+
# For negative uniaxial type-II (oee),
|
|
148
|
+
elif pol1 == 'o' and pol2 == 'e' and pol3 == 'e':
|
|
149
|
+
rho2 = self.woa_theta(wl2, theta_rad, T_degC, pol=pol2)
|
|
150
|
+
deff = _d11 * np.sin(3*phi_rad) * np.cos(theta_rad + rho2) * np.cos(theta_rad + rho3)\
|
|
151
|
+
+ _d14 * np.sin(theta_rad + rho2) * np.cos(theta_rad + rho3)
|
|
152
|
+
|
|
153
|
+
# For negative uniaxial type-II (eoe),
|
|
154
|
+
elif pol1 == 'e' and pol2 == 'o' and pol3 == 'e':
|
|
155
|
+
rho1 = self.woa_theta(wl1, theta_rad, T_degC, pol=pol1)
|
|
156
|
+
deff = _d11 * np.sin(3*phi_rad) * np.cos(theta_rad + rho1) * np.cos(theta_rad + rho3)\
|
|
157
|
+
+ _d14 * np.sin(theta_rad + rho1) * np.cos(theta_rad + rho3)
|
|
158
|
+
|
|
159
|
+
else:
|
|
160
|
+
raise ValueError("For negative uniaxial crystals,pol1, pol2, pol3 must be among {ooe, oee, eoe}.")
|
|
161
|
+
|
|
162
|
+
return deff
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
from ndispers._baseclass import Medium
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
class Uniax_neg_3m(Medium):
|
|
5
|
+
"""
|
|
6
|
+
Class of negative uniaxial crystals of point group 3m.
|
|
7
|
+
|
|
8
|
+
d = [[ 0, 0, 0, 0, d15, -d22 ],
|
|
9
|
+
[ -d22, d22, 0, d15, 0, 0 ],
|
|
10
|
+
[ d31, d31, d33, 0, 0, 0 ]]
|
|
11
|
+
= [d_il]
|
|
12
|
+
i = 1,2,3 (1=x, 2=y, 3=z)
|
|
13
|
+
l = 1,2,...,6 (1=xx, 2=yy, 3=zz, 4=yz, 5=zx, 6=xy)
|
|
14
|
+
|
|
15
|
+
Under the Kleinman symmetry,
|
|
16
|
+
dxzx = dzxx, or d15 = d31.
|
|
17
|
+
Nonzero independent elements are d22(=-d16), d31(=d15) and d33.
|
|
18
|
+
(d33 does not come up in deff since its geometric factor is zero for all the interaction types.)
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self):
|
|
23
|
+
super().__init__()
|
|
24
|
+
|
|
25
|
+
def delta22(self, d22_known, wl1, wl2, T_degC):
|
|
26
|
+
"""
|
|
27
|
+
Miller' delta for d22 coefficient (ijk = yyy).
|
|
28
|
+
|
|
29
|
+
Note
|
|
30
|
+
----
|
|
31
|
+
This value is almost constant and can be used to estimate wavelength dependence of d22
|
|
32
|
+
from a known d22 of SFG with wl1 and wl2 as two pump wavelengths (Miller's rule).
|
|
33
|
+
For yyy, two pump (wl1, wl2) and sum-frequency (wl3) waves are all ordinary waves.
|
|
34
|
+
|
|
35
|
+
Parameters
|
|
36
|
+
----------
|
|
37
|
+
d22_known : float, second-order nonlinear coefficient, d22, known from literature.
|
|
38
|
+
wl1 : float, first pump wavelength in µm for the d22
|
|
39
|
+
wl2 : float, second pump wavelength in µm for the d22
|
|
40
|
+
T_degC : float, crystal temperature in degC.
|
|
41
|
+
|
|
42
|
+
return
|
|
43
|
+
------
|
|
44
|
+
float, Miller's delta for 22 (ijk = yyy) component.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
48
|
+
theta_rad = 0 # This is an arbitrary value since n of o-wave does not depends on theta.
|
|
49
|
+
# chi : linear susceptibility
|
|
50
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
51
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
52
|
+
chi_wl3o = self.n(wl3, theta_rad, T_degC, pol='o')**2 - 1
|
|
53
|
+
return d22_known / (chi_wl3o * chi_wl2o * chi_wl1o)
|
|
54
|
+
|
|
55
|
+
def d22_sfg(self, wl1, wl2, T_degC, delta22=0):
|
|
56
|
+
"""
|
|
57
|
+
d22 coefficient as a function of two pump wavelengths for SFG.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
wl1 : float, first pump wavelength in µm.
|
|
62
|
+
wl2 : float, second pump wavelength in µm.
|
|
63
|
+
T_degC : crystal temperature in degC.
|
|
64
|
+
|
|
65
|
+
return
|
|
66
|
+
------
|
|
67
|
+
float, d22 coefficient (pm/V).
|
|
68
|
+
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
72
|
+
theta_rad = 0 # This is an arbitrary value since n of o-wave does not depends on theta.
|
|
73
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
74
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
75
|
+
chi_wl3o = self.n(wl3, theta_rad, T_degC, pol='o')**2 - 1
|
|
76
|
+
return delta22 * chi_wl3o * chi_wl2o * chi_wl1o
|
|
77
|
+
|
|
78
|
+
def delta31(self, d31_known, wl1, wl2, T_degC):
|
|
79
|
+
"""
|
|
80
|
+
Miller' delta for d31 coefficient (ijk = zxx).
|
|
81
|
+
|
|
82
|
+
Note
|
|
83
|
+
----
|
|
84
|
+
This value is almost constant and can be used to estimate wavelength dependence of d31
|
|
85
|
+
from a known d31 of SFG with wl1 and wl2 as two pump wavelengths (Miller's rule).
|
|
86
|
+
For zxx, two pump waves are ordinary and the SF wave is extra-ordinary.
|
|
87
|
+
|
|
88
|
+
Parameters
|
|
89
|
+
----------
|
|
90
|
+
d31_known : float, second-order nonlinear coefficient, d31, known from literature.
|
|
91
|
+
wl1 : float, first pump wavelength in µm for the d31
|
|
92
|
+
wl2 : float, second pump wavelength in µm for the d31
|
|
93
|
+
T_degC : float, crystal temperature in degC.
|
|
94
|
+
|
|
95
|
+
return
|
|
96
|
+
------
|
|
97
|
+
float, Miller's delta for 31 (ijk = zxx) component.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
101
|
+
pmAngles = self.pmAngles_sfg(wl1, wl2, T_degC)
|
|
102
|
+
theta_rad = pmAngles['ooe']['theta'][0] # rad
|
|
103
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
104
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
105
|
+
chi_wl3e = self.n(wl3, theta_rad, T_degC, pol='e')**2 - 1
|
|
106
|
+
return d31_known / (chi_wl3e * chi_wl2o * chi_wl1o)
|
|
107
|
+
|
|
108
|
+
def d31_sfg(self, wl1, wl2, T_degC, delta31=0):
|
|
109
|
+
"""
|
|
110
|
+
d31 coefficient as a function of two pump wavelengths for SFG.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
wl1 : float, first pump wavelength in µm.
|
|
115
|
+
wl2 : float, second pump wavelength in µm.
|
|
116
|
+
T_degC : float, crystal temperature in degC.
|
|
117
|
+
|
|
118
|
+
return
|
|
119
|
+
------
|
|
120
|
+
float, d31 coefficient (pm/V).
|
|
121
|
+
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
125
|
+
pmAngles = self.pmAngles_sfg(wl1, wl2, T_degC)
|
|
126
|
+
theta_rad = pmAngles['ooe']['theta'][0] # rad
|
|
127
|
+
chi_wl1o = self.n(wl1, theta_rad, T_degC, pol='o')**2 - 1
|
|
128
|
+
chi_wl2o = self.n(wl2, theta_rad, T_degC, pol='o')**2 - 1
|
|
129
|
+
chi_wl3e = self.n(wl3, theta_rad, T_degC, pol='e')**2 - 1
|
|
130
|
+
return delta31 * chi_wl3e * chi_wl2o * chi_wl1o
|
|
131
|
+
|
|
132
|
+
def deff_sfg(self, wl1, wl2, theta_rad, phi_rad, T_degC, pol1, pol2, pol3):
|
|
133
|
+
"""
|
|
134
|
+
Effective second-order nonlinear coefficient, d_eff, of SFG.
|
|
135
|
+
Wavelength dependence is estimated from Miller's rule.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
wl1 : float, first pump wavelength in µm.
|
|
140
|
+
wl2 : float, second pump wavelength in µm.
|
|
141
|
+
T_degC : float, crystal temperature in degC.
|
|
142
|
+
pol1 : str, {'o', 'e'}, polarization of the 1st pump wave.
|
|
143
|
+
pol2 : str, {'o', 'e'}, polarization of the 2nd pump wave.
|
|
144
|
+
pol3 : str, {'o', 'e'}, polarization of the sum-frequency wave.
|
|
145
|
+
|
|
146
|
+
return
|
|
147
|
+
------
|
|
148
|
+
float, Effective d coefficient (pm/V).
|
|
149
|
+
|
|
150
|
+
"""
|
|
151
|
+
wl3 = 1./(1./wl1 + 1./wl2)
|
|
152
|
+
_d31 = self.d31_sfg(wl1, wl2, T_degC)
|
|
153
|
+
_d22 = self.d22_sfg(wl1, wl2, T_degC)
|
|
154
|
+
rho3 = self.woa_theta(wl3, theta_rad, T_degC, pol=pol3)
|
|
155
|
+
# For negative uniaxial type-I (ooe),
|
|
156
|
+
if pol1 == 'o' and pol2 == 'o' and pol3 == 'e':
|
|
157
|
+
deff = _d31 * np.sin(theta_rad + rho3) - _d22 * np.cos(theta_rad + rho3) * np.sin(3*phi_rad)
|
|
158
|
+
|
|
159
|
+
# For negative uniaxial type-II (oee),
|
|
160
|
+
elif pol1 == 'o' and pol2 == 'e' and pol3 == 'e':
|
|
161
|
+
rho2 = self.woa_theta(wl2, theta_rad, T_degC, pol=pol2)
|
|
162
|
+
deff = _d22 * np.cos(theta_rad + rho2) * np.cos(theta_rad + rho3) * np.cos(3*phi_rad)
|
|
163
|
+
|
|
164
|
+
# For negative uniaxial type-II (eoe),
|
|
165
|
+
elif pol1 == 'e' and pol2 == 'o' and pol3 == 'e':
|
|
166
|
+
rho1 = self.woa_theta(wl1, theta_rad, T_degC, pol=pol1)
|
|
167
|
+
deff = _d22 * np.cos(theta_rad + rho1) * np.cos(theta_rad + rho3) * np.cos(3*phi_rad)
|
|
168
|
+
|
|
169
|
+
else:
|
|
170
|
+
raise ValueError("For negative uniaxial crystals,pol1, pol2, pol3 must be among {ooe, oee, eoe}.")
|
|
171
|
+
|
|
172
|
+
return deff
|
ndispers/helper.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def vars2(obj):
|
|
4
|
+
return {k: getattr(obj, k) for k in obj.__slots__}
|
|
5
|
+
|
|
6
|
+
def returnShape(*args):
|
|
7
|
+
return np.broadcast(*args).shape
|
|
8
|
+
|
|
9
|
+
def arg_signchange(a):
|
|
10
|
+
a_sign = np.sign(a)
|
|
11
|
+
if_signflip = ((np.roll(a_sign, 1) - a_sign) != 0).astype(int)
|
|
12
|
+
if_signflip[0] = 0
|
|
13
|
+
arg_signflip = np.where(if_signflip == 1)
|
|
14
|
+
return arg_signflip
|
|
15
|
+
|
|
16
|
+
from scipy.interpolate import interp1d
|
|
17
|
+
def resample(x, y, N=1, kind='linear'):
|
|
18
|
+
x = np.asarray(x)
|
|
19
|
+
y = np.asarray(y)
|
|
20
|
+
y_func = interp1d(x, y, kind=kind)
|
|
21
|
+
x_new = np.linspace(x[0], x[-1], x.size * N)
|
|
22
|
+
y_new = y_func(x_new)
|
|
23
|
+
return x_new, y_new
|
|
24
|
+
|
|
25
|
+
def fullWidth(x_ar, y_ar, threshold=0.5, N=1):
|
|
26
|
+
x, y = resample(x_ar, y_ar, N=N)
|
|
27
|
+
idx_3dB = np.where(y >= np.max(y) * threshold)
|
|
28
|
+
x_3dB = x[idx_3dB]
|
|
29
|
+
width_3dB = x_3dB[-1] - x_3dB[0]
|
|
30
|
+
return width_3dB
|
|
31
|
+
|
|
32
|
+
def peak_position(x_ar, y_ar, N=1):
|
|
33
|
+
x, y = resample(x_ar, y_ar, N=N)
|
|
34
|
+
idx_peak = np.argmax(y_ar)
|
|
35
|
+
return x_peak[idx_peak]
|