ElectroMagneticPython 2.2.1__py3-none-any.whl → 2.2.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.
Files changed (46) hide show
  1. EMpy/RCWA.py +32 -12
  2. EMpy/__init__.py +10 -2
  3. EMpy/_version.py +24 -0
  4. EMpy/constants.py +0 -2
  5. EMpy/devices.py +8 -32
  6. EMpy/materials.py +1 -8
  7. EMpy/modesolvers/FD.py +34 -76
  8. EMpy/modesolvers/FMM.py +24 -23
  9. EMpy/modesolvers/__init__.py +1 -0
  10. EMpy/modesolvers/geometries.py +8 -9
  11. EMpy/transfer_matrix.py +10 -6
  12. EMpy/utils.py +12 -26
  13. electromagneticpython-2.2.4.dist-info/METADATA +99 -0
  14. electromagneticpython-2.2.4.dist-info/RECORD +45 -0
  15. {ElectroMagneticPython-2.2.1.dist-info → electromagneticpython-2.2.4.dist-info}/WHEEL +1 -1
  16. electromagneticpython-2.2.4.dist-info/top_level.txt +4 -0
  17. examples/ex_APRR.py +18 -0
  18. examples/ex_NRR.py +40 -0
  19. examples/ex_RCWA.py +95 -0
  20. examples/ex_RCWA_2.py +52 -0
  21. examples/ex_RCWA_3.py +58 -0
  22. examples/ex_SRR.py +23 -0
  23. examples/ex_laser_etch_monitor.py +241 -0
  24. examples/ex_modesolver.py +68 -0
  25. examples/ex_modesolver_2.py +58 -0
  26. examples/ex_modesolver_3.py +48 -0
  27. examples/ex_modesolver_4.py +43 -0
  28. examples/ex_modesolver_dch.py +296 -0
  29. examples/ex_modesolver_dch_anis.py +318 -0
  30. examples/ex_transfer_matrix.py +82 -0
  31. examples/ex_transfer_matrix_2.py +44 -0
  32. examples/ex_transfer_matrix_2.py.png +0 -0
  33. examples/ex_transfer_matrix_3.py +60 -0
  34. examples/lc_data.dat +27 -0
  35. examples/nk.py +1468 -0
  36. scripts/FDTD.py +741 -0
  37. tests/devices_test.py +31 -0
  38. tests/materials_test.py +1 -1
  39. tests/modesolvers_test.py +36 -0
  40. tests/transfer_matrix_test.py +134 -0
  41. tests/utils_test.py +9 -5
  42. EMpy/version.py +0 -1
  43. ElectroMagneticPython-2.2.1.dist-info/METADATA +0 -109
  44. ElectroMagneticPython-2.2.1.dist-info/RECORD +0 -22
  45. ElectroMagneticPython-2.2.1.dist-info/top_level.txt +0 -2
  46. {ElectroMagneticPython-2.2.1.dist-info → electromagneticpython-2.2.4.dist-info/licenses}/LICENSE +0 -0
