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.
Files changed (36) hide show
  1. ndispers/__init__.py +7 -0
  2. ndispers/_baseclass.py +302 -0
  3. ndispers/groups/__init__.py +6 -0
  4. ndispers/groups/_uniax_neg_32.py +162 -0
  5. ndispers/groups/_uniax_neg_3m.py +172 -0
  6. ndispers/helper.py +35 -0
  7. ndispers/media/__init__.py +6 -0
  8. ndispers/media/crystals/_CLBO.py +173 -0
  9. ndispers/media/crystals/_KBBF_Li2016.py +176 -0
  10. ndispers/media/crystals/_KDP.py +148 -0
  11. ndispers/media/crystals/_KTP.py +430 -0
  12. ndispers/media/crystals/_LB4.py +180 -0
  13. ndispers/media/crystals/_LBO_Castech.py +455 -0
  14. ndispers/media/crystals/_LBO_EKSMA.py +455 -0
  15. ndispers/media/crystals/_LBO_Ghosh1995.py +460 -0
  16. ndispers/media/crystals/_LBO_KK1994.py +462 -0
  17. ndispers/media/crystals/_LBO_KK2018.py +447 -0
  18. ndispers/media/crystals/_LBO_Newlight.py +444 -0
  19. ndispers/media/crystals/_SLN_MgO_doped.py +173 -0
  20. ndispers/media/crystals/_SLT_MgO_doped.py +197 -0
  21. ndispers/media/crystals/__init__.py +33 -0
  22. ndispers/media/crystals/_alphaBBO.py +142 -0
  23. ndispers/media/crystals/_betaBBO_Eimerl1987.py +181 -0
  24. ndispers/media/crystals/_betaBBO_Ghosh1995.py +196 -0
  25. ndispers/media/crystals/_betaBBO_KK2010.py +209 -0
  26. ndispers/media/crystals/_betaBBO_Tamosauskas2018.py +201 -0
  27. ndispers/media/crystals/_calcite.py +158 -0
  28. ndispers/media/glasses/__init__.py +7 -0
  29. ndispers/media/glasses/_caf2.py +106 -0
  30. ndispers/media/glasses/_fusedsilica.py +104 -0
  31. ndispers/tests/test_LBO.py +63 -0
  32. ndispers/tests/test_betaBBO.py +57 -0
  33. ndispers-0.3.0.dist-info/METADATA +124 -0
  34. ndispers-0.3.0.dist-info/RECORD +36 -0
  35. ndispers-0.3.0.dist-info/WHEEL +5 -0
  36. ndispers-0.3.0.dist-info/licenses/LICENSE +21 -0
ndispers/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ """
2
+ init file of ndispers package
3
+ """
4
+ __version__ = "0.3.0"
5
+
6
+ from . import _baseclass, media
7
+
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,6 @@
1
+ """
2
+ ndispers.groups contains the crystal groups inherited from base class `Medium`.
3
+ """
4
+
5
+ from ._uniax_neg_3m import Uniax_neg_3m
6
+ from ._uniax_neg_32 import Uniax_neg_32
@@ -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]
@@ -0,0 +1,6 @@
1
+ """
2
+ ndispers.media contains the collections of specific crystals and glasses.
3
+ """
4
+ from . import crystals
5
+ from . import glasses
6
+