Skip to content

Commit

Permalink
Squashed commit of several new features and enhancements from Lumeric…
Browse files Browse the repository at this point in the history
…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
areid-van authored and crees-lumerical committed Apr 16, 2019
1 parent 93094d4 commit 6532eef
Show file tree
Hide file tree
Showing 21 changed files with 888 additions and 46 deletions.
Binary file added QA/base_script_test.fsp
Binary file not shown.
9 changes: 9 additions & 0 deletions QA/base_script_test.lsf
@@ -0,0 +1,9 @@
################

deleteall;
addfdtd;

addpoly;

###########

52 changes: 52 additions & 0 deletions QA/base_script_test.py
@@ -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__])
76 changes: 76 additions & 0 deletions QA/modematch_parallel_plate_waveguide_TM_base.lsf
@@ -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);
121 changes: 121 additions & 0 deletions QA/modematch_parallel_plate_waveguide_TM_test.py
@@ -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__])
82 changes: 82 additions & 0 deletions QA/modematch_wavelength_integration_test.py
@@ -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__])

0 comments on commit 6532eef

Please sign in to comment.