scripts/FDTD.py ADDED
@@ -0,0 +1,741 @@
1
+ """FDTD Postprocessing."""
2
+
3
+ import os
4
+
5
+ from matplotlib import pyplot as plt
6
+ import numpy
7
+
8
+ import EMpy.utils
9
+
10
+ __author__ = "Lorenzo Bolla"
11
+
12
+
13
+ class Input:
14
+ """Data structure to handle input files."""
15
+
16
+ def __init__(self, filename):
17
+ """Set the input filename."""
18
+ self.filename = filename
19
+
20
+ def __str__(self):
21
+ """Return a representation of the input file."""
22
+
23
+ dftmon_str = "%g ! #timemonitors \n" % len(self.dftmonitors)
24
+ if len(self.dftmonitors) > 0:
25
+ dftmon_str += "".join(
26
+ [
27
+ "%g %g %g %g %g %g\n%g %g\n"
28
+ % (
29
+ dm[0][0],
30
+ dm[0][1],
31
+ dm[0][2],
32
+ dm[0][3],
33
+ dm[0][4],
34
+ dm[0][5],
35
+ dm[1][0],
36
+ dm[1][1],
37
+ )
38
+ for dm in self.dftmonitors
39
+ ]
40
+ )
41
+
42
+ timemon_str = "%g ! #timemonitors \n" % len(self.dftmonitors)
43
+ if len(timemon_str) > 0:
44
+ timemon_str += "%g %g \n %s" % (
45
+ self.timemonitors_time_interval[0],
46
+ self.timemonitors_time_interval[1],
47
+ "".join(
48
+ [
49
+ "%g %g %g ! time_monitor #%d\n" % (s[0], s[1], s[2], iss)
50
+ for iss, s in enumerate(self.timemonitors)
51
+ ]
52
+ ),
53
+ )
54
+
55
+ return (
56
+ "%g %g %g %g ! dx dy dz cfl \n"
57
+ "%g %g %g %g %g %g %s %g %g ! xmax ymax zmax pmlx pmly pmlz pmltype pmlsmooth pmlref \n"
58
+ "%g %g %g %g ! xmax ymax zmax pmlx pmly pmlz \n"
59
+ "%g ! output3deps? \n"
60
+ "%g ! number diel slices \n"
61
+ "%s \n"
62
+ "%g ! number field slices \n"
63
+ "%s \n"
64
+ "%g %g %g ! #dielobjs, index of bg, conductivity of bg \n"
65
+ "%s"
66
+ "%g ! smoothing method \n"
67
+ "%g ! #sources \n"
68
+ "%s"
69
+ "%g %g %g ! lambdamin, lambdamax, dlambda \n"
70
+ "%s"
71
+ "%s"
72
+ % (
73
+ self.dx,
74
+ self.dy,
75
+ self.dz,
76
+ self.cfl,
77
+ self.xmax,
78
+ self.ymax,
79
+ self.zmax,
80
+ self.pmlx,
81
+ self.pmly,
82
+ self.pmlz,
83
+ self.pmltype,
84
+ self.pmlsmooth,
85
+ self.pmlref,
86
+ self.start,
87
+ self.end,
88
+ self.slides,
89
+ self.snapshot,
90
+ self.output3deps,
91
+ len(self.dielslices),
92
+ "\n".join(
93
+ [
94
+ "%g %g %g ! dielslice #%d" % (d[0], d[1], d[2], dd)
95
+ for (dd, d) in enumerate(self.dielslices)
96
+ ]
97
+ ),
98
+ len(self.fieldslices),
99
+ "\n".join(
100
+ [
101
+ "%g %g %g ! fieldslice #%d" % (f[0], f[1], f[2], ff)
102
+ for (ff, f) in enumerate(self.fieldslices)
103
+ ]
104
+ ),
105
+ len(self.dielobjs),
106
+ self.bgrix,
107
+ self.bgsigma,
108
+ "".join(["%s %s\n" % obj for obj in self.dielobjs]),
109
+ self.smoothing_method,
110
+ len(self.sources),
111
+ "".join(["%s\n%s\n%s\n%s\n" % src for src in self.sources]),
112
+ self.lambdamin,
113
+ self.lambdamax,
114
+ self.dlambda,
115
+ dftmon_str,
116
+ timemon_str,
117
+ )
118
+ )
119
+
120
+ def tofile(self, filename=None):
121
+ """Save the input data to the input file."""
122
+ if filename is None:
123
+ filename = self.filename
124
+ f = open(filename, "w")
125
+ f.write(self.__str__())
126
+ f.close()
127
+
128
+
129
+ class Param:
130
+ """Data structure to handle the param file."""
131
+
132
+ def __str__(self):
133
+ """Return a representation of the input file."""
134
+ return (
135
+ "%g\n"
136
+ "%g\n"
137
+ "%g\n"
138
+ "%g\n"
139
+ "%g\n"
140
+ "%g\n"
141
+ "%g\n"
142
+ "%g\n"
143
+ "%g\n"
144
+ "%g\n"
145
+ "%g\n"
146
+ "%g\n"
147
+ "%g\n"
148
+ "%g\n"
149
+ "%s"
150
+ % (
151
+ self.dx,
152
+ self.dy,
153
+ self.dz,
154
+ self.dt,
155
+ self.mx,
156
+ self.my,
157
+ self.mz,
158
+ self.pmlx,
159
+ self.pmly,
160
+ self.pmlz,
161
+ self.nflux,
162
+ self.ntime,
163
+ self.step1,
164
+ self.step2,
165
+ "\n".join(
166
+ [
167
+ "%d\n%d\n%d\n%d\n%d\n%d"
168
+ % (
169
+ dm["direction"],
170
+ dm["nfreq"],
171
+ dm["flxlim"][0],
172
+ dm["flxlim"][1],
173
+ dm["flxlim"][2],
174
+ dm["flxlim"][3],
175
+ )
176
+ for dm in self.dftmonitors
177
+ ]
178
+ ),
179
+ )
180
+ )
181
+
182
+
183
+ class Sensor:
184
+ """Data structure to handle the FFT sensor's data."""
185
+
186
+ def plot(self, n):
187
+ """Plot the sensor's fields."""
188
+ plt.clf()
189
+ plt.hot()
190
+ plt.subplot(2, 2, 1)
191
+ plt.contour(numpy.abs(self.E1[:, :, n].T), 16)
192
+ plt.axis("image")
193
+ plt.title("E1")
194
+ plt.subplot(2, 2, 2)
195
+ plt.contour(numpy.abs(self.H1[:, :, n].T), 16)
196
+ plt.axis("image")
197
+ plt.title("H1")
198
+ plt.subplot(2, 2, 3)
199
+ plt.contour(numpy.abs(self.E2[:, :, n].T), 16)
200
+ plt.axis("image")
201
+ plt.title("E2")
202
+ plt.subplot(2, 2, 4)
203
+ plt.contour(numpy.abs(self.H2[:, :, n].T), 16)
204
+ plt.axis("image")
205
+ plt.title("H2")
206
+ plt.show()
207
+
208
+ def __str__(self):
209
+ """Return a representation of the sensor."""
210
+ return "E1\n%s\nH1\n%s\nE2\n%s\nH2\n%s\n" % (self.E1, self.H1, self.E2, self.H2)
211
+
212
+
213
+ class TimeSensor:
214
+ """Data structure to handle the time sensor's data."""
215
+
216
+ def plot_Ex(self, logplot=False):
217
+ self.__plot_field(self.Ex, logplot)
218
+
219
+ def plot_Ey(self, logplot=False):
220
+ self.__plot_field(self.Ey, logplot)
221
+
222
+ def plot_Ez(self, logplot=False):
223
+ self.__plot_field(self.Ez, logplot)
224
+
225
+ def plot_Hx(self, logplot=False):
226
+ self.__plot_field(self.Hx, logplot)
227
+
228
+ def plot_Hy(self, logplot=False):
229
+ self.__plot_field(self.Hy, logplot)
230
+
231
+ def plot_Hz(self, logplot=False):
232
+ self.__plot_field(self.Hz, logplot)
233
+
234
+ def __plot_field(self, field, logplot=False):
235
+ if logplot:
236
+ data = 20 * numpy.log10(1e-20 + numpy.abs(field))
237
+ plt.plot(self.t, data)
238
+ else:
239
+ data = field
240
+ plt.plot(self.t, data)
241
+ plt.show()
242
+
243
+
244
+ class FDTD:
245
+ """FDTD.
246
+ Data structure to handle an FDTD simulation. It manages an input file, a param file and the sensors' output.
247
+ It can run a simulation via a system call.
248
+ """
249
+
250
+ def __init__(self):
251
+ self.input = None
252
+ self.param = None
253
+ self.sensors = None
254
+
255
+ def fetch_data(
256
+ self,
257
+ remote_dir_="./",
258
+ input_file="inp.txt",
259
+ param_file="param",
260
+ directory_="./",
261
+ ):
262
+ remote_dir = fixdir(remote_dir_)
263
+ directory = fixdir(directory_)
264
+ # input file
265
+ os.system(
266
+ "scp -C bollalo001@pico:" + remote_dir + "/" + input_file + " " + directory
267
+ )
268
+ # param file
269
+ os.system(
270
+ "scp -C bollalo001@pico:" + remote_dir + "/" + param_file + " " + directory
271
+ )
272
+ # fieldslices, flux and time sensors
273
+ os.system(
274
+ "scp -C bollalo001@pico:" + remote_dir + "/[EHeh]*_*" + " " + directory
275
+ )
276
+ # dielslices
277
+ os.system("scp -C bollalo001@pico:" + remote_dir + "/diel*" + " " + directory)
278
+
279
+ def put_data(self, remote_dir_="./", input_file="inp.txt", directory_="./"):
280
+ remote_dir = fixdir(remote_dir_)
281
+ directory = fixdir(directory_)
282
+ # input file
283
+ os.system("scp -C" + directory + input_file + " bollalo001@pico:" + remote_dir)
284
+ # .dat modesolver's files
285
+ os.system("scp -C" + directory + "*.dat bollalo001@pico:" + remote_dir)
286
+
287
+ def load(
288
+ self, directory_="./", input_file="inp.txt", param_file="param", remote_dir_=""
289
+ ):
290
+ """Load input, param and sensors."""
291
+ remote_dir = fixdir(remote_dir_)
292
+ directory = fixdir(directory_)
293
+ if remote_dir != "":
294
+ self.fetch_data(remote_dir, input_file, param_file, directory)
295
+ self.load_input_file(directory, input_file)
296
+ self.load_param(directory, param_file)
297
+ self.load_sensors(directory)
298
+ self.load_time_sensors(directory)
299
+
300
+ def load_input_file(self, directory_="./", filename="inp.txt"):
301
+ """Load input file."""
302
+ directory = fixdir(directory_)
303
+ try:
304
+ f = open(directory + filename)
305
+ except Exception:
306
+ print("ERROR: input file")
307
+ return
308
+ inp = Input(filename)
309
+
310
+ inp.dx, inp.dy, inp.dz, inp.cfl = numpy.fromstring(
311
+ strip_comment(f.readline()), sep=" "
312
+ )
313
+ tmp = strip_comment(f.readline())
314
+ tmp_idx = tmp.find("P")
315
+ if tmp_idx > 0:
316
+ inp.pmltype = "P"
317
+ else:
318
+ tmp_idx = tmp.find("G")
319
+ if tmp_idx > 0:
320
+ inp.pmltype = "G"
321
+ else:
322
+ raise ValueError("wrong pmltype")
323
+ inp.xmax, inp.ymax, inp.zmax, inp.pmlx, inp.pmly, inp.pmlz = numpy.fromstring(
324
+ tmp[:tmp_idx], sep=" "
325
+ )
326
+ inp.pmlsmooth, inp.pmlref = numpy.fromstring(tmp[tmp_idx + 1 :], sep=" ")
327
+ inp.start, inp.end, inp.slides, inp.snapshot = numpy.fromstring(
328
+ strip_comment(f.readline()), sep=" "
329
+ )
330
+ inp.output3deps = numpy.fromstring(strip_comment(f.readline()), sep=" ")
331
+
332
+ # dielslices
333
+ ndielslices = numpy.fromstring(strip_comment(f.readline()), sep=" ")
334
+ inp.dielslices = []
335
+ for i in range(ndielslices):
336
+ inp.dielslices.append(
337
+ numpy.fromstring(strip_comment(f.readline()), sep=" ")
338
+ )
339
+
340
+ # fieldslices
341
+ nfieldslices = numpy.fromstring(strip_comment(f.readline()), sep=" ")
342
+ inp.fieldslices = []
343
+ for i in range(nfieldslices):
344
+ inp.fieldslices.append(
345
+ numpy.fromstring(strip_comment(f.readline()), sep=" ")
346
+ )
347
+
348
+ # dielobjs
349
+ ndielobjs, inp.bgrix, inp.bgsigma = numpy.fromstring(
350
+ strip_comment(f.readline()), sep=" "
351
+ )
352
+ inp.dielobjs = []
353
+ for i in range(int(ndielobjs)):
354
+ inp.dielobjs.append(
355
+ (strip_comment(f.readline()), strip_comment(f.readline()))
356
+ )
357
+ inp.smoothing_method = numpy.fromstring(strip_comment(f.readline()), sep=" ")
358
+
359
+ # sources
360
+ nsources = numpy.fromstring(strip_comment(f.readline()), dtype=int, sep=" ")
361
+ inp.sources = []
362
+ # (inp.time_dependence, inp.wls, inp.pwidth, inp.shift) = numpy.fromstring(strip_comment(f.readline()), sep = ' ')
363
+ for i in range(nsources):
364
+ inp.sources.append(
365
+ (
366
+ strip_comment(f.readline()),
367
+ strip_comment(f.readline()),
368
+ strip_comment(f.readline()),
369
+ strip_comment(f.readline()),
370
+ )
371
+ )
372
+
373
+ # dft monitors
374
+ inp.lambdamin, inp.lambdamax, inp.dlambda = numpy.fromstring(
375
+ strip_comment(f.readline()), sep=" "
376
+ )
377
+ ndftmonitors = numpy.fromstring(strip_comment(f.readline()), dtype=int, sep=" ")
378
+ inp.dftmonitors = []
379
+ for i in range(ndftmonitors):
380
+ inp.dftmonitors.append(
381
+ (
382
+ numpy.fromstring(strip_comment(f.readline()), sep=" "),
383
+ numpy.fromstring(strip_comment(f.readline()), sep=" "),
384
+ )
385
+ )
386
+
387
+ # time monitors
388
+ ntimemonitors = numpy.fromstring(strip_comment(f.readline()), sep=" ")
389
+ inp.timemonitors_time_interval = numpy.fromstring(
390
+ strip_comment(f.readline()), sep=" "
391
+ )
392
+ inp.timemonitors = []
393
+ for i in range(ntimemonitors):
394
+ inp.timemonitors.append(
395
+ numpy.fromstring(strip_comment(f.readline()), sep=" ")
396
+ )
397
+
398
+ f.close()
399
+ self.input = inp
400
+
401
+ def load_param(self, directory_="./", filename="param"):
402
+ """Load param file."""
403
+ directory = fixdir(directory_)
404
+ param = Param()
405
+ try:
406
+ data = numpy.fromfile(directory + filename, sep=" ")
407
+ except Exception:
408
+ print("ERROR: param file")
409
+ return
410
+ param.dx, param.dy, param.dz, param.dt = data[0:4]
411
+ (
412
+ param.mx,
413
+ param.my,
414
+ param.mz,
415
+ param.pmlx,
416
+ param.pmly,
417
+ param.pmlz,
418
+ param.nflux,
419
+ param.ntime,
420
+ param.step1,
421
+ param.step2,
422
+ ) = data[4:14].astype(numpy.int32)
423
+ param.dftmonitors = []
424
+ for iflux in range(int(param.nflux)):
425
+ direction, nfreq = data[14 + iflux * 6 : 16 + iflux * 6]
426
+ flxlim = data[16 + iflux * 6 : 20 + iflux * 6]
427
+ param.dftmonitors.append(
428
+ {"direction": int(direction), "nfreq": int(nfreq), "flxlim": flxlim}
429
+ )
430
+ self.param = param
431
+
432
+ def load_time_sensors(self, directory_="./"):
433
+ """Load time sensors."""
434
+ directory = fixdir(directory_)
435
+ time_sensors = []
436
+ if self.param is None:
437
+ self.load_param(directory)
438
+ for itime in range(self.param.ntime):
439
+ tmp = TimeSensor()
440
+ tmp.Ex = load_fortran_unformatted(directory + "Ex_time_%02d" % (itime + 1))
441
+ tmp.Ey = load_fortran_unformatted(directory + "Ey_time_%02d" % (itime + 1))
442
+ tmp.Ez = load_fortran_unformatted(directory + "Ez_time_%02d" % (itime + 1))
443
+ tmp.Hx = load_fortran_unformatted(directory + "Hx_time_%02d" % (itime + 1))
444
+ tmp.Hy = load_fortran_unformatted(directory + "Hy_time_%02d" % (itime + 1))
445
+ tmp.Hz = load_fortran_unformatted(directory + "Hz_time_%02d" % (itime + 1))
446
+ tmp.t = self.param.dt * numpy.arange(len(tmp.Ex))
447
+ time_sensors.append(tmp)
448
+
449
+ self.time_sensors = time_sensors
450
+
451
+ def load_sensors(self, directory_="./"):
452
+ """Load sensors."""
453
+ directory = fixdir(directory_)
454
+ sensors = []
455
+ if self.param is None:
456
+ self.load_param(directory)
457
+ for iflux in range(self.param.nflux):
458
+ tmp = Sensor()
459
+ dm = self.param.dftmonitors[iflux]
460
+ tmp.E1 = load_fortran_unformatted(directory + "E1_%02d" % (iflux + 1))
461
+ tmp.H1 = load_fortran_unformatted(directory + "H1_%02d" % (iflux + 1))
462
+ tmp.E2 = load_fortran_unformatted(directory + "E2_%02d" % (iflux + 1))
463
+ tmp.H2 = load_fortran_unformatted(directory + "H2_%02d" % (iflux + 1))
464
+ # [tmp.E1, tmp.H1, tmp.E2, tmp.H2] = map(lambda x: x[0::2] + 1j * x[1::2], [tmp.E1, tmp.H1, tmp.E2, tmp.H2])
465
+ # more memory efficient!
466
+ tmp.E1 = tmp.E1[0::2] + 1j * tmp.E1[1::2]
467
+ tmp.H1 = tmp.H1[0::2] + 1j * tmp.H1[1::2]
468
+ tmp.E2 = tmp.E2[0::2] + 1j * tmp.E2[1::2]
469
+ tmp.H2 = tmp.H2[0::2] + 1j * tmp.H2[1::2]
470
+
471
+ n1 = dm["flxlim"][1] - dm["flxlim"][0] + 1
472
+ n2 = dm["flxlim"][3] - dm["flxlim"][2] + 1
473
+ tmp.E1 = tmp.E1.reshape((n1, n2 + 1, dm["nfreq"]), order="F")
474
+ tmp.H1 = tmp.H1.reshape((n1, n2 + 1, dm["nfreq"]), order="F")
475
+ tmp.E2 = tmp.E2.reshape((n1 + 1, n2, dm["nfreq"]), order="F")
476
+ tmp.H2 = tmp.H2.reshape((n1 + 1, n2, dm["nfreq"]), order="F")
477
+ if dm["direction"] == 1:
478
+ # sensors in the x-direction
479
+ tmp.dx1 = self.param.dy
480
+ tmp.dx2 = self.param.dz
481
+ elif dm["direction"] == 2:
482
+ # sensors in the y-direction
483
+ tmp.dx1 = self.param.dx
484
+ tmp.dx2 = self.param.dz
485
+ elif dm["direction"] == 3:
486
+ # sensors in the z-direction
487
+ tmp.dx1 = self.param.dx
488
+ tmp.dx2 = self.param.dy
489
+ else:
490
+ raise ValueError("wrong direction")
491
+
492
+ sensors.append(tmp)
493
+
494
+ self.sensors = sensors
495
+
496
+ def viz2D(self, filename, directory_="./", const_dir="z", logplot=False):
497
+ """Visualize a slice."""
498
+ directory = fixdir(directory_)
499
+ data = load_fortran_unformatted(directory + filename)
500
+ if self.param is None:
501
+ self.load_param(directory)
502
+ x = numpy.linspace(
503
+ self.param.dx / 2.0,
504
+ self.param.dx * self.param.mx - self.param.dx / 2.0,
505
+ self.param.mx,
506
+ )
507
+ y = numpy.linspace(
508
+ self.param.dy / 2.0,
509
+ self.param.dy * self.param.my - self.param.dy / 2.0,
510
+ self.param.my,
511
+ )
512
+ z = numpy.linspace(
513
+ self.param.dz / 2.0,
514
+ self.param.dz * self.param.mz - self.param.dz / 2.0,
515
+ self.param.mz,
516
+ )
517
+ if const_dir == "x":
518
+ n1 = self.param.my
519
+ n2 = self.param.mz
520
+ x1 = y
521
+ x2 = z
522
+ x1label = "y"
523
+ x2label = "z"
524
+ elif const_dir == "y":
525
+ n1 = self.param.mx
526
+ n2 = self.param.mz
527
+ x1 = x
528
+ x2 = z
529
+ x1label = "x"
530
+ x2label = "z"
531
+ else:
532
+ n1 = self.param.mx
533
+ n2 = self.param.my
534
+ x1 = x
535
+ x2 = y
536
+ x1label = "x"
537
+ x2label = "y"
538
+ data = data.reshape((n2, n1))
539
+ plt.clf()
540
+ if logplot:
541
+ data = 20 * numpy.log10(numpy.abs(data).clip(1e-30, 1e30))
542
+ plt.jet()
543
+ else:
544
+ plt.hot()
545
+ plt.contour(x1, x2, data, 64)
546
+ plt.colorbar()
547
+ plt.axis("image")
548
+ plt.xlabel(x1label + " /um")
549
+ plt.ylabel(x2label + " /um")
550
+ plt.show()
551
+
552
+ def memory(self):
553
+ """Estimate the memory occupation."""
554
+ # size_of_char = 1
555
+ # size_of_int = 4
556
+ size_of_real = 4
557
+ # size_of_complex = 2 * size_of_real
558
+ # size_of_dielobj = size_of_int + 31 * size_of_real + 2 * 16 * size_of_char
559
+ # size_of_source = 9 * size_of_int + 5 * size_of_real + 6 * 16 * size_of_char
560
+ # size_of_monitor = (6 + 2) * 6 * size_of_int
561
+
562
+ Gb = 1024**3
563
+ max_available_RAM = 32 * Gb
564
+
565
+ dynamic_alloc_memory = 0
566
+
567
+ # epsx, epsy, epsz
568
+ dynamic_alloc_memory = (
569
+ dynamic_alloc_memory
570
+ + 3
571
+ * (self.param.mx + 2 * self.input.pmlx)
572
+ * (self.param.my + 2 * self.input.pmly)
573
+ * (self.param.mz + 2 * self.input.pmlz)
574
+ * size_of_real
575
+ )
576
+ # sigma
577
+ dynamic_alloc_memory = (
578
+ dynamic_alloc_memory
579
+ + 2
580
+ * (self.param.mx + 2 * self.input.pmlx)
581
+ * (self.param.my + 2 * self.input.pmly)
582
+ * (self.param.mz + 2 * self.input.pmlz)
583
+ * size_of_real
584
+ )
585
+ # cex, cmx
586
+ dynamic_alloc_memory = (
587
+ dynamic_alloc_memory
588
+ + 2 * 2 * (self.param.mx + 2 * self.input.pmlx) * size_of_real
589
+ )
590
+ # cey, cmy
591
+ dynamic_alloc_memory = (
592
+ dynamic_alloc_memory
593
+ + 2 * 2 * (self.param.my + 2 * self.input.pmly) * size_of_real
594
+ )
595
+ # cez, cmz
596
+ dynamic_alloc_memory = (
597
+ dynamic_alloc_memory
598
+ + 2 * 2 * (self.param.mz + 2 * self.input.pmlz) * size_of_real
599
+ )
600
+
601
+ # exy, exz, eyx, eyz, ...
602
+ dynamic_alloc_memory = (
603
+ dynamic_alloc_memory
604
+ + 12
605
+ * (self.param.mx + 2 * self.input.pmlx)
606
+ * (self.param.my + 2 * self.input.pmly)
607
+ * (self.param.mz + 2 * self.input.pmlz)
608
+ * size_of_real
609
+ )
610
+
611
+ print(
612
+ "Alloc mem = %g Gb, [%d%%]"
613
+ % (
614
+ 1.0 * dynamic_alloc_memory / Gb,
615
+ int(1.0 * dynamic_alloc_memory / max_available_RAM * 100),
616
+ )
617
+ )
618
+
619
+ def run(
620
+ self,
621
+ directory_="./",
622
+ exe_file="/xlv1/labsoi_devices/devices/f3d",
623
+ output_file="output",
624
+ ncpu=12,
625
+ bg=False,
626
+ remote=True,
627
+ ):
628
+ """Run the simulation, possibly in remote."""
629
+ directory = fixdir(directory_)
630
+ # os.environ['OMP_NUM_THREAD'] = str(ncpu)
631
+ # cmd = 'dplace -x6 ' + exe_file + ' > ' + output_file
632
+ cmd = (
633
+ "cd"
634
+ + directory
635
+ + "; setenv OMP_NUM_THREAD"
636
+ + str(ncpu)
637
+ + "dplace -x6 "
638
+ + exe_file
639
+ + " > "
640
+ + output_file
641
+ )
642
+ if bg:
643
+ cmd += "&"
644
+ if remote:
645
+ cmd = 'ssh pico "' + cmd + '"'
646
+ os.system(cmd)
647
+
648
+ def __str__(self):
649
+ """Return a representation of the FDTD data structure."""
650
+ return "INPUT\n%s\nPARAM\n%s\nSENSORS\n%s\n" % (
651
+ self.input,
652
+ self.param,
653
+ self.sensors,
654
+ )
655
+
656
+
657
+ def load_fortran_unformatted(filename):
658
+ """Load data from an unformatted fortran binary file."""
659
+ try:
660
+ f = open(filename, "rb")
661
+ except Exception:
662
+ print("ERROR")
663
+ return
664
+ nbytes = numpy.fromfile(f, dtype=numpy.int32, count=1)
665
+ n = nbytes / numpy.float32().nbytes
666
+ data = numpy.fromfile(f, dtype=numpy.float32, count=n)
667
+ f.close()
668
+ return data
669
+
670
+
671
+ def strip_comment(line):
672
+ """Get rid of fortran comments."""
673
+ idx = line.find("!")
674
+ if idx != -1:
675
+ return line[:idx].strip()
676
+ return line
677
+
678
+
679
+ def fixdir(str, sep="/"):
680
+ tmp = str
681
+ if len(tmp) > 0:
682
+ if tmp[-1] != sep:
683
+ tmp += sep
684
+ return tmp
685
+
686
+
687
+ # def overlap_f(simul, solver, nwl):
688
+ # vu = numpy.zeros((len(simul.sensors), len(solver.modes)), dtype=complex)
689
+ # ju = numpy.zeros((len(simul.sensors), len(solver.modes)), dtype=complex)
690
+ # for isens, sens in enumerate(simul.sensors):
691
+ # for imode, mode in enumerate(solver.modes):
692
+ # Ex, Ey, Ez, Hx, Hy, Hz = mode.get_fields_for_FDTD()
693
+ # vu[isens, imode] = 0.5 * (
694
+ # numpy.trapz(numpy.trapz(sens.E1[:,1:-1,nwl] * Hy, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6) -
695
+ # numpy.trapz(numpy.trapz(sens.E2[1:-1,:,nwl] * Hx, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6))
696
+ # ju[isens, imode] = 0.5 * (
697
+ # numpy.trapz(numpy.trapz(sens.H2[1:-1,:,nwl] * Ey, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6) -
698
+ # numpy.trapz(numpy.trapz(sens.H1[:,1:-1,nwl] * Ex, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6))
699
+ # A = (vu + ju) / 2.
700
+ # B = (vu - ju) / 2.
701
+ # Pm = numpy.abs(A)**2 - numpy.abs(B)**2
702
+ # P = Pm.sum(axis=1)
703
+ # return (vu, ju, A, B, Pm, P)
704
+
705
+
706
+ def overlap_f(sensors, solver, nwl):
707
+ vu = numpy.zeros((len(sensors), len(solver.modes)), dtype=complex)
708
+ ju = numpy.zeros((len(sensors), len(solver.modes)), dtype=complex)
709
+ for isens, sens in enumerate(sensors):
710
+ x = sens.dx1 * numpy.arange(sens.E2.shape[0])
711
+ y = sens.dx2 * numpy.arange(sens.E1.shape[1])
712
+ for imode, mode in enumerate(solver.modes):
713
+ # resample the mode to the sensor's grid
714
+ Ex, Ey, Ez, Hx, Hy, Hz = mode.get_fields_for_FDTD(x, y)
715
+ # vu[isens, imode] = 0.5 * (
716
+ # numpy.trapz(numpy.trapz(sens.E1[:,1:-1,nwl] * Hy, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6) -
717
+ # numpy.trapz(numpy.trapz(sens.E2[1:-1,:,nwl] * Hx, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6))
718
+ # ju[isens, imode] = 0.5 * (
719
+ # numpy.trapz(numpy.trapz(sens.H2[1:-1,:,nwl] * Ey, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6) -
720
+ # numpy.trapz(numpy.trapz(sens.H1[:,1:-1,nwl] * Ex, dx=sens.dx2*1e-6), dx=sens.dx1*1e-6))
721
+ vu[isens, imode] = 0.5 * (
722
+ EMpy.utils.trapz2(
723
+ sens.E1[:, 1:-1, nwl] * Hy, dx=sens.dx1 * 1e-6, dy=sens.dx2 * 1e-6
724
+ )
725
+ - EMpy.utils.trapz2(
726
+ sens.E2[1:-1, :, nwl] * Hx, dx=sens.dx1 * 1e-6, dy=sens.dx2 * 1e-6
727
+ )
728
+ )
729
+ ju[isens, imode] = 0.5 * (
730
+ EMpy.utils.trapz2(
731
+ sens.H2[1:-1, :, nwl] * Ey, dx=sens.dx1 * 1e-6, dy=sens.dx1 * 1e-6
732
+ )
733
+ - EMpy.utils.trapz2(
734
+ sens.H1[:, 1:-1, nwl] * Ex, dx=sens.dx1 * 1e-6, dy=sens.dx1 * 1e-6
735
+ )
736
+ )
737
+ A = (vu + ju) / 2.0
738
+ B = (vu - ju) / 2.0
739
+ Pm = numpy.abs(A) ** 2 - numpy.abs(B) ** 2
740
+ P = Pm.sum(axis=1)
741
+ return (vu, ju, A, B, Pm, P)