-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Squashed commit of several new features and enhancements from Lumeric…
…al, this commit corresponds to the version shipped with FDTD Solutions 2019A R5: - Base script can now be a python callable or project file - Users can now disable multi-frequency source injection - Small fixes and enhancements
- Loading branch information
1 parent
93094d4
commit 6532eef
Showing
21 changed files
with
888 additions
and
46 deletions.
There are no files selected for viewing
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
################ | ||
|
||
deleteall; | ||
addfdtd; | ||
|
||
addpoly; | ||
|
||
########### | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
""" Copyright chriskeraly | ||
Copyright (c) 2019 Lumerical Inc. """ | ||
|
||
import sys | ||
sys.path.append(".") | ||
import os | ||
|
||
from qatools import * | ||
|
||
from lumopt.utilities.simulation import Simulation | ||
from lumopt.utilities.base_script import BaseScript | ||
|
||
class TestBaseScript(TestCase): | ||
""" | ||
Unit test for BaseScript class. It verifies that the object is able to run an *.lsf script, a *.fsp project file or a plain script in a string. | ||
""" | ||
|
||
file_dir = os.path.abspath(os.path.dirname(__file__)) | ||
|
||
def setUp(self): | ||
self.sim = Simulation(workingDir = self.file_dir, hide_fdtd_cad = True) | ||
|
||
def test_eval_project_file(self): | ||
my_project_file = os.path.join(self.file_dir,'base_script_test.fsp') | ||
base_script_obj = BaseScript(my_project_file) | ||
base_script_obj(self.sim.fdtd) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('FDTD') == 1) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('source') == 1) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('polygon') == 1) | ||
|
||
def test_eval_python_script(self): | ||
my_fun = lambda fdtd_handle: fdtd_handle.addfdtd() | ||
base_script_obj = BaseScript(my_fun) | ||
base_script_obj.eval(self.sim.fdtd) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('FDTD') == 1) | ||
|
||
def test_eval_script_file(self): | ||
my_script_file = os.path.join(self.file_dir,'base_script_test.lsf') | ||
base_script_obj = BaseScript(my_script_file) | ||
base_script_obj.eval(self.sim.fdtd) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('FDTD') == 1) | ||
|
||
def test_eval_script(self): | ||
my_script = "load('base_script_test.fsp');" | ||
base_script_obj = BaseScript(my_script) | ||
base_script_obj.eval(self.sim.fdtd) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('FDTD') == 1) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('source') == 1) | ||
self.assertTrue(self.sim.fdtd.getnamednumber('polygon') == 1) | ||
|
||
if __name__ == "__main__": | ||
run([__file__]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
deleteall; | ||
clear; | ||
|
||
## FDTD | ||
mesh_dx = 10.0e-9; | ||
mesh_dy = mesh_dz = mesh_dx; | ||
fdtd_span_x = 8.0*mesh_dx; | ||
fdtd_span_y = 4.0*mesh_dy; | ||
fdtd_span_z = 4.0*mesh_dz; | ||
background_index = 1.0; | ||
pml_layers = 12; | ||
sim_time = 5.0e-12; | ||
|
||
## GEOMETRY | ||
wg_width = 1.0*fdtd_span_y; | ||
wg_length = 2.0*fdtd_span_x; | ||
wg_height = wg_width; | ||
wg_material = '<Object defined dielectric>'; | ||
wg_ref_index = 4.0; | ||
|
||
#FILLING | ||
addrect; | ||
set('name','filling'); | ||
set('x span',wg_length); | ||
set('y span',wg_width); | ||
set('z span',wg_height); | ||
set('y',0.0); | ||
set('x',wg_length/2.0); | ||
set('index',wg_ref_index); | ||
set('material',wg_material); | ||
|
||
## SOURCE | ||
addmode; | ||
set('direction','Forward'); | ||
set('injection axis','x-axis'); | ||
set('y',0.0); | ||
set('y span',fdtd_span_y); | ||
set('z',0.0); | ||
set('z span',fdtd_span_y); | ||
set('x',-3.0*mesh_dx); | ||
set('override global source settings',false); | ||
set('mode selection','fundamental TM mode'); | ||
|
||
## FDTD | ||
addfdtd; | ||
set('simulation time',sim_time); | ||
set('background index',background_index); | ||
set('pml layers',pml_layers); | ||
set('mesh type','uniform'); | ||
set('dx',mesh_dx); | ||
set('dy',mesh_dx); | ||
set('dz',mesh_dx); | ||
set('x',0.0); | ||
set('x span',fdtd_span_x); | ||
set('y',0.0); | ||
set('y span',fdtd_span_y); | ||
set('z',0.0); | ||
set('z span',fdtd_span_z); | ||
set('force symmetric y mesh',true); | ||
set('force symmetric x mesh',true); | ||
set('force symmetric z mesh',true); | ||
set('y min bc','PMC'); | ||
set('y max bc','PMC'); | ||
set('z min bc','Metal'); | ||
set('z max bc','Metal'); | ||
#set('dimension','2D'); | ||
|
||
## FOM FIELDS | ||
addpower; | ||
set('monitor type','2D X-normal'); | ||
set('name','figure_of_merit'); | ||
set('x',2.0*mesh_dx); | ||
set('y',0.0); | ||
set('y span',fdtd_span_y); | ||
set('z',0.0); | ||
set('z span',fdtd_span_z); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
""" Copyright chriskeraly | ||
Copyright (c) 2019 Lumerical Inc. """ | ||
|
||
import sys | ||
sys.path.append(".") | ||
import os | ||
import numpy as np | ||
|
||
from qatools import * | ||
|
||
from lumopt.utilities.load_lumerical_scripts import load_from_lsf | ||
from lumopt.utilities.wavelengths import Wavelengths | ||
from lumopt.utilities.simulation import Simulation | ||
from lumopt.figures_of_merit.modematch import ModeMatch | ||
from lumopt.optimization import Optimization | ||
|
||
class TestModeMatchParallelPlateWaveguideTM(TestCase): | ||
""" | ||
Unit test for the ModeMatch class: it performs a quick check that the figure of merit is computed correctly | ||
using a simple a parallel plate waveguide partially filled by a dielectric. The waveguide has a material interface | ||
in the middle, and the figure of merit should be the same regardless of the material in which the source is placed. | ||
This is used to verify that the ModeMatch inputs monitor_name, direction and mode number work correctly. | ||
""" | ||
|
||
file_dir = os.path.abspath(os.path.dirname(__file__)) | ||
|
||
def setUp(self): | ||
# base script | ||
self.base_script = load_from_lsf(os.path.join(self.file_dir, 'modematch_parallel_plate_waveguide_TM_base.lsf')) | ||
# bandwidth | ||
self.wavelengths = Wavelengths(start = 1540e-9, stop = 1560e-9, points = 3) | ||
# simulation | ||
self.sim = Simulation(workingDir = self.file_dir, hide_fdtd_cad = True) | ||
self.sim.fdtd.eval(self.base_script) | ||
Optimization.set_global_wavelength(self.sim, self.wavelengths) | ||
# reference | ||
self.ref_fom = 0.6643986 | ||
|
||
def test_forward_injection_in_3D(self): | ||
""" Test forward injection in 3D with mode source in vacuum. """ | ||
self.fom = ModeMatch(monitor_name = 'figure_of_merit', | ||
mode_number = 1, | ||
direction = 'Forward', | ||
multi_freq_src = True, | ||
target_T_fwd = lambda wl: np.ones(wl.size), | ||
norm_p = 1) | ||
Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) | ||
self.sim.fdtd.setnamed('FDTD','dimension','3D') | ||
self.fom.add_to_sim(self.sim) | ||
self.sim.run(name = 'modematch_forward_injection_in_3D', iter = 0) | ||
FOM = self.fom.get_fom(self.sim) | ||
self.assertAlmostEqual(FOM, self.ref_fom, 5) | ||
|
||
def test_backward_injection_in_3D(self): | ||
""" Test backward injection in 3D with mode source in dielectric region. """ | ||
self.fom = ModeMatch(monitor_name = 'figure_of_merit', | ||
mode_number = 1, | ||
direction = 'Backward', | ||
multi_freq_src = True, | ||
target_T_fwd = lambda wl: np.ones(wl.size), | ||
norm_p = 1) | ||
Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) | ||
self.sim.fdtd.setnamed('FDTD','dimension','3D') | ||
self.sim.fdtd.setnamed('source', 'x', -self.sim.fdtd.getnamed('source','x')) | ||
self.sim.fdtd.setnamed('source','direction','Backward') | ||
self.sim.fdtd.setnamed('figure_of_merit','x', -self.sim.fdtd.getnamed('figure_of_merit','x')) | ||
self.fom.add_to_sim(self.sim) | ||
self.sim.run(name = 'modematch_backward_injection_in_3D', iter = 1) | ||
FOM = self.fom.get_fom(self.sim) | ||
self.assertAlmostEqual(FOM, self.ref_fom, 5) | ||
|
||
def test_forward_injection_in_2D(self): | ||
""" Test forward injection in 2D with mode source in vacuum. """ | ||
self.fom = ModeMatch(monitor_name = 'figure_of_merit', | ||
mode_number = 1, | ||
direction = 'Forward', | ||
multi_freq_src = True, | ||
target_T_fwd = lambda wl: np.ones(wl.size), | ||
norm_p = 1) | ||
Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) | ||
self.sim.fdtd.setnamed('FDTD','dimension','2D') | ||
self.fom.add_to_sim(self.sim) | ||
self.sim.run(name = 'modematch_forward_injection_in_2D', iter = 2) | ||
FOM = self.fom.get_fom(self.sim) | ||
self.assertAlmostEqual(FOM, self.ref_fom, 5) | ||
|
||
def test_no_forward_injection_in_2D(self): | ||
""" Test no forward injection in 2D with mode source in vacuum. """ | ||
self.fom = ModeMatch(monitor_name = 'figure_of_merit', | ||
mode_number = 2, # evanescent mode | ||
direction = 'Forward', | ||
multi_freq_src = False, | ||
target_T_fwd = lambda wl: np.ones(wl.size), | ||
norm_p = 1) | ||
Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) | ||
self.sim.fdtd.setnamed('FDTD','dimension','2D') | ||
self.fom.add_to_sim(self.sim) | ||
self.sim.run(name = 'modematch_no_forward_injection_in_2D', iter = 3) | ||
FOM = self.fom.get_fom(self.sim) | ||
self.assertAlmostEqual(FOM, 0.0, 5) | ||
|
||
def test_backward_injection_in_2D(self): | ||
""" Test backward injection in 2D with mode source in dielectric region. """ | ||
self.fom = ModeMatch(monitor_name = 'figure_of_merit', | ||
mode_number = 1, | ||
direction = 'Backward', | ||
multi_freq_src = True, | ||
target_T_fwd = lambda wl: np.ones(wl.size), | ||
norm_p = 1) | ||
Optimization.set_source_wavelength(self.sim, 'source', self.fom.multi_freq_src, len(self.wavelengths)) | ||
self.sim.fdtd.setnamed('FDTD','dimension','2D') | ||
self.sim.fdtd.setnamed('source', 'x', -self.sim.fdtd.getnamed('source','x')) | ||
self.sim.fdtd.setnamed('source','direction','Backward') | ||
self.sim.fdtd.setnamed('figure_of_merit','x', -self.sim.fdtd.getnamed('figure_of_merit','x')) | ||
self.fom.add_to_sim(self.sim) | ||
self.sim.run(name = 'modematch_backward_injection_in_2D', iter = 4) | ||
FOM = self.fom.get_fom(self.sim) | ||
self.assertAlmostEqual(FOM, self.ref_fom, 5) | ||
|
||
if __name__ == "__main__": | ||
run([__file__]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
|
||
""" Copyright chriskeraly | ||
Copyright (c) 2019 Lumerical Inc. """ | ||
|
||
import sys | ||
sys.path.append(".") | ||
import os | ||
import numpy as np | ||
|
||
from qatools import * | ||
|
||
from lumopt.figures_of_merit.modematch import ModeMatch | ||
|
||
class ModeMatchWavelengthIntegrationTest(TestCase): | ||
""" Unit test for class ModeMatch. Checks that the integrals of the figure of merit and its gradient with respect to wavelength are working correctly. """ | ||
|
||
def test_single_wavelength_fom_integral(self): | ||
""" Test FOM integral for single wavelength case: result should be input FOM for backward compatibility. """ | ||
exact_fom = 0.4896 | ||
fom = ModeMatch.fom_wavelength_integral(T_fwd_vs_wavelength = np.array([exact_fom]), | ||
wavelengths = np.array([1300e-9]), | ||
target_T_fwd = lambda wl: 0.5 * np.ones(wl.size), | ||
norm_p = 1) # unused | ||
self.assertAlmostEqual(fom, exact_fom, 15) | ||
|
||
def test_fom_integral_norm_p1(self): | ||
""" Test FOM integral with norm p = 1. """ | ||
wl_points = 5 | ||
fom = ModeMatch.fom_wavelength_integral(T_fwd_vs_wavelength = np.ones(wl_points), | ||
wavelengths = np.linspace(1300e-9, 1800e-9, wl_points), | ||
target_T_fwd = lambda wl: np.power(np.sin(np.pi * (wl - wl.min()) / (wl.max() - wl.min())), 2), | ||
norm_p = 1) | ||
exact_fom = 0.0 | ||
self.assertAlmostEqual(fom, exact_fom, 15) | ||
|
||
def test_fom_integral_norm_p2(self): | ||
""" Test FOM integral with norm p = 2. """ | ||
wl_points = 5000 | ||
fom = ModeMatch.fom_wavelength_integral(T_fwd_vs_wavelength = 0.5 * np.ones(wl_points), | ||
wavelengths = np.linspace(1.0e-9, 1.0e-8, wl_points), | ||
target_T_fwd = lambda wl: np.exp(-1.0 * (wl - wl.min()) / (wl.max() - wl.min())), | ||
norm_p = 2) | ||
exact_fom = 0.5 * np.exp(-1) * (np.sqrt(2.0 * (np.exp(2.0) - 1.0)) - np.sqrt(4.0 * np.exp(1.0) - np.exp(2.0) - 2.0)) | ||
self.assertAlmostEqual(fom, exact_fom, 7) | ||
|
||
def test_single_wavelength_gradient_integral(self): | ||
""" Test FOM gradient integral for single wavelength case. """ | ||
fom_grad = ModeMatch.fom_gradient_wavelength_integral_impl(T_fwd_vs_wavelength = np.array([0.2851]), | ||
T_fwd_partial_derivs_vs_wl = np.array([2.0]), | ||
target_T_fwd_vs_wavelength = np.array([1.0]), | ||
wl = np.array([1800e-9]), | ||
norm_p = 1) | ||
exact_fom_grad = -1.0 * np.sign(0.2851 - 1.0) * 2.0 | ||
self.assertAlmostEqual(fom_grad[0], exact_fom_grad, 15) | ||
|
||
def test_fom_gradient_integral_p1(self): | ||
""" Test FOM gradient integral with norm p = 1. """ | ||
wl_points = 3 | ||
wavelengths = np.linspace(1300e-9, 1800e-9, wl_points) | ||
target_T_fwd = lambda wl: np.linspace(0.0, 1.0, wl.size) | ||
fom_grad = ModeMatch.fom_gradient_wavelength_integral_impl(T_fwd_vs_wavelength = 0.25 * np.ones(wl_points), | ||
T_fwd_partial_derivs_vs_wl = np.ones((wl_points,1)), | ||
target_T_fwd_vs_wavelength = target_T_fwd(wavelengths), | ||
wl = wavelengths, | ||
norm_p = 1) | ||
self.assertAlmostEqual(fom_grad[0], 0.5, 15) | ||
|
||
def test_fom_gradient_integral_p2(self): | ||
""" Test FOM gradient integral with norm p = 2. """ | ||
wl_points = 5000 | ||
wavelengths = np.linspace(1.0e-9, 1.0e-8, wl_points) | ||
target_T_fwd = lambda wl: np.exp(-1.0 * (wl - wl.min()) / (wl.max() - wl.min())) | ||
fom_grad = ModeMatch.fom_gradient_wavelength_integral_impl(T_fwd_vs_wavelength = 0.5 * np.ones(wl_points), | ||
T_fwd_partial_derivs_vs_wl = np.ones((wl_points,1)), | ||
target_T_fwd_vs_wavelength = target_T_fwd(wavelengths), | ||
wl = wavelengths, | ||
norm_p = 2) | ||
exact_fom_grad = (np.exp(1.0) - 2.0) / np.sqrt(4.0 * np.exp(1.0) - np.exp(2.0) - 2.0) | ||
self.assertAlmostEqual(fom_grad[0], exact_fom_grad, 7) | ||
|
||
if __name__ == "__main__": | ||
run([__file__]) |
Oops, something went wrong.