Notes on evaluating Δ_n factor in the lattice sums.
[qpms.git] / qpms / cymaterials.pyx
blob17303815b3baccc0b4229f20c98edae8457a2fd7
1 # Cythonized parts of QPMS here
2 # -----------------------------
4 import numpy as np
5 import cmath
6 from .qpms_cdefs cimport qpms_permittivity_interpolator_from_yml, qpms_permittivity_interpolator_free, qpms_permittivity_interpolator_omega_min, qpms_permittivity_interpolator_omega_max, gsl_interp_type, qpms_permittivity_interpolator_t, gsl_interp_cspline, qpms_permittivity_interpolator_eps_at_omega, qpms_epsmu_const_g, qpms_permittivity_interpolator_epsmu_g, qpms_epsmu_const_g, qpms_lorentzdrude_epsmu_g, qpms_ldparams_triple_t, qpms_lorentzdrude_eps, cdouble
7 from .cycommon cimport make_c_string
8 cimport cython
9 import enum
10 import warnings
11 import os
12 from scipy.constants import e as eV, hbar, c
13 from libc.stdlib cimport malloc, free, calloc, abort
15 class EpsMuGeneratorType(enum.Enum):
16 CONSTANT = 1
17 PERMITTIVITY_INTERPOLATOR = 2
18 LORENTZ_DRUDE = 3
19 PYTHON_CALLABLE = 4
21 cdef class EpsMu:
22 """Permittivity and permeability of an isotropic material.
24 This wraps the C structure qpms_epsmu_t.
26 See Also
27 --------
28 EpsMuGenerator : generates EpsMu objects as a function of frequency.
29 """
30 def __init__(self, *args ,**kwargs):
31 """EpsMu object constructor
33 Parameters
34 ----------
35 eps : complex
36 Relative electric permittivity.
37 mu : complex
38 Relative magnetic permeability.
39 """
40 self.em.eps = 1
41 self.em.mu = 1
42 if(len(args)>=1):
43 self.em.eps = args[0]
44 if(len(args)>=2):
45 self.em.mu = args[1]
46 if 'eps' in kwargs.keys():
47 self.em.eps = kwargs['eps']
48 if 'mu' in kwargs.keys():
49 self.em.mu = kwargs['mu']
50 return
52 def __repr__(self):
53 return 'EpsMu(' + repr(self.em.eps) + ', ' + repr(self.em.mu) + ')'
55 def k(self, omega):
56 """Wavenumber of the material at frequency omega
58 Parameters
59 ----------
60 omega : complex
61 Angular frequency in 1/s.
63 Returns
64 -------
65 out : complex
66 Wavenumber of the material at omega assuming permittivity and permeability
67 from self.
68 """
69 return self.n * omega / c
71 property n:
72 """Refractive index of a material specified by this permittivity and permeability."""
73 def __get__(self):
74 return (self.em.eps * self.em.mu)**.5
75 property Z:
76 """Wave impedance of a material specified by this permittivity and permeability."""
77 def __get__(self):
78 return (self.em.mu / self.em.eps)**.5
80 cdef class LorentzDrudeModel:
81 """Lorentz-Drude model of material permittivity.
83 This wraps the C structure qpms_ldparams_t.
85 Some materials are available in the `lorentz_drude` dictionary.
86 """
88 def __cinit__(self, eps_inf, omega_p, f_arr, omega_arr, gamma_arr):
89 cdef size_t n = len(omega_arr)
90 if (n != len(gamma_arr) or n != len(f_arr)):
91 raise ValueError("omega_arr, gamma_arr and f_arr must have equal lengths!")
92 cdef qpms_ldparams_t *p
93 p = <qpms_ldparams_t *>malloc(sizeof(qpms_ldparams_t) + sizeof(qpms_ldparams_triple_t) * n)
94 p[0].eps_inf = eps_inf
95 p[0].omega_p = omega_p
96 p[0].n = n
97 cdef size_t i
98 for i in range(0,n):
99 p[0].data[i].f = f_arr[i]
100 p[0].data[i].omega = omega_arr[i]
101 p[0].data[i].gamma = gamma_arr[i]
102 self.params = p
104 def __dealloc__(self):
105 free(self.params)
106 self.params = NULL
108 def __call__(self, omega):
109 """Evaluates the permittivity at a given frequency
111 Parameters
112 ----------
113 omega : complex
114 Angular frequency in 1/s.
116 Returns
117 -------
118 eps : complex
119 Relative permittivity from the Lorentz-Drude model
120 """
121 return qpms_lorentzdrude_eps(omega, self.params)
123 cdef class _CLorentzDrudeModel:
124 """Lorentz-Drude model of material permittivity.
126 This is an auxilliary class making the pre-compiled C Lorentz-Drude models accessible.
127 For defining own Lorentz-Drude models from python, use LorentzDrudeModel class instead.
129 def __cinit__(self):
130 "Do not use directly. Do not use from Python. Use the link() method instead."
131 pass
133 @staticmethod
134 cdef link(const qpms_ldparams_t *params):
135 self = _CLorentzDrudeModel()
136 self.params = params
137 return self
139 def __call__(self, omega):
140 """Evaluates the permittivity at a given frequency
142 Parameters
143 ----------
144 omega : complex
145 Angular frequency in 1/s.
147 Returns
148 -------
149 eps : complex
150 Relative permittivity from the Lorentz-Drude model
152 return qpms_lorentzdrude_eps(omega, self.params)
154 cdef double eh = eV/hbar
156 # Some basic Lorentz-Drude parameters
157 lorentz_drude = {
158 'Au_py' : # This should give the same results as 'Au'; to be removed.
159 LorentzDrudeModel(1, 9.03*eh,
160 (0.76, 0.024, 0.01, 0.071, 0.601, 4.384),
161 (0, 0.415*eh, 0.83*eh, 2.969*eh, 4.304*eh, 13.32*eh),
162 (0.053*eh, 0.241*eh, 0.345*eh, 0.87*eh, 2.494*eh, 2.214*eh)),
163 'Ag_py' : # This should give the same results as 'Ag'; to be removed.
164 LorentzDrudeModel(1, 9.01*eh,
165 (0.84, 0.065,0.124, 0.111, 0.840, 5.646),
166 (0, 0.816*eh,4.481*eh, 8.185*eh, 9.083*eh, 20.29*eh),
167 (0.053*eh, 3.886*eh, 0.452*eh,0.065*eh, 0.916*eh, 2.419*eh)),
168 'Au' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_AU),
169 'Ag' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_AG),
170 'Cu' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_CU),
171 'Al' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_AL),
172 'Cr' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_CR),
173 'Ti' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_TI),
174 'Be' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_BE),
175 'Ni' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_NI),
176 'Pd' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_PD),
177 'Pt' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_PT),
178 'W' : _CLorentzDrudeModel.link(QPMS_LDPARAMS_W),
182 cdef qpms_epsmu_t python_epsmu_generator(cdouble omega, const void *params):
183 cdef object fun = <object> params
184 cdef qpms_epsmu_t em
185 em.eps, em.mu = fun(omega)
186 return em
188 cdef class EpsMuGenerator:
189 def __init__(self, what):
190 if isinstance(what, EpsMu):
191 self.holder = what
192 self.g.function = qpms_epsmu_const_g
193 self.g.params = (<EpsMu?>self.holder).rawpointer()
194 elif isinstance(what, LorentzDrudeModel):
195 self.holder = what
196 self.g.function = qpms_lorentzdrude_epsmu_g
197 self.g.params = (<LorentzDrudeModel?>self.holder).rawpointer()
198 elif isinstance(what, _CLorentzDrudeModel):
199 self.holder = what
200 self.g.function = qpms_lorentzdrude_epsmu_g
201 self.g.params = (<_CLorentzDrudeModel?>self.holder).rawpointer()
202 elif isinstance(what, MaterialInterpolator):
203 self.holder = what
204 self.g.function = qpms_permittivity_interpolator_epsmu_g
205 self.g.params = (<MaterialInterpolator?>self.holder).rawpointer()
206 elif isinstance(what, EpsMuGenerator): # Copy constructor
207 self.holder = (<EpsMuGenerator?>what).holder
208 self.g = (<EpsMuGenerator?>what).g
209 elif callable(what):
210 warnings.warn("Custom python (eps,mu) generators are an experimental feature")
211 self.holder = what
212 self.g.function = python_epsmu_generator
213 self.g.params = <const void *> what
214 else:
215 raise ValueError("Must be constructed from EpsMu, LorentzDrudeModel or MaterialInterpolator, or a python callable object that returns an (epsilon, mu) tuple.")
217 property typ:
218 def __get__(self):
219 if(self.g.function == qpms_epsmu_const_g):
220 return EpsMuGeneratorType.CONSTANT
221 elif(self.g.function == qpms_lorentzdrude_epsmu_g):
222 return EpsMuGeneratorType.LORENTZ_DRUDE
223 elif(self.g.function == qpms_permittivity_interpolator_epsmu_g):
224 return EpsMuGeneratorType.PERMITTIVITY_INTERPOLATOR
225 elif(self.g.function == python_epsmu_generator):
226 return EpsMuGeneratorType.PYTHON_CALLABLE
227 else:
228 raise ValueError("Damn, this is a bug.")
230 def __call__(self, omega):
231 cdef qpms_epsmu_t em
232 if self.g.function == qpms_permittivity_interpolator_epsmu_g:
233 i = self.holder.freq_interval
234 if(omega < i[0] or omega > i[1]):
235 raise ValueError("Input frequency %g is outside the interpolator domain (%g, %g)."
236 % (omega, i[0], i[1]))
237 em = self.g.function(omega, self.g.params)
238 return EpsMu(em.eps, em.mu)
240 def n(self, omega):
241 return self(omega).n
242 def Z(self, omega):
243 return self(omega).Z
244 def k(self, omega):
245 return self(omega).k(omega)
247 cdef qpms_epsmu_generator_t raw(self):
248 return self.g
251 cdef class MaterialInterpolator:
253 Wrapper over the qpms_permittivity_interpolator_t structure.
256 def __cinit__(self, filename, *args, **kwargs):
257 '''Creates a permittivity interpolator.'''
258 cdef char *cpath = make_c_string(filename)
259 self.interp = qpms_permittivity_interpolator_from_yml(cpath, gsl_interp_cspline)
260 if not self.interp:
261 raise IOError("Could not load permittivity data from %s" % filename)
262 self.omegamin = qpms_permittivity_interpolator_omega_min(self.interp)
263 self.omegamax = qpms_permittivity_interpolator_omega_max(self.interp)
265 def __dealloc__(self):
266 qpms_permittivity_interpolator_free(self.interp)
268 def __call__(self, double freq):
269 '''Returns interpolated permittivity, corresponding to a given angular frequency.'''
270 if freq < self.omegamin or freq > self.omegamax:
271 raise ValueError("Input frequency %g is outside the interpolator domain (%g, %g)."
272 % (freq, self.minomega, self.freqs[self.maxomega]))
273 return qpms_permittivity_interpolator_eps_at_omega(self.interp, freq)
275 property freq_interval:
276 def __get__(self):
277 return [self.omegamin, self.omegamax]