ElectroMagneticPython 2.1.4__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.
- EMpy/RCWA.py +885 -0
- EMpy/__init__.py +39 -0
- EMpy/constants.py +25 -0
- EMpy/devices.py +1414 -0
- EMpy/materials.py +371 -0
- EMpy/modesolvers/FD.py +2536 -0
- EMpy/modesolvers/FMM.py +2422 -0
- EMpy/modesolvers/__init__.py +13 -0
- EMpy/modesolvers/geometries.py +231 -0
- EMpy/modesolvers/interface.py +71 -0
- EMpy/scattering.py +236 -0
- EMpy/transfer_matrix.py +423 -0
- EMpy/utils.py +1342 -0
- EMpy/version.py +1 -0
- ElectroMagneticPython-2.1.4.dist-info/LICENSE +21 -0
- ElectroMagneticPython-2.1.4.dist-info/METADATA +95 -0
- ElectroMagneticPython-2.1.4.dist-info/RECORD +22 -0
- ElectroMagneticPython-2.1.4.dist-info/WHEEL +5 -0
- ElectroMagneticPython-2.1.4.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/materials_test.py +60 -0
- tests/utils_test.py +23 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"""Geometries.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
import numpy
|
|
5
|
+
import pylab
|
|
6
|
+
|
|
7
|
+
from EMpy.modesolvers.interface import interface_matrix
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def S2T(S):
|
|
11
|
+
dim = S.shape[0] / 2.0
|
|
12
|
+
s11 = S[:dim, :dim]
|
|
13
|
+
s22 = S[dim:, dim:]
|
|
14
|
+
s12 = S[:dim, dim:]
|
|
15
|
+
s21 = S[dim:, :dim]
|
|
16
|
+
T = numpy.zeros_like(S)
|
|
17
|
+
s12_1 = numpy.linalg.inv(s12)
|
|
18
|
+
T[:dim, :dim] = s21 - numpy.dot(numpy.dot(s22, s12_1), s11)
|
|
19
|
+
T[dim:, dim:] = s12_1
|
|
20
|
+
T[:dim, dim:] = numpy.dot(s22, s12_1)
|
|
21
|
+
T[dim:, :dim] = -numpy.dot(s12_1, s11)
|
|
22
|
+
return T
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def T2S(T):
|
|
26
|
+
dim = T.shape[0] / 2.0
|
|
27
|
+
t11 = T[:dim, :dim]
|
|
28
|
+
t22 = T[dim:, dim:]
|
|
29
|
+
t12 = T[:dim, dim:]
|
|
30
|
+
t21 = T[dim:, :dim]
|
|
31
|
+
S = numpy.zeros_like(T)
|
|
32
|
+
t22_1 = numpy.linalg.inv(t22)
|
|
33
|
+
S[:dim, :dim] = -numpy.dot(t22_1, t21)
|
|
34
|
+
S[dim:, dim:] = numpy.dot(t12, t22_1)
|
|
35
|
+
S[:dim, dim:] = t22_1
|
|
36
|
+
S[dim:, :dim] = t11 - numpy.dot(numpy.dot(t12, t22_1), t21)
|
|
37
|
+
return S
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SWG:
|
|
41
|
+
|
|
42
|
+
# def __init__(self, cs, solver, length, inputLHS=None, inputRHS=None):
|
|
43
|
+
def __init__(self, solver, length, inputLHS=None, inputRHS=None):
|
|
44
|
+
# self.cs = cs
|
|
45
|
+
self.solver = solver
|
|
46
|
+
self.length = length
|
|
47
|
+
self.build_matrix()
|
|
48
|
+
self.compute_output(inputLHS, inputRHS)
|
|
49
|
+
|
|
50
|
+
def compute_output(self, inputLHS=None, inputRHS=None):
|
|
51
|
+
if inputLHS is None:
|
|
52
|
+
self.inputLHS = numpy.zeros(self.solver.nmodes)
|
|
53
|
+
self.inputLHS[0] = 1.0
|
|
54
|
+
else:
|
|
55
|
+
self.inputLHS = inputLHS
|
|
56
|
+
if inputRHS is None:
|
|
57
|
+
self.inputRHS = numpy.zeros(self.solver.nmodes)
|
|
58
|
+
else:
|
|
59
|
+
self.inputRHS = inputRHS
|
|
60
|
+
input = numpy.r_[self.inputLHS, self.inputRHS]
|
|
61
|
+
output = numpy.dot(self.S, input)
|
|
62
|
+
self.outputLHS = output[: self.solver.nmodes]
|
|
63
|
+
self.outputRHS = output[self.solver.nmodes :]
|
|
64
|
+
|
|
65
|
+
def build_matrix(self):
|
|
66
|
+
neffs = numpy.array([m.neff for m in self.solver.modes])
|
|
67
|
+
betas = 2 * numpy.pi / self.solver.wl * neffs
|
|
68
|
+
neigs = self.solver.nmodes
|
|
69
|
+
T = numpy.zeros((2 * neigs, 2 * neigs), dtype=complex)
|
|
70
|
+
T[:neigs, :neigs] = numpy.diag(numpy.exp(1j * betas * self.length))
|
|
71
|
+
T[neigs:, neigs:] = numpy.diag(numpy.exp(-1j * numpy.conj(betas) * self.length))
|
|
72
|
+
self.T = T
|
|
73
|
+
self.S = T2S(self.T)
|
|
74
|
+
|
|
75
|
+
def plot(self, sumx=1, nxy=100, nz=100, z0=0):
|
|
76
|
+
if sumx is None: # sum in y
|
|
77
|
+
x = self.solver.modes[0].get_y(nxy)
|
|
78
|
+
axis = 0
|
|
79
|
+
else: # sum in x
|
|
80
|
+
x = self.solver.modes[0].get_x(nxy)
|
|
81
|
+
axis = 1
|
|
82
|
+
z = numpy.linspace(0, self.length, nz)
|
|
83
|
+
const_z = numpy.ones_like(z)
|
|
84
|
+
f = numpy.zeros((len(x), len(z)), dtype=complex)
|
|
85
|
+
for im, (coeffLHS, coeffRHS) in enumerate(zip(self.inputLHS, self.inputRHS)):
|
|
86
|
+
m = self.solver.modes[im]
|
|
87
|
+
# beta = 2 * numpy.pi / self.solver.wl * m.neff
|
|
88
|
+
tmp = numpy.sum(m.intensity(x, x), axis=axis)
|
|
89
|
+
f += (
|
|
90
|
+
numpy.abs(coeffLHS) ** 2 * tmp[:, numpy.newaxis] * const_z
|
|
91
|
+
+ numpy.abs(coeffRHS) ** 2 * tmp[:, numpy.newaxis] * const_z
|
|
92
|
+
)
|
|
93
|
+
pylab.hot()
|
|
94
|
+
pylab.contourf(x, z0 + z, numpy.abs(f).T, 16)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class SimpleJoint:
|
|
98
|
+
|
|
99
|
+
# def __init__(self, cs1, cs2, solver1, solver2, inputLHS=None, inputRHS=None):
|
|
100
|
+
def __init__(self, solver1, solver2, inputLHS=None, inputRHS=None):
|
|
101
|
+
# self.cs1 = cs1
|
|
102
|
+
self.solver1 = solver1
|
|
103
|
+
# self.cs2 = cs2
|
|
104
|
+
self.solver2 = solver2
|
|
105
|
+
self.length = 0.0
|
|
106
|
+
self.build_matrix()
|
|
107
|
+
self.compute_output(inputLHS, inputRHS)
|
|
108
|
+
|
|
109
|
+
def compute_output(self, inputLHS=None, inputRHS=None):
|
|
110
|
+
if inputLHS is None:
|
|
111
|
+
self.inputLHS = numpy.zeros(self.solver1.nmodes)
|
|
112
|
+
self.inputLHS[0] = 1.0
|
|
113
|
+
else:
|
|
114
|
+
self.inputLHS = inputLHS
|
|
115
|
+
if inputRHS is None:
|
|
116
|
+
self.inputRHS = numpy.zeros(self.solver1.nmodes)
|
|
117
|
+
else:
|
|
118
|
+
self.inputRHS = inputRHS
|
|
119
|
+
input = numpy.r_[self.inputLHS, self.inputRHS]
|
|
120
|
+
output = numpy.dot(self.S, input)
|
|
121
|
+
self.outputLHS = output[: self.solver1.nmodes]
|
|
122
|
+
self.outputRHS = output[self.solver1.nmodes :]
|
|
123
|
+
|
|
124
|
+
def build_matrix(self):
|
|
125
|
+
O11, O22, O12, O21 = interface_matrix(self.solver1, self.solver2)
|
|
126
|
+
neffA = numpy.array([m.neff for m in self.solver1.modes])
|
|
127
|
+
neffB = numpy.array([m.neff for m in self.solver2.modes])
|
|
128
|
+
betaA = 2 * numpy.pi / self.solver1.wl * neffA
|
|
129
|
+
betaB = 2 * numpy.pi / self.solver1.wl * neffB
|
|
130
|
+
|
|
131
|
+
BetaA = numpy.diag(betaA)
|
|
132
|
+
BetaB = numpy.diag(betaB)
|
|
133
|
+
|
|
134
|
+
# MRA1 = numpy.dot(O12, BetaA + BetaB)
|
|
135
|
+
# MRA2 = numpy.dot(O12, BetaA - BetaB)
|
|
136
|
+
# MRA = numpy.dot(numpy.linalg.inv(MRA1), MRA2)
|
|
137
|
+
# S11 = MRA
|
|
138
|
+
#
|
|
139
|
+
# MRB1 = numpy.dot(O21, BetaA + BetaB)
|
|
140
|
+
# MRB2 = numpy.dot(O21, BetaB - BetaA)
|
|
141
|
+
# MRB = numpy.dot(numpy.linalg.inv(MRB1), MRB2)
|
|
142
|
+
# S22 = MRB
|
|
143
|
+
#
|
|
144
|
+
# MTAB1 = numpy.dot(O22, BetaA + BetaB)
|
|
145
|
+
# MTAB2 = numpy.dot(O12, 2. * BetaA)
|
|
146
|
+
# MTAB = numpy.dot(numpy.linalg.inv(MTAB1), MTAB2)
|
|
147
|
+
# S12 = MTAB
|
|
148
|
+
#
|
|
149
|
+
# MTBA1 = numpy.dot(O11, BetaA + BetaB)
|
|
150
|
+
# MTBA2 = numpy.dot(O21, 2. * BetaB)
|
|
151
|
+
# MTBA = numpy.dot(numpy.linalg.inv(MTBA1), MTBA2)
|
|
152
|
+
# S21 = MTBA
|
|
153
|
+
|
|
154
|
+
SUM = numpy.dot(BetaA, O21) + numpy.dot(O21, BetaB)
|
|
155
|
+
DIF = numpy.dot(BetaA, O21) - numpy.dot(O21, BetaB)
|
|
156
|
+
A = 0.5 * numpy.dot(numpy.linalg.inv(BetaA), SUM)
|
|
157
|
+
B = 0.5 * numpy.dot(numpy.linalg.inv(BetaA), DIF)
|
|
158
|
+
A_1 = numpy.linalg.inv(A)
|
|
159
|
+
S11 = numpy.dot(B, A_1)
|
|
160
|
+
S12 = A - numpy.dot(numpy.dot(B, A_1), B)
|
|
161
|
+
S21 = A_1
|
|
162
|
+
S22 = -numpy.dot(A_1, B)
|
|
163
|
+
|
|
164
|
+
dim = O11.shape[0]
|
|
165
|
+
|
|
166
|
+
S = numpy.zeros((2 * dim, 2 * dim), dtype=complex)
|
|
167
|
+
S[:dim, :dim] = S11
|
|
168
|
+
S[dim:, dim:] = S22
|
|
169
|
+
S[:dim, dim:] = S12
|
|
170
|
+
S[dim:, :dim] = S21
|
|
171
|
+
|
|
172
|
+
self.S = S
|
|
173
|
+
self.T = S2T(self.S)
|
|
174
|
+
|
|
175
|
+
def plot(self, sumx=1, nxy=100, nz=100, z0=0):
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class GenericDevice:
|
|
180
|
+
def __init__(self, devlist, inputLHS=None, inputRHS=None):
|
|
181
|
+
self.devlist = devlist
|
|
182
|
+
self.build_matrix()
|
|
183
|
+
self.compute_output(inputLHS, inputRHS)
|
|
184
|
+
|
|
185
|
+
def compute_output(self, inputLHS=None, inputRHS=None):
|
|
186
|
+
if inputLHS is None:
|
|
187
|
+
self.inputLHS = numpy.zeros_like(self.devlist[0].inputLHS)
|
|
188
|
+
self.inputLHS[0] = 1.0
|
|
189
|
+
else:
|
|
190
|
+
self.inputLHS = inputLHS
|
|
191
|
+
if inputRHS is None:
|
|
192
|
+
self.inputRHS = numpy.zeros_like(self.devlist[0].inputRHS)
|
|
193
|
+
else:
|
|
194
|
+
self.inputRHS = inputRHS
|
|
195
|
+
input = numpy.r_[self.inputLHS, self.inputRHS]
|
|
196
|
+
output = numpy.dot(self.S, input)
|
|
197
|
+
self.outputLHS = output[: len(self.inputLHS)]
|
|
198
|
+
self.outputRHS = output[len(self.inputLHS) :]
|
|
199
|
+
# compute for each device
|
|
200
|
+
inputLHS = self.inputLHS
|
|
201
|
+
outputLHS = self.outputLHS
|
|
202
|
+
dim = len(inputLHS)
|
|
203
|
+
for d in self.devlist:
|
|
204
|
+
d.inputLHS = inputLHS
|
|
205
|
+
d.outputLHS = outputLHS
|
|
206
|
+
LHS = numpy.r_[inputLHS, outputLHS]
|
|
207
|
+
RHS = numpy.dot(d.T, LHS)
|
|
208
|
+
d.outputRHS = RHS[:dim]
|
|
209
|
+
d.inputRHS = RHS[dim:]
|
|
210
|
+
inputLHS = d.outputRHS
|
|
211
|
+
outputLHS = d.inputRHS
|
|
212
|
+
|
|
213
|
+
def build_matrix(self):
|
|
214
|
+
dim = self.devlist[0].solver.nmodes
|
|
215
|
+
T = numpy.eye(2 * dim, dtype=complex)
|
|
216
|
+
for d in self.devlist:
|
|
217
|
+
T = numpy.dot(d.T, T)
|
|
218
|
+
self.T = T
|
|
219
|
+
self.S = T2S(self.T)
|
|
220
|
+
|
|
221
|
+
def plot(self, sumx=1, nxy=100, nz=100, z0=0):
|
|
222
|
+
z = z0
|
|
223
|
+
for d in self.devlist:
|
|
224
|
+
d.plot(sumx, nxy, nz, z0=z)
|
|
225
|
+
z += d.length
|
|
226
|
+
pylab.ylabel("z")
|
|
227
|
+
if sumx is None:
|
|
228
|
+
pylab.xlabel("y")
|
|
229
|
+
else:
|
|
230
|
+
pylab.xlabel("x")
|
|
231
|
+
pylab.axis("tight")
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ModeSolver:
|
|
5
|
+
def solve(self, *argv):
|
|
6
|
+
raise NotImplementedError()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Mode:
|
|
10
|
+
def get_x(self, x=None, y=None):
|
|
11
|
+
raise NotImplementedError()
|
|
12
|
+
|
|
13
|
+
def get_y(self, x=None, y=None):
|
|
14
|
+
raise NotImplementedError()
|
|
15
|
+
|
|
16
|
+
def intensity(self, x=None, y=None):
|
|
17
|
+
raise NotImplementedError()
|
|
18
|
+
|
|
19
|
+
def TEfrac(self, x=None, y=None):
|
|
20
|
+
raise NotImplementedError()
|
|
21
|
+
|
|
22
|
+
def overlap(self, x=None, y=None):
|
|
23
|
+
raise NotImplementedError()
|
|
24
|
+
|
|
25
|
+
def get_fields_for_FDTD(self, x, y):
|
|
26
|
+
raise NotImplementedError()
|
|
27
|
+
|
|
28
|
+
def save_for_FDTD(self, mode_id="", x=None, y=None):
|
|
29
|
+
"""Save mode's fields on a staggered grid."""
|
|
30
|
+
Ex_FDTD, Ey_FDTD, Ez_FDTD, Hx_FDTD, Hy_FDTD, Hz_FDTD = self.get_fields_for_FDTD(
|
|
31
|
+
x, y
|
|
32
|
+
)
|
|
33
|
+
Ex_FDTD.real.T.tofile("ex" + mode_id + ".dat", sep=" ")
|
|
34
|
+
Ex_FDTD.imag.T.tofile("iex" + mode_id + ".dat", sep=" ")
|
|
35
|
+
Ey_FDTD.real.T.tofile("ey" + mode_id + ".dat", sep=" ")
|
|
36
|
+
Ey_FDTD.imag.T.tofile("iey" + mode_id + ".dat", sep=" ")
|
|
37
|
+
Ez_FDTD.real.T.tofile("ez" + mode_id + ".dat", sep=" ")
|
|
38
|
+
Ez_FDTD.imag.T.tofile("iez" + mode_id + ".dat", sep=" ")
|
|
39
|
+
Hx_FDTD.real.T.tofile("hx" + mode_id + ".dat", sep=" ")
|
|
40
|
+
Hx_FDTD.imag.T.tofile("ihx" + mode_id + ".dat", sep=" ")
|
|
41
|
+
Hy_FDTD.real.T.tofile("hy" + mode_id + ".dat", sep=" ")
|
|
42
|
+
Hy_FDTD.imag.T.tofile("ihy" + mode_id + ".dat", sep=" ")
|
|
43
|
+
Hz_FDTD.real.T.tofile("hz" + mode_id + ".dat", sep=" ")
|
|
44
|
+
Hz_FDTD.imag.T.tofile("ihz" + mode_id + ".dat", sep=" ")
|
|
45
|
+
|
|
46
|
+
def plot(self, x=None, y=None):
|
|
47
|
+
raise NotImplementedError()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def overlap(m1, m2, x=None, y=None):
|
|
51
|
+
return m1.overlap(m2, x, y)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def interface_matrix(solver1, solver2, x=None, y=None):
|
|
55
|
+
|
|
56
|
+
neigs = solver1.nmodes
|
|
57
|
+
|
|
58
|
+
O11 = numpy.zeros((neigs, neigs), dtype=complex)
|
|
59
|
+
O22 = numpy.zeros((neigs, neigs), dtype=complex)
|
|
60
|
+
O12 = numpy.zeros((neigs, neigs), dtype=complex)
|
|
61
|
+
O21 = numpy.zeros((neigs, neigs), dtype=complex)
|
|
62
|
+
|
|
63
|
+
for i in range(neigs):
|
|
64
|
+
for j in range(neigs):
|
|
65
|
+
|
|
66
|
+
O11[i, j] = overlap(solver1.modes[i], solver1.modes[j], x, y)
|
|
67
|
+
O22[i, j] = overlap(solver2.modes[i], solver2.modes[j], x, y)
|
|
68
|
+
O12[i, j] = overlap(solver1.modes[i], solver2.modes[j], x, y)
|
|
69
|
+
O21[i, j] = overlap(solver2.modes[i], solver1.modes[j], x, y)
|
|
70
|
+
|
|
71
|
+
return (O11, O22, O12, O21)
|
EMpy/scattering.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import numpy
|
|
2
|
+
import EMpy
|
|
3
|
+
|
|
4
|
+
__author__ = "Julien Hillairet"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Field:
|
|
8
|
+
"""Class to describe an electromagnetic field.
|
|
9
|
+
|
|
10
|
+
A EM field is a vector field which is the combination of an electric field E and a magnetic field H.
|
|
11
|
+
These fields are defined on some space point r
|
|
12
|
+
|
|
13
|
+
@ivar E: Electric vector on point M{r} in the cartesian coordinate system M{(Ex,Ey,Ez)}
|
|
14
|
+
@type E: 2d numpy.ndarray of shape (3,N)
|
|
15
|
+
@ivar H: Magnetic vector on point M{r} in the cartesian coordinate system M{(Mx,My,Mz)}
|
|
16
|
+
@type H: 2d numpy.ndarray of shape (3,N)
|
|
17
|
+
@ivar r: Position vector in cartesian coordinates M{(x,y,z)}
|
|
18
|
+
@type r: 2d numpy.ndarray of shape (3,N)
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self, E=numpy.zeros((3, 1)), H=numpy.zeros((3, 1)), r=numpy.zeros((3, 1))
|
|
24
|
+
):
|
|
25
|
+
"""Initialize the Field object with an Electric, Magnetic and Position vectors."""
|
|
26
|
+
self.E = E
|
|
27
|
+
self.H = H
|
|
28
|
+
self.r = r
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
##############################################################
|
|
33
|
+
utils functions for field manipulations
|
|
34
|
+
##############################################################
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def stack(X, Y, Z):
|
|
39
|
+
"""Stack 3 vectors of different lengths into one vector.
|
|
40
|
+
|
|
41
|
+
stack 3 vectors of different length to one vector of type
|
|
42
|
+
M{P(n) = [x(n); y(n); z(n)]} with M{n=[1:Nx*Ny*Nz]}
|
|
43
|
+
|
|
44
|
+
Examples
|
|
45
|
+
========
|
|
46
|
+
>>> import numpy
|
|
47
|
+
>>> X = numpy.arange(4); Y = numpy.arange(3); Z = numpy.arange(2)
|
|
48
|
+
>>> S = field.stack(X,Y,Z)
|
|
49
|
+
>>> print S
|
|
50
|
+
[[ 0. 0. 0. 0. 0. 0. 1. 1. 1. 1. 1. 1. 2. 2. 2. 2. 2. 2. 3. 3. 3. 3. 3. 3.]
|
|
51
|
+
[ 0. 0. 1. 1. 2. 2. 0. 0. 1. 1. 2. 2. 0. 0. 1. 1. 2. 2. 0. 0. 1. 1. 2. 2.]
|
|
52
|
+
[ 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1. 0. 1.]]
|
|
53
|
+
|
|
54
|
+
Checking the size of the returned array
|
|
55
|
+
>>> numpy.size(S,axis=1) == numpy.size(X,axis=0)*numpy.size(Y,axis=0)*numpy.size(Z,axis=0)
|
|
56
|
+
True
|
|
57
|
+
|
|
58
|
+
@param X: first vector.of length Nx
|
|
59
|
+
@type X: numpy.ndarray of shape (1, Nx).
|
|
60
|
+
@param Y: second vector.of length Ny
|
|
61
|
+
@type Y: numpy.ndarray of shape (1, Ny).
|
|
62
|
+
@param Z: third vector.of length Nz
|
|
63
|
+
@type Z: numpy.ndarray of shape (1, Nz).
|
|
64
|
+
|
|
65
|
+
@return: stacked array.
|
|
66
|
+
@rtype: numpy.ndarray of shape (3, Nx*Ny*Nz)
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
Nx = numpy.size(X, axis=0)
|
|
70
|
+
Ny = numpy.size(Y, axis=0)
|
|
71
|
+
Nz = numpy.size(Z, axis=0)
|
|
72
|
+
|
|
73
|
+
XX = numpy.reshape(numpy.transpose(numpy.ones((Ny * Nz, 1)) * X), (1, Ny * Nz * Nx))
|
|
74
|
+
YY = numpy.tile(
|
|
75
|
+
numpy.reshape(numpy.transpose(numpy.ones((Nz, 1)) * Y), (1, Ny * Nz)), (1, Nx)
|
|
76
|
+
)
|
|
77
|
+
ZZ = numpy.tile(Z, (1, Nx * Ny))
|
|
78
|
+
|
|
79
|
+
return numpy.vstack((XX, YY, ZZ))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def matlab_dot(a, b):
|
|
83
|
+
"""
|
|
84
|
+
"classical" dot product (as defined in octave or matlab)
|
|
85
|
+
|
|
86
|
+
Example
|
|
87
|
+
=======
|
|
88
|
+
>>> a=numpy.array([1,2,3]).repeat(6).reshape((3,6))
|
|
89
|
+
>>> print a
|
|
90
|
+
[[1 1 1 1 1 1]
|
|
91
|
+
[2 2 2 2 2 2]
|
|
92
|
+
[3 3 3 3 3 3]]
|
|
93
|
+
>>> field.matlab_dot(a,a)
|
|
94
|
+
array([14, 14, 14, 14, 14, 14])
|
|
95
|
+
|
|
96
|
+
where M{14=1*1+2*2+3*3}
|
|
97
|
+
|
|
98
|
+
@param a: first vector of length N
|
|
99
|
+
@type a: numpy.ndarray of shape (M,N)
|
|
100
|
+
@param b: second vector of length N
|
|
101
|
+
@type b: numpy.ndarray of shape (M,N)
|
|
102
|
+
|
|
103
|
+
@return: "classical" dot product between M{a} and M{b}
|
|
104
|
+
@rtype: numpy.ndarray of shape (1,N)
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
return numpy.sum(a * b, axis=0)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
##########################################
|
|
112
|
+
Common functions for calculating EM fields
|
|
113
|
+
##########################################
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def currentsScatteringKottler(P, J, M, Q, dS, f, epsr=1):
|
|
118
|
+
"""
|
|
119
|
+
Compute the scattered fields on some point M{P} in cartesian coordinates M{P(Px,Py,Pz)}
|
|
120
|
+
by electric (and even magnetic) currents densities M{J}, M{M}
|
|
121
|
+
defined on a surface M{Q(Qx,Qy,Qz)}.
|
|
122
|
+
M{dS} corresponds to the surface element M{dS} of surface M{Q}
|
|
123
|
+
|
|
124
|
+
TODO:
|
|
125
|
+
- Maybe an option to compute only main expression in farfield
|
|
126
|
+
- Optimisation ? (At the present time, python version is slower than the matlab code)
|
|
127
|
+
|
|
128
|
+
@param P: observation points M{P(Px,Py,Pz)}.
|
|
129
|
+
@type P: numpy.ndarray of shape (3,NbP)
|
|
130
|
+
@param J: electric current density on M{Q}
|
|
131
|
+
@type J: numpy.ndarray of shape (3,NbQ)
|
|
132
|
+
@param M: magnetic current density on M{Q}
|
|
133
|
+
@type M: numpy.ndarray of shape (3,NbQ)
|
|
134
|
+
@param Q: radiating (surface) points M{Q(Qx,Qy,Qz)}.
|
|
135
|
+
@type Q: numpy.ndarray of shape (3,NbQ)
|
|
136
|
+
@param dS: surface elements of the radiating surface
|
|
137
|
+
@type dS: numpy.ndarray of shape (1,NbQ)
|
|
138
|
+
@param f: frequency
|
|
139
|
+
@type f: float
|
|
140
|
+
@param epsr: relative permittivity (dielectric constant) (defaut : 1, vacuum)
|
|
141
|
+
@type epsr: float
|
|
142
|
+
|
|
143
|
+
@return: EM Field on M{P}
|
|
144
|
+
@rtype: L{Field}
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
# test if the parameters size are correct
|
|
149
|
+
if (
|
|
150
|
+
numpy.size(P, axis=0) != 3
|
|
151
|
+
or numpy.size(Q, axis=0) != 3
|
|
152
|
+
or numpy.size(J, axis=0) != 3
|
|
153
|
+
or numpy.size(M, axis=0) != 3
|
|
154
|
+
):
|
|
155
|
+
EMpy.utils.warning(
|
|
156
|
+
"Bad parameters size : number of rows must be 3 for vectors P,Q,J,M"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if (
|
|
160
|
+
not numpy.size(Q, axis=1)
|
|
161
|
+
== numpy.size(J, axis=1)
|
|
162
|
+
== numpy.size(M, axis=1)
|
|
163
|
+
== numpy.size(dS, axis=1)
|
|
164
|
+
):
|
|
165
|
+
EMpy.utils.warning(
|
|
166
|
+
"Bad parameters size : number of columns between Q,J,M and dS must be equal"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
lambda0 = EMpy.constants.c / f
|
|
170
|
+
lambdam = lambda0 / numpy.sqrt(epsr)
|
|
171
|
+
# k0 = 2*numpy.pi/lambda0
|
|
172
|
+
km = 2 * numpy.pi / lambdam
|
|
173
|
+
Z0 = 120 * numpy.pi
|
|
174
|
+
NbP = numpy.size(P, axis=1)
|
|
175
|
+
NbQ = numpy.size(Q, axis=1)
|
|
176
|
+
|
|
177
|
+
# preallocation
|
|
178
|
+
EMF_P = Field(
|
|
179
|
+
numpy.zeros((3, NbP), dtype=complex), numpy.zeros((3, NbP), dtype=complex), P
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# for all observation point
|
|
183
|
+
# PB = EMpy.utils.ProgressBar()
|
|
184
|
+
for ind in numpy.arange(NbP):
|
|
185
|
+
# distance between scattering and observation point
|
|
186
|
+
QP = P[:, ind].reshape((3, 1)) * numpy.ones((1, NbQ)) - Q
|
|
187
|
+
r = numpy.sqrt(numpy.sum(QP**2, axis=0))
|
|
188
|
+
# unit vector
|
|
189
|
+
r1 = QP / r
|
|
190
|
+
# integrand expression shorcuts
|
|
191
|
+
kmr = km * r
|
|
192
|
+
kmr_1 = 1 / kmr
|
|
193
|
+
kmr_2 = kmr_1**2
|
|
194
|
+
aa = 1 - 1j * kmr_1 - kmr_2
|
|
195
|
+
bb = -1 + 3 * 1j * kmr_1 + 3 * kmr_2
|
|
196
|
+
cc = 1 - 1j * kmr_1
|
|
197
|
+
# scalar product
|
|
198
|
+
r1dotJ = matlab_dot(r1, J)
|
|
199
|
+
r1dotM = matlab_dot(r1, -M)
|
|
200
|
+
# Kottler EM fields
|
|
201
|
+
EMF_P.E[:, ind] = (
|
|
202
|
+
km
|
|
203
|
+
/ (4 * numpy.pi * 1j)
|
|
204
|
+
* numpy.sum(
|
|
205
|
+
(
|
|
206
|
+
Z0
|
|
207
|
+
/ numpy.sqrt(epsr)
|
|
208
|
+
* (aa * J + bb * (r1dotJ * numpy.ones((3, 1))) * r1)
|
|
209
|
+
+ cc * numpy.cross(r1, -M, axis=0)
|
|
210
|
+
)
|
|
211
|
+
* numpy.exp(-1j * km * r)
|
|
212
|
+
/ r
|
|
213
|
+
* dS,
|
|
214
|
+
axis=1,
|
|
215
|
+
)
|
|
216
|
+
)
|
|
217
|
+
EMF_P.H[:, ind] = (
|
|
218
|
+
-km
|
|
219
|
+
/ (4 * numpy.pi * 1j)
|
|
220
|
+
* numpy.sum(
|
|
221
|
+
(
|
|
222
|
+
numpy.sqrt(epsr)
|
|
223
|
+
/ Z0
|
|
224
|
+
* (-aa * M + bb * (r1dotM * numpy.ones((3, 1))) * r1)
|
|
225
|
+
- cc * numpy.cross(r1, J, axis=0)
|
|
226
|
+
)
|
|
227
|
+
* numpy.exp(-1j * km * r)
|
|
228
|
+
/ r
|
|
229
|
+
* dS,
|
|
230
|
+
axis=1,
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
# waitbar update
|
|
234
|
+
# PB.update((ind+1)*100.0/NbP)
|
|
235
|
+
|
|
236
|
+
return EMF_P
|