├── LICENSE ├── MATLAB ├── analyticalModelGaAs.m ├── buildScriptAndEnqueue.m ├── buildSweepAndEnqueue.m ├── fieldModeArea.m ├── fieldMsq.m ├── fieldOverlap.m ├── plotMQW.m ├── plotResult3D.m ├── plotResultYZ.m ├── retrieveCompleted.m ├── sweepPlots_Base.m └── template │ ├── MQW_peaks │ ├── MQW_n_Rst_N_lambda_20230412.m │ ├── def_MQW_peaks.m │ ├── plot_MQW_peaks.m │ └── sweep_MQW_peaks.m │ ├── devEulerU │ ├── def_GaAs_EulerU_AR8.m │ ├── plot_GaAs_dev_AR8.m │ └── sweep_GaAs_dev_AR8.m │ ├── devMMI │ ├── MMI_wWG_1.5.mat │ ├── MMI_wWG_2.0.mat │ ├── def_GaAs_MMI_AR8.m │ ├── plot_GaAs_dev_AR8.m │ └── sweep_GaAs_dev_AR8.m │ ├── devSSC │ ├── def_GaAs_SSC_AR8.m │ ├── plot_GaAs_dev_AR8.m │ └── sweep_GaAs_dev_AR8.m │ ├── devsBend │ ├── def_GaAs_sBend_AR8.m │ ├── plot_GaAs_dev_AR8.m │ └── sweep_GaAs_dev_AR8.m │ ├── epi1D │ ├── def_GaAs_1D_AR8.m │ ├── plot_GaAs_1D_AR8_active.m │ ├── plot_GaAs_1D_AR8_mod.m │ └── sweep_GaAs_1D_AR8.m │ ├── lsf │ ├── 10_header_template.lsf │ ├── 20_etch_GaAs_WG.lsf │ ├── 20_etch_LiNbO3_x_WG.lsf │ ├── 20_etch_LiNbO3_z_WG.lsf │ ├── 22_etch_GaAs_EulerU.lsf │ ├── 22_etch_GaAs_MMI.lsf │ ├── 22_etch_GaAs_SSC.lsf │ ├── 22_etch_GaAs_sBend.lsf │ ├── 25_epi_GaAs_MQW.lsf │ ├── 30_epi_GaAs_AR8.lsf │ ├── 30_epi_LiNbO3_x.lsf │ ├── 30_epi_LiNbO3_z.lsf │ ├── 40_inst_GaAs.lsf │ ├── 40_inst_LiNbO3.lsf │ ├── 90_footer.lsf │ └── 99_MQW_peaks.lsf │ ├── wgActive │ ├── def_GaAs_WG_AR8.m │ ├── plot_GaAs_2D_AR8.m │ └── sweep_GaAs_2D_AR8.m │ ├── wgMod │ ├── def_GaAs_WG_AR8.m │ ├── plot_GaAs_2D_AR8.m │ └── sweep_GaAs_2D_AR8.m │ ├── wgMod_LiNbO3_x │ ├── def_LiNbO3_x_WG.m │ ├── plot_LiNbO3_x_WG.m │ └── sweep_LiNbO3_x_WG.m │ ├── wgMod_LiNbO3_z │ ├── def_LiNbO3_z_WG.m │ ├── plot_LiNbO3_z_WG.m │ └── sweep_LiNbO3_z_WG.m │ └── wgPassive │ ├── def_GaAs_WG_AR8.m │ ├── plot_GaAs_2D_AR8.m │ └── sweep_GaAs_2D_AR8.m ├── README.md ├── common ├── lum_analyze.lsf ├── lum_analyze_CHARGE.lsf ├── lum_analyze_EME.lsf ├── lum_analyze_FDE.lsf ├── lum_analyze_allFDTD.lsf ├── lum_buildEpitaxy.lsf ├── lum_buildEtch.lsf ├── lum_buildSim.lsf ├── lum_buildSim_functions.lsf ├── lum_buildSim_sub_CHARGE.lsf ├── lum_buildSim_sub_EME.lsf ├── lum_processEtch.lsf ├── lum_setup.lsf ├── templates │ ├── template_new_OE.lsf │ ├── template_new_activeEpi.lsf │ └── template_new_angleEtch.lsf ├── util_defineIndex.lsf ├── util_fCommon.lsf └── util_paramDefaults.lsf ├── remote-dedicated ├── Q_parallel.sh ├── Q_selected.sh ├── dispatch-device.cmd ├── dispatch-eme.cmd ├── dispatch-fde.cmd ├── dispatch-fdtd.cmd ├── dispatch-varfdtd.cmd ├── lumanalysis_template.lsf ├── lumerical_setup.sh ├── pueue_remaining.sh ├── refresh.lsf ├── run_device.sh ├── run_eme.sh ├── run_fde.sh ├── run_fdtd.sh ├── run_varfdtd.sh └── xvnc-run └── remote-slurm ├── MQ_parallel_O-E.sh ├── MQ_parallel_device.sh ├── MQ_parallel_eme.sh ├── MQ_parallel_fde_lms.sh ├── MQ_parallel_fdtd_fsp.sh ├── MQ_parallel_mode.sh ├── MQ_parallel_unprocessed_O-E.sh ├── MQ_parallel_unprocessed_varfdtd.sh ├── MQ_parallel_varfdtd.sh ├── MQ_parallel_varfdtd_lms.sh ├── MQ_queue_device.sh ├── MQ_queue_fdtd.sh ├── MQ_queue_mode.sh ├── MQ_queue_varfdtd.sh ├── Qrun_device.sh ├── Qrun_eme.sh ├── Qrun_fde.sh ├── Qrun_fdtd.sh ├── Qrun_varfdtd.sh ├── dispatch-device.cmd ├── dispatch-eme.cmd ├── dispatch-fde.cmd ├── dispatch-fdtd.cmd ├── dispatch-varfdtd.cmd ├── lumanalysis_template.lsf ├── pueue_remaining.sh ├── refresh.lsf └── xvnc-run /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Michael Nickerson ('nickersonm') https://nickersondevices.com/ 2 | All rights reserved. 3 | 4 | Redistribution and use in source, binary, and physical forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code or schematics must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary or physical form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS PRODUCT IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /MATLAB/fieldModeArea.m: -------------------------------------------------------------------------------- 1 | %% fieldModeArea.m 2 | % Michael Nickerson 2023-04-17 3 | % Effective modal area of given field 4 | % 5 | % Requirements: 6 | % - 7 | % 8 | % Usage: A = fieldModeArea(EM[, option, [value]]) 9 | % Returns: 10 | % A: Effective modal area of field, integral(|E|^2)^2 / integral(|E|^4) 11 | % 12 | % Parameters: 13 | % EM: structure with field 'E' describing an EM field, optionally including 14 | % fields 'x', 'y', 'z' 15 | % 16 | % Options: 17 | % none so far 18 | % 19 | % TODO: 20 | % x Initial development 21 | % - Test 22 | 23 | function A = fieldModeArea(EM, varargin) 24 | %% Helper functions, if any 25 | % Get the next argument or error 26 | function arg = nextarg(strExpected) 27 | if isempty(strExpected); strExpected = ''; end 28 | if ~isempty(varargin) 29 | arg = varargin{1}; varargin(1) = []; 30 | else 31 | error('Expected next argument "%s", but no more arguments present!', strExpected); 32 | end 33 | end 34 | 35 | % Standardize field 36 | function f = fieldStd(f) 37 | % Make 4D if not already; assume anything specified is last dimension 38 | if numel(size(f.E)) == 2 && size(f.E,2) == 1; f.E = reshape(f.E, [1, 1, numel(f.E)]); end 39 | if numel(size(f.E)) == 2; f.E = reshape(f.E, [1, size(f.E)]); end 40 | % Avoid moving scalar field to 4th dimension 41 | if numel(size(f.E)) == 3 && size(f.E,3) == 3; f.E = reshape(f.E, [1, size(f.E)]); end 42 | 43 | % Permute vectors to match if needed 44 | dims = ["x" "y" "z"]; 45 | d = 1; 46 | while d < 4 47 | if isfield(f, dims(d)) && numel(f.(dims(d))) ~= size(f.E,d) && any(numel(f.(dims(d))) == size(f.E)) 48 | dd = find(numel(f.(dims(d))) == size(f.E), d); 49 | f.E = permute(f.E, [dd, setdiff(1:numel(size(f.E)), dd)]); 50 | d = 1; % Restart search 51 | else 52 | d = d + 1; 53 | end 54 | end 55 | 56 | % Create or alter space vectors if needed 57 | for d = 1:numel(dims) 58 | % Create if not exist 59 | if ~isfield(f, dims(d)); f.(dims(d)) = linspace(-1,1,size(f.E,d))'; end 60 | 61 | % Resize if mismatched 62 | if numel(f.(dims(d))) ~= size(f.E, d) 63 | if size(f.E, d) == 1; f.(dims(d)) = mean(f.(dims(d))); 64 | else; f.(dims(d)) = linspace(min(f.(dims(d))), max(f.(dims(d))), size(f.E, d)); end 65 | end 66 | 67 | % Require at least 2 points per dimension for assorted calculations 68 | if numel(f.(dims(d))) == 1 69 | f.(dims(d)) = f.(dims(d)) + [0; 1]*1e-7; 70 | r = ones(size(size(f.E))); r(d) = 2; 71 | f.E = repmat(f.E, r); 72 | end 73 | end 74 | end 75 | 76 | % 3D trapz 77 | function r = trapz3(x, y, z, f) 78 | % Assume that numel(dim) == 2 means the dimensions have been previously replicated 79 | % with a small separation; change that to unit separation 80 | if numel(x) == 2; x = [0;1]; end 81 | if numel(y) == 2; y = [0,1]; end 82 | if numel(z) == 2; z = [0;1]; end 83 | 84 | r = trapz(x, trapz(y, trapz(z, f, 3), 2), 1); 85 | end 86 | 87 | 88 | %% Defaults and magic numbers 89 | 90 | 91 | %% Argument parsing 92 | % Check required inputs 93 | if ~exist("EM", "var") || isempty(EM);return; end 94 | if isa(EM, "double"); EM = struct("E", EM); end 95 | assert( isa(EM, "struct"), '"EM" not a struct'); 96 | 97 | % Parameter parsing 98 | while ~isempty(varargin) 99 | arg = lower(varargin{1}); varargin(1) = []; 100 | 101 | % Look for valid arguments 102 | switch arg 103 | case {'dummy', 'dumb'} 104 | unused = double(nextarg('dummy argument')); 105 | otherwise 106 | if ~isempty(arg) 107 | warning('Unexpected option "%s", ignoring', num2str(arg)); 108 | end 109 | end 110 | end 111 | 112 | 113 | %% Run calculations 114 | % Validate and normalize field 115 | assert( isfield(EM, 'E'), 'No "E" field present in "EM"'); 116 | EM = fieldStd(EM); 117 | 118 | % Calculate effective area 119 | % https://www.rp-photonics.com/effective_mode_area.html 120 | A = trapz3(EM.x, EM.y, EM.z, sum(abs(EM.E).^2, 4))^2 / trapz3(EM.x, EM.y, EM.z, sum(abs(EM.E).^4, 4)); 121 | 122 | end 123 | 124 | -------------------------------------------------------------------------------- /MATLAB/fieldMsq.m: -------------------------------------------------------------------------------- 1 | %% fieldMsq.m 2 | % Michael Nickerson 2023-04-17 3 | % M^2 calculations for x-normal fields 4 | % Computed by method in https://doi.org/10.1109/JLT.2005.863337 5 | % 6 | % Requirements: 7 | % - 8 | % 9 | % Usage: [Msq, MsqY, MsqZ, x0] = fieldMsq(EM, lambda[, option, [value]]) 10 | % Returns: 11 | % Msq: Average M^2 value = sqrt(My^2 * Mz^2) 12 | % MsqY: M^2 value in y-dimension only 13 | % MsqZ: M^2 value in z-dimension only 14 | % x0y: Offset from minimum beam waist in y-dimension 15 | % x0z: Offset from minimum beam waist in z-dimension 16 | % 17 | % Parameters: 18 | % EM: structure with field 'E' describing an EM field, optionally including 19 | % fields 'x', 'y', 'z' 20 | % lambda: wavelength in meters 21 | % 22 | % Options: 23 | % none so far 24 | % 25 | % TODO: 26 | % x Initial development 27 | % - Test 28 | 29 | function [Msq, MsqY, MsqZ, x0y, x0z] = fieldMsq(EM, lambda, varargin) 30 | %% Helper functions, if any 31 | % Get the next argument or error 32 | function arg = nextarg(strExpected) 33 | if isempty(strExpected); strExpected = ''; end 34 | if ~isempty(varargin) 35 | arg = varargin{1}; varargin(1) = []; 36 | else 37 | error('Expected next argument "%s", but no more arguments present!', strExpected); 38 | end 39 | end 40 | 41 | % Standardize field 42 | function f = fieldStd(f) 43 | % Make 4D if not already; assume anything specified is last dimension 44 | if numel(size(f.E)) == 2 && size(f.E,2) == 1; f.E = reshape(f.E, [1, 1, numel(f.E)]); end 45 | if numel(size(f.E)) == 2; f.E = reshape(f.E, [1, size(f.E)]); end 46 | % Avoid moving scalar field to 4th dimension 47 | if numel(size(f.E)) == 3 && size(f.E,3) == 3; f.E = reshape(f.E, [1, size(f.E)]); end 48 | 49 | % Permute vectors to match if needed 50 | dims = ["x" "y" "z"]; 51 | d = 1; 52 | while d < 4 53 | if isfield(f, dims(d)) && numel(f.(dims(d))) ~= size(f.E,d) && any(numel(f.(dims(d))) == size(f.E)) 54 | dd = find(numel(f.(dims(d))) == size(f.E), d); 55 | f.E = permute(f.E, [dd, setdiff(1:numel(size(f.E)), dd)]); 56 | d = 1; % Restart search 57 | else 58 | d = d + 1; 59 | end 60 | end 61 | 62 | % Create or alter space vectors if needed 63 | for d = 1:numel(dims) 64 | % Create if not exist 65 | if ~isfield(f, dims(d)); f.(dims(d)) = linspace(-1,1,size(f.E,d))'; end 66 | 67 | % Resize if mismatched 68 | if numel(f.(dims(d))) ~= size(f.E, d) 69 | if size(f.E, d) == 1; f.(dims(d)) = mean(f.(dims(d))); 70 | else; f.(dims(d)) = linspace(min(f.(dims(d))), max(f.(dims(d))), size(f.E, d)); end 71 | end 72 | 73 | % Require at least 2 points per dimension for assorted calculations 74 | if numel(f.(dims(d))) == 1 75 | f.(dims(d)) = f.(dims(d)) + [0; 1]*1e-7; 76 | r = ones(size(size(f.E))); r(d) = 2; 77 | f.E = repmat(f.E, r); 78 | end 79 | end 80 | end 81 | 82 | % 3D trapz 83 | function r = trapz3(x, y, z, f) 84 | % Assume that numel(dim) == 2 means the dimensions have been previously replicated 85 | % with a small separation; change that to unit separation 86 | if numel(x) == 2; x = [0;1]; end 87 | if numel(y) == 2; y = [0,1]; end 88 | if numel(z) == 2; z = [0;1]; end 89 | 90 | r = trapz(x, trapz(y, trapz(z, f, 3), 2), 1); 91 | end 92 | 93 | 94 | %% Defaults and magic numbers 95 | 96 | 97 | %% Argument parsing 98 | % Check required inputs 99 | if ~exist("EM", "var") || isempty(EM); Msq = 0; MsqY = 0; MsqZ = 0; return; end 100 | if isa(EM, "double"); EM = struct("E", EM); end 101 | assert( isa(EM, "struct"), '"EM" not a struct'); 102 | 103 | % Parameter parsing 104 | while ~isempty(varargin) 105 | arg = lower(varargin{1}); varargin(1) = []; 106 | 107 | % Look for valid arguments 108 | switch arg 109 | case {'dummy', 'dumb'} 110 | unused = double(nextarg('dummy argument')); 111 | otherwise 112 | if ~isempty(arg) 113 | warning('Unexpected option "%s", ignoring', num2str(arg)); 114 | end 115 | end 116 | end 117 | 118 | 119 | %% Run calculations 120 | % Validate and normalize field 121 | assert( isfield(EM, 'E'), 'No "E" field present in "EM"'); 122 | EM = fieldStd(EM); 123 | EM.E = EM.E ./ sqrt(trapz3(EM.x, EM.y, EM.z, sum(abs(EM.E).^2, 4))); 124 | EM.P = sum(abs(EM.E).^2, 4); 125 | 126 | % Gridded values 127 | [~, Y, Z] = ndgrid(EM.x, EM.y, EM.z); 128 | 129 | % Expectation values 130 | exY = trapz3(EM.x, EM.y, EM.z, Y .* EM.P); 131 | exZ = trapz3(EM.x, EM.y, EM.z, Z .* EM.P); 132 | 133 | % Variances 134 | sgYsq = trapz3(EM.x, EM.y, EM.z, (Y - exY).^2 .* EM.P); 135 | sgZsq = trapz3(EM.x, EM.y, EM.z, (Z - exZ).^2 .* EM.P); 136 | 137 | % Derivatives 138 | [~, dY, dZ] = ndgrid(gradient(EM.x), gradient(EM.y), gradient(EM.z)); 139 | [dEdY, ~, dEdZ] = gradient(EM.E); % Note row and column output are swapped: first is column, 2nd is row 140 | dEdY = dEdY ./ dY; 141 | dEdZ = dEdZ ./ dZ; 142 | 143 | % Field-dot-derivative products 144 | EddY = sum(EM.E .* conj(dEdY), 4); EddY = EddY - conj(EddY); 145 | EddZ = sum(EM.E .* conj(dEdZ), 4); EddZ = EddZ - conj(EddZ); 146 | 147 | % A and B parameters 148 | Ay = trapz3(EM.x, EM.y, EM.z, (Y - exY) .* EddY); 149 | Az = trapz3(EM.x, EM.y, EM.z, (Z - exZ) .* EddZ); 150 | By = trapz3(EM.x, EM.y, EM.z, sum(abs(dEdY).^2, 4)) + 0.25 * trapz3(EM.x, EM.y, EM.z, EddY).^2; 151 | Bz = trapz3(EM.x, EM.y, EM.z, sum(abs(dEdZ).^2, 4)) + 0.25 * trapz3(EM.x, EM.y, EM.z, EddZ).^2; 152 | 153 | % Final parameters 154 | x0y = 1i*(pi/lambda)*(Ay/By); 155 | x0z = 1i*(pi/lambda)*(Az/Bz); 156 | MsqY = sqrt(4*By*sgYsq+Ay^2); 157 | MsqZ = sqrt(4*Bz*sgZsq+Az^2); 158 | Msq = sqrt(MsqY * MsqZ); 159 | 160 | end 161 | 162 | -------------------------------------------------------------------------------- /MATLAB/fieldOverlap.m: -------------------------------------------------------------------------------- 1 | %% fieldOverlap.m 2 | % Michael Nickerson 2022-10-06 3 | % Complex overlap between two optical fields 4 | % 5 | % Requirements: 6 | % - 7 | % 8 | % Usage: [overlap, o, IC, N1, N2] = fieldOverlap(f1, f2[, option, [value]]) 9 | % Returns: 10 | % overlap: complex field overlap, integral(conj(f1) .* f2)^2/(integral(|f1|^2)*integral(|f2|^2)) 11 | % equivalent to S_12^2 * (norm1/norm2) 12 | % o: abs(overlap) 13 | % IC: integral(conj(f1) . f2)^2 14 | % N1: integral(|f1|^2) 15 | % N2: integral(|f2|^2) 16 | % 17 | % Parameters: 18 | % f1, f2: structure with field 'E' describing an EM field, optionally including 19 | % fields 'x', 'y', 'z'; will resample f2 to f1 if needed 20 | % 21 | % Options: 22 | % none so far 23 | % 24 | % TODO: 25 | % x Initial development 26 | % x Test 27 | 28 | function [overlap, o, IC, N1, N2] = fieldOverlap(f1, f2, varargin) 29 | %% Helper functions, if any 30 | % Get the next argument or error 31 | function arg = nextarg(strExpected) 32 | if isempty(strExpected); strExpected = ''; end 33 | if ~isempty(varargin) 34 | arg = varargin{1}; varargin(1) = []; 35 | else 36 | error('Expected next argument "%s", but no more arguments present!', strExpected); 37 | end 38 | end 39 | 40 | % Standardize field 41 | function f = fieldStd(f) 42 | % Make 4D if not already; assume anything specified is last dimension 43 | if numel(size(f.E)) == 2 && size(f.E,2) == 1; f.E = reshape(f.E, [1, 1, numel(f.E)]); end 44 | if numel(size(f.E)) == 2; f.E = reshape(f.E, [1, size(f.E)]); end 45 | % Avoid moving scalar field to 4th dimension 46 | if numel(size(f.E)) == 3 && size(f.E,3) == 3; f.E = reshape(f.E, [1, size(f.E)]); end 47 | 48 | % Permute vectors to match if needed 49 | dims = ["x" "y" "z"]; 50 | d = 1; 51 | while d < 4 52 | if isfield(f, dims(d)) && numel(f.(dims(d))) ~= size(f.E,d) && any(numel(f.(dims(d))) == size(f.E)) 53 | dd = find(numel(f.(dims(d))) == size(f.E), d); 54 | f.E = permute(f.E, [dd, setdiff(1:numel(size(f.E)), dd)]); 55 | d = 1; % Restart search 56 | else 57 | d = d + 1; 58 | end 59 | end 60 | 61 | % Create or alter space vectors if needed 62 | for d = 1:numel(dims) 63 | % Create if not exist 64 | if ~isfield(f, dims(d)); f.(dims(d)) = linspace(-1,1,size(f.E,d))'; end 65 | 66 | % Resize if mismatched 67 | if numel(f.(dims(d))) ~= size(f.E, d) 68 | if size(f.E, d) == 1; f.(dims(d)) = mean(f.(dims(d))); 69 | else; f.(dims(d)) = linspace(min(f.(dims(d))), max(f.(dims(d))), size(f.E, d)); end 70 | end 71 | 72 | % Require at least 2 points per dimension for assorted calculations 73 | if numel(f.(dims(d))) == 1 74 | f.(dims(d)) = f.(dims(d)) + [0; 1]*1e-7; 75 | r = ones(size(size(f.E))); r(d) = 2; 76 | f.E = repmat(f.E, r); 77 | end 78 | end 79 | end 80 | 81 | % 3D trapz 82 | function r = trapz3(x, y, z, f) 83 | % Assume that numel(dim) == 2 means the dimensions have been previously replicated 84 | % with a small separation; change that to unit separation 85 | if numel(x) == 2; x = [0;1]; end 86 | if numel(y) == 2; y = [0,1]; end 87 | if numel(z) == 2; z = [0;1]; end 88 | 89 | r = trapz(x, trapz(y, trapz(z, f, 3), 2), 1); 90 | end 91 | 92 | 93 | %% Defaults and magic numbers 94 | 95 | 96 | %% Argument parsing 97 | % Check required inputs 98 | if ~exist("f1", "var") || ~exist("f2", "var") || isempty(f1) || isempty(f2); overlap = 0; o = 0; return; end 99 | if isa(f1, "double"); f1 = struct("E", f1); end 100 | if isa(f2, "double"); f2 = struct("E", f2); end 101 | assert( isa(f1, "struct"), '"f1" not a struct'); 102 | assert( isa(f2, "struct"), '"f2" not a struct'); 103 | 104 | % Parameter parsing 105 | while ~isempty(varargin) 106 | arg = lower(varargin{1}); varargin(1) = []; 107 | 108 | % Look for valid arguments 109 | switch arg 110 | case {'dummy', 'dumb'} 111 | unused = double(nextarg('dummy argument')); 112 | otherwise 113 | if ~isempty(arg) 114 | warning('Unexpected option "%s", ignoring', num2str(arg)); 115 | end 116 | end 117 | end 118 | 119 | 120 | %% Validate inputs 121 | % f1, f2: structure with field 'E' describing an EM field 122 | assert( isfield(f1, 'E'), 'No "E" field present in "f1"'); 123 | assert( isfield(f2, 'E'), 'No "E" field present in "f2"'); 124 | f1 = fieldStd(f1); 125 | f2 = fieldStd(f2); 126 | 127 | % Convert to scalar if f1 or f2 is scalar 128 | if size(f1.E, 4) ~= 3 || size(f2.E, 4) ~= 3 129 | f1.E = sum(f1.E, 4); f2.E = sum(f2.E, 4); 130 | end 131 | 132 | % Resample if needed 133 | if any(size(f1.E, 1:3) ~= size(f2.E, 1:3)) || any([f1.x(:);f1.y(:);f1.z(:)] ~= [f2.x(:);f2.y(:);f2.z(:)]) 134 | f2.E = interpn(f2.x, f2.y, f2.z, f2.E, f1.x(:), f1.y(:)', f1.z); 135 | f2.E(isnan(f2.E)) = 0; % Remove NaNs 136 | f2.x = f1.x; f2.y = f1.y; f2.z = f1.z; 137 | end 138 | 139 | % Calculate complex overlap: integral(conj(f1) .* f2)^2/(integral(|f1|^2)*integral(|f2|^2)) 140 | IC = trapz3(f1.x, f1.y, f1.z, sum(conj(f1.E) .* f2.E, 4))^2; 141 | N1 = trapz3(f1.x, f1.y, f1.z, sum(abs(f1.E).^2, 4)); 142 | N2 = trapz3(f2.x, f2.y, f2.z, sum(abs(f2.E).^2, 4)); 143 | overlap = IC / (N1*N2); 144 | o = abs(overlap); 145 | 146 | end 147 | 148 | -------------------------------------------------------------------------------- /MATLAB/plotMQW.m: -------------------------------------------------------------------------------- 1 | %% plotMQW.m MN 2022-10-11 2 | % Helper function to plot Lumerical MQW results 3 | % 4 | % Requirements: 5 | % .mat file with `mqw` structure produced by addMQW() in 'lum_analyze' 6 | % 7 | % Usage: [plotHandle] = plotMQW(result, [options]) 8 | % Returns: 9 | % plotHandle: handle to the plot 10 | % 11 | % Parameters: 12 | % result: path to the result file(s) to load or structure from same; wildcards accepted 13 | % 14 | % Options: 15 | % 'title', string: overall title for the plot (default none) 16 | % 'save', filename: save png of the plot 17 | % 'handle', h: use this figure handle to plot 18 | % 'size', [x, y]: figure size (default [1600, 600]) 19 | % 'margin', [x, y]: subplot margin (default [0.10, 0.05]) 20 | 21 | function plotHandle = plotMQW(result, varargin) 22 | %% Defaults and magic numbers 23 | saveFile = []; 24 | plotHandle = []; 25 | plotTitle = ""; 26 | plotSize = [1600, 600]; 27 | mgn = [0.10, 0.05]; 28 | plotShift = [0, 0]; 29 | 30 | 31 | %% Argument parsing 32 | % Check required inputs 33 | if isempty(result) || ( (~isstruct(result)) && (exist(result, 'file') ~= 2 && (numel(dir(result)) < 1) ) ) 34 | error('Required input "result" is empty or does not correspond to any valid files.'); 35 | end 36 | 37 | % Parameter parsing 38 | while ~isempty(varargin) 39 | arg = lower(varargin{1}); varargin(1) = []; 40 | 41 | % Look for options 42 | switch lower(string(arg)) 43 | case "title" 44 | plotTitle = string(nextarg('plot title')); 45 | case "save" 46 | saveFile = string(nextarg('save filename')); 47 | case "handle" 48 | plotHandle = nextarg('plot handle'); 49 | if ~(isa(plotHandle, 'double') || ishandle(plotHandle)) 50 | plotHandle = []; 51 | end 52 | case "size" 53 | plotSize = double(nextarg('plot size')); 54 | if numel(plotSize) < 2 55 | plotSize = repmat(plotSize,1,2); 56 | end 57 | case "margin" 58 | mgn = double(nextarg('margin')); 59 | otherwise 60 | if ~isempty(arg) 61 | warning('Unexpected option "%s", ignoring', num2str(arg)); 62 | end 63 | end 64 | end 65 | 66 | 67 | %% Helper functions, if any 68 | % Get the next argument or error 69 | function arg = nextarg(strExpected) 70 | if isempty(strExpected); strExpected = ''; end 71 | if ~isempty(varargin) 72 | arg = varargin{1}; varargin(1) = []; 73 | else 74 | error('Expected next argument "%s", but no more arguments present!', strExpected); 75 | end 76 | end 77 | 78 | 79 | %% Load data 80 | if isstruct(result) 81 | R = result; 82 | else 83 | f = dir(result); 84 | 85 | % Load first result 86 | i = 1; 87 | R = load([f(i).folder, '\', f(i).name], '-mat'); 88 | 89 | % Any further files; append to existing structure 90 | for i = 2:numel(f) 91 | R = appendstruct(R, load([f(i).folder, '\', f(i).name], '-mat')); 92 | end 93 | end 94 | 95 | if ~isfield(R, 'mqw') 96 | fprintf("Cannot plot '%s'\n", result); 97 | return; 98 | end 99 | mqw = R.mqw; 100 | 101 | 102 | %% Break out variables for readability 103 | % Band diagram 104 | z = mqw.banddiagram.z*1e9; 105 | Ec = mqw.banddiagram.Ec; 106 | Ev = mqw.banddiagram.Ev; 107 | 108 | % Composition and strain 109 | if isfield(mqw, 'length') && isfield(mqw, 'strain') 110 | length = reshape(repmat(cumsum(mqw.length'), 2, 1), 1, [])*1e9; 111 | length = [0,length(1:end-1)]; 112 | strain = reshape(repmat(mqw.strain', 2, 1), 1, []); 113 | plotShift = plotShift + [35, 0]; mgn = mgn + [0, 0.015]; 114 | end 115 | 116 | % Emission 117 | lv = mqw.emission.wavelength*1e6; 118 | TE = squeeze(mqw.emission.spontaneous_TE); 119 | TM = squeeze(mqw.emission.spontaneous_TM); 120 | 121 | % Naming: materials 122 | % matLabel = titlewrap(strrep(mqw.materials, "_", " /"), 140, "/"); 123 | 124 | 125 | %% Plot 126 | % Title and figure 127 | if ~isempty(plotTitle) 128 | plotTitle = titlewrap(plotTitle, 140);% + newline; 129 | end 130 | % plotTitle = plotTitle + matLabel; 131 | % Increase size for larger title 132 | plotSize = plotSize + [0, 120*numel(strfind(plotTitle, newline))]; 133 | plotShift = plotShift + [0, -0 + -15*numel(strfind(plotTitle, newline))]; 134 | plotHandle = figureSize(plotHandle, plotSize); clf(plotHandle); 135 | 136 | if ~isempty(plotTitle) 137 | figureTitle(plotHandle, plotTitle, 0.03*(1.5+numel(strfind(plotTitle, newline))), ... 138 | "Interpreter", "none", "FontSize", 14); 139 | end 140 | 141 | % Band structure 142 | h = subplot_tight(1, 2, 1, mgn); 143 | h.Units = "pixels"; h.Position = h.Position + [plotShift.*[-1, 1], 0, 0]; 144 | ax = plot(z, Ec, 'LineWidth', 4); 145 | hold on; 146 | ax = [ax, plot(z, Ev, 'LineWidth', 4)]; 147 | xlabel('Epitaxial Depth [nm]', 'FontWeight','bold'); 148 | ylabel('Energy [eV]', 'FontWeight','bold'); 149 | h.XLimitMethod = "tight"; h.YLim = [-0.5, max([Ec;1.8])]; 150 | if exist('strain', 'var') 151 | yyaxis(h, 'right'); h.YLim = [-1, 1]*0.04; 152 | ax = [ax, plot(length, strain, ':', 'LineWidth', 2)]; 153 | ylabel('Strain [fractional]', 'FontWeight','bold'); 154 | % ax(end).YColor = 155 | end 156 | hold off; grid on; 157 | title("MQW Structure", 'FontSize', 14, 'FontName', 'Source Sans Pro'); 158 | legend(ax, ["Ec", "Ev", "Strain"], 'FontSize', 14, 'Location', 'ne'); 159 | 160 | % Emission 161 | h = subplot_tight(1, 2, 2, mgn); 162 | h.Units = "pixels"; h.Position = h.Position + [plotShift, 0, 0]; 163 | ax = plot(lv, TE, 'LineWidth', 4); 164 | hold on; 165 | ax = [ax, plot(lv, TM, ':', 'LineWidth', 2)]; 166 | ylabel('Rsp [arb]', 'FontWeight','bold'); 167 | xlabel('Wavelegnth [µm]', 'FontWeight','bold'); 168 | hold off; grid on; h.XLim = [0.9,1.7]; 169 | title("MQW Emission", 'FontSize', 14, 'FontName', 'Source Sans Pro'); 170 | legend(ax, ["TE", "TM"], 'FontSize', 14, 'Location', 'ne'); 171 | 172 | drawnow; 173 | 174 | if ~isempty(saveFile) 175 | [savePath, saveName] = fileparts(saveFile); 176 | if strlength(savePath) == 0; savePath = "."; end 177 | print(savePath + "/" + matlab.lang.makeValidName(saveName) + ".png", '-dpng'); 178 | end 179 | 180 | 181 | end 182 | -------------------------------------------------------------------------------- /MATLAB/template/MQW_peaks/def_MQW_peaks.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | template = lsfDir + "99_MQW_peaks.lsf"; 18 | 19 | % Define naming 20 | scriptName = "MQW_peaks"; 21 | 22 | % Default variable alterations, if any 23 | setVars = {"resultFile", "'" + scriptName + ".dat'", "mqwRecord", 0}; 24 | 25 | 26 | %% Process 27 | % Assemble script 28 | script = string(fileread(template)); 29 | 30 | 31 | %% Clean up 32 | clearvars -except script scriptName setVars 33 | -------------------------------------------------------------------------------- /MATLAB/template/MQW_peaks/plot_MQW_peaks.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_MQW_peaks; clear script setVars; 10 | 11 | % Set customizations 12 | dualMode = 0; 13 | resExts = ".mat"; 14 | % sweepName = "MQW_nominal_N"; 15 | sweepName = "MQW_nominal_cden"; 16 | 17 | noPlotParams = ["Peak Rsp [arb]"]; 18 | noPlotTogether = ["", ""]; 19 | savePlot = 0; 20 | plot1D = 0; 21 | 22 | 23 | %% Definitions 24 | % Plotting script 25 | baseScript = "sweepPlots_Base.m"; 26 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 27 | 28 | componentName = scriptName; 29 | 30 | % Files to load 31 | resFiles = []; 32 | for sweep = sweepName 33 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 34 | end 35 | resFiles = regexprep(string({resFiles.folder}') + "\" + string({resFiles.name}'), "\.mat", ""); 36 | 37 | % Parameters to plot; needs to evaluate to doubles 38 | params = ["R.mqwStrain", "R.Nqw", "R.cden", ... 39 | "R.tQW*1e3", "R.tQWB*1e3", "R.xQW"]; 40 | pLabels = ["Strain Calculated", "# QW", "Carrier Density [1e18 cm^-3]", ... 41 | "QW Thickness [nm]", "Barrier Thickness [nm]", "In-fraction QW"]; 42 | 43 | % Metrics 44 | preComp = "[~,R.peakI] = sort(-R.mqw.emission.spontaneous_TE(:));"; 45 | metrics = ["mean(maxk(R.mqw.emission.spontaneous_TE(:), 3))", ... 46 | "-abs(mean(R.mqw.emission.wavelength(R.peakI(1:3)))*1e9-980)", ... 47 | "mean(R.mqw.emission.spontaneous_TE(find(R.mqw.emission.wavelength >= 1.02e-6, 3)))", ... 48 | "mean(R.mqw.emission.stimulated_TE(find(R.mqw.emission.wavelength >= 1.02e-6, 3)))"]; 49 | mLabels = ["Peak Rsp", ... 50 | "Peak Wavelength Offset from 980 [nm]", ... 51 | "Rsp at 980 nm", ... 52 | "Rst at 980 nm"]; 53 | 54 | % Data reduction 55 | rejectData = "0"; 56 | 57 | % Optional plotting options 58 | contour=10; 59 | contourlim=nan(2,numel(params)+numel(metrics)); 60 | % contourlim(:,params=="wWG")=[1; 2]; 61 | % contourlim(:,params=="etchDepth")=[0.2; 0.6]; 62 | % contourlim(:,params=="d")=[0.4; 1.2]; 63 | % contourlim(:,params=="d2")=[0.4; 1.2]; 64 | nominal=nan(size(contourlim(1,:))); 65 | nominal(params=="R.cden")=2; 66 | nominal(params=="R.Nqw")=3; 67 | nominal(params=="R.tQW*1e3") =4; 68 | nominal(params=="R.tQWB*1e3")=4; 69 | nominal(params=="R.xQW") =0.35; 70 | % nominalvar=nominal * 0.2; % 20% variation 71 | nominalvar=NaN*nominal; % No nominal variation 72 | 73 | 74 | %% Call main plot function 75 | fprintf("Plotting '%s'...\n", sweepName); 76 | run(baseScript); 77 | -------------------------------------------------------------------------------- /MATLAB/template/MQW_peaks/sweep_MQW_peaks.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2023-04-25 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_MQW_peaks; 11 | 12 | 13 | %% Define sweep parameters 14 | % sweep = {"Nqw", [3 4]}; 15 | % sweepName = "MQW_nominal_N"; 16 | 17 | sweep = {"cden", linspace(0.1^0.5, 15^0.5, 30).^2}; 18 | sweepName = "MQW_nominal_cden"; 19 | 20 | 21 | %% Build scripts locally 22 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 23 | 'submitjob', 0, 'deletetmp', 0, 'randomize', 0); 24 | 25 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 26 | % 'submitjob', 1, 'session', 'lumerical', 'randomize', 0, ... 27 | % 'submitcmd', "BUILDONLY=1 ~/lumerical/Q_selected.sh fde"); 28 | -------------------------------------------------------------------------------- /MATLAB/template/devEulerU/def_GaAs_EulerU_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "EulerU"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_WG.lsf"; 31 | devScript = lsfDir + "22_etch_" + epiType + "_" + componentName + ".lsf"; 32 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 33 | 34 | % Script naming 35 | scriptName = epiType + "_" + epiName + "_" + componentName; 36 | 37 | % Default variable alterations, if any 38 | setVars = {"sim2D", 2, "simResFine", 0.05, "simAccuracy", 2, ... 39 | "Vpad", 0, "lActive", 0, "outMFD", 0, ... 40 | "etch2", "etch1", "etch1", 3, "wWG1", 2, "wWG2", "wWG1", "wgBR", 10}; 41 | 42 | 43 | %% Process 44 | % Assemble script 45 | script = string(fileread(templateHeader)) + "\n\n"; 46 | script = script + string(fileread(etchScript)) + "\n\n"; 47 | script = script + string(fileread(devScript)) + "\n\n"; 48 | script = script + string(fileread(mqwScript)) + "\n\n"; 49 | script = script + string(fileread(epiScript)) + "\n\n"; 50 | script = script + string(fileread(instScript)) + "\n\n"; 51 | script = script + string(fileread(templateFooter)); 52 | 53 | 54 | %% Clean up 55 | clearvars -except script scriptName setVars 56 | -------------------------------------------------------------------------------- /MATLAB/template/devEulerU/plot_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_EulerU_AR8; 10 | 11 | % Non-modulating 12 | dualMode = 0; 13 | resExts = "_varFDTD.mat"; 14 | sweepName = "AR8_passive_EulerU_wgBR_varFDTD"; 15 | 16 | % resExts = "_FDTD.mat"; 17 | % sweepName = "AR8_passive_EulerU_wgBR_FDTD"; 18 | 19 | 20 | % Plot parameters 21 | noPlotParams = ["P22 [dB]"]; 22 | noPlotTogether = ["P12 [dB]", "P22 [dB]"]; 23 | savePlot = 0; 24 | plot1D = 1; 25 | 26 | 27 | %% Definitions 28 | % Plotting script 29 | baseScript = "sweepPlots_Base.m"; 30 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 31 | 32 | componentName = scriptName; 33 | 34 | % Precomputation 35 | % preComp = "R.outField.mfd(1) = 2e-6;"; 36 | 37 | % Files to load 38 | resFiles = []; 39 | for sweep = sweepName 40 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 41 | end 42 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 43 | 44 | % Parameters to plot; needs to evaluate to doubles 45 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", ... 46 | "R.maxModes", "R.dataRes", "R.simResFine", "R.simAccuracy", ... 47 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.outField.pol", "R.wgBR", "R.lWG", ... 48 | "R.n", "R.th"]; 49 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", ... 50 | "# Modes", "Data Resolution", "Simulation WG Resolution", "Simulation Accuracy", ... 51 | "2D", "1D", "SiO2 Clad", "Polarization", "Minimum Bend Radius", "WG Length", ... 52 | "Polygon Points", "Euler Angle"]; 53 | 54 | % Metrics 55 | metrics = ["10*log10(R.results.P12)", "10*log10(R.results.P22)"]; 56 | mLabels = ["P12 [dB]", "P22 [dB]"]; 57 | 58 | % Data reduction 59 | rejectData = "R.wgBR == 0"; 60 | 61 | % Optional plotting options 62 | contour=10; 63 | contourlim=nan(2,numel(params)+numel(metrics)); 64 | % contourlim(:,params=="wWG1")=[1; 2]; 65 | nominal=nan(size(contourlim(1,:))); 66 | nominal(params=="R.wWG1")=2.0; 67 | nominal(params=="R.etch1")=2.5; 68 | % nominalvar=nominal * 0.2; % 20% variation 69 | nominalvar=NaN*nominal; % No nominal variation 70 | 71 | 72 | %% Call main plot function 73 | fprintf("Plotting '%s'...\n", sweepName); 74 | run(baseScript); 75 | -------------------------------------------------------------------------------- /MATLAB/template/devEulerU/sweep_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_EulerU_AR8; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"sim2D", 2, ... 15 | "wWG1", [1.5, 2.0], ... 16 | "wgBR", [logspace(log10(2), log10(40), 30)]}; 17 | sweepName = "AR8_passive_EulerU_wgBR_varFDTD"; 18 | 19 | % sweep = {"sim2D", 0, "simAccuracy", 3, ... 20 | % "wWG1", [1.5, 2.0], ... 21 | % "wgBR", [logspace(log10(2), log10(20), 20)]}; 22 | % sweepName = "AR8_passive_EulerU_wgBR_FDTD"; 23 | 24 | 25 | %% Build scripts locally 26 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 27 | 'submitjob', 0, 'randomize', 0*0.02); 28 | -------------------------------------------------------------------------------- /MATLAB/template/devMMI/MMI_wWG_1.5.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickersonm/LumericalTools/7d921012abe36a592d6b439f6200f58c4b49b119/MATLAB/template/devMMI/MMI_wWG_1.5.mat -------------------------------------------------------------------------------- /MATLAB/template/devMMI/MMI_wWG_2.0.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nickersonm/LumericalTools/7d921012abe36a592d6b439f6200f58c4b49b119/MATLAB/template/devMMI/MMI_wWG_2.0.mat -------------------------------------------------------------------------------- /MATLAB/template/devMMI/def_GaAs_MMI_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "MMI"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_WG.lsf"; 31 | devScript = lsfDir + "22_etch_" + epiType + "_" + componentName + ".lsf"; 32 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 33 | 34 | % Script naming 35 | scriptName = epiType + "_" + epiName + "_" + componentName; 36 | 37 | % Default variable alterations, if any 38 | setVars = {"sim2D", 2, "Vpad", 0, "outMFD", 0, "lWG", 5, "lActive", 0, "etch1", 3.0, "etch2", 3.0, "wWG1", 1.5}; 39 | 40 | 41 | %% Process 42 | % Assemble script 43 | script = string(fileread(templateHeader)) + "\n\n"; 44 | script = script + string(fileread(etchScript)) + "\n\n"; 45 | script = script + string(fileread(devScript)) + "\n\n"; 46 | script = script + string(fileread(mqwScript)) + "\n\n"; 47 | script = script + string(fileread(epiScript)) + "\n\n"; 48 | script = script + string(fileread(instScript)) + "\n\n"; 49 | script = script + string(fileread(templateFooter)); 50 | 51 | 52 | %% Clean up 53 | clearvars -except script scriptName setVars 54 | -------------------------------------------------------------------------------- /MATLAB/template/devMMI/plot_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_MMI_AR8; 10 | 11 | % Non-modulating 12 | dualMode = 0; 13 | resExts = "_varFDTD.mat"; 14 | sweepName = "AR8_passive_MMI_1x1_deep_dl"; 15 | % sweepName = "AR8_passive_MMI_1x2_deep_dl"; 16 | % sweepName = "AR8_passive_MMI_1x3_deep_dl"; 17 | % sweepName = "AR8_passive_MMI_1x4_deep_dl"; 18 | % sweepName = "AR8_passive_MMI_1x2_deep_varFDTD_lambda"; 19 | % sweepName = "AR8_passive_MMI_1x4_deep_varFDTD_lambda"; 20 | 21 | 22 | % Plot parameters 23 | noPlotParams = ["simY", "MMI Length", "MMI Width", "Transmitted Fraction [dB]"]; 24 | noPlotTogether = ["Pout [dB]", "P12 [dB]"]; 25 | savePlot = 0; 26 | plot1D = 0; 27 | 28 | 29 | %% Definitions 30 | % Plotting script 31 | baseScript = "sweepPlots_Base.m"; 32 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 33 | 34 | componentName = scriptName; 35 | 36 | % Precomputation 37 | preComp = "R.N = R.N + 20*(R.dyIn ~= 0);"; 38 | 39 | % Files to load 40 | resFiles = []; 41 | for sweep = sweepName 42 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 43 | end 44 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 45 | 46 | % Parameters to plot; needs to evaluate to doubles 47 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 48 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.inPort{1}.pol", "R.outField.pol", "R.wgBR", "R.lWG", ... 49 | "R.wMMI", "R.dwMMI", "R.lMMI", "R.dlMMI", "R.N", "R.wSpace", "R.lTaperIn", "R.lTaperOut", "R.dTaper"]; 50 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 51 | "2D", "1D", "SiO2 Clad", "Input Polarization", "Output Polarization", "Bend Radius", "WG Length", ... 52 | "MMI Width", "MMI δWidth", "MMI Length", "MMI δLength", "N Outputs", "MMI Output Space", "MMI Input Taper", "MMI Output Taper", "MMI Output Space Between Tapers"]; 53 | 54 | % Metrics 55 | metrics = ["10*log10(R.results.Pout)", "10*log10(R.results.P12)", "10*log10(R.results.Ptr)"]; 56 | mLabels = ["Pout [dB]", "P12 [dB]", "Transmitted Fraction [dB]"]; 57 | 58 | % Data reduction 59 | rejectData = "0"; 60 | 61 | % Optional plotting options 62 | contour=10; 63 | contourlim=nan(2,numel(params)+numel(metrics)); 64 | % contourlim(:,params=="wWG1")=[1; 2]; 65 | nominal=nan(size(contourlim(1,:))); 66 | nominal(params=="R.wWG1")=2.0; 67 | nominal(params=="R.etch1")=2.5; 68 | % nominalvar=nominal * 0.2; % 20% variation 69 | nominalvar=NaN*nominal; % No nominal variation 70 | 71 | 72 | %% Call main plot function 73 | fprintf("Plotting '%s'...\n", sweepName); 74 | run(baseScript); 75 | -------------------------------------------------------------------------------- /MATLAB/template/devMMI/sweep_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_MMI_AR8; 11 | 12 | %% MMI: 1x1 deep 13 | def_GaAs_MMI_AR8; 14 | sweep = {"N", 1, "dlMMI", linspace(-15, 8.5, 11)}; 15 | sweepName = "AR8_passive_MMI_1x1_deep_dl"; 16 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 17 | 'submitjob', 0, 'randomize', 0); 18 | 19 | % %% MMI: 1x2 deep 20 | % def_GaAs_MMI_AR8; 21 | % sweep = {"N", 2, "dlMMI", linspace(-18, -10.5, 11)}; 22 | % sweepName = "AR8_passive_MMI_1x2_deep_dl"; 23 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 24 | % 'submitjob', 0, 'randomize', 0); 25 | 26 | % %% MMI: 1x3 deep 27 | % def_GaAs_MMI_AR8; 28 | % sweep = {"N", 3, "wMMI", "2*(wSpace + wWG1)", "lMMI", "lPi/4", "dlMMI", linspace(-20, -10.5, 11)}; 29 | % sweepName = "AR8_passive_MMI_1x3_deep_dl"; 30 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 31 | % 'submitjob', 0, 'randomize', 0); 32 | 33 | % %% MMI: 1x4 deep 34 | % def_GaAs_MMI_AR8; 35 | % sweep = {"N", 4, "dlMMI", linspace(-25, 14.5, 11)}; 36 | % sweepName = "AR8_passive_MMI_1x4_deep_dl"; 37 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 38 | % 'submitjob', 0, 'randomize', 0); 39 | 40 | 41 | %% Large wavelength sweep for 1x2 and 1x4 42 | % sweep = {"wWG1", 1.5, "lPi", "(4 * nr * wMMI^2 ) / ( 3 * 1.03 )", ... 43 | % "N", 2, "dlMMI", -9, "lambda", linspace(0.80, 1.4, 150)}; 44 | % sweepName = "AR8_passive_MMI_1x2_deep_varFDTD_lambda"; 45 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 46 | % 'submitjob', 0, 'randomize', 0); 47 | 48 | % sweep = {"wWG1", 1.5, "lPi", "(4 * nr * wMMI^2 ) / ( 3 * 1.03 )", ... 49 | % "N", 4, "dlMMI", -10, "lambda", linspace(0.80, 1.4, 150)}; 50 | % sweepName = "AR8_passive_MMI_1x4_deep_varFDTD_lambda"; 51 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 52 | % 'submitjob', 0, 'randomize', 0); 53 | -------------------------------------------------------------------------------- /MATLAB/template/devSSC/def_GaAs_SSC_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "SSC"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_WG.lsf"; 31 | devScript = lsfDir + "22_etch_" + epiType + "_" + componentName + ".lsf"; 32 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 33 | 34 | % Script naming 35 | scriptName = epiType + "_" + epiName + "_" + componentName; 36 | 37 | % Default variable alterations, if any 38 | setVars = {"sim2D", 2, "Vpad", 0, "outMFD", 2, "lActive", 0, "aFacet", 0, ... 39 | "lWG", 5, "lFS", 0.5, "etch1", 3, "etch2", 3, "wWG1", 2.0}; 40 | 41 | 42 | %% Process 43 | % Assemble script 44 | script = string(fileread(templateHeader)) + "\n\n"; 45 | script = script + string(fileread(etchScript)) + "\n\n"; 46 | script = script + string(fileread(devScript)) + "\n\n"; 47 | script = script + string(fileread(mqwScript)) + "\n\n"; 48 | script = script + string(fileread(epiScript)) + "\n\n"; 49 | script = script + string(fileread(instScript)) + "\n\n"; 50 | script = script + string(fileread(templateFooter)); 51 | 52 | 53 | %% Clean up 54 | clearvars -except script scriptName setVars 55 | -------------------------------------------------------------------------------- /MATLAB/template/devSSC/plot_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_SSC_AR8; 10 | 11 | % Non-modulating 12 | dualMode = 0; 13 | resExts = "_varFDTD.mat"; 14 | sweepName = "AR8_passive_SSC_deep"; 15 | % sweepName = "AR8_passive_deep_aFacet"; 16 | 17 | % resExts = "_FDTD.mat"; 18 | % sweepName = "AR8_passive_SSC_deep_FDTD"; 19 | % sweepName = "AR8_passive_deep_aFacet_FDTD"; 20 | 21 | % Plot parameters 22 | noPlotParams = ["simY", "SSC2 Length"]; 23 | noPlotTogether = ["Pout [dB]", "Transmitted Fraction [dB]"]; 24 | savePlot = 0; 25 | plot1D = 1; 26 | 27 | 28 | %% Definitions 29 | % Plotting script 30 | baseScript = "../sweepPlots_Base_20220929.m"; 31 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 32 | 33 | componentName = scriptName; 34 | 35 | % Precomputation 36 | preComp = "if ~isfield(R, 'aFacet'); R.aFacet=0; end"; 37 | 38 | % Files to load 39 | resFiles = []; 40 | for sweep = sweepName 41 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 42 | end 43 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 44 | 45 | % Parameters to plot; needs to evaluate to doubles 46 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 47 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.inPort{1}.pol", "R.outField.pol", "R.wgBR", "R.lWG", ... 48 | "R.wSSC1", "R.wSSC2", "R.dSSC1", "R.dSSC2", "R.lSSC1", "R.lSSC2", "R.lStrt", "R.lFS", "R.Q", "R.aFacet"]; 49 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 50 | "2D", "1D", "SiO2 Clad", "Input Polarization", "Output Polarization", "Bend Radius", "WG Length", ... 51 | "SSC1 Width", "SSC2 Width", "SSC1 Etch Depth", "SSC2 Etch Depth", "SSC1 Length", "SSC2 Length", "SSC Straight", "SSC FS Length", "SSC Curvature", "Facet Angle"]; 52 | 53 | % Metrics 54 | metrics = ["10*log10(R.results.Pout)", "10*log10(R.results.Ptr)", "10*log10(R.results.P11)"]; 55 | mLabels = ["Pout [dB]", "Transmitted Fraction [dB]", "Pin [dB]"]; 56 | 57 | % Data reduction 58 | rejectData = "R.sim2D == 1"; 59 | 60 | % Optional plotting options 61 | contour=10; 62 | contourlim=nan(2,numel(params)+numel(metrics)); 63 | % contourlim(:,params=="wWG1")=[1; 2]; 64 | nominal=nan(size(contourlim(1,:))); 65 | nominal(params=="R.wWG1")=2.0; 66 | nominal(params=="R.etch1")=2.5; 67 | % nominalvar=nominal * 0.2; % 20% variation 68 | nominalvar=NaN*nominal; % No nominal variation 69 | 70 | 71 | %% Call main plot function 72 | fprintf("Plotting '%s'...\n", sweepName); 73 | run(baseScript); 74 | -------------------------------------------------------------------------------- /MATLAB/template/devSSC/sweep_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_SSC_AR8; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"lActive", 0, ... 15 | "lWG", 2, "lFS", 0.5, ... 16 | "simY", 6, ... 17 | "etch2", 3.0, "wWG2", 20.0, ... 18 | "etch1", 3.0, "wWG1", 2.0, ... 19 | "wSSC1", 5, "lSSC1", 80:5:100}; 20 | sweepName = "AR8_passive_SSC_deep"; 21 | 22 | % sweep = {"lActive", 0, "sim2D", 0, ... 23 | % "lWG", 2, "lFS", 0.5, ... 24 | % "simY", 6, ... 25 | % "etch2", 3.0, "wWG2", 20.0, ... 26 | % "etch1", 3.0, "wWG1", 2.0, ... 27 | % "wSSC1", 5, "lSSC1", 80:5:100}; 28 | % sweepName = "AR8_passive_SSC_deep_FDTD"; 29 | 30 | % sweep = {"lActive", 0, "sim2D", 2, ... 31 | % "lWG", 10, "lFS", 4, ... 32 | % "wWG1", 5, "wWG2", 5, ... 33 | % "lSSC1", 0, "lSSC2", 0, "lStrt", 0, ... 34 | % "aFacet", 0:0.1:16}; 35 | % sweepName = "AR8_passive_deep_aFacet"; 36 | 37 | % sweep = {"lActive", 0, "sim2D", 0, ... 38 | % "lWG", 15, "lFS", 3, ... 39 | % "wWG1", 5, "wWG2", 5, ... 40 | % "lSSC1", 0, "lSSC2", 0, "lStrt", 0, ... 41 | % "aFacet", [0:0.1:18]}; 42 | % sweepName = "AR8_passive_deep_aFacet_FDTD"; 43 | 44 | 45 | %% Build scripts locally 46 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 47 | 'submitjob', 0, 'randomize', 0); 48 | -------------------------------------------------------------------------------- /MATLAB/template/devsBend/def_GaAs_sBend_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "sBend"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_WG.lsf"; 31 | devScript = lsfDir + "22_etch_" + epiType + "_" + componentName + ".lsf"; 32 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 33 | 34 | % Script naming 35 | scriptName = epiType + "_" + epiName + "_" + componentName; 36 | 37 | % Default variable alterations, if any 38 | setVars = {"sim2D", 2, "Vpad", 0, "lActive", 0, "outMFD", 0, "wWG1", 3.0, "wWG2", 10, "wgBR", 100}; 39 | 40 | 41 | %% Process 42 | % Assemble script 43 | script = string(fileread(templateHeader)) + "\n\n"; 44 | script = script + string(fileread(etchScript)) + "\n\n"; 45 | script = script + string(fileread(devScript)) + "\n\n"; 46 | script = script + string(fileread(mqwScript)) + "\n\n"; 47 | script = script + string(fileread(epiScript)) + "\n\n"; 48 | script = script + string(fileread(instScript)) + "\n\n"; 49 | script = script + string(fileread(templateFooter)); 50 | 51 | 52 | %% Clean up 53 | clearvars -except script scriptName setVars 54 | -------------------------------------------------------------------------------- /MATLAB/template/devsBend/plot_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_sBend_AR8; 10 | 11 | dualMode = 0; 12 | resExts = "_varFDTD.mat"; 13 | sweepName = "AR8_dev_Sbend_varFDTD_1"; 14 | % sweepName = ["AR8_dev_Sbend_varFDTD_2", "AR8_dev_Sbend_varFDTD_1"]; 15 | 16 | % Plot parameters 17 | noPlotParams = ["simY", "simX", "P12 [dB]"]; 18 | noPlotTogether = ["Pout [dB]", "P12 [dB]"]; 19 | savePlot = 0; 20 | plot1D = 0; 21 | 22 | 23 | %% Definitions 24 | % Plotting script 25 | baseScript = "sweepPlots_Base.m"; 26 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 27 | 28 | componentName = scriptName; 29 | 30 | % Precomputation 31 | % preComp = "R.outField.mfd(1) = 2e-6;"; 32 | 33 | % Files to load 34 | resFiles = []; 35 | for sweep = sweepName 36 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 37 | end 38 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 39 | 40 | % Parameters to plot; needs to evaluate to doubles 41 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", "diff(R.simX)", "R.maxModes", "R.dataRes", ... 42 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.inPort{1}.pol", "R.outField.pol", "R.wgBR", "R.lWG", ... 43 | "R.dY"]; 44 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", "simX", "# Modes", "Data Resolution", ... 45 | "2D", "1D", "SiO2 Clad", "Input Polarization", "Output Polarization", "Bend Radius", "WG Length", ... 46 | "Y-Offset"]; 47 | 48 | % Metrics 49 | metrics = ["10*log10(R.results.Pout)", "10*log10(R.results.P12)", "10*log10(R.results.Ptr)"]; 50 | mLabels = ["Pout [dB]", "P12 [dB]", "Transmitted Fraction [dB]"]; 51 | 52 | % Data reduction 53 | rejectData = "R.dY == 0"; 54 | 55 | % Optional plotting options 56 | contour=10; 57 | contourlim=nan(2,numel(params)+numel(metrics)); 58 | % contourlim(:,params=="wWG1")=[1; 2]; 59 | nominal=nan(size(contourlim(1,:))); 60 | nominal(params=="R.wWG1")=2.0; 61 | nominal(params=="R.etch1")=2.5; 62 | % nominalvar=nominal * 0.2; % 20% variation 63 | nominalvar=NaN*nominal; % No nominal variation 64 | 65 | 66 | %% Call main plot function 67 | fprintf("Plotting '%s'...\n", sweepName); 68 | run(baseScript); 69 | -------------------------------------------------------------------------------- /MATLAB/template/devsBend/sweep_GaAs_dev_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_sBend_AR8; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"sim2D", 2, ... 15 | "wWG1", [2.0, 3.0, 4.0], ... 16 | "wgBR", [0, linspace(10, 100, 30)]}; 17 | sweepName = "AR8_dev_Sbend_varFDTD_1"; 18 | 19 | % sweep = {"sim2D", 2, ... 20 | % "wWG1", [2.0, 3.0, 4.0], ... 21 | % "wgBR", linspace(100, 300, 30)}; 22 | % sweepName = "AR8_dev_Sbend_varFDTD_2"; 23 | 24 | 25 | %% Build scripts locally 26 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 27 | 'submitjob', 0, 'randomize', 0*0.02); 28 | 29 | %% Build and submit scripts 30 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 31 | % 'submitjob', 1, 'session', 'lumerical', 'randomize', 0*0.02, ... 32 | % 'submitcmd', "~/lumerical/Q_selected.sh varfdtd"); 33 | -------------------------------------------------------------------------------- /MATLAB/template/epi1D/def_GaAs_1D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "1D"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_WG.lsf"; 31 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 32 | 33 | % Script naming 34 | scriptName = epiType + "_" + epiName + "_" + componentName; 35 | 36 | % Default variable alterations, if any 37 | setVars = {"sim2D", 1, "sim1D", 1, "simY", 1, "simRes", 0.005, "dataRes", 0.005, "Vpad", 0}; 38 | 39 | 40 | %% Process 41 | % Assemble script 42 | script = string(fileread(templateHeader)) + "\n\n"; 43 | script = script + string(fileread(etchScript)) + "\n\n"; 44 | script = script + string(fileread(mqwScript)) + "\n\n"; 45 | script = script + string(fileread(epiScript)) + "\n\n"; 46 | script = script + string(fileread(instScript)) + "\n\n"; 47 | script = script + string(fileread(templateFooter)); 48 | 49 | 50 | %% Clean up 51 | clearvars -except script scriptName setVars 52 | -------------------------------------------------------------------------------- /MATLAB/template/epi1D/sweep_GaAs_1D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_1D_AR8; 11 | 12 | 13 | %% Define sweep parameters 14 | % sweep = {"tG", 0.10:0.05:0.60}; 15 | % sweepName = "AR8_active_tG"; 16 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 17 | % 'submitjob', 0, 'randomize', 0*0.05); 18 | 19 | sweep = {"lActive", 0, "Vpad", -10, ... 20 | "tG", 0.10:0.05:0.6}; 21 | sweepName = "AR8_mod_tG"; 22 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 23 | 'submitjob', 1, 'session', 'lumerical', 'randomize', 0*0.02, ... 24 | 'submitcmd', ["~/lumerical/Q_selected.sh charge", "~/lumerical/Q_selected.sh fde"]); 25 | 26 | % sweep = {"lActive", [0, 10]}; 27 | % sweepName = "AR8_AP"; 28 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 29 | % 'submitjob', 0, 'randomize', 0*0.05); 30 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/20_etch_GaAs_WG.lsf: -------------------------------------------------------------------------------- 1 | ## Set customizations 2 | # Simulation parameters 3 | savName = 'GaAs_AR8_WG'; 4 | sim2D = 1; # '0' for full 3D EME, FDTD, or CHARGE, '1' for YZ FDE or CHARGE, '2' for XY varFDTD, FDTD, or CHARGE, '3' for XZ EME or CHARGE; default 2 5 | sim1D = 0; # '1' for FDE 1D & pseudo-1D CHARGE 6 | simAccuracy = 4; 7 | simRes = 0.15; 8 | simResFine = 0.05; 9 | simModes = 200; 10 | simModeN = 3.49; 11 | lambda = 0.980; 12 | 13 | # Active material 14 | lActive = 10; 15 | 16 | # Modulation voltage 17 | Vpad = -10; 18 | contactSide = 0; # P-contact also on side of waveguide? 19 | 20 | # Structure 21 | wWG1 = 6.0; 22 | wWG2 = 30.0; 23 | lWG = 10; 24 | lPassive = lWG - lActive; 25 | simY = max([wWG1, wWG2]) + 2; 26 | dSimZ = [0, 0]; 27 | 28 | # Etch 29 | etch1 = 1.7; 30 | etch2 = 6; 31 | etchMat = 'SiO2'; # Cladding material 32 | passivationDepth = 1.0; # Passivation thickness 33 | 34 | # Analysis parameters 35 | dataRes = 0.05; 36 | wgBR = 0; # Bend radius 37 | inMFD = 0; # Default fundamental mode 38 | inPol = 0; 39 | outMFD = 2; # Look for output mode closest to a Gaussian 40 | outPol = 0; # 0 = TE, 1 = TM 41 | outField = {'pol': outPol, 'mfd': [outMFD, 0, 0]}; # .mfd(3) to be adjusted manually or in 40_inst 42 | 43 | # Recording parameters 44 | matFile = savName; 45 | maxModes = 10; # Save the best 10 modes only 46 | 47 | ## Etches 48 | etchDef = { 49 | # Waveguide definition 50 | {'name': 'Etch1', 'wgspace': simY/2+4, 'width': wWG1, 'length': lWG, 'depth': etch1, 'bend': wgBR, 'res': simRes}, 51 | {'name': 'Etch2', 'wgspace': simY/2+4, 'start': [0, 0, wWG2], 'length': lWG, 'depth': etch2} 52 | }; 53 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/20_etch_LiNbO3_x_WG.lsf: -------------------------------------------------------------------------------- 1 | ## Set customizations 2 | # Simulation parameters 3 | savName = 'LiNbO3_x_WG'; 4 | sim2D = 1; # '0' for full 3D EME, FDTD, or CHARGE, '1' for YZ FDE or CHARGE, '2' for XY varFDTD, FDTD, or CHARGE, '3' for XZ EME or CHARGE; default 2 5 | sim1D = 0; # '1' for FDE 1D & pseudo-1D CHARGE 6 | simAccuracy = 4; 7 | simRes = 0.15; 8 | simResFine = 0.05; 9 | simModes = 50; 10 | lambda = 1.03; 11 | 12 | # Modulation voltage 13 | Vpad = 10; 14 | 15 | # Etch 16 | etchDepth = 0.4; 17 | d = 0.4; 18 | d2 = 0; 19 | padDepth = d2 + 0.6; 20 | etchMat = 'SiO2'; # Cladding material 21 | passivationDepth = 1.0; # Passivation thickness 22 | 23 | # Structure 24 | wWG = 1.5; 25 | lWG = 10; 26 | dSimZ = [0, 0]; 27 | simY = 2*(wWG + d); 28 | 29 | # Analysis parameters 30 | dataRes = 0.05; 31 | wgBR = 0; # Bend radius 32 | inMFD = 0; # Default fundamental mode 33 | inPol = 0; 34 | outMFD = wWG/2; 35 | outPol = 0; # 0 = TE, 1 = TM 36 | outField = {'pol': outPol, 'mfd': [outMFD, 0, 0]}; 37 | 38 | # Recording parameters 39 | matFile = savName; 40 | maxModes = 5; # Save the best 10 modes only 41 | 42 | ## Etches 43 | etchDef = { 44 | {'name': 'Waveguide', 'wgspace': d, 'width': wWG, 'length': lWG, 'depth': etchDepth}, 45 | {'name': 'TopClad', 'start': [0,0,simY], 'length': lWG, 'depth': 0.001}, 46 | 47 | {'name': 'Npad', 'start': [0,-(wWG+d+d2),wWG], 'length': lWG, 'depth': padDepth, 'thickness': 1}, 48 | {'name': 'Ppad', 'start': [0,wWG+d+d2,wWG], 'length': lWG, 'depth': padDepth, 'thickness': 1} 49 | }; 50 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/20_etch_LiNbO3_z_WG.lsf: -------------------------------------------------------------------------------- 1 | ## Set customizations 2 | # Simulation parameters 3 | savName = 'LiNbO3_z_WG'; 4 | sim2D = 1; # '0' for full 3D EME, FDTD, or CHARGE, '1' for YZ FDE or CHARGE, '2' for XY varFDTD, FDTD, or CHARGE, '3' for XZ EME or CHARGE; default 2 5 | sim1D = 0; # '1' for FDE 1D & pseudo-1D CHARGE 6 | simAccuracy = 4; 7 | simRes = 0.15; 8 | simResFine = 0.05; 9 | simModes = 50; 10 | lambda = 1.03; 11 | 12 | # Modulation voltage 13 | Vpad = 10; 14 | 15 | # Etch 16 | etchDepth = 0.4; 17 | d = 0.4; 18 | d2 = 0.4; 19 | padDepth = d2 + 0.6; 20 | etchMat = 'SiO2'; # Cladding material 21 | passivationDepth = 1.0; # Passivation thickness 22 | 23 | # Structure 24 | wWG = 1.5; 25 | lWG = 10; 26 | dSimZ = [0, 0]; 27 | simY = 2*(wWG + d + d2); 28 | 29 | # Analysis parameters 30 | dataRes = 0.05; 31 | wgBR = 0; # Bend radius 32 | inMFD = 0; # Default fundamental mode 33 | inPol = 1; 34 | outMFD = wWG/2; 35 | outPol = 1; # 0 = TE, 1 = TM 36 | outField = {'pol': outPol, 'mfd': [outMFD, 0, 0]}; 37 | 38 | # Recording parameters 39 | matFile = savName; 40 | maxModes = 5; # Save the best 10 modes only 41 | 42 | ## Etches 43 | etchDef = { 44 | {'name': 'Waveguide', 'wgspace': d, 'width': wWG, 'length': lWG, 'depth': etchDepth+d2}, 45 | {'name': 'TopClad', 'start': [0,0,simY], 'length': lWG, 'depth': 0.001}, 46 | 47 | {'name': 'Npad1', 'start': [0,-(wWG+d),wWG], 'length': lWG, 'depth': padDepth, 'thickness': 1}, 48 | {'name': 'Npad2', 'start': [0,wWG+d,wWG], 'length': lWG, 'depth': padDepth, 'thickness': 1}, 49 | {'name': 'Ppad', 'start': [0,0,wWG], 'length': lWG, 'depth': 0.01, 'thickness': 1} 50 | }; 51 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/22_etch_GaAs_EulerU.lsf: -------------------------------------------------------------------------------- 1 | ## Additional component definition customizations or defaults 2 | # Simulation parameters 3 | makeMov = 0; 4 | etch2 = etch1; 5 | 6 | # Euler bend geometry 7 | # Using definitions from https://mathcurve.com/courbes2d.gb/cornu/cornu.shtml 8 | # Input and output waveguides assumed deep ridge and identical widths (wWG1) 9 | n = 300; 10 | th = pi/2; 11 | R = wgBR; 12 | L = 2*R*th; 13 | a = 2*R*(th^0.5); 14 | z = linspace(0, th^0.5, n); 15 | 16 | # Generate Euler curve centerline 17 | # Series expansion solved for x, see `Euler Curve.nb` 18 | x0 = a*(z - z^5/10 + z^9/216); 19 | y0 = -a*(z^3/3 - z^7/42 + z^11/1320); 20 | 21 | # Resample to semilinear x; crude, but it works 22 | x = linspace(0, max(x0), n); 23 | y = interp(y0, x0, x); 24 | 25 | # Calculate bounds using curve offsetting 26 | x1 = linspace(0, max(x) + wWG1/2, n); 27 | x2 = linspace(0, max(x) - wWG1/2, n); 28 | y1 = y; y2 = y; 29 | for( i = 1:n ) { 30 | yM = sqrt((wWG1/2)^2 - (x1 - x(i))^2); 31 | iR = find(yM == real(yM)); 32 | y1(iR) = amax([y1(iR), y(i) + yM(iR)], 2); 33 | 34 | yM = sqrt((wWG1/2)^2 - (x2 - x(i))^2); 35 | iR = find(yM == real(yM)); 36 | y2(iR) = amin([y2(iR), y(i) - yM(iR)], 2); 37 | } 38 | # closeall; plotxy(x1, y1, x2, y2, x, y); break; 39 | 40 | # Assemble polygons 41 | polyE1 = [x1, y1; flip(x1, 1), -flip(y1, 1) + 2*min(y1)]; 42 | polyE1(:,2) = polyE1(:,2) - mean(polyE1(:,2)); 43 | maxE1 = [max(polyE1(:,1)), max(polyE1(:,2))]; 44 | polyE1 = [maxE1(1) + 10, maxE1(2) + 10; 45 | 0, maxE1(2) + 10; 46 | 0, maxE1(2); 47 | polyE1; 48 | 0, -maxE1(2); 49 | 0, -maxE1(2) - 10; 50 | maxE1(1) + 10, -maxE1(2) - 10]; 51 | 52 | polyE2 = [x2, y2; flip(x2, 1), -flip(y2, 1) + 2*min(y2)]; 53 | polyE2(:,2) = polyE2(:,2) - mean(polyE2(:,2)); 54 | maxE2 = [max(polyE2(:,1)), max(polyE2(:,2))]; 55 | polyE2 = [0, maxE2(2); 56 | polyE2; 57 | 0, -maxE2(2)]; 58 | # closeall; plotxy(polyE1(:,1), polyE1(:,2), polyE2(:,1), polyE2(:,2)); break; 59 | 60 | clear(x, y, x1, y1, x2, y2, R, L, a, z); 61 | 62 | # Euler bend etch definition: replace initial 63 | etchDef = { 64 | {'name': 'etchPedestal', 'wgspace': 10, 'width': 2*maxE1(2) + wWG2, 'length': lWG + maxE1(1) + 10, 'depth': etch1}, 65 | 66 | {'name': 'wgIn_left', 'start': [0, maxE2(2) + wWG1 + lWG/2, lWG], 'length': lWG, 'depth': etch1}, 67 | {'name': 'wg_center', 'start': [0, 0, 2*maxE2(2)], 'length': lWG, 'depth': etch1}, 68 | {'name': 'wgOut_right', 'start': [0, -maxE2(2) - wWG1 - lWG/2, lWG], 'length': lWG, 'depth': etch1}, 69 | 70 | {'name': 'E1', 'start': lWG + min(polyE1(:,1)), 'poly': polyE1, 'depth': etch1}, 71 | {'name': 'E2', 'start': lWG + min(polyE2(:,1)), 'poly': polyE2, 'depth': etch1} 72 | }; 73 | 74 | # Adjust simulation extents 75 | simBuffer = 1; 76 | simX = [0, lWG + maxE1(1) + 4] + [1, -1]*simBuffer; 77 | simY = 2*maxE2(2) + 4*wWG1 + 2*simBuffer; 78 | dyIn = maxE2(2) + wWG1/2; 79 | portY = [-1, 1]*wWG1; 80 | 81 | # Set output port 82 | simMon = { 83 | 'type': 'port', 84 | 'name': 'port_2', 85 | 'x': 'in', 86 | 'y': portY - dyIn, 87 | 'mfd': 0 88 | }; 89 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/22_etch_GaAs_MMI.lsf: -------------------------------------------------------------------------------- 1 | ## Additional component definition customizations or defaults 2 | # Simulation parameters 3 | nr = 3.441; 4 | makeMov = 0; 5 | N = 2; 6 | wSpace = wWG1; 7 | lTaperIn = 5; 8 | lTaperOut = 5; 9 | dTaper = 0.5; # Un-tapered space between outputs 10 | inPol = 0; 11 | outPol = 0; 12 | 13 | # MMI calculations - see MMI.nb; high contrast assumed 14 | if( N ~= round(N) | N < 1 ) { N = max([round(N), 1]); } # Must be at least 1 output! 15 | wMMI = N * (wSpace + wWG1); 16 | lPi = (4 * nr * wMMI^2 ) / ( 3 * lambda ); 17 | lMMI = 3 * lPi / (4 * N); 18 | 19 | # Empirical corrections 20 | dwMMI = 0; 21 | dlMMI = 0; 22 | wMMI = wMMI + dwMMI; 23 | lMMI = lMMI + dlMMI; 24 | 25 | # Output positions 26 | y = wMMI * (2*(0:(N-1)) - (N-1)) / (2*N); 27 | 28 | # Input position 29 | dyIn = 0; 30 | 31 | # Input taper end width 32 | wIn = wMMI; 33 | if( abs(dyIn) > 0 ) { 34 | wIn = (wMMI/N - dTaper/2); 35 | } 36 | 37 | # MMI geometry: replace existing etchDef 38 | etchDef = { 39 | {'name': 'etchPedestal', 'wgspace': simY/2, 'width': wWG2, 'length': lWG + lTaperIn + lMMI + lTaperOut + lWG, 'depth': etch2}, 40 | 41 | {'name': 'inWG', 'wgspace': simY/2, 'start': [0, dyIn, wWG1], 'length': lWG, 'depth': etch1}, 42 | {'name': 'inTaper', 'wgspace': simY/2, 'end': [lWG + lTaperIn, dyIn, wIn], 'depth': etch1, 'res': simRes}, 43 | 44 | {'name': 'MMI', 'wgspace': simY/2, 'start': [lWG + lTaperIn, 0, wMMI], 'length': lMMI, 'depth': etch1, 'res': simRes}, 45 | 46 | {'name': 'outTaper', 'wgspace': simY/2, 'end': [lWG + lTaperIn + lMMI + lTaperOut, 0, 2*max(y) + wWG1], 'depth': etch1, 'res': simRes}, 47 | {'name': 'outBorder', 'wgspace': simY/2, 'length': lWG, 'depth': etch1} 48 | }; 49 | 50 | # Output ports 51 | if( N > 1 ) { 52 | dy = y(2) - y(1); 53 | for( yi = (y(1:end-1) + dy/2) ) { 54 | etchDef = appendcell(etchDef, { 55 | {'name': 'outTaper', 'start': [lWG + lTaperIn + lMMI, yi, dTaper], 'end': [lWG + lTaperIn + lMMI + lTaperOut, yi, dy-wWG1], 'depth': etch1}, 56 | {'name': 'outWG', 'length': lWG, 'depth': etch1} 57 | }); 58 | } 59 | clear(dy, yi); # Temporary variables only 60 | } 61 | 62 | # Adjust simulation extents 63 | simBuffer = 1; 64 | if( etch1 >= 2.5 ) { simY = wMMI + 2*simBuffer; } 65 | # simZ = [0, -etch1*2/3]; 66 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/22_etch_GaAs_SSC.lsf: -------------------------------------------------------------------------------- 1 | ## Additional component definition customizations or defaults 2 | # Simulation parameters 3 | makeMov = 0; 4 | 5 | # SSC geometry 6 | wSSC1 = 5; 7 | wSSC2 = wWG2; 8 | dSSC1 = etch1; 9 | dSSC2 = etch1; 10 | lSSC1 = 80; 11 | lSSC2 = lSSC1; 12 | lStrt = 2; 13 | lFS = 0.5; 14 | 15 | # SSC etch definition 16 | etchDef = appendcell(etchDef, { 17 | # SSC definition 18 | {'name': 'SSC1', 'wgspace': simY/2, 'start': [lWG, 0, wWG1], 'end': [lWG + lSSC1, 0, wSSC1], 'depth': dSSC1}, 19 | {'name': 'Straight1', 'wgspace': simY/2, 'length': lStrt, 'depth': dSSC1}, 20 | {'name': 'SSC2', 'wgspace': simY/2, 'start': [lWG + lSSC1 - lSSC2, 0, wWG2], 'end': [lWG + lSSC1, 0, wSSC2], 'depth': dSSC2}, 21 | {'name': 'Straight2', 'wgspace': simY/2, 'length': lStrt, 'depth': dSSC2}, 22 | {'name': 'FS', 'width': simY, 'depth': 5, 'length': lFS + 5} 23 | }); 24 | 25 | # Adjust simulation extents 26 | simBuffer = 1; 27 | simX = [simBuffer, lWG + lSSC1 + lStrt + lFS + simBuffer]; 28 | 29 | if( etch1 >= 2.5 ) { simY = wSSC1 + 2*simBuffer; } 30 | 31 | # Optional quadratic coupler lens 32 | Q = 0; 33 | if( Q ~= 0 ) { 34 | polyQ = linspace(-wSSC1/2, wSSC1/2, 51); 35 | polyQ = [Q * polyQ^2, polyQ]; 36 | dQ = max(abs(polyQ(:,1))); 37 | polyQ = [dQ + 3, -simY/2; 38 | 0, -simY/2; 39 | 0, -wSSC1/2; 40 | polyQ; 41 | 0, wSSC1/2; 42 | 0, simY/2; 43 | dQ + 3, simY/2]; 44 | 45 | ## Additional etches 46 | etchDef = appendcell(etchDef, { 47 | # Corner cube definition 48 | {'name': 'QCC', 'start': lWG + lSSC1 + lStrt - dQ, 'poly': polyQ, 'depth': etch1} 49 | }); 50 | } 51 | 52 | # Optional angled output 53 | aFacet = 0; 54 | if( aFacet ~= 0 ) { 55 | totalWG = lWG + lSSC1 + lStrt; 56 | 57 | etchRot = asin(3.441*sin(aFacet*pi/180))*180/pi - 2*aFacet; 58 | etchShift = -sin(etchRot*pi/180)*totalWG/2 - outMFD; 59 | dyIn = etchShift; 60 | dyOut = -dyIn - 2*outMFD; 61 | inRot = etchRot; 62 | portY = [-1, 1]*wWG1*(1 + abs(sin(etchRot*pi/180))); 63 | 64 | xFacet = wSSC1*sin(aFacet*pi/180); 65 | polyFacet = [0, -wSSC1/2; xFacet, -wSSC1/2; 66 | 0, wSSC1/2; 0, simY + 2*abs(etchShift); 67 | xFacet + lFS + 10, simY + 2*abs(etchShift); 68 | xFacet + lFS + 10, -(simY + 2*abs(etchShift)); 69 | 0, -(simY + 2*abs(etchShift))]; 70 | dxOut = abs(sin(etchRot*pi/180))*wSSC1/2 + abs(cos(etchRot*pi/180))*xFacet/4; 71 | simX = [ 1 + wWG1*sin(aFacet*pi/180), lWG + max([lSSC1, lSSC2]) + lFS + dxOut ]; 72 | simY = 2*abs(etchShift) + simY; 73 | 74 | for( i = 1:length(etchDef) ) { 75 | if( isfield(etchDef{i}, 'wgSpace') ) { 76 | etchDef{i}.wgspace = simY; 77 | } 78 | } 79 | etchDef{length(etchDef)} = {'name': 'Output Facet', 'poly': polyFacet, 'depth': 5}; 80 | } 81 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/22_etch_GaAs_sBend.lsf: -------------------------------------------------------------------------------- 1 | ## Additional component definition customizations or defaults 2 | # Simply redefine basic etch geometry for an sBend instead of FDE BR 3 | dY = 5; 4 | if( wgBR == 0 ) { dY = 0; } 5 | 6 | lBend = (2*pi*wgBR*dY)^0.5; 7 | 8 | # sBend etch definition 9 | etchDef = { 10 | {'name': 'Input1', 'wgspace': simY, 'start': [0, dY/2, wWG1], 'end': [1.5, dY/2, wWG1], 'depth': etch1}, 11 | {'name': 'Etch1', 'wgspace': simY, 'start': [1.5, dY/2, wWG1], 'end': [lBend+1.5, -dY/2, wWG1], 'depth': etch1}, 12 | {'name': 'Straight1', 'wgspace': simY, 'length': lWG, 'depth': etch1}, 13 | {'name': 'Input2', 'wgspace': simY, 'start': [0, dY/2, wWG2], 'end': [1.5, dY/2, wWG2], 'depth': etch1}, 14 | {'name': 'Etch2', 'wgspace': simY, 'start': [1.5, dY/2, wWG2], 'end': [lBend+1.5, -dY/2, wWG2], 'depth': etch2}, 15 | {'name': 'Straight2', 'wgspace': simY, 'length': lWG, 'depth': etch2} 16 | }; 17 | 18 | # Adjust input and output port location 19 | dyIn = dY/2; 20 | dyOut = -dY/2; 21 | 22 | # Adjust simulation extents 23 | simBuffer = 1; 24 | if( etch1 < 2.0 ) { simBuffer = simBuffer + wWG2/2; } 25 | simY = dY + 1.5*wWG1 + 2*simBuffer; 26 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/25_epi_GaAs_MQW.lsf: -------------------------------------------------------------------------------- 1 | ## Active material 2 | # Parameters 3 | mqwStrain = 1; 4 | cden = 2; 5 | Nqw = 3; 6 | mQW = 'InGaAs'; 7 | tQW = 6.0e-3; # IntelliEpi design; IQE may be 5 nm 8 | xQW = 0.31; # IntelliEpi design; IQE may be 0.30 9 | mQWB = 'GaAsP'; 10 | tQWB = 8e-3; # IntelliEpi design; IQE may be 6 nm 11 | xQWB = 1.00; # IntelliEpi design; IQE may be 0.90 12 | 13 | # MQW definition 14 | mqw = { 15 | {'thickness': 0.01, 'material': 'GaAs', 'doping': -0.05}, 16 | {'qw': round(tQWB*1e4)*1e-4, 'material': mQWB, 'x': round(100*xQWB)/100, 17 | 'cden': round(cden), 'strain': round(mqwStrain)} 18 | }; 19 | for( i = 1:round(Nqw) ) { 20 | mqw = appendcell(mqw, { 21 | {'qw': round(tQW*1e4)*1e-4, 'material': mQW, 'x': round(100*xQW)/100}, 22 | {'qw': round(tQWB*1e4)*1e-4, 'material': mQWB, 'x': round(100*xQWB)/100} 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/30_epi_GaAs_AR8.lsf: -------------------------------------------------------------------------------- 1 | ## Epitaxy 'AR8' regrowth design 2 | # Parameters 3 | tUC = 0.90; # Upper Clad 4 | xUC = 0.30; 5 | dUC = 0.20; 6 | 7 | tG = 0.30; # Guide (*2 for total thickness) 8 | 9 | tLC = 1.00; # Lower Clad 10 | xLC = 0.30; 11 | 12 | # Definition 13 | regrowth = {'xmin': round(lActive*10)/10, 'depth': sumstruct(mqw, 'qw')+sumstruct(mqw, 'thickness'), 'epitaxy': { 14 | {'thickness':0.2, 'material':'GaAs', 'doping': 3.0}, 15 | {'thickness':tUC, 'material':'AlGaAs', 'x':xUC, 'doping': dUC}, 16 | {'thickness':0.1, 'material':'GaAs', 'doping': 0.1}, 17 | {'thickness':tG, 'material':'GaAs', 'doping': 0.05} 18 | } }; 19 | epitaxy = appendcell(mqw, { 20 | {'thickness':tG, 'material':'GaAs', 'doping':-0.05, 'guiding': 1}, 21 | {'thickness':0.1, 'material':'GaAs', 'doping':-0.1}, 22 | {'thickness':tLC, 'material':'AlGaAs', 'x':xLC, 'doping':-0.2}, 23 | {'thickness':1.5, 'material':'GaAs', 'doping':-3.0, 'name':'buffer'}, 24 | {'thickness':5.0, 'material':'GaAs', 'doping':-1.0, 'name':'substrate'} 25 | }); 26 | 27 | # For shallow etch, increase simulation width 28 | if( etchDef{1}.depth <= sumstruct(regrowth, 'thickness') ) { simY = simY + 5; } 29 | if( etchDef{1}.depth <= sumstruct(regrowth, 'thickness')/2 ) { simY = simY + 5; } 30 | etchDef{1}.wgspace = simY/2; 31 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/30_epi_LiNbO3_x.lsf: -------------------------------------------------------------------------------- 1 | ## Typical x-cut LiNbO3 epitaxy 2 | epitaxy = { 3 | {'thickness':0.6, 'material':'LiNbO3_x', 'guiding': 1}, 4 | {'thickness':3.0, 'material':'SiO2'}, 5 | {'thickness':5.0, 'material':'Si'} 6 | }; 7 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/30_epi_LiNbO3_z.lsf: -------------------------------------------------------------------------------- 1 | ## Typical x-cut LiNbO3 epitaxy 2 | epitaxy = { 3 | {'thickness':d2, 'material':'SiO2'}, 4 | {'thickness':0.6, 'material':'LiNbO3_z', 'guiding': 1}, 5 | {'thickness':3.0, 'material':'SiO2'}, 6 | {'thickness':5.0, 'material':'Si'} 7 | }; 8 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/40_inst_GaAs.lsf: -------------------------------------------------------------------------------- 1 | ## Generic instrumentation for most possible arrangements 2 | 3 | ## Set defaults 4 | # Scalar defaults 5 | dVars = 'etchAngle, etchRot, makeMov, y, inMFD, outMFD, dyIn, dyOut, inPol, outPol, inRot, outRot, dPhi, passivationDepth, contactSide'; 6 | dVals = '0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0'; 7 | util_paramDefaults; 8 | 9 | # More complicated defaults 10 | N = length(y); 11 | 12 | 13 | ## Set z-limits and analysis limits 14 | lGuide = find(matstruct(epitaxy, 'guiding'),1); 15 | if( lGuide <= 0 ) { lGuide = round(length(epitaxy)/2); } 16 | zGuide = -(sumstruct(epitaxy{1:lGuide}, 'thickness') + sumstruct(epitaxy{1:lGuide}, 'qw')) + epitaxy{lGuide}.thickness/2; 17 | if( ~exist('simZ') ) { simZ = [simRes, -1]; } 18 | simZ = [max([max(simZ), simRes]), 19 | min([min(simZ), min(zGuide) - min([epitaxy{lGuide}.thickness, 0.75]) - 0.5 - max([inMFD, outMFD])/2])]; 20 | maxZ = 0; 21 | if( exist('regrowth') ) { 22 | maxZ = sumstruct(regrowth.epitaxy, 'thickness'); 23 | if( regrowth.xmin < 4 ) { maxZ = maxZ - regrowth.depth; } # Don't leave floating! 24 | } else if( lActive < 1 ) { 25 | maxZ = -sum(matstruct(etchDef, 'depth') * eqstruct(etchDef, 'name', 'Passive Etch') * (matstruct(etchDef, 'start') < 1.1) ); 26 | } 27 | if( ~exist('dSimZ') ) { dSimZ = [0, 0]; } 28 | simZ = simZ + [maxZ, 0] + dSimZ; 29 | 30 | 31 | ## Build ports and monitors 32 | # Nominal monitor size 33 | monY = [-1, 1] * simY/2; 34 | monYZ = [monY, simZ]; 35 | # Port size 36 | if( ~exist('portY') ) { 37 | if( etch1 > 0.75*etch2 ) { portY = [-1, 1] * (wWG1 + 2)/2; } # Deep etch 38 | else { portY = [-1, 1] * max([wWG1 + 2, min([wWG2 + 2, simY])])/2; } 39 | } 40 | 41 | # Input port 42 | inPort = { 'type': 'port', 43 | 'y': portY + dyIn, 44 | 'mfd': [inMFD, 0, zGuide], 45 | 'pol': inPol, 46 | 'rot': inRot 47 | }; 48 | 49 | # Output field centered on guiding layer, if exists 50 | if( exist('outField') ) { 51 | if( isfield(outField, 'mfd') ) { 52 | if( outField.mfd(3) == 0 ) { 53 | outField.mfd(3) = zGuide; 54 | } 55 | if( outField.mfd(2) == 0 ) { 56 | outField.mfd(2) = outRot; 57 | } 58 | } 59 | } 60 | 61 | # Output port(s) centered on output waveguide(s) 62 | if( ~exist('simMon') ) { 63 | simMon = cell(N); 64 | for( i = 1:N ) { 65 | simMon{i} = { 66 | 'type': 'port', 67 | 'name': 'port_'+num2str(i+1), 68 | 'phase': (i-1)*nanmean(dPhi/(N-1)), 69 | 'y': portY + y(i) + dyOut, 70 | 'mfd': [outMFD, dyOut, zGuide], 71 | 'pol': outPol, 72 | 'rot': outRot 73 | }; 74 | } 75 | clear(i); 76 | } 77 | 78 | # Add movies if specified 79 | if( makeMov > 0 ) { 80 | simMon = appendcell(simMon, { 81 | 'type': 'mov', 82 | 'name': 'mov_plane', 83 | 'geo': 'xy' 84 | }); 85 | if( sim2D == 0 ) { 86 | simMon = appendcell(simMon, { 87 | 'type': 'mov', 88 | 'name': 'mov_side', 89 | 'geo': 'xz' 90 | }); 91 | } 92 | } 93 | 94 | # Add top plane monitor in the case of a facet etch 95 | if( etchAngle ~= 0 & sim2D == 2 ) { 96 | simMon = appendcell(simMon, { 97 | 'type': 'E', 98 | 'name': 'topPlane', 99 | 'geo': 'xy', 100 | 'z': max(simZ) - 0.5 101 | }); 102 | } 103 | 104 | # If direction specified and flipped, swap the ports 105 | if( etchRot == 180 ) { 106 | tmpMode = inPort; inPort = simMon; simMon = tmpMode; 107 | clear(tmpMode); 108 | } 109 | 110 | 111 | ## Special handling for electrical contacts and CHARGE 112 | # Find reasonable pad depth for back-side contact 113 | padDepth = max([-min(simZ), sumstruct(epitaxy{1:find(eqstruct(epitaxy, 'name', 'buffer'))}, 'thickness')]) + 0.5; 114 | 115 | # Electrical contact geometry 116 | if( lActive > 0.5 | max(abs(Vpad)) > 0 | isDEVICE() ) { 117 | tC = 0.2; 118 | wP = wWG1*0.75 + passivationDepth*(contactSide>0.5); 119 | yP = (wP/2 - wWG1/4)*(contactSide>0.5); 120 | etchDef = appendcell(etchDef, { 121 | {'name': 'Npad', 'start': [0, 0, simY], 'length': lWG, 'depth': padDepth, 'thickness': tC}, 122 | {'name': 'Ppad', 'start': [0, yP + tC/10, wP + tC/20], 'length': lWG, 'depth': -maxZ + tC/10, 'thickness': tC} 123 | }); 124 | # Side contact 125 | if( contactSide >= 0.5 ) { 126 | etchDef = appendcell(etchDef, { 127 | {'name': 'Ppad_side', 'start': [0, wWG1/2 + passivationDepth + tC/2, tC], 128 | 'length': lWG, 'depth': -maxZ + etch1 - passivationDepth, 'thickness': abs(etch1 - passivationDepth) + 0.9*tC}, 129 | {'name': 'Ppad_extended', 'start': [0, yP + wP/2 + 10, 20], 130 | 'length': lWG, 'depth': -maxZ + etch1 - passivationDepth, 'thickness': tC} 131 | }); 132 | } 133 | } 134 | 135 | if( isDEVICE() ) { 136 | # Expand to include contacts 137 | simZ = [maxZ + 0.15, min([min(simZ), -padDepth + 0.05])]; 138 | 139 | # Reduce unneeded Y-span 140 | if( sim2D == 1 ) { simY = wWG1 + 4; } # Want at least some width for shallow-ridge overlaps 141 | if( sim1D == 1 ) { simY = 0.5; etchDef{1}.res = 0.25; } 142 | 143 | # Electrical contact values 144 | if( ~exist('Vpad') ) { Vpad = [0, -10]; } 145 | Vpad = unique([0, Vpad]); 146 | Vpad = Vpad(sortmap(abs(Vpad))); # Initialize at zero for speed; assume only positive or negative sweeping 147 | contacts = { 148 | {'name': 'Ppad', 'V': Vpad}, 149 | {'name': 'Npad', 'V': 0} # Back-side contacted 150 | }; 151 | if( contactSide >= 0.5 ) { 152 | contacts = appendcell(contacts, { 153 | {'name': 'Ppad_side', 'V': Vpad}, 154 | {'name': 'Ppad_extended', 'V': Vpad} 155 | }); 156 | } 157 | 158 | } 159 | 160 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/40_inst_LiNbO3.lsf: -------------------------------------------------------------------------------- 1 | ## Generic instrumentation for most possible arrangements 2 | 3 | ## Set defaults 4 | # Scalar defaults 5 | dVars = 'etchAngle, etchRot, makeMov, y, inMFD, outMFD, dyIn, dyOut, inPol, outPol, inRot, outRot, dPhi, passivationDepth, contactSide'; 6 | dVals = '0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0'; 7 | util_paramDefaults; 8 | 9 | # More complicated defaults 10 | N = length(y); 11 | 12 | 13 | ## Set z-limits and analysis limits 14 | lGuide = find(matstruct(epitaxy, 'guiding'),1); 15 | if( lGuide <= 0 ) { lGuide = round(length(epitaxy)/2); } 16 | zGuide = -(sumstruct(epitaxy{1:lGuide}, 'thickness') + sumstruct(epitaxy{1:lGuide}, 'qw')) + epitaxy{lGuide}.thickness/2; 17 | if( ~exist('simZ') ) { simZ = [simRes, -1]; } 18 | simZ = [max([max(simZ), simRes]), 19 | min([min(simZ), min(zGuide) - min([epitaxy{lGuide}.thickness, 0.5]) - max([inMFD, outMFD])/2])]; 20 | maxZ = -sum(matstruct(etchDef, 'depth') * eqstruct(etchDef, 'name', 'Passive Etch') * (matstruct(etchDef, 'start') < 1.1) ); 21 | if( ~exist('dSimZ') ) { dSimZ = [0, 0]; } 22 | simZ = simZ + [maxZ, 0] + dSimZ; 23 | 24 | simZ = [-epitaxy{1}.thickness-0.75, 0.25]; 25 | 26 | 27 | ## Build ports and monitors 28 | # Nominal monitor size 29 | monY = [-1, 1] * simY/2; 30 | monYZ = [monY, simZ]; 31 | # Port size 32 | if( ~exist('portY') ) { 33 | portY = [-1, 1] * (wWG + d + d2); 34 | } 35 | 36 | # Input port 37 | inPort = { 'type': 'port', 38 | 'y': portY + dyIn, 39 | 'mfd': [inMFD, 0, zGuide], 40 | 'pol': inPol, 41 | 'rot': inRot 42 | }; 43 | 44 | # Output field centered on guiding layer, if exists 45 | if( exist('outField') ) { 46 | if( isfield(outField, 'mfd') ) { 47 | if( outField.mfd(3) == 0 ) { 48 | outField.mfd(3) = zGuide; 49 | } 50 | if( outField.mfd(2) == 0 ) { 51 | outField.mfd(2) = outRot; 52 | } 53 | } 54 | } 55 | 56 | # Output port(s) centered on output waveguide(s) 57 | if( ~exist('simMon') ) { 58 | simMon = cell(N); 59 | for( i = 1:N ) { 60 | simMon{i} = { 61 | 'type': 'port', 62 | 'name': 'port_'+num2str(i+1), 63 | 'phase': (i-1)*nanmean(dPhi/(N-1)), 64 | 'y': portY + y(i) + dyOut, 65 | 'mfd': [outMFD, dyOut, zGuide], 66 | 'pol': outPol, 67 | 'rot': outRot 68 | }; 69 | } 70 | clear(i); 71 | } 72 | 73 | # Add movies if specified 74 | if( makeMov > 0 ) { 75 | simMon = appendcell(simMon, { 76 | 'type': 'mov', 77 | 'name': 'mov_plane', 78 | 'geo': 'xy' 79 | }); 80 | if( sim2D == 0 ) { 81 | simMon = appendcell(simMon, { 82 | 'type': 'mov', 83 | 'name': 'mov_side', 84 | 'geo': 'xz' 85 | }); 86 | } 87 | } 88 | 89 | # Add top plane monitor in the case of a facet etch 90 | if( etchAngle ~= 0 & sim2D == 2 ) { 91 | simMon = appendcell(simMon, { 92 | 'type': 'E', 93 | 'name': 'topPlane', 94 | 'geo': 'xy', 95 | 'z': max(simZ) - 0.5 96 | }); 97 | } 98 | 99 | # If direction specified and flipped, swap the ports 100 | if( etchRot == 180 ) { 101 | tmpMode = inPort; inPort = simMon; simMon = tmpMode; 102 | clear(tmpMode); 103 | } 104 | 105 | 106 | ## Special handling for electrical contacts and CHARGE 107 | # Find reasonable pad depth for back-side contact 108 | padDepth = max([-min(simZ), sumstruct(epitaxy{1:find(eqstruct(epitaxy, 'name', 'buffer'))}, 'thickness')]) + 0.5; 109 | 110 | if( isDEVICE() ) { 111 | # Needs to include semiconductor for a CHARGE simulation to work 112 | simZ = [max(simZ), min(simZ)-sumstruct(epitaxy{1:length(epitaxy)-1}, 'thickness') - 0.25]; 113 | 114 | # Electrical contact values 115 | if( ~exist('Vpad') ) { Vpad = [0, -10]; } 116 | Vpad = unique([0, Vpad]); 117 | Vpad = Vpad(sortmap(abs(Vpad))); # Initialize at zero for speed; assume only positive or negative sweeping 118 | contacts = { {'name': 'Ppad', 'V': Vpad} }; 119 | if( sum(eqstruct(etchDef, 'name', 'Npad')) > 0 ) { 120 | contacts = appendcell(contacts, {{'name': 'Npad', 'V': 0}}); 121 | } 122 | if( sum(eqstruct(etchDef, 'name', 'Npad1')) > 0 ) { 123 | contacts = appendcell(contacts, {{'name': 'Npad1', 'V': 0}}); 124 | } 125 | if( sum(eqstruct(etchDef, 'name', 'Npad2')) > 0 ) { 126 | contacts = appendcell(contacts, {{'name': 'Npad2', 'V': 0}}); 127 | } 128 | } 129 | 130 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/90_footer.lsf: -------------------------------------------------------------------------------- 1 | ## Initialize and build 2 | lum_setup; 3 | 4 | # Post-setup instructions 5 | if( exist('postSetup') ) { 6 | if( length(postSetup) > 0 ) { 7 | eval(postSetup); 8 | } 9 | } 10 | 11 | # Change FDE to 1D if desired 12 | if( solverFDE() & sim1D > 0 ) { setnamed('FDE', 'solver type', '1D Z:X prop'); } 13 | 14 | 15 | ## Save self 16 | cd(filedirectory(currentscriptname)); 17 | save(replacestring(savName, '.', '')); 18 | -------------------------------------------------------------------------------- /MATLAB/template/lsf/99_MQW_peaks.lsf: -------------------------------------------------------------------------------- 1 | addpath('/home/nickersonm/lumerical/common'); # Replace as necessary, optionally adding multiple potential locations for different environments 2 | util_fCommon; # Load common functions 3 | 4 | ### Run MQW simulation and return peaks 5 | ## New simulation 6 | clear; newproject; 7 | clear; newproject; 8 | cd(filedirectory(currentscriptname)); 9 | 10 | 11 | ## Set customizations 12 | # Simulation parameters 13 | savName = 'MQW_peaks'; 14 | matFile = savName; 15 | mqwStrain = 1; 16 | mqwRecord = 0; 17 | 18 | # Active material 19 | cden = 2; 20 | Nqw = 3; 21 | mQW = 'InGaAs'; 22 | tQW = 6.0e-3; # IntelliEpi design; IQE may be 5 nm 23 | xQW = 0.31; # IntelliEpi design; IQE may be 0.30 24 | mQWB = 'GaAsP'; 25 | tQWB = 8e-3; # IntelliEpi design; IQE may be 6 nm 26 | xQWB = 1.00; # IntelliEpi design; IQE may be 0.90 27 | 28 | 29 | ## Epitaxy 30 | etchDef = {{'name': 'dummy', 'width': 1, 'length': 1, 'depth': 1e-3}}; 31 | epitaxy = {{'name': 'dummy', 'thickness': 1, 'material':'GaAs'}}; 32 | mqw = { 33 | {'thickness': 0.01, 'material':'GaAs', 'cden': cden, 'strain': mqwStrain, 'record': mqwRecord}, 34 | {'thickness': tQWB, 'material':mQWB, 'x':xQWB} 35 | }; 36 | for( i = 1:round(Nqw) ) { 37 | mqw = appendcell(mqw, { 38 | {'thickness': tQW, 'material':mQW, 'x':xQW}, 39 | {'thickness': tQWB, 'material':mQWB, 'x':xQWB} 40 | }); 41 | } 42 | mqw = appendcell(mqw, {{'thickness': 0.01, 'material':'GaAs'}}); 43 | 44 | 45 | 46 | ### Run MQW calculations 47 | lum_setup; # Load `addMQW` function 48 | 49 | # Calculate 50 | mqw = addMQW(mqw); mqw = mqw{2}; 51 | 52 | 53 | ## Analyze 54 | # PL peak 55 | peakI = sortmap(mqw.emission.stimulated_TE(:,1), 0); 56 | mqw.peak = [mean(mqw.emission.wavelength(peakI(1:3))), 57 | mean(mqw.emission.stimulated_TE(peakI(1:3)))]; 58 | 59 | 60 | ## Save MQW results 61 | matlabsave(matFile); 62 | 63 | -------------------------------------------------------------------------------- /MATLAB/template/wgActive/def_GaAs_WG_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "WG"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_" + componentName + ".lsf"; 31 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 32 | 33 | % Script naming 34 | scriptName = epiType + "_" + epiName + "_" + componentName; 35 | 36 | % Default variable alterations, if any 37 | setVars = {"sim2D", 1, "Vpad", 0}; 38 | 39 | 40 | %% Process 41 | % Assemble script 42 | script = string(fileread(templateHeader)) + "\n\n"; 43 | script = script + string(fileread(etchScript)) + "\n\n"; 44 | script = script + string(fileread(mqwScript)) + "\n\n"; 45 | script = script + string(fileread(epiScript)) + "\n\n"; 46 | script = script + string(fileread(instScript)) + "\n\n"; 47 | script = script + string(fileread(templateFooter)); 48 | 49 | 50 | %% Clean up 51 | clearvars -except script scriptName setVars 52 | -------------------------------------------------------------------------------- /MATLAB/template/wgActive/plot_GaAs_2D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_WG_AR8; 10 | 11 | 12 | %% Set customizations 13 | dualMode = 0; 14 | resExts = "_FDE.mat"; 15 | sweepName = "AR8_2D_active_deep"; 16 | % sweepName = "AR8_2D_active_shallow"; 17 | 18 | 19 | %% Plot parameters 20 | noPlotParams = ["R.padDepth", "diff(R.simY)", "R.maxModes", ... 21 | "TM Modal Gain [dB/cm]", "TE Modal Gain [dB/cm]", ... 22 | "TM Phase Modulation [rad/mm]", "TM RAM [dB/mm]", ... 23 | "Desired Output Overlap", "High Isolation & Overlap", ... 24 | "Modal Isolation [dB/cm]", "TM Modal Isolation [dB/cm]", ... 25 | "Effective Index", "Fundamental Modal Gain [dB/cm]", ... 26 | "Fundamental Mode Number"]; 27 | noPlotTogether = ["Fundamental TE Gain [dB/cm]", "Modal Isolation [dB/cm]"; 28 | "Fundamental TE Gain [dB/cm]", "TE RAM [dB/mm]"; 29 | "Fundamental TE Gain [dB/cm]", "TE Phase Modulation [rad/mm]"; 30 | "Fundamental TE Gain [dB/cm]", "Effective Index"; 31 | "TE RAM [dB/mm]", "Modal Isolation [dB/cm]"; 32 | "TE RAM [dB/mm]", "TE Phase Modulation [rad/mm]"; 33 | "Modal Isolation [dB/cm]", "TE Phase Modulation [rad/mm]"; 34 | "Effective Index", "TE RAM [dB/mm]"; 35 | "Effective Index", "Modal Isolation [dB/cm]"; 36 | "Effective Index", "TE Phase Modulation [rad/mm]"; 37 | "\Gamma_{MQW}", "Effective Index"; 38 | "\Gamma_{MQW}", "Modal Isolation [dB/cm]"; 39 | "\Gamma_{MQW}", "Fundamental TE Gain [dB/cm]"; 40 | "\Gamma_{MQW}", "Fundamental Modal Gain [dB/cm]"]; 41 | savePlot = 0; 42 | plot1D = 0; 43 | 44 | 45 | %% Definitions 46 | % Plotting script 47 | baseScript = "sweepPlots_Base.m"; 48 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 49 | 50 | componentName = scriptName; 51 | 52 | % Precomputation 53 | preComp = ""; 54 | 55 | % Postcomputation 56 | postComp = ['if ~exist("iP", "var"); [~, iP] = sort( R.results.modeL + -1000*log10(R.results.Pout) ); end;', ... 57 | 'mode0 = getfield(R.simData, "mode"+string(R.results.modeN(iP(1)))); mode0.E = sum(mode0.E, [1,2]); mode0.y = 0;', ... 58 | 'activeRegion = struct("x", mode0.x, "y", 0);', ... 59 | 'activeRegion.z = 1e-6*unique(cell2mat(arrayfun(@(i) linspace(R.layerProps(i,1), R.layerProps(i,2), 50), 1:size(R.layerProps,1), "UniformOutput", false))'');', ... 60 | 'activeRegion.E = zeros(1, 1, numel(activeRegion.z), 3);', ... 61 | 'layerMQW = real(R.layerProps( imag(R.layerProps(:,3)) < 0, 1:2));', ... 62 | 'activeRegion.E(:, :, layerMQW(2) <= 1e6*activeRegion.z & 1e6*activeRegion.z <= layerMQW(1), 2) = 1;']; 63 | 64 | % Files to load 65 | resFiles = []; 66 | for sweep = sweepName 67 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 68 | end 69 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 70 | 71 | % Parameters to plot; needs to evaluate to doubles 72 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 73 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.outField.pol", "R.wgBR", ... 74 | "R.cden", "R.Nqw", "R.tQW*1e3", "R.tQWB*1e3", "R.xQW", "R.xQWB", "R.mqwStrain", ... 75 | "R.tUC", "R.xUC", ... 76 | "R.tG", ... 77 | "R.tLC", "R.xLC"]; 78 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 79 | "2D", "1D", "SiO2 Clad", "Polarization", "Bend Radius", ... 80 | "Carrier Density [1e18 cm^-3]", "# QW", "QW Thickness [nm]", "Barrier Thickness [nm]", "In-fraction QW", "As-fraction Barrier", "Strain Calculated", ... 81 | "Upper Clad Thickness [µm]", "Upper Clad x-frac", ... 82 | "Guide Thickness [µm]", ... 83 | "Lower Clad Thickness [µm]", "Lower Clad x-frac"]; 84 | 85 | % Metrics 86 | metrics = ["R.results.modeN(iP(1))", ... 87 | "-R.results.lossTE(iP(1)) - 1e3*(R.results.Pout(iP(1))<0.05)", ... 88 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 89 | "min([R.results.lossTM(iTM(iTM ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 90 | "min([R.results.modeL(iP(iP ~= iP(1))) - R.results.modeL(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 91 | "R.results.Pout(iP(1))", ... 92 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1)); 100])*R.results.Pout(iP(1))", ... 93 | "abs(R.results.modeNeff(iP(1)))", ... 94 | "real(fieldOverlap(mode0, activeRegion)) * (R.Nqw * R.tQW) / (R.Nqw * (R.tQW + R.tQWB) + R.tQWB)"]; 95 | mLabels = ["Fundamental Mode Number", ... 96 | "Fundamental TE Gain [dB/cm]", ... 97 | "TE Modal Isolation [dB/cm]", ... 98 | "TM Modal Isolation [dB/cm]", ... 99 | "Modal Isolation [dB/cm]", ... 100 | "Desired Output Overlap", ... 101 | "High Isolation & Overlap", ... 102 | "Effective Index", ... 103 | "\Gamma_{MQW}"]; 104 | 105 | % Data reduction 106 | rejectData = "~isfield(R, 'tG')"; 107 | 108 | % Optional plotting options 109 | contour=10; 110 | contourlim=nan(2,numel(params)+numel(metrics)); 111 | % contourlim(:,params=="wWG1")=[1; 2]; 112 | nominal=nan(size(contourlim(1,:))); 113 | nominal(params=="R.wWG1")=2.0; 114 | nominal(params=="R.etch1")=2.5; 115 | % nominalvar=nominal * 0.2; % 20% variation 116 | nominalvar=NaN*nominal; % No nominal variation 117 | 118 | 119 | %% Call main plot function 120 | fprintf("Plotting '%s'...\n", sweepName); 121 | run(baseScript); 122 | -------------------------------------------------------------------------------- /MATLAB/template/wgActive/sweep_GaAs_2D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_WG_AR8; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"lActive", 10, ... 15 | "simY", "wWG1 + 2", "wWG2", 30, ... 16 | "etch1", linspace(2, 6, 15), "wWG1", linspace(2, 6, 15)}; 17 | sweepName = "AR8_2D_active_deep"; 18 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 19 | 'submitjob', 0, 'randomize', 0.02); 20 | 21 | % sweep = {"lActive", 10, "outMFD", 3, ... 22 | % "simResFine", 0.1, ... 23 | % "simY", 30, "dSimZ", "[0, 1]", "wWG2", 40, ... 24 | % "etch1", linspace(0.5, 3.5, 15), ... 25 | % "wWG1", linspace(2.5, 6, 15)}; 26 | % sweepName = "AR8_2D_active_shallow"; 27 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 28 | % 'submitjob', 0, 'randomize', 0.02); 29 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod/def_GaAs_WG_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "WG"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_" + componentName + ".lsf"; 31 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 32 | 33 | % Script naming 34 | scriptName = epiType + "_" + epiName + "_" + componentName; 35 | 36 | % Default variable alterations, if any 37 | setVars = {"sim2D", 1, "lActive", 0, "Vpad", -10}; 38 | 39 | 40 | %% Process 41 | % Assemble script 42 | script = string(fileread(templateHeader)) + "\n\n"; 43 | script = script + string(fileread(etchScript)) + "\n\n"; 44 | script = script + string(fileread(mqwScript)) + "\n\n"; 45 | script = script + string(fileread(epiScript)) + "\n\n"; 46 | script = script + string(fileread(instScript)) + "\n\n"; 47 | script = script + string(fileread(templateFooter)); 48 | 49 | 50 | %% Clean up 51 | clearvars -except script scriptName setVars 52 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod/plot_GaAs_2D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_WG_AR8; 10 | 11 | %% Set customizations 12 | resExts = ["_FDE.mat", "_CHARGE.mat"]; 13 | dualMode = 1; 14 | sweepName = "AR8_2D_mod_wWG"; 15 | 16 | 17 | %% Plot parameters 18 | noPlotParams = ["R.padDepth", "diff(R.simY)", "R.maxModes", ... 19 | "TM Modal Gain [dB/cm]", "TE Modal Gain [dB/cm]", ... 20 | "TM Phase Modulation [rad/mm]", "TM RAM [dB/mm]", ... 21 | "Desired Output Overlap", "High Isolation & Overlap", ... 22 | "Modal Isolation [dB/cm]", "TM Modal Isolation [dB/cm]", ... 23 | "Effective Index", "Fundamental Modal Gain [dB/cm]", ... 24 | "Fundamental Mode Number"]; 25 | noPlotTogether = ["Fundamental TE Gain [dB/cm]", "Modal Isolation [dB/cm]"; 26 | "Fundamental TE Gain [dB/cm]", "TE RAM [dB/mm]"; 27 | "Fundamental TE Gain [dB/cm]", "TE Phase Modulation [rad/mm]"; 28 | "Fundamental TE Gain [dB/cm]", "Effective Index"; 29 | "TE RAM [dB/mm]", "Modal Isolation [dB/cm]"; 30 | "TE RAM [dB/mm]", "TE Phase Modulation [rad/mm]"; 31 | "Modal Isolation [dB/cm]", "TE Phase Modulation [rad/mm]"; 32 | "Effective Index", "TE RAM [dB/mm]"; 33 | "Effective Index", "Modal Isolation [dB/cm]"; 34 | "Effective Index", "TE Phase Modulation [rad/mm]"]; 35 | savePlot = 0; 36 | plot1D = 1; 37 | 38 | 39 | %% Definitions 40 | % Plotting script 41 | baseScript = "sweepPlots_Base.m"; 42 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 43 | 44 | componentName = scriptName; 45 | 46 | % Precomputation 47 | preComp = ""; 48 | 49 | % Postcomputation 50 | % postComp = ['if ~exist("iP", "var"); [~, iP] = sort( R.results.modeL + -1000*log10(R.results.Pout) ); end;', ... 51 | % 'mode0 = getfield(R.simData, "mode"+string(R.results.modeN(iP(1)))); mode0.E = sum(mode0.E, [1,2]); mode0.y = 0;', ... 52 | % 'activeRegion = struct("x", mode0.x, "y", 0);', ... 53 | % 'activeRegion.z = 1e-6*unique(cell2mat(arrayfun(@(i) linspace(R.layerProps(i,1), R.layerProps(i,2), 50), 1:size(R.layerProps,1), "UniformOutput", false))'');', ... 54 | % 'activeRegion.E = zeros(1, 1, numel(activeRegion.z), 3);', ... 55 | % 'layerMQW = real(R.layerProps( imag(R.layerProps(:,3)) < 0, 1:2));', ... 56 | % 'activeRegion.E(:, :, layerMQW(2) <= 1e6*activeRegion.z & 1e6*activeRegion.z <= layerMQW(1), 2) = 1;']; 57 | 58 | % Files to load 59 | resFiles = []; 60 | for sweep = sweepName 61 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 62 | end 63 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 64 | 65 | % Parameters to plot; needs to evaluate to doubles 66 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 67 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.outField.pol", "R.wgBR", ... 68 | "R.cden", "R.Nqw", "R.tQW*1e3", "R.tQWB*1e3", "R.xQW", "R.xQWB", "R.mqwStrain", ... 69 | "R.tUC", "R.xUC", ... 70 | "R.tG", ... 71 | "R.tLC", "R.xLC"]; 72 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 73 | "2D", "1D", "SiO2 Clad", "Polarization", "Bend Radius", ... 74 | "Carrier Density [1e18 cm^-3]", "# QW", "QW Thickness [nm]", "Barrier Thickness [nm]", "In-fraction QW", "As-fraction Barrier", "Strain Calculated", ... 75 | "Upper Clad Thickness [µm]", "Upper Clad x-frac", ... 76 | "Guide Thickness [µm]", ... 77 | "Lower Clad Thickness [µm]", "Lower Clad x-frac"]; 78 | 79 | % Metrics 80 | metrics = ["R.results.modeN(iP(1))", ... 81 | "-R.results.lossTE(iP(1)) - 1e3*(R.results.Pout(iP(1))<0.05)", ... 82 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 83 | "min([R.results.lossTM(iTM(iTM ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 84 | "min([R.results.modeL(iP(iP ~= iP(1))) - R.results.modeL(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 85 | "R.results.Pout(iP(1))", ... 86 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1)); 100])*R.results.Pout(iP(1))", ... 87 | "abs(R.results.modeNeff(iP(1)))"]; 88 | mLabels = ["Fundamental Mode Number", ... 89 | "Fundamental TE Gain [dB/cm]", ... 90 | "TE Modal Isolation [dB/cm]", ... 91 | "TM Modal Isolation [dB/cm]", ... 92 | "Modal Isolation [dB/cm]", ... 93 | "Desired Output Overlap", ... 94 | "High Isolation & Overlap", ... 95 | "Effective Index"]; 96 | 97 | % Data reduction 98 | rejectData = "~isfield(R, 'tG')"; 99 | 100 | % Optional plotting options 101 | contour=10; 102 | contourlim=nan(2,numel(params)+numel(metrics)); 103 | % contourlim(:,params=="wWG1")=[1; 2]; 104 | nominal=nan(size(contourlim(1,:))); 105 | nominal(params=="R.wWG1")=2.0; 106 | nominal(params=="R.etch1")=2.5; 107 | % nominalvar=nominal * 0.2; % 20% variation 108 | nominalvar=NaN*nominal; % No nominal variation 109 | 110 | 111 | %% Call main plot function 112 | fprintf("Plotting '%s'...\n", sweepName); 113 | run(baseScript); 114 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod/sweep_GaAs_2D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_WG_AR8; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"simY", 6, ... 15 | "wWG1", linspace(1.5, 5, 15)}; 16 | sweepName = "AR8_2D_mod_wWG"; 17 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 18 | 'submitjob', 1, 'session', 'lumerical', 'randomize', 0*0.02, ... 19 | 'submitcmd', ["~/lumerical/Q_selected.sh charge", "~/lumerical/Q_selected.sh fde"]); 20 | 21 | 22 | %% Build scripts locally 23 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 24 | % 'submitjob', 0, 'randomize', 0*0.02); 25 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod_LiNbO3_x/def_LiNbO3_x_WG.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiName = "LiNbO3"; 22 | epiType = "x"; 23 | componentName = "WG"; 24 | 25 | % Epitaxial components 26 | epiScript = lsfDir + "30_epi_" + epiName + "_" + epiType + ".lsf"; 27 | 28 | % Structure and instrumentation (sources, monitors) definition 29 | etchScript = lsfDir + "20_etch_" + epiName + "_" + epiType + "_" + componentName + ".lsf"; 30 | instScript = lsfDir + "40_inst_" + epiName + ".lsf"; 31 | 32 | % Script naming 33 | scriptName = epiName + "_" + epiType + "_" + componentName; 34 | 35 | % Default variable alterations, if any 36 | setVars = {"sim2D", 1}; 37 | 38 | 39 | %% Process 40 | % Assemble script 41 | script = string(fileread(templateHeader)) + "\n\n"; 42 | script = script + string(fileread(etchScript)) + "\n\n"; 43 | script = script + string(fileread(epiScript)) + "\n\n"; 44 | script = script + string(fileread(instScript)) + "\n\n"; 45 | script = script + string(fileread(templateFooter)); 46 | 47 | 48 | %% Clean up 49 | clearvars -except script scriptName setVars 50 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod_LiNbO3_x/plot_LiNbO3_x_WG.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_LiNbO3_x_WG; 10 | 11 | %% Set customizations 12 | resExts = ["_FDE.mat", "_CHARGE.mat"]; 13 | dualMode = 1; 14 | sweepName = "LiNbO3_x_mod_wWG"; 15 | % sweepName = "LiNbO3_x_mod_fullsweep_1"; 16 | 17 | 18 | %% Plot parameters 19 | noPlotParams = ["R.padDepth", "diff(R.simY)", "R.maxModes", ... 20 | "TM Modal Gain [dB/cm]", "TE Modal Gain [dB/cm]", ... 21 | "TM Phase Modulation [rad/mm]", "TM RAM [dB/mm]", ... 22 | "Desired Output Overlap", "High Isolation & Overlap", ... 23 | "Modal Isolation [dB/cm]", "TM Modal Isolation [dB/cm]", ... 24 | "Effective Index", "Fundamental Modal Gain [dB/cm]", ... 25 | "Fundamental Mode Number"]; 26 | noPlotTogether = ["Fundamental TE Gain [dB/cm]", "Modal Isolation [dB/cm]"; 27 | "Fundamental TE Gain [dB/cm]", "TE RAM [dB/mm]"; 28 | "Fundamental TE Gain [dB/cm]", "TE Phase Modulation [rad/mm]"; 29 | "Fundamental TE Gain [dB/cm]", "Effective Index"; 30 | "TE RAM [dB/mm]", "Modal Isolation [dB/cm]"; 31 | "TE RAM [dB/mm]", "TE Phase Modulation [rad/mm]"; 32 | "Modal Isolation [dB/cm]", "TE Phase Modulation [rad/mm]"; 33 | "Effective Index", "TE RAM [dB/mm]"; 34 | "Effective Index", "Modal Isolation [dB/cm]"; 35 | "Effective Index", "TE Phase Modulation [rad/mm]"]; 36 | savePlot = 0; 37 | plot1D = 1; 38 | 39 | 40 | %% Definitions 41 | % Plotting script 42 | baseScript = "sweepPlots_Base.m"; 43 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 44 | 45 | componentName = scriptName; 46 | 47 | % Precomputation 48 | preComp = ""; 49 | 50 | % Postcomputation 51 | % postComp = ['if ~exist("iP", "var"); [~, iP] = sort( R.results.modeL + -1000*log10(R.results.Pout) ); end;', ... 52 | % 'mode0 = getfield(R.simData, "mode"+string(R.results.modeN(iP(1)))); mode0.E = sum(mode0.E, [1,2]); mode0.y = 0;', ... 53 | % 'activeRegion = struct("x", mode0.x, "y", 0);', ... 54 | % 'activeRegion.z = 1e-6*unique(cell2mat(arrayfun(@(i) linspace(R.layerProps(i,1), R.layerProps(i,2), 50), 1:size(R.layerProps,1), "UniformOutput", false))'');', ... 55 | % 'activeRegion.E = zeros(1, 1, numel(activeRegion.z), 3);', ... 56 | % 'layerMQW = real(R.layerProps( imag(R.layerProps(:,3)) < 0, 1:2));', ... 57 | % 'activeRegion.E(:, :, layerMQW(2) <= 1e6*activeRegion.z & 1e6*activeRegion.z <= layerMQW(1), 2) = 1;']; 58 | 59 | % Files to load 60 | resFiles = []; 61 | for sweep = sweepName 62 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 63 | end 64 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 65 | 66 | % Parameters to plot; needs to evaluate to doubles 67 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG", "R.etchDepth", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 68 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.outField.pol", "R.wgBR", ... 69 | "R.d", "R.d2"]; 70 | pLabels = ["Active", "Wavelength", "WG Width", "Etch Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 71 | "2D", "1D", "SiO2 Clad", "Polarization", "Bend Radius", ... 72 | "d", "d2"]; 73 | 74 | % Metrics 75 | metrics = ["R.results.modeN(iP(1))", ... 76 | "-R.results.lossTE(iP(1)) - 1e3*(R.results.Pout(iP(1))<0.05)", ... 77 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 78 | "min([R.results.lossTM(iTM(iTM ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 79 | "min([R.results.modeL(iP(iP ~= iP(1))) - R.results.modeL(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 80 | "R.results.Pout(iP(1))", ... 81 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1)); 100])*R.results.Pout(iP(1))", ... 82 | "abs(R.results.modeNeff(iP(1)))"]; 83 | mLabels = ["Fundamental Mode Number", ... 84 | "Fundamental TE Gain [dB/cm]", ... 85 | "TE Modal Isolation [dB/cm]", ... 86 | "TM Modal Isolation [dB/cm]", ... 87 | "Modal Isolation [dB/cm]", ... 88 | "Desired Output Overlap", ... 89 | "High Isolation & Overlap", ... 90 | "Effective Index"]; 91 | 92 | % Data reduction 93 | rejectData = "0"; 94 | 95 | % Optional plotting options 96 | contour=10; 97 | contourlim=nan(2,numel(params)+numel(metrics)); 98 | % contourlim(:,params=="wWG")=[0.5; 2]; 99 | nominal=nan(size(contourlim(1,:))); 100 | nominal(params=="R.wWG")=1.5; 101 | nominal(params=="R.etchDepth")=0.4; 102 | % nominalvar=nominal * 0.2; % 20% variation 103 | nominalvar=NaN*nominal; % No nominal variation 104 | 105 | 106 | %% Call main plot function 107 | fprintf("Plotting '%s'...\n", sweepName); 108 | run(baseScript); 109 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod_LiNbO3_x/sweep_LiNbO3_x_WG.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; close('all'); 8 | 9 | %% Build structure 10 | def_LiNbO3_x_WG; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"wWG", 1.0:0.1:1.5}; 15 | sweepName = "LiNbO3_x_mod_wWG"; 16 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 17 | % 'submitjob', 1, 'session', 'lumerical', 'randomize', 0*0.02, ... 18 | % 'submitcmd', ["~/lumerical/Q_selected.sh charge", "~/lumerical/Q_selected.sh fde"]); 19 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 20 | 'submitjob', 0, 'randomize', 0*0.02); 21 | 22 | % sweep = {"etchMat", ["'etch'", "'SiO2'"], ... 23 | % "wWG", 1.0:0.1:1.5, ... 24 | % "etchDepth", 0.1:0.1:0.5, ... 25 | % "d", 0.4:0.1:0.8, ... 26 | % "d2", 0.4:0.1:0.8}; 27 | % sweepName = "LiNbO3_x_mod_fullsweep_1"; 28 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 29 | % 'submitjob', 0, 'randomize', 0.02); 30 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 31 | % 'submitjob', 1, 'session', 'lumerical', 'randomize', 0.02, ... 32 | % 'submitcmd', ["~/lumerical/Q_selected.sh charge", "~/lumerical/Q_selected.sh fde"]); 33 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod_LiNbO3_z/def_LiNbO3_z_WG.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiName = "LiNbO3"; 22 | epiType = "z"; 23 | componentName = "WG"; 24 | 25 | % Epitaxial components 26 | epiScript = lsfDir + "30_epi_" + epiName + "_" + epiType + ".lsf"; 27 | 28 | % Structure and instrumentation (sources, monitors) definition 29 | etchScript = lsfDir + "20_etch_" + epiName + "_" + epiType + "_" + componentName + ".lsf"; 30 | instScript = lsfDir + "40_inst_" + epiName + ".lsf"; 31 | 32 | % Script naming 33 | scriptName = epiName + "_" + epiType + "_" + componentName; 34 | 35 | % Default variable alterations, if any 36 | setVars = {"sim2D", 1}; 37 | 38 | 39 | %% Process 40 | % Assemble script 41 | script = string(fileread(templateHeader)) + "\n\n"; 42 | script = script + string(fileread(etchScript)) + "\n\n"; 43 | script = script + string(fileread(epiScript)) + "\n\n"; 44 | script = script + string(fileread(instScript)) + "\n\n"; 45 | script = script + string(fileread(templateFooter)); 46 | 47 | 48 | %% Clean up 49 | clearvars -except script scriptName setVars 50 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod_LiNbO3_z/plot_LiNbO3_z_WG.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_LiNbO3_z_WG; 10 | 11 | %% Set customizations 12 | resExts = ["_FDE.mat", "_CHARGE.mat"]; 13 | dualMode = 1; 14 | sweepName = "LiNbO3_z_mod_wWG"; 15 | % sweepName = "LiNbO3_z_mod_fullsweep_1"; 16 | 17 | 18 | %% Plot parameters 19 | noPlotParams = ["R.padDepth", "diff(R.simY)", "R.maxModes", ... 20 | "TM Modal Gain [dB/cm]", "TE Modal Gain [dB/cm]", ... 21 | "TM Phase Modulation [rad/mm]", "TM RAM [dB/mm]", ... 22 | "Desired Output Overlap", "High Isolation & Overlap", ... 23 | "Modal Isolation [dB/cm]", "TM Modal Isolation [dB/cm]", ... 24 | "Effective Index", "Fundamental Modal Gain [dB/cm]", ... 25 | "Fundamental Mode Number"]; 26 | noPlotTogether = ["Fundamental TE Gain [dB/cm]", "Modal Isolation [dB/cm]"; 27 | "Fundamental TE Gain [dB/cm]", "TE RAM [dB/mm]"; 28 | "Fundamental TE Gain [dB/cm]", "TE Phase Modulation [rad/mm]"; 29 | "Fundamental TE Gain [dB/cm]", "Effective Index"; 30 | "TE RAM [dB/mm]", "Modal Isolation [dB/cm]"; 31 | "TE RAM [dB/mm]", "TE Phase Modulation [rad/mm]"; 32 | "Modal Isolation [dB/cm]", "TE Phase Modulation [rad/mm]"; 33 | "Effective Index", "TE RAM [dB/mm]"; 34 | "Effective Index", "Modal Isolation [dB/cm]"; 35 | "Effective Index", "TE Phase Modulation [rad/mm]"]; 36 | savePlot = 0; 37 | plot1D = 1; 38 | 39 | 40 | %% Definitions 41 | % Plotting script 42 | baseScript = "sweepPlots_Base.m"; 43 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 44 | 45 | componentName = scriptName; 46 | 47 | % Precomputation 48 | preComp = ""; 49 | 50 | % Postcomputation 51 | % postComp = ['if ~exist("iP", "var"); [~, iP] = sort( R.results.modeL + -1000*log10(R.results.Pout) ); end;', ... 52 | % 'mode0 = getfield(R.simData, "mode"+string(R.results.modeN(iP(1)))); mode0.E = sum(mode0.E, [1,2]); mode0.y = 0;', ... 53 | % 'activeRegion = struct("x", mode0.x, "y", 0);', ... 54 | % 'activeRegion.z = 1e-6*unique(cell2mat(arrayfun(@(i) linspace(R.layerProps(i,1), R.layerProps(i,2), 50), 1:size(R.layerProps,1), "UniformOutput", false))'');', ... 55 | % 'activeRegion.E = zeros(1, 1, numel(activeRegion.z), 3);', ... 56 | % 'layerMQW = real(R.layerProps( imag(R.layerProps(:,3)) < 0, 1:2));', ... 57 | % 'activeRegion.E(:, :, layerMQW(2) <= 1e6*activeRegion.z & 1e6*activeRegion.z <= layerMQW(1), 2) = 1;']; 58 | 59 | % Files to load 60 | resFiles = []; 61 | for sweep = sweepName 62 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 63 | end 64 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 65 | 66 | % Parameters to plot; needs to evaluate to doubles 67 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG", "R.etchDepth", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 68 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.outField.pol", "R.wgBR", ... 69 | "R.d", "R.d2"]; 70 | pLabels = ["Active", "Wavelength", "WG Width", "Etch Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 71 | "2D", "1D", "SiO2 Clad", "Polarization", "Bend Radius", ... 72 | "d", "d2"]; 73 | 74 | % Metrics 75 | metrics = ["R.results.modeN(iP(1))", ... 76 | "-R.results.lossTE(iP(1)) - 1e3*(R.results.Pout(iP(1))<0.05)", ... 77 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 78 | "min([R.results.lossTM(iTM(iTM ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 79 | "min([R.results.modeL(iP(iP ~= iP(1))) - R.results.modeL(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 80 | "R.results.Pout(iP(1))", ... 81 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1)); 100])*R.results.Pout(iP(1))", ... 82 | "abs(R.results.modeNeff(iP(1)))"]; 83 | mLabels = ["Fundamental Mode Number", ... 84 | "Fundamental TE Gain [dB/cm]", ... 85 | "TE Modal Isolation [dB/cm]", ... 86 | "TM Modal Isolation [dB/cm]", ... 87 | "Modal Isolation [dB/cm]", ... 88 | "Desired Output Overlap", ... 89 | "High Isolation & Overlap", ... 90 | "Effective Index"]; 91 | 92 | % Data reduction 93 | rejectData = "0"; 94 | 95 | % Optional plotting options 96 | contour=10; 97 | contourlim=nan(2,numel(params)+numel(metrics)); 98 | % contourlim(:,params=="wWG")=[0.5; 2]; 99 | nominal=nan(size(contourlim(1,:))); 100 | nominal(params=="R.wWG")=1.5; 101 | nominal(params=="R.etchDepth")=0.4; 102 | % nominalvar=nominal * 0.2; % 20% variation 103 | nominalvar=NaN*nominal; % No nominal variation 104 | 105 | 106 | %% Call main plot function 107 | fprintf("Plotting '%s'...\n", sweepName); 108 | run(baseScript); 109 | -------------------------------------------------------------------------------- /MATLAB/template/wgMod_LiNbO3_z/sweep_LiNbO3_z_WG.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; close('all'); 8 | 9 | %% Build structure 10 | def_LiNbO3_z_WG; 11 | 12 | 13 | % Define sweep parameters 14 | sweep = {"wWG", 1.0:0.1:1.5}; 15 | sweepName = "LiNbO3_z_mod_wWG"; 16 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 17 | % 'submitjob', 1, 'session', 'lumerical', 'randomize', 0*0.02, ... 18 | % 'submitcmd', ["~/lumerical/Q_selected.sh charge", "~/lumerical/Q_selected.sh fde"]); 19 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 20 | 'submitjob', 0, 'randomize', 0*0.02); 21 | 22 | % sweep = {"etchMat", ["'etch'", "'SiO2'"], ... 23 | % "wWG", 1.0:0.1:1.5, ... 24 | % "etchDepth", 0.1:0.1:0.5, ... 25 | % "d", 0.4:0.1:0.8, ... 26 | % "d2", 0.4:0.1:0.8}; 27 | % sweepName = "LiNbO3_z_mod_fullsweep_1"; 28 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 29 | % 'submitjob', 0, 'randomize', 0.02); 30 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 31 | % 'submitjob', 1, 'session', 'lumerical', 'randomize', 0.02, ... 32 | % 'submitcmd', ["~/lumerical/Q_selected.sh charge", "~/lumerical/Q_selected.sh fde"]); 33 | -------------------------------------------------------------------------------- /MATLAB/template/wgPassive/def_GaAs_WG_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical construction script for structure 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Creates script by merging: 5 | % templateHeader 6 | % structScript 7 | % epiScript 8 | % instScript 9 | % templateFooter 10 | % 11 | 12 | clear; %close('all'); 13 | 14 | %% Definitions 15 | % Common components 16 | lsfDir = "../lsf/"; 17 | templateHeader = lsfDir + "10_header_template.lsf"; 18 | templateFooter = lsfDir + "90_footer.lsf"; 19 | 20 | % Define naming 21 | epiType = "GaAs"; 22 | epiName = "AR8"; 23 | componentName = "WG"; 24 | 25 | % Epitaxial components 26 | mqwScript = lsfDir + "25_epi_" + epiType + "_MQW.lsf"; 27 | epiScript = lsfDir + "30_epi_" + epiType + "_" + epiName + ".lsf"; 28 | 29 | % Structure and instrumentation (sources, monitors) definition 30 | etchScript = lsfDir + "20_etch_" + epiType + "_" + componentName + ".lsf"; 31 | instScript = lsfDir + "40_inst_" + epiType + ".lsf"; 32 | 33 | % Script naming 34 | scriptName = epiType + "_" + epiName + "_" + componentName; 35 | 36 | % Default variable alterations, if any 37 | setVars = {"sim2D", 1, "Vpad", 0, "lActive", 0}; 38 | 39 | 40 | %% Process 41 | % Assemble script 42 | script = string(fileread(templateHeader)) + "\n\n"; 43 | script = script + string(fileread(etchScript)) + "\n\n"; 44 | script = script + string(fileread(mqwScript)) + "\n\n"; 45 | script = script + string(fileread(epiScript)) + "\n\n"; 46 | script = script + string(fileread(instScript)) + "\n\n"; 47 | script = script + string(fileread(templateFooter)); 48 | 49 | 50 | %% Clean up 51 | clearvars -except script scriptName setVars 52 | -------------------------------------------------------------------------------- /MATLAB/template/wgPassive/plot_GaAs_2D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Load, process, and plot simulation result data 2 | % Michael Nickerson 2021-08-02 3 | 4 | clear; close('all'); 5 | 6 | 7 | %% Simulation-specific definitions 8 | % Load definitions 9 | def_GaAs_WG_AR8; 10 | 11 | %% Set customizations 12 | dualMode = 0; 13 | resExts = "_FDE.mat"; 14 | % sweepName = "AR8_2D_passive_wWG"; 15 | sweepName = "AR8_2D_passive_BR"; 16 | 17 | 18 | %% Plot parameters 19 | noPlotParams = ["R.padDepth", "diff(R.simY)", "R.maxModes", ... 20 | "TM Modal Gain [dB/cm]", "TE Modal Gain [dB/cm]", ... 21 | "TM Phase Modulation [rad/mm]", "TM RAM [dB/mm]", ... 22 | "Desired Output Overlap", "High Isolation & Overlap", ... 23 | "Modal Isolation [dB/cm]", "TM Modal Isolation [dB/cm]", ... 24 | "Effective Index", "Fundamental Modal Gain [dB/cm]", ... 25 | "Fundamental Mode Number"]; 26 | noPlotTogether = ["Fundamental TE Gain [dB/cm]", "Modal Isolation [dB/cm]"; 27 | "Fundamental TE Gain [dB/cm]", "TE RAM [dB/mm]"; 28 | "Fundamental TE Gain [dB/cm]", "TE Phase Modulation [rad/mm]"; 29 | "Fundamental TE Gain [dB/cm]", "Effective Index"; 30 | "TE RAM [dB/mm]", "Modal Isolation [dB/cm]"; 31 | "TE RAM [dB/mm]", "TE Phase Modulation [rad/mm]"; 32 | "Modal Isolation [dB/cm]", "TE Phase Modulation [rad/mm]"; 33 | "Effective Index", "TE RAM [dB/mm]"; 34 | "Effective Index", "Modal Isolation [dB/cm]"; 35 | "Effective Index", "TE Phase Modulation [rad/mm]"]; 36 | savePlot = 0; 37 | plot1D = 1; 38 | 39 | 40 | %% Definitions 41 | % Plotting script 42 | baseScript = "sweepPlots_Base.m"; 43 | outDir = pwd + "/" + datestr(now, "yyyymmdd") + "/" + sweepName(1) + "/"; 44 | 45 | componentName = scriptName; 46 | 47 | % Precomputation 48 | preComp = ""; 49 | 50 | % Postcomputation 51 | % postComp = ['if ~exist("iP", "var"); [~, iP] = sort( R.results.modeL + -1000*log10(R.results.Pout) ); end;', ... 52 | % 'mode0 = getfield(R.simData, "mode"+string(R.results.modeN(iP(1)))); mode0.E = sum(mode0.E, [1,2]); mode0.y = 0;', ... 53 | % 'activeRegion = struct("x", mode0.x, "y", 0);', ... 54 | % 'activeRegion.z = 1e-6*unique(cell2mat(arrayfun(@(i) linspace(R.layerProps(i,1), R.layerProps(i,2), 50), 1:size(R.layerProps,1), "UniformOutput", false))'');', ... 55 | % 'activeRegion.E = zeros(1, 1, numel(activeRegion.z), 3);', ... 56 | % 'layerMQW = real(R.layerProps( imag(R.layerProps(:,3)) < 0, 1:2));', ... 57 | % 'activeRegion.E(:, :, layerMQW(2) <= 1e6*activeRegion.z & 1e6*activeRegion.z <= layerMQW(1), 2) = 1;']; 58 | 59 | % Files to load 60 | resFiles = []; 61 | for sweep = sweepName 62 | resFiles = [resFiles; dir("./sweeps/" + sweep + "/" + scriptName+"*.mat")]; 63 | end 64 | resFiles = unique(regexprep(string({resFiles.folder}') + "/" + string({resFiles.name}'), "_[a-zA-Z]+\.mat", "")); 65 | 66 | % Parameters to plot; needs to evaluate to doubles 67 | params = ["double(R.lActive > 1)", "R.lambda", "R.wWG1", "R.wWG2", "R.etch1", "R.etch2", "R.padDepth", "diff(R.simY)", "R.maxModes", "R.dataRes", ... 68 | "R.sim2D", "R.sim1D", "double(strcmp(R.etchMat, 'SiO2'))", "R.outField.pol", "R.wgBR", ... 69 | "R.cden", "R.Nqw", "R.tQW*1e3", "R.tQWB*1e3", "R.xQW", "R.xQWB", "R.mqwStrain", ... 70 | "R.tUC", "R.xUC", ... 71 | "R.tG", ... 72 | "R.tLC", "R.xLC"]; 73 | pLabels = ["Active", "Wavelength", "WG Width 1", "WG Width 2", "Etch 1 Depth", "Etch 2 Depth", "Pad Depth", "simY", "# Modes", "Data Resolution", ... 74 | "2D", "1D", "SiO2 Clad", "Polarization", "Bend Radius", ... 75 | "Carrier Density [1e18 cm^-3]", "# QW", "QW Thickness [nm]", "Barrier Thickness [nm]", "In-fraction QW", "As-fraction Barrier", "Strain Calculated", ... 76 | "Upper Clad Thickness [µm]", "Upper Clad x-frac", ... 77 | "Guide Thickness [µm]", ... 78 | "Lower Clad Thickness [µm]", "Lower Clad x-frac"]; 79 | 80 | % Metrics 81 | metrics = ["R.results.modeN(iP(1))", ... 82 | "-R.results.lossTE(iP(1)) - 1e3*(R.results.Pout(iP(1))<0.05)", ... 83 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 84 | "min([R.results.lossTM(iTM(iTM ~= iP(1))) - R.results.lossTE(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 85 | "min([R.results.modeL(iP(iP ~= iP(1))) - R.results.modeL(iP(1))])*(R.results.Pout(iP(1))>0.01)", ... 86 | "R.results.Pout(iP(1))", ... 87 | "min([R.results.lossTE(iTE(iTE ~= iP(1))) - R.results.lossTE(iP(1)); 100])*R.results.Pout(iP(1))", ... 88 | "abs(R.results.modeNeff(iP(1)))"]; 89 | mLabels = ["Fundamental Mode Number", ... 90 | "Fundamental TE Gain [dB/cm]", ... 91 | "TE Modal Isolation [dB/cm]", ... 92 | "TM Modal Isolation [dB/cm]", ... 93 | "Modal Isolation [dB/cm]", ... 94 | "Desired Output Overlap", ... 95 | "High Isolation & Overlap", ... 96 | "Effective Index"]; 97 | 98 | % Data reduction 99 | rejectData = "~isfield(R, 'tG')"; 100 | 101 | % Optional plotting options 102 | contour=10; 103 | contourlim=nan(2,numel(params)+numel(metrics)); 104 | % contourlim(:,params=="wWG1")=[1; 2]; 105 | nominal=nan(size(contourlim(1,:))); 106 | nominal(params=="R.wWG1")=2.0; 107 | nominal(params=="R.etch1")=2.5; 108 | % nominalvar=nominal * 0.2; % 20% variation 109 | nominalvar=NaN*nominal; % No nominal variation 110 | 111 | 112 | %% Call main plot function 113 | fprintf("Plotting '%s'...\n", sweepName); 114 | run(baseScript); 115 | -------------------------------------------------------------------------------- /MATLAB/template/wgPassive/sweep_GaAs_2D_AR8.m: -------------------------------------------------------------------------------- 1 | %% Define Lumerical variable sweeps 2 | % Michael Nickerson 2021-07-05 3 | % 4 | % Defines a set of variable values to conduct a sweep 5 | % 6 | 7 | clear; %close('all'); 8 | 9 | %% Build structure 10 | def_GaAs_WG_AR8; 11 | 12 | 13 | % Define sweep parameters 14 | % sweep = {"simY", 6, ... 15 | % "wWG1", linspace(1.5, 5, 15)}; 16 | % sweepName = "AR8_2D_passive_wWG"; 17 | % buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 18 | % 'submitjob', 0, 'randomize', 0*0.02); 19 | 20 | sweep = {"simY", 5, ... 21 | "wWG1", [1.5, 2.0, 3.0, 4.0], ... 22 | "wgBR", [0, linspace(25, 200, 21)]}; 23 | sweepName = "AR8_2D_passive_BR"; 24 | buildSweepAndEnqueue(scriptName, script, sweep, 'allvars', setVars, 'sweepname', sweepName, ... 25 | 'submitjob', 0, 'randomize', 0*0.02); 26 | -------------------------------------------------------------------------------- /common/lum_analyze_CHARGE.lsf: -------------------------------------------------------------------------------- 1 | ### Specialized analysis for CHARGE simulation results 2 | solver = 'CHARGE'; 3 | 4 | # Verify correct simulation 5 | if( ~contains(getresult(), solver) ) { 6 | ?('Error: No '+solver+' results! Aborting analysis.'); break; 7 | } 8 | 9 | 10 | ## Simulation results (restricted) 11 | # Simple results first 12 | dataSolver = { 'name': solver, 13 | 'vtx': getdata(solver, 'electrostatics', 'vertices'), 14 | 'elem': getdata(solver, 'electrostatics', 'elements'), 15 | 'epsr': getdata(solver, 'electrostatics', 'epsr'), 16 | 'ID': getdata(solver, 'electrostatics', 'ID') }; 17 | 18 | # Append Cartesian resample grid 19 | dataSolver = appendstruct(dataSolver, cartesianGrid(dataSolver.vtx, dataRes*1e-6)); 20 | 21 | # Data result, processed in to standard formats 22 | for( data = {{'dataset': 'electrostatics', 'field': 'E'}, 23 | {'dataset': 'electrostatics', 'field': 'V'}, 24 | {'dataset': 'charge', 'field': 'n'}, 25 | {'dataset': 'charge', 'field': 'p'}, 26 | {'dataset': 'doping', 'field': 'NA'}, 27 | {'dataset': 'doping', 'field': 'ND'} } ) { 28 | 29 | # Collect the raw data in standard format 30 | dataSolver = setfield(dataSolver, data.field+'_raw', 31 | collapseSweepCHARGE(getresult(solver, data.dataset), data.field)); 32 | 33 | # Interpolate to Cartesian grid 34 | dataSolver = setfield(dataSolver, data.field, interpfemsol(dataSolver, data.field+'_raw')); 35 | } 36 | 37 | 38 | ## Pad results 39 | dataPad = struct; 40 | # Process each monitor 41 | selectpartial('CHARGE::boundary conditions::'); 42 | for( i = 1:getnumber() ) { 43 | try { # Some contacts may not have results 44 | # Get contact name and results 45 | pad = {'name': get('name', i)}; 46 | dataP = getresult(solver, pad.name); 47 | pad.I = pinch(dataP.I); 48 | pad.V = pinch(getparameter(dataP, 'V_'+pad.name)); 49 | 50 | # Append to record 51 | dataPad = setfield(dataPad, pad.name, pad); 52 | 53 | # Assemble results for export 54 | outHeader = outHeader + ' ' + pad.name + '_I' + ' ' + pad.name + '_V'; 55 | outLine = [outLine, pad.I(1), pad.V(1)]; 56 | } catch(errMsg); 57 | } 58 | clear(dataP, pad); 59 | 60 | -------------------------------------------------------------------------------- /common/lum_analyze_EME.lsf: -------------------------------------------------------------------------------- 1 | ### Specialized analysis for EME simulation results 2 | solver = 'EME'; 3 | 4 | 5 | ## If needed, find output port closest to center and use as 'outField' 6 | if( ~isfield(outField, 'E') & outField.mfd == 0 ) { 7 | ?'Finding "outField"'; 8 | # Search ports for centermost output, if any 9 | if( length(portNames) > 0 ) { 10 | ?'Searching ports'; 11 | bestY = 1/0; 12 | for( port = portNames ) { 13 | port = getfield(simData, port); 14 | if( (port.left == 0) & (abs(mean(port.y)) < bestY) ) { 15 | outField.E = port.E; outField.field = port.E; 16 | outField.H = port.H; 17 | outField.x = port.x; 18 | outField.y = port.y; 19 | outField.z = port.z; 20 | 21 | bestY = abs(mean(port.y)); 22 | ?'Found output field "'+port.name+'"'; 23 | } 24 | } 25 | clear(bestY); 26 | } 27 | 28 | # Search output cell modes if still needed 29 | if( ~isfield(outField, 'E') ) { 30 | # TODO only if needed 31 | ?'UNIMPLEMENTED: no output port found; need to find output field via cell modes'; break; 32 | # Find the mode that's lowest loss and closest to desired outField.pol - see FDE processing 33 | # selectpartial('EME::Cells::'); 34 | # port = getresult('EME::Cells::'+get('name', getnumber), 'mode fields'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /common/lum_analyze_FDE.lsf: -------------------------------------------------------------------------------- 1 | ### Specialized analysis for FDE simulation results 2 | # results structure with summarized results as: 3 | # modeN vector mapping position in list to mode number 4 | # Pout vector of modal power overlap with outField, if specified 5 | # modeL vector of modal loss 6 | # modeNeff vector of modal effective index 7 | # modePol polarization of mode, TE = 0 8 | # 9 | # simData: structure of modal reslts as structures with fields: 10 | # .name 'mode##' 11 | # .x,.y,.z Cartesian geometry vectors 12 | # . full return of (varies by monitor) in Cartesian format 13 | # .loss [modes only] propagation loss of mode 14 | # .pol [modes only] polarization of mode, TE = 0 15 | # .neff [modes only] effective index 16 | # .ng [modes only] group index 17 | # .overlap [modes only] overlap with outField, if specified 18 | 19 | # Definitions 20 | solver = 'FDE'; 21 | datamat = 'FDE::data::material'; 22 | datamode = 'FDE::data::mode'; 23 | 24 | 25 | ## Save index 26 | simData.index = {'name': 'index', 27 | 'x': getresult(datamat, 'x'), 28 | 'y': getresult(datamat, 'y'), 29 | 'z': getresult(datamat, 'z'), 30 | 'nx': pinch(getresult(datamat, 'index_x')), 31 | 'ny': pinch(getresult(datamat, 'index_y')), 32 | 'nz': pinch(getresult(datamat, 'index_z'))}; 33 | 34 | 35 | ## Save modes 36 | Nmodes = length(splitstring(getresult, endl)) - 1; # Results are ::material, ::modeN 37 | if( ~exist('maxModes') ) { maxModes = Nmodes; } 38 | results.Pout = zeros(max([Nmodes,2]),1); 39 | results.modeL = results.Pout/0; 40 | results.modePol = results.Pout; 41 | results.modeNeff = results.Pout; 42 | modes = cell(max([Nmodes,2])); 43 | for( i = 1:Nmodes ) { 44 | modes{i} = { 45 | 'name': 'mode'+num2str(i), 46 | 'x': getresult(datamode+num2str(i), 'x'), 47 | 'y': getresult(datamode+num2str(i), 'y'), 48 | 'z': getresult(datamode+num2str(i), 'z'), 49 | 'E': pinch(getattribute(getresult(datamode+num2str(i), 'E'), 'E'), 4), # Extraneous dimension hanging around 50 | 'loss': getresult(datamode+num2str(i), 'loss'), 51 | 'pol': 1-getresult(datamode+num2str(i), 'TE polarization fraction'), 52 | 'neff': getresult(datamode+num2str(i), 'neff'), 53 | 'ng': getresult(datamode+num2str(i), 'ng') 54 | }; 55 | 56 | # Assign separately reported results 57 | results.modeL(i) = modes{i}.loss; 58 | results.modeNeff(i) = modes{i}.neff; 59 | results.modePol(i) = modes{i}.pol; 60 | } 61 | 62 | 63 | ## If needed, determine lowest-loss mode at output that is of the desired polarization and use as 'outField' 64 | if( ~isfield(outField, 'E') & outField.mfd == 0 ) { 65 | ?'Finding best mode to use as outField.'; 66 | effLoss = -10*log10(10^(-results.modeL/1e3) * (1-abs(outField.pol - results.modePol))); 67 | outField = modes{find(effLoss, -1e12)}; 68 | clear(effLoss); 69 | } 70 | 71 | 72 | ## Overlap calculations 73 | if( isfield(outField, 'E') ) { 74 | # Field may be specified by 'E' 75 | for( i = 1:Nmodes ) { 76 | modes{i}.overlap = powerOverlap(outField, modes{i}); 77 | results.Pout(i) = modes{i}.overlap; 78 | } 79 | } 80 | 81 | 82 | ## Sort output 83 | # Sort by loss modified by overlap 84 | ll = sortmap( -log10(10^(-results.modeL/1e3) * results.Pout^2 ) ); 85 | 86 | # Remove irrelevant modes 87 | ll = ll(find(abs(results.modeL(ll)) < 1e6)); 88 | 89 | # Add relevant modes 90 | maxModes = min([length(ll), maxModes]); # Restrict if needed 91 | for( i = ll(1:maxModes) ) { 92 | simData = setfield(simData, modes{i}.name, modes{i}); 93 | } 94 | 95 | # Adjust vectors 96 | results.modeN = ll(1:maxModes); 97 | results.Pout = results.Pout(results.modeN); 98 | results.modeNeff= results.modeNeff(results.modeN); 99 | results.modeL = results.modeL(results.modeN); 100 | results.modePol = results.modePol(results.modeN); 101 | 102 | 103 | ## Clean up 104 | clear(datamat, datamode, i, modes, ll); 105 | -------------------------------------------------------------------------------- /common/lum_analyze_allFDTD.lsf: -------------------------------------------------------------------------------- 1 | ### Specialized analysis for all FDTD simulation results 2 | 3 | 4 | ## If needed, try to find output port closest to center to use as 'outField' 5 | if( ~isfield(outField, 'E') & outField.mfd == 0 ) { 6 | ?'Finding "outField"'; 7 | # Search ports for centermost output, if any 8 | # TODO: change to sum of all output ports 9 | if( length(portNames) > 0 ) { 10 | ?'Searching ports'; 11 | bestY = 1/0; 12 | for( port = portNames ) { 13 | port = getfield(simData, port); 14 | if( (port.left == 0) & (abs(mean(port.y)) < bestY) ) { 15 | outField.E = port.E; 16 | outField.H = port.H; 17 | outField.x = port.x; 18 | outField.y = port.y; 19 | outField.z = port.z; 20 | 21 | bestY = abs(mean(port.y)); 22 | ?'Found output field "'+port.name+'"'; 23 | } 24 | } 25 | clear(bestY); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /common/lum_buildEtch.lsf: -------------------------------------------------------------------------------- 1 | addpath('/home/nickersonm/lumerical/common'); # Replace as necessary, optionally adding multiple potential locations for different environments 2 | util_fCommon; # Load common functions 3 | 4 | ### Build specified waveguide etch 5 | ## Units: µm lengths, cm^-1 loss, 1e18 cm^-3 doping 6 | ## Inputs as defined variables: 7 | # Required: 8 | # etchList: cell list of verified and calculated etches, presumably from lum_processEtch, 9 | # each element being a structure with fields: 10 | # .depth etch depth from top of epitaxy 11 | # .poly polygon defining etch in entirety 12 | # .name name for etch object 13 | # .start starting [x,y] coordinates 14 | # Optional etchList fields: 15 | # .material etch material for single etch entry 16 | # .angle specify vertical rotation angle; will convert to planarsolid and calculate top and bottom polygon points 17 | # Optional: 18 | # simBuffer: simulation buffers for etch on top, default 1 [µm] 19 | # etchMat: optional default etch-filling material name, default 'etch' 20 | # etchShift: y-axis shift for etch structure, default 0 [µm] 21 | # etchRot: z-axis rotation for etch structure, default 0 [degrees] 22 | 23 | 24 | 25 | ### Set defaults if needed 26 | etchVars = 'simBuffer, etchRot, etchShift'; 27 | dVals = '1, 0, 0'; 28 | dVars = etchVars; dRequired = 'etchList'; 29 | util_paramDefaults; 30 | 31 | um=1e-6; 32 | 33 | # Etch material - continues to lum_buildEpitaxy where materials are defined 34 | if( ~exist('etchMat') ) { 35 | etchMat = 'etch'; 36 | } 37 | 38 | 39 | 40 | ### Function definitions and initial calculations 41 | # Add a polygon in the XY plane 42 | function addPolyXY(group, name, start, xyPoints, zSpan) { 43 | select(group); addpoly; addtogroup(group); 44 | set("name", name); 45 | set("x", start(1)*1e-6); set("y", start(2)*1e-6); 46 | set("z max", 0); set("z min", -zSpan*1e-6); 47 | set("vertices", xyPoints*1e-6); 48 | if( ~isDEVICE() ) { 49 | set('override mesh order from material database', 1); 50 | } else { 51 | set('preserve surfaces', 0); 52 | } 53 | set('mesh order', 1); 54 | } 55 | 56 | # Add a planar solid comprising two shifted polygons 57 | function addAngledSolid(group, name, start, xyPoints, zMin, zMax, ang) { 58 | # Calculate shift 59 | dx = abs(diff([zMax, zMin])) / (2 * tan(ang*pi/180) ); 60 | 61 | # Calculate vertices 62 | N = size(xyPoints, 1); 63 | vtx = [[xyPoints + [dx*ones(N,1), zeros(N,1)], zMax*ones(N,1)]; 64 | [xyPoints - [dx*ones(N,1), zeros(N,1)], zMin*ones(N,1)]]*1e-6; 65 | 66 | # Calculate facets 67 | # Top and bottom sides 68 | fct = {{[1:N]},{[N:-1:1]+N}}; 69 | # Sides 70 | for( i = 1:N ) { 71 | fct = appendcell(fct, {{ [i, mod(i,N)+1, N+mod(i,N)+1, N+i] }}); 72 | } 73 | 74 | # Basic settings 75 | select(group); addplanarsolid(vtx, fct); 76 | set("name", name); addtogroup(group); 77 | set("x", start(1)*1e-6); set("y", start(2)*1e-6); set("z", 0); 78 | if( ~isDEVICE() ) { 79 | set('override mesh order from material database', 1); 80 | } else { 81 | set('preserve surfaces', 0); 82 | } 83 | set('mesh order', 1); 84 | } 85 | 86 | 87 | 88 | ### Traverse etchList and build each etch separately 89 | ## Make etch group 90 | geoGroup = selectGeom(); 91 | addstructuregroup; set("x", 0); set("y", 0); set("z", simBuffer*um); 92 | set('name', 'etchGroup'); if(isFDE() | isFDTD()) { addtogroup(geoGroup); } 93 | etchGroup = geoGroup + '::etchGroup'; 94 | 95 | 96 | ## Process each etch 97 | if( ~iscell(etchList) ) { etchList = {etchList}; } 98 | for( i = 1:length(etchList) ) { 99 | if( ~isfield(etchList{i}, 'material') ) { etchList{i}.material = etchMat; } 100 | if( ~isfield(etchList{i}, 'angle') ) { etchList{i}.angle = 0; } 101 | 102 | if( etchList{i}.depth > 0 ) { 103 | if( etchList{i}.angle == 0 ) { 104 | # Unangled etch: just extrude 2D polygon 105 | addPolyXY(etchGroup, etchList{i}.name, 106 | etchList{i}.start, 107 | etchList{i}.poly, (etchList{i}.depth + simBuffer)); 108 | } else { 109 | # Angled etch: make 3D planar solid 110 | addAngledSolid(etchGroup, etchList{i}.name, 111 | etchList{i}.start, 112 | etchList{i}.poly, 113 | -(etchList{i}.depth + simBuffer), 0, etchList{i}.angle); 114 | } 115 | } 116 | } 117 | 118 | 119 | ## Optionally shift 120 | if( etchShift ~= 0 ) { 121 | setnamed(etchGroup, 'y', etchShift*um); 122 | } 123 | 124 | 125 | ## Optionally rotate 126 | if( etchRot ~= 0 ) { 127 | setnamed(etchGroup, 'first axis', 'z'); 128 | setnamed(etchGroup, 'rotation 1', etchRot); 129 | } 130 | 131 | 132 | ### Cleanup temporary variables 133 | geoGroup = ''; 134 | clear(geoGroup, etchGroup); 135 | -------------------------------------------------------------------------------- /common/lum_buildSim_sub_CHARGE.lsf: -------------------------------------------------------------------------------- 1 | ### CHARGE-specific processing for lum_buildSim 2 | 3 | ## Electrical Contacts 4 | for( i = 1:length(contacts) ) { 5 | pad = contacts{i}; 6 | if( isstruct(pad) ) { # Ignore non-structures 7 | ## Verify inputs 8 | # Required inputs 9 | if( ~isfield(pad, 'name') ) { 10 | ?('Error: contact ' + num2str(i) + ' has no name defined!'); break; 11 | } 12 | if( ~isfield(pad, 'V') ) { 13 | ?('Error: contact ' + pad.name + ' has no voltage defined!'); break; 14 | } 15 | 16 | # Optional inputs 17 | if( ~isfield(pad, 'meshorder') ) { pad.meshorder = 1; } 18 | if( ~isfield(pad, 'poly') ) { pad.poly = 0; } 19 | if( ~isfield(pad, 'dz') ) { pad.poly = 0; } 20 | if( ~isfield(pad, 'material') ) { pad.material = 'Au'; } 21 | 22 | 23 | ## Assign/generate contacts 24 | # Generate new geometry if needed 25 | if( getnamednumber('geometry::epitaxialStack::'+pad.name) == 0 & 26 | getnamednumber('geometry::'+pad.name) == 0 ) { 27 | # Verify now-required inputs 28 | if( length(pad.poly) < 3 ) { 29 | ?('Error: no existing geometry for "' + pad.name + '" but no "poly" field defined!'); break; 30 | } 31 | if( length(pad.dz) <= 0 ) { 32 | ?('Error: no "dz" specified for "' + pad.name + '"!'); break; 33 | } 34 | if( ~materialexists(pad.material) ) { 35 | pad.material = 'Au'; 36 | ?('Warning: pad "' + pad.name + '" specifies unknown material "' + pad.material + '", assuming "Au".'); 37 | } 38 | 39 | # Make new polygon 40 | selectGeom(); addpoly; set('name', pad.name); 41 | set("x", rMin(1)*um); set("y", mean([rMin(2), rMax(2)])*um); 42 | set("z min", pad.z*um); set("z max", (pad.z + pad.thickness)*um); 43 | set("vertices", pad.poly*um); 44 | set("material", pad.material); 45 | set("mesh order", pad.meshorder); 46 | } 47 | 48 | # Make boundary condition and assign to geometry 49 | addelectricalcontact; set('name', pad.name); 50 | set('surface type', 'solid'); set('solid', pad.name); 51 | if( length(pad.V) > 1 ) { 52 | set('sweep type', 'value'); 53 | set('value table', pad.V); 54 | } else { 55 | set('sweep type', 'single'); 56 | set('voltage', pad.V); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /common/lum_buildSim_sub_EME.lsf: -------------------------------------------------------------------------------- 1 | ### EME-specific processing for lum_buildSim 2 | 3 | # Remove NaNs (nan ~= nan) 4 | simRes = simRes(find( prod(simRes==simRes,2) ~= 0), :); 5 | 6 | # Remove zero-extent groups 7 | if( size(simRes,2) >= 5) { 8 | simRes = simRes(find( (simRes(:,3)-simRes(:,2))*(simRes(:,5)-simRes(:,4)) ~= 0),:); 9 | } 10 | 11 | # Build cell groups if needed and possible 12 | if(~exist('simCellLen')) { 13 | ?'Error: unexpected lack of "simCellLen" in "lum_buildSim_sub_EME.lsf"!'; break; 14 | } 15 | 16 | # Add cell length groups if needed so bend segments comprise discrete groups 17 | if( exist('simBend') ) { 18 | # Make sure the ordering is correct 19 | simBend = [amin(simBend(:,1:2), 2), amax(simBend(:,1:2), 2), simBend(:,3)]; 20 | 21 | # simCellLen needs to be in [xMin, xMax] format for this 22 | if( max(size(simCellLen)) == length(simCellLen) ) { 23 | simCellLen = cumsum(simCellLen(:)); 24 | simCellLen = [simCellLen(1:(end-1)), simCellLen(2:end)]; 25 | } 26 | 27 | # Insert bend radius groups and N=2 minimum cell number 28 | simCellLen = [simCellLen; simBend(:,1:2)]; 29 | simCellN = [simCellN(:); ones(size(simBend,1), 1)*2]; 30 | } 31 | 32 | # Normalize simCellLen to list of non-overlapping lengths if in [xMin,xMax] format 33 | if( min(size(simCellLen)) < length(simCellLen) ) { 34 | # Remove zero-extent groups from simCellN 35 | if( exist('simCellN') ) { 36 | if( length(simCellN) > 1 ) { 37 | simCellN = simCellN(find(simCellLen(:,1) ~= simCellLen(:,2))); 38 | } 39 | } 40 | 41 | # Remove zero-extent groups 42 | simCellLen = simCellLen(find(simCellLen(:,1) ~= simCellLen(:,2)),:); 43 | 44 | # Make sure min and max are correctly ordered 45 | simCellLen = [amin(simCellLen, 2), amax(simCellLen, 2)]; 46 | 47 | # Calculate unique cell boundaries and sort 48 | cellBoundaries = unique(simCellLen(:)); 49 | 50 | # Change simCellLen to actual list of lengths 51 | simCells = simCellLen; 52 | simCellLen = simCells(:,2) - simCells(:,1); 53 | 54 | # Convert cell number to size because we're reshaping potentially overlapping regions 55 | if( exist('simCellN') ) { 56 | simCellRes = simCellLen / simCellN; 57 | } 58 | 59 | # Create unique simCellLen 60 | simCellLen = cellBoundaries(2:end) - cellBoundaries(1:(end-1)); 61 | 62 | # Build appropriate simCellN for each unique cell boundary region 63 | simCellN = ones(size(simCellLen,1)); 64 | if( length(simCellRes) > 1 ) { 65 | # Can vectorize this and nest more, but it's clearer in a loop 66 | for( i=1:(length(cellBoundaries)-1) ) { 67 | overlapCells = find( (cellBoundaries(i) >= simCells(:,1)) & (cellBoundaries(i+1) <= simCells(:,2)) ); 68 | simCellN(i) = ceil(simCellLen(i) / min( simCellRes(overlapCells) )); 69 | } 70 | clear(overlapCells); 71 | } else { simCellN = ceil(simCellLen/simCellRes); } 72 | 73 | clear(cellBoundaries, simCells); 74 | } 75 | 76 | # Use simCellN instead of simCellRes 77 | if(~exist('simCellN')) { 78 | simCellN = ceil(simCellLen/simCellRes); 79 | } 80 | 81 | # Find which cell groups need bend radii if specified, and convert simBend to [groupN, radius] 82 | if( exist('simBend') ) { 83 | cellBoundaries = [0; cumsum(simCellLen(:))]; 84 | 85 | # Find all groups that overlap this bend radius requirement 86 | # Can vectorize this, but it's clearer in a loop 87 | bendGroups = [0, 0]; 88 | for( i=1:size(simBend,1) ) { 89 | overlapCells = find( (cellBoundaries(1:(end-1)) >= simBend(i, 1)) & (cellBoundaries(2:end) <= simBend(i, 2)) ); 90 | bendGroups = [bendGroups; overlapCells, ones(length(overlapCells))*simBend(i,3)]; 91 | } 92 | bendGroups = bendGroups(2:end,:); 93 | 94 | # Now must convert group number to number of FIRST cell in the group, because of EME strangeness: cell group custom properties set by selecting FIRST cell only 95 | # See https://support.lumerical.com/hc/en-us/articles/360034929113 96 | firstCellN = cumsum(simCellN) - simCellN + 1; 97 | bendGroups(:,1) = firstCellN(bendGroups(:,1)); 98 | simBend = bendGroups; 99 | 100 | clear(cellBoundaries, firstCellN, bendGroups); 101 | } 102 | 103 | 104 | -------------------------------------------------------------------------------- /common/util_paramDefaults.lsf: -------------------------------------------------------------------------------- 1 | ### Set specified parameters to default if not already existing 2 | ## Inputs defined as strings: 3 | # dVars: comma-separated list of variables 4 | # dVals: comma-separated list of values for specified variables 5 | # dRequired: optional; comma-separated list of values that are required (error if not exist) 6 | 7 | # Clean inputs 8 | dVars = replacestring(dVars, ' ', ''); 9 | dVals = replacestring(dVals, ' ', ''); 10 | if(exist('dRequired')) { dRequired = replacestring(dRequired, ' ', ''); } 11 | 12 | # Change to cell 13 | dVars = splitstring(dVars, ','); 14 | dVals = splitstring(dVals, ','); 15 | if(exist('dRequired')) { dRequired = splitstring(dRequired, ','); } 16 | 17 | # Verify input 18 | if(length(dVars) ~= length(dVals)) { ?('Error: number of values does not match number of variables!'); break; } 19 | 20 | # Check for required variables 21 | if(exist('dRequired')) { 22 | if(iscell(dRequired)) { 23 | for(i=1:length(dRequired)) { 24 | if(~exist(dRequired{i}) & (dRequired{i} ~= '' )) { 25 | ?('Error: required variable "' + dRequired{i} + '" not present!'); 26 | break; 27 | } 28 | } 29 | } 30 | clear(dRequired); 31 | } 32 | 33 | # Simply check for existence and set if not exist 34 | for(i=1:length(dVars)) { 35 | if( (exist(dVars{i}) ~= 1) & (length(dVars{i}) > 0) ) { 36 | eval(dVars{i}+'='+dVals{i}+';'); 37 | } 38 | } 39 | 40 | 41 | ## Cleanup 42 | clear(i, dVars, dVals); 43 | -------------------------------------------------------------------------------- /remote-dedicated/Q_parallel.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | SOLV=$1 8 | shift 9 | [[ -z "$SOLV" ]] && { 10 | echo "No solver specified!" 11 | exit 1 12 | } 13 | EXECDIR=~/lumerical/tmp/ 14 | RUN="~/lumerical/Q_selected.sh" 15 | 16 | 17 | # Helper functions 18 | function relPath() { 19 | cd $EXECDIR || exit 1 20 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 21 | } 22 | 23 | 24 | # Process all inputs 25 | TOPDIR="$(pwd)" 26 | for file in "$@"; do { 27 | cd "$TOPDIR" || exit 1 28 | 29 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 30 | INPUT=${EXECDIR}/$( relPath "$( readlink -f "$file" )" ) 31 | cd $EXECDIR || exit 1 32 | [ -f "$INPUT" ] || { 33 | echo "'$1' not found, skipping" 34 | continue 35 | } 36 | 37 | # Check that it's an appropriate file type 38 | [[ "${INPUT##*.}" == "lsf" ]] || { 39 | echo "'$INPUT' not a .lsf file, skipping" 40 | continue; 41 | } 42 | 43 | # Check if output exists; skip if so 44 | [[ -n $(find "$(dirname "$INPUT")" -iname "$(basename "${INPUT/.lsf/_$SOLV.mat}")") ]] || { 45 | # Submit for processing 46 | "$RUN" "$SOLV" "$INPUT" 47 | } 48 | } 49 | done 50 | 51 | echo 'Submitted all jobs!' 52 | -------------------------------------------------------------------------------- /remote-dedicated/Q_selected.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-10 4 | # Run small Lumerical job 5 | # Input: single .lsf script to define structure and simulation 6 | 7 | 8 | ## Variable definitions 9 | SOLV=$1 10 | [[ -z "$SOLV" ]] && { 11 | echo "No solver specified!" 12 | exit 1 13 | } 14 | 15 | BUILDONLY="${BUILDONLY:=0}" 16 | PREBUILD="${PREBUILD:=0}" 17 | POSTBUILD="${POSTBUILD:=0}" 18 | 19 | EXECDIR=~/lumerical/tmp/ 20 | 21 | # Local pueue queue definitions 22 | CADQ="cad" 23 | ENGQ="engine" 24 | 25 | # Path definitions 26 | ANALYSIS="~/lumerical/lumanalysis_template.lsf" 27 | NICE="nice -n 15" 28 | XVNC="~/lumerical/xvnc-run -a" 29 | 30 | 31 | ## Process input 32 | # Get full path of input and verify existence 33 | INPUT="$( readlink -f "$2" )" 34 | BASENAME="$( basename "$INPUT" )" 35 | 36 | cd $EXECDIR || exit 1 37 | [[ -f "$INPUT" ]] || { 38 | echo "'$2' not found, skipping" 39 | exit 1 40 | } 41 | 42 | [[ "${INPUT##*.}" == "lsf" ]] || { 43 | echo "'$INPUT' not a .lsf file, aborting" 44 | exit 1 45 | } 46 | 47 | # Find solver 48 | case $SOLV in 49 | fde | mode) 50 | CAD="/opt/lumerical/v222/bin/mode-solutions-app -use-solve" 51 | ENG="/opt/lumerical/v222/bin/fd-engine -t 4" 52 | SOL=${INPUT/.lsf/.lms} 53 | ;; 54 | eme) 55 | CAD="/opt/lumerical/v222/bin/mode-solutions-app" 56 | ENG="/opt/lumerical/v222/bin/eme-engine-mpich2nem -n 1 -t 4" 57 | SOL=${INPUT/.lsf/.lms} 58 | ;; 59 | fdtd) 60 | CAD="/opt/lumerical/v222/bin/fdtd-solutions-app" 61 | ENG="/opt/lumerical/v222/bin/fdtd-engine-mpich2nem -n 1 -t 16" 62 | SOL=${INPUT/.lsf/.fsp} 63 | ENGQ="fdtd-engine" 64 | ;; 65 | varfdtd) 66 | CAD="/opt/lumerical/v222/bin/mode-solutions-app -use-solve" 67 | ENG="/opt/lumerical/v222/bin/varfdtd-engine-mpich2nem -t 4" 68 | SOL=${INPUT/.lsf/.lms} 69 | ;; 70 | device | charge) 71 | CAD="/opt/lumerical/v222/bin/device-app -use-solve" 72 | ENG="/opt/lumerical/v222/bin/device-engine-mpich2nem -t 4" 73 | SOL=${INPUT/.lsf/.ldev} 74 | ;; 75 | *) 76 | echo "Unknown solver $SOLV" 77 | exit 0 78 | ;; 79 | esac 80 | 81 | 82 | # Make temporary script file 83 | TMPSCRIPT="${INPUT/.lsf/_working_${SOLV}.lsf}" 84 | cp "$INPUT" "$TMPSCRIPT" 85 | 86 | # Datafile to pass variables for analysis 87 | TMPLDF=${TMPSCRIPT/.lsf/.ldf} 88 | 89 | # Set save location for file and environment to expected output 90 | sed -i -n '/\bsave(/!p;$a\save("'"$SOL"'")\;\nsavedata("'"$TMPLDF"'")\;\n' "$TMPSCRIPT" 91 | 92 | 93 | ## Build temporary self-cleaning files to submit to pueue 94 | SHBUILD=${TMPSCRIPT/.lsf/_build.sh} 95 | SHENGINE=${TMPSCRIPT/.lsf/_engine.sh} 96 | SHANALYZE=${TMPSCRIPT/.lsf/_analyze.sh} 97 | 98 | 99 | [[ $POSTBUILD -eq 1 ]] || { 100 | # Script to build solver file via CAD 101 | # $XVNC -s "-screen 0 1600x1200x16" $CAD -nw -run "$TMPSCRIPT" -exit -trust-script 102 | cat > "$SHBUILD" <> "$SHBUILD" 119 | [[ $PREBUILD -eq 1 ]] && echo "rm \"$TMPSCRIPT\"" >> "$SHBUILD" 120 | 121 | chmod a+x "$SHBUILD" 122 | } 123 | 124 | 125 | [[ $BUILDONLY -eq 1 ]] || [[ $PREBUILD -eq 1 ]] || { 126 | [[ $POSTBUILD -eq 1 ]] || { 127 | # Script to run the engine on resulting solver 128 | cat > "$SHENGINE" < "$SHANALYZE" <#${SOL}#;s##${TMPLDF}#" $ANALYSIS > "$TMPSCRIPT" # Reusing previous $TMPSCRIPT 149 | $NICE $XVNC $CAD -nw -run "$TMPSCRIPT" -exit -trust-script 150 | rm "$TMPSCRIPT" "$TMPLDF" # Clean up temporary files 151 | 152 | echo "Work on $BASENAME complete!" 153 | 154 | # Delete self 155 | rm "$SHANALYZE" 156 | EOT 157 | 158 | chmod a+x "$SHANALYZE" 159 | } 160 | 161 | 162 | 163 | # Make sure pueue daemon is started 164 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 165 | pueue clean &>/dev/null 166 | 167 | ## Execute: submit to pueue queue with chained dependencies 168 | [[ $POSTBUILD -eq 1 ]] || { 169 | # Build 170 | ID=$( pueue add -g $CADQ "$SHBUILD" | grep -Po '(?<=id )[0-9]+' ) 171 | # echo pueue add -g $CADQ "$SHBUILD" 172 | } 173 | 174 | [[ $BUILDONLY -eq 1 ]] || [[ $PREBUILD -eq 1 ]] || [[ $POSTBUILD -eq 1 ]] || { 175 | # Add 'after $ID completes' queue for $SHENGINE 176 | ID=$( pueue add -a $ID -g $ENGQ "$SHENGINE" | grep -Po '(?<=id )[0-9]+' ) 177 | 178 | # Add 'after $ID completes' queue for $SHANALYZE 179 | pueue add -a $ID -g $CADQ "$SHANALYZE" 180 | } 181 | [[ $POSTBUILD -eq 1 ]] && pueue add -g $CADQ "$SHANALYZE" 182 | -------------------------------------------------------------------------------- /remote-dedicated/dispatch-device.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to local server, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@lumerical 7 | SET remoteDIR=~/lumerical/tmp 8 | SET remoteRUN=~/lumerical/run_device.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%remoteDIR%/%~nx1" 12 | %plink% -batch %login% %remoteRUN% "%remoteDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%remoteDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%remoteDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-dedicated/dispatch-eme.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to local server, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@lumerical 7 | SET remoteDIR=~/lumerical/tmp 8 | SET remoteRUN=~/lumerical/run_eme.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%remoteDIR%/%~nx1" 12 | %plink% -batch %login% %remoteRUN% "%remoteDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%remoteDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%remoteDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-dedicated/dispatch-fde.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to local server, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@lumerical 7 | SET remoteDIR=~/lumerical/tmp 8 | SET remoteRUN=~/lumerical/run_fde.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%remoteDIR%/%~nx1" 12 | %plink% -batch %login% %remoteRUN% "%remoteDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%remoteDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%remoteDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-dedicated/dispatch-fdtd.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to local server, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@lumerical 7 | SET remoteDIR=~/lumerical/tmp 8 | SET remoteRUN=~/lumerical/run_fdtd.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%remoteDIR%/%~nx1" 12 | %plink% -batch %login% %remoteRUN% "%remoteDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%remoteDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%remoteDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-dedicated/dispatch-varfdtd.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to local server, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@lumerical 7 | SET remoteDIR=~/lumerical/tmp 8 | SET remoteRUN=~/lumerical/run_varfdtd.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%remoteDIR%/%~nx1" 12 | %plink% -batch %login% %remoteRUN% "%remoteDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%remoteDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%remoteDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-dedicated/lumanalysis_template.lsf: -------------------------------------------------------------------------------- 1 | addpath('/home/nickersonm/lumerical/common'); 2 | util_fCommon; 3 | 4 | cd(filedirectory(currentscriptname)); 5 | load(""); 6 | loaddata(""); 7 | 8 | if( layoutmode() > 0 ) { 9 | run; save; 10 | if( getnamednumber('FDE') > 0 ) { 11 | # eval() gets around the lack of 'nummodes()' in CHARGE and FDTD 12 | eval("if( nummodes() == 0 ) { findmodes; }"); 13 | } 14 | } 15 | 16 | lum_analyze; 17 | -------------------------------------------------------------------------------- /remote-dedicated/lumerical_setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | NEWUSER=user 3 | 4 | # Debian container or dedicated Lumerical host 5 | export PKGREM="snapd muon bind9* open-vm-tools packagekit* build-essential unattended-upgrades snapd" 6 | 7 | # apt-get flags 8 | export APTREM="-q --yes --fix-broken --auto-remove --purge" 9 | export APTADD="$APTREM --no-install-recommends" 10 | 11 | 12 | apt-get remove $APTREM $PKGREM 13 | 14 | # Update repositories and install some basic required utilities 15 | apt-get update 16 | apt-get install $APTADD openssh-server nano zsh curl wget bash-completion samba samba-vfs-modules wireguard sudo 17 | 18 | apt dist-upgrade $APTADD 19 | apt-get remove $APTREM $PKGREM 20 | apt-get clean 21 | 22 | 23 | ## Main user 24 | echo "%admin ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/10-admin 25 | /usr/sbin/groupadd admin 26 | /usr/sbin/adduser $NEWUSER admin 27 | sudo -u $NEWUSER mkdir /home/$NEWUSER/.ssh 28 | sudo -u $NEWUSER touch /home/$NEWUSER/.ssh/authorized_keys 29 | echo "# Paste an authorized SSH key" >> /home/$NEWUSER/.ssh/authorized_keys 30 | sudo -u $NEWUSER nano /home/$NEWUSER/.ssh/authorized_keys 31 | 32 | 33 | ## Samba for easy access via Windows clients 34 | sudo smbpasswd -a "$USER" 35 | sudo rm /etc/sambda/smb.conf 36 | sudo tee /etc/samba/smb.conf <"); 2 | switchtolayout; 3 | save; 4 | -------------------------------------------------------------------------------- /remote-dedicated/run_device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-07-27 4 | # Run small Lumerical DEVICE job on a Linux machine, to be used with remote dispatch 5 | # Input: ldev file with DEVICE simulation 6 | # Set REFRESH=1 to refresh/regenerate ldev file before execution; useful when working with disparate versions 7 | 8 | 9 | ## Variable definitions 10 | export EXECDIR=~/lumerical/tmp/ 11 | 12 | # Performance and path definitions 13 | THREADS=16 # Default 14 | CAD="/opt/lumerical/v222/bin/device-app" 15 | ENG="/opt/lumerical/v222/bin/device-engine-mpich2nem" 16 | LSFREFRESH="~/lumerical/refresh.lsf" 17 | NICE="nice -n 15" 18 | XVNC="~/lumerical/xvnc-run -a" 19 | 20 | 21 | ## Process input 22 | # Get full path of input and verify existence 23 | INPUT="$( readlink -f "$1" )" 24 | BASENAME="$( basename "$INPUT" )" 25 | 26 | cd $EXECDIR || exit 1 27 | [[ -f "$INPUT" ]] || { 28 | echo "'$1' not found, skipping" 29 | exit 1 30 | } 31 | 32 | [[ "${INPUT##*.}" == "ldev" ]] || { 33 | echo "'$INPUT' not a .ldev file, aborting" 34 | exit 1 35 | } 36 | 37 | # Possibly refresh/regenerate the ldev file 38 | [[ "$REFRESH" -eq 1 ]] && { 39 | # Make temporary refresh script 40 | TMPSCRIPT="${INPUT/.ldev/_refresh_$RANDOM.lsf}" 41 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 42 | 43 | # Load and refresh file with CAD to avoid cross-version bugs 44 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 45 | $NICE $XVNC $CAD -nw -run "$TMPSCRIPT" -exit 46 | rm "$TMPSCRIPT" 47 | printf " done\n" 48 | } 49 | 50 | 51 | ## Run job 52 | echo "Running $(basename $ENG) on $BASENAME..." 53 | $NICE $ENG -t $THREADS "$INPUT" || { 54 | echo "Error processing $BASENAME, aborting" 55 | exit 1 56 | } 57 | echo "Processed $BASENAME" 58 | rm "${INPUT/.ldev/_p0.log}" # Remove engine log file 59 | 60 | echo "Work on $BASENAME complete!" 61 | -------------------------------------------------------------------------------- /remote-dedicated/run_eme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-07-27 4 | # Run small Lumerical EME job on a Linux machine, to be used with remote dispatch 5 | # Input: lms file with EME simulation 6 | # Set REFRESH=1 to refresh/regenerate lms file before execution; useful when working with disparate versions 7 | 8 | 9 | ## Variable definitions 10 | export EXECDIR=~/lumerical/tmp/ 11 | 12 | # Performance and path definitions 13 | THREADS=16 # Default 14 | CAD="/opt/lumerical/v222/bin/mode-solutions-app" 15 | ENG="/opt/lumerical/v222/bin/eme-engine" 16 | LSFREFRESH="~/lumerical/refresh.lsf" 17 | NICE="nice -n 15" 18 | XVNC="~/lumerical/xvnc-run -a" 19 | 20 | 21 | ## Process input 22 | # Get full path of input and verify existence 23 | INPUT="$( readlink -f "$1" )" 24 | BASENAME="$( basename "$INPUT" )" 25 | 26 | cd $EXECDIR || exit 1 27 | [[ -f "$INPUT" ]] || { 28 | echo "'$1' not found, skipping" 29 | exit 1 30 | } 31 | 32 | [[ "${INPUT##*.}" == "lms" ]] || { 33 | echo "'$INPUT' not a .lms file, aborting" 34 | exit 1 35 | } 36 | 37 | # Possibly refresh/regenerate the lms file 38 | [[ "$REFRESH" -eq 1 ]] && { 39 | # Make temporary refresh script 40 | TMPSCRIPT="${INPUT/.lms/_refresh_$RANDOM.lsf}" 41 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 42 | 43 | # Load and refresh file with CAD to avoid cross-version bugs 44 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 45 | $NICE $XVNC $CAD -nw -run "$TMPSCRIPT" -exit 46 | rm "$TMPSCRIPT" 47 | printf " done\n" 48 | } 49 | 50 | 51 | ## Run job 52 | echo "Running $(basename $ENG) on $BASENAME..." 53 | $NICE $ENG -t $THREADS \"$INPUT\" || { 54 | echo "Error processing $BASENAME, aborting" 55 | exit 1 56 | } 57 | echo "Processed $BASENAME" 58 | rm "${INPUT/.lms/_p0.log}" # Remove engine log file 59 | 60 | echo "Work on $BASENAME complete!" 61 | -------------------------------------------------------------------------------- /remote-dedicated/run_fde.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-07-27 4 | # Run small Lumerical FDE job on a Linux machine, to be used with remote dispatch 5 | # Input: lms file with FDE simulation 6 | # Set REFRESH=1 to refresh/regenerate lms file before execution; useful when working with disparate versions 7 | 8 | 9 | ## Variable definitions 10 | export EXECDIR=~/lumerical/tmp/ 11 | 12 | # Performance and path definitions 13 | THREADS=16 # Default 14 | CAD="/opt/lumerical/v222/bin/mode-solutions-app" 15 | ENG="/opt/lumerical/v222/bin/fd-engine" 16 | LSFREFRESH="~/lumerical/refresh.lsf" 17 | NICE="nice -n 15" 18 | XVNC="~/lumerical/xvnc-run -a" 19 | 20 | 21 | ## Process input 22 | # Get full path of input and verify existence 23 | INPUT="$( readlink -f "$1" )" 24 | BASENAME="$( basename "$INPUT" )" 25 | 26 | cd $EXECDIR || exit 1 27 | [[ -f "$INPUT" ]] || { 28 | echo "'$1' not found, skipping" 29 | exit 1 30 | } 31 | 32 | [[ "${INPUT##*.}" == "lms" ]] || { 33 | echo "'$INPUT' not a .lms file, aborting" 34 | exit 1 35 | } 36 | 37 | # Possibly refresh/regenerate the lms file 38 | [[ "$REFRESH" -eq 1 ]] && { 39 | # Make temporary refresh script 40 | TMPSCRIPT="${INPUT/.lms/_refresh_$RANDOM.lsf}" 41 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 42 | 43 | # Load and refresh file with CAD to avoid cross-version bugs 44 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 45 | $NICE $XVNC $CAD -nw -run "$TMPSCRIPT" -exit 46 | rm "$TMPSCRIPT" 47 | printf " done\n" 48 | } 49 | 50 | 51 | ## Run job 52 | echo "Running $(basename $ENG) on $BASENAME..." 53 | $NICE $ENG -t $THREADS \"$INPUT\" || { 54 | echo "Error processing $BASENAME, aborting" 55 | exit 1 56 | } 57 | echo "Processed $BASENAME" 58 | rm "${INPUT/.lms/_p0.log}" # Remove engine log file 59 | 60 | echo "Work on $BASENAME complete!" 61 | -------------------------------------------------------------------------------- /remote-dedicated/run_fdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-07-27 4 | # Run small Lumerical FDTD job on a Linux machine, to be used with remote dispatch 5 | # Input: fsp file with FDTD simulation 6 | # Set REFRESH=1 to refresh/regenerate fsp file before execution; useful when working with disparate versions 7 | 8 | 9 | ## Variable definitions 10 | export EXECDIR=~/lumerical/tmp/ 11 | 12 | # Performance and path definitions 13 | THREADS=16 # Default 14 | CAD="/opt/lumerical/v222/bin/fdtd-solutions-app" 15 | ENG="/opt/lumerical/v222/bin/fdtd-engine-mpich2nem" 16 | LSFREFRESH="~/lumerical/refresh.lsf" 17 | NICE="nice -n 15" 18 | XVNC="~/lumerical/xvnc-run -a" 19 | 20 | 21 | ## Process input 22 | # Get full path of input and verify existence 23 | INPUT="$( readlink -f "$1" )" 24 | BASENAME="$( basename "$INPUT" )" 25 | 26 | cd $EXECDIR || exit 1 27 | [[ -f "$INPUT" ]] || { 28 | echo "'$1' not found, skipping" 29 | exit 1 30 | } 31 | 32 | [[ "${INPUT##*.}" == "fsp" ]] || { 33 | echo "'$INPUT' not a .fsp file, aborting" 34 | exit 1 35 | } 36 | 37 | # Possibly refresh/regenerate the fsp file 38 | [[ "$REFRESH" -eq 1 ]] && { 39 | # Make temporary refresh script 40 | TMPSCRIPT="${INPUT/.fsp/_refresh_$RANDOM.lsf}" 41 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 42 | 43 | # Load and refresh file with CAD to avoid cross-version bugs 44 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 45 | $NICE $XVNC $CAD -nw -run "$TMPSCRIPT" -exit 46 | rm "$TMPSCRIPT" 47 | printf " done\n" 48 | } 49 | 50 | 51 | ## Run job 52 | echo "Running $(basename $ENG) on $BASENAME..." 53 | $NICE $ENG -t $THREADS \"$INPUT\" || { 54 | echo "Error processing $BASENAME, aborting" 55 | exit 1 56 | } 57 | echo "Processed $BASENAME" 58 | rm "${INPUT/.fsp/_p0.log}" # Remove engine log file 59 | 60 | echo "Work on $BASENAME complete!" 61 | -------------------------------------------------------------------------------- /remote-dedicated/run_varfdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-07-27 4 | # Run small Lumerical varfdtd job on a Linux machine, to be used with remote dispatch 5 | # Input: lms file with varfdtd simulation 6 | # Set REFRESH=1 to refresh/regenerate lms file before execution; useful when working with disparate versions 7 | 8 | 9 | ## Variable definitions 10 | export EXECDIR=~/lumerical/tmp/ 11 | 12 | # Performance and path definitions 13 | THREADS=16 # Default 14 | CAD="/opt/lumerical/v222/bin/mode-solutions-app" 15 | ENG="/opt/lumerical/v222/bin/varfdtd-engine-mpich2nem" 16 | LSFREFRESH="~/lumerical/refresh.lsf" 17 | NICE="nice -n 15" 18 | XVNC="~/lumerical/xvnc-run -a" 19 | 20 | 21 | ## Process input 22 | # Get full path of input and verify existence 23 | INPUT="$( readlink -f "$1" )" 24 | BASENAME="$( basename "$INPUT" )" 25 | 26 | cd $EXECDIR || exit 1 27 | [[ -f "$INPUT" ]] || { 28 | echo "'$1' not found, skipping" 29 | exit 1 30 | } 31 | 32 | [[ "${INPUT##*.}" == "lms" ]] || { 33 | echo "'$INPUT' not a .lms file, aborting" 34 | exit 1 35 | } 36 | 37 | # Possibly refresh/regenerate the lms file 38 | [[ "$REFRESH" -eq 1 ]] && { 39 | # Make temporary refresh script 40 | TMPSCRIPT="${INPUT/.lms/_refresh_$RANDOM.lsf}" 41 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 42 | 43 | # Load and refresh file with CAD to avoid cross-version bugs 44 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 45 | $NICE $XVNC $CAD -nw -run "$TMPSCRIPT" -exit 46 | rm "$TMPSCRIPT" 47 | printf " done\n" 48 | } 49 | 50 | 51 | ## Run job 52 | echo "Running $(basename $ENG) on $BASENAME..." 53 | $NICE $ENG -t $THREADS \"$INPUT\" || { 54 | echo "Error processing $BASENAME, aborting" 55 | exit 1 56 | } 57 | echo "Processed $BASENAME" 58 | rm "${INPUT/.lms/_p0.log}" # Remove engine log file 59 | 60 | echo "Work on $BASENAME complete!" 61 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_O-E.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | DEVICESUB="~/lumerical/MQ_queue_device.sh" 9 | MODESUB="~/lumerical/MQ_queue_mode.sh" 10 | 11 | 12 | # Helper functions 13 | function relPath() { 14 | cd $EXECDIR || exit 1 15 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 16 | } 17 | 18 | 19 | # Make sure pueue daemon is started 20 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 21 | pueue clean 22 | 23 | 24 | # Process all inputs 25 | TOPDIR="$(pwd)" 26 | for file in "$@"; do { 27 | cd $TOPDIR || exit 1 28 | 29 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 30 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 31 | cd $EXECDIR || exit 1 32 | [ -f "$INPUT" ] || { 33 | echo "'$1' not found, skipping" 34 | continue 35 | } 36 | 37 | # Check that it's an appropriate file type 38 | [[ "${INPUT##*.}" == "lsf" ]] || { 39 | echo "'$INPUT' not a .lsf file, skipping" 40 | continue; 41 | } 42 | 43 | # Submit to local pueue queue for DEVICE 44 | pueue add -g local "$DEVICESUB" "$INPUT" 45 | 46 | # Submit to local pueue queue for MODE 47 | pueue add -g local "$MODESUB" "$INPUT" 48 | } 49 | done 50 | 51 | echo 'Submitted all jobs!' 52 | 53 | pueue clean 54 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | DEVICESUB="~/lumerical/MQ_queue_device.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "lsf" ]] || { 38 | echo "'$INPUT' not a .lsf file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for processing 43 | pueue add -g local "$DEVICESUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_eme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | QSUB="~/lumerical/Qrun_eme.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "lms" ]] || { 38 | echo "'$INPUT' not a .lms file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for MODE 43 | pueue add -g eme-engine "$QSUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_fde_lms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | MODESUB="~/lumerical/Qrun_fde.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "lms" ]] || { 38 | echo "'$INPUT' not a .lms file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for MODE 43 | pueue add -g 'mode-engine' "$MODESUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean &>/dev/null 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_fdtd_fsp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | FDTDSUB="~/lumerical/Qrun_fdtd.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "fsp" ]] || { 38 | echo "'$INPUT' not a .fsp file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for FDTD 43 | pueue add -g 'fdtd-engine' "$FDTDSUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean &>/dev/null 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | MODESUB="~/lumerical/MQ_queue_mode.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "lsf" ]] || { 38 | echo "'$INPUT' not a .lsf file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for MODE 43 | pueue add -g local "$MODESUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_unprocessed_O-E.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | DEVICESUB="~/lumerical/MQ_queue_device.sh" 9 | MODESUB="~/lumerical/MQ_queue_mode.sh" 10 | 11 | 12 | # Helper functions 13 | function relPath() { 14 | cd $EXECDIR || exit 1 15 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 16 | } 17 | 18 | 19 | # Make sure pueue daemon is started 20 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 21 | pueue clean 22 | 23 | 24 | # Process all inputs 25 | TOPDIR="$(pwd)" 26 | for file in "$@"; do { 27 | cd $TOPDIR || exit 1 28 | 29 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 30 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 31 | cd $EXECDIR || exit 1 32 | [ -f "$INPUT" ] || { 33 | echo "'$1' not found, skipping" 34 | continue 35 | } 36 | 37 | # Check that it's an appropriate file type 38 | [[ "${INPUT##*.}" == "lsf" ]] || { 39 | echo "'$INPUT' not a .lsf file, skipping" 40 | continue; 41 | } 42 | 43 | # Submit to local pueue queue for DEVICE if no result file exists 44 | [ ! -f "${INPUT/.lsf/_CHARGE.mat}" ] && pueue add -g local "$DEVICESUB" "$INPUT" 45 | 46 | # Submit to local pueue queue for MODE if no result file exists 47 | [ ! -f "${INPUT/.lsf/_MODE.mat}" ] && pueue add -g local "$MODESUB" "$INPUT" 48 | } 49 | done 50 | 51 | echo 'Submitted all jobs!' 52 | 53 | pueue clean 54 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_unprocessed_varfdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | SUB="~/lumerical/MQ_queue_varfdtd.sh" 9 | 10 | 11 | # Make sure pueue daemon is started 12 | TOPDIR="$(pwd)" 13 | cd ~ || exit 1 14 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 15 | pueue clean 16 | 17 | 18 | # Process all inputs 19 | for file in "$@"; do { 20 | cd "$TOPDIR" || exit 1 21 | 22 | # Get full path of input and verify existence 23 | INPUT="$( readlink -f "$file" )" 24 | cd $EXECDIR || exit 1 25 | [ -f "$INPUT" ] || { 26 | echo "Not submitting; '$INPUT' not found, skipping" 27 | continue 28 | } 29 | 30 | # Check that it's an appropriate file type 31 | [[ "${INPUT##*.}" == "lsf" ]] || { 32 | echo "'$INPUT' not a .lsf file, skipping" 33 | continue 34 | } 35 | 36 | # Submit to local pueue queue if no result file exists 37 | [ ! -f "${INPUT/.lsf/_varFDTD.mat}" ] && pueue add -g local "$SUB" "$INPUT" 38 | } 39 | done 40 | 41 | echo 'Submitted all jobs!' 42 | 43 | pueue clean 44 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_varfdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | FDTDSUB="~/lumerical/MQ_queue_varfdtd.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "lsf" ]] || { 38 | echo "'$INPUT' not a .lsf file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for MODE 43 | pueue add -g local "$FDTDSUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_parallel_varfdtd_lms.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | 5 | 6 | # Variable definitions 7 | export EXECDIR=~/lumerical/tmp/ 8 | FDTDSUB="~/lumerical/Qrun_varfdtd.sh" 9 | 10 | 11 | # Helper functions 12 | function relPath() { 13 | cd $EXECDIR || exit 1 14 | perl -e 'use File::Spec; print File::Spec->abs2rel(@ARGV) . "\n"' "$1" ./ 15 | } 16 | 17 | 18 | # Make sure pueue daemon is started 19 | [[ "$( pueue status 2>&1 | grep -c 'Error' )" -eq 0 ]] || pueued -d; 20 | pueue clean 21 | 22 | 23 | # Process all inputs 24 | TOPDIR="$(pwd)" 25 | for file in "$@"; do { 26 | cd $TOPDIR || exit 1 27 | 28 | # Get full path of input (including ~/lumerical symlink present on both clusters) and verify existence 29 | INPUT=${EXECDIR}/$( relPath "$( readlink -f $file )" ) 30 | cd $EXECDIR || exit 1 31 | [ -f "$INPUT" ] || { 32 | echo "'$1' not found, skipping" 33 | continue 34 | } 35 | 36 | # Check that it's an appropriate file type 37 | [[ "${INPUT##*.}" == "lms" ]] || { 38 | echo "'$INPUT' not a .lms file, skipping" 39 | continue; 40 | } 41 | 42 | # Submit to local pueue queue for FDTD 43 | pueue add -g 'varfdtd-engine' "$FDTDSUB" "$INPUT" 44 | } 45 | done 46 | 47 | echo 'Submitted all jobs!' 48 | 49 | pueue clean &>/dev/null 50 | -------------------------------------------------------------------------------- /remote-slurm/MQ_queue_device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | # Run small Lumerical DEVICE job on slurm host 5 | # Input: single .lsf script to define structure and simulation 6 | 7 | 8 | ## Variable definitions 9 | export EXECDIR=~/lumerical/tmp/ 10 | 11 | # Queue commands 12 | THREADS=10 13 | QENG="srun --nodes=1 --cpus-per-task=$THREADS --time=2:00:00 --mem=10G -p batch --job-name=DEVICE" 14 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 15 | # The login node cannot run DEVICE without core dumping either, so for now run it locally 16 | # Unable to determine cause, even with extensive Xvfb testing 17 | QCAD="nice -n 15" 18 | 19 | # Local pueue queue definitions 20 | CADQ="local" 21 | ENGQ="charge-engine" 22 | 23 | # Path definitions 24 | CAD="/sw/cnsi/lumerical/2021R1/bin/device-app" 25 | ENG="/sw/cnsi/lumerical/2021R1/bin/device-engine -t $THREADS" 26 | ANALYSIS="~/lumerical/lumanalysis_template.lsf" 27 | XVFB="~/lumerical/xvnc-run -a" 28 | 29 | 30 | ## Process input 31 | # Get full path of input and verify existence 32 | INPUT="$( readlink -f "$1" )" 33 | BASENAME="$( basename "$INPUT" )" 34 | LDEV=${INPUT/.lsf/.ldev} 35 | 36 | cd $EXECDIR || exit 1 37 | [[ -f "$INPUT" ]] || { 38 | echo "'$1' not found, skipping" 39 | exit 1 40 | } 41 | 42 | [[ "${INPUT##*.}" == "lsf" ]] || { 43 | echo "'$INPUT' not a .lsf file, aborting" 44 | exit 1 45 | } 46 | 47 | # Make temporary script file 48 | TMPLSF="${INPUT/.lsf/_$RANDOM.lsf}" 49 | cp "$INPUT" "$TMPLSF" 50 | 51 | # Datafile to pass variables for analysis 52 | TMPLDF=${TMPLSF/.lsf/.ldf} 53 | 54 | # Set save location for file and environment to expected output 55 | sed -i -n '/\bsave(/!p;$a\save("'"$LDEV"'")\;\nsavedata("'"$TMPLDF"'")\;\n' "$TMPLSF" 56 | 57 | 58 | ## Build temporary self-cleaning files to submit to pueue 59 | SHBUILD=${TMPLSF/.lsf/_build.sh} 60 | SHENGINE=${TMPLSF/.lsf/_engine.sh} 61 | SHANALYZE=${TMPLSF/.lsf/_analyze.sh} 62 | 63 | # Script to build .ldev file via CAD 64 | cat > $SHBUILD < $SHENGINE < $SHANALYZE <#${LDEV}#;s##${TMPLDF}#" $ANALYSIS > "$TMPLSF" # Reusing previous $TMPLSF 105 | $QCAD $XVFB -s \"-screen 0 1600x1200x16\" $CAD -nw -run "$TMPLSF" -exit 106 | rm "$TMPLSF" "$TMPLDF" # Clean up temporary files 107 | 108 | echo "Work on $BASENAME complete!" 109 | 110 | # Delete self 111 | rm $SHANALYZE 112 | EOT 113 | 114 | chmod a+x $SHBUILD $SHENGINE $SHANALYZE 115 | 116 | 117 | ## Execute: submit to pueue queue with chained dependencies 118 | # Build 119 | ID=$( pueue add -g $CADQ "$SHBUILD" | grep -Po '(?<=id )[0-9]+' ) 120 | 121 | # Add 'after $ID completes' queue for $SHENGINE 122 | ID=$( pueue add -a $ID -g $ENGQ "$SHENGINE" | grep -Po '(?<=id )[0-9]+' ) 123 | 124 | # Add 'after $ID completes' queue for $SHANALYZE 125 | pueue add -a $ID -g $CADQ "$SHANALYZE" 126 | 127 | -------------------------------------------------------------------------------- /remote-slurm/MQ_queue_fdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-01 4 | # Run small Lumerical FDTD job on slurm host 5 | # Input: single .lsf script to define structure and simulation 6 | 7 | 8 | ## Variable definitions 9 | export EXECDIR=~/lumerical/tmp/ 10 | 11 | # Local pueue queue definitions 12 | CADQ="local" 13 | ENGQ="fdtd-engine" 14 | 15 | # Path definitions 16 | CAD="/sw/cnsi/lumerical/2021R1/bin/fdtd-solutions-app" 17 | ENG="~/lumerical/Qrun_fdtd.sh" # Separate script as it needs to determine memory and node requirements 18 | ANALYSIS="~/lumerical/lumanalysis_template.lsf" 19 | XVFB="~/lumerical/xvnc-run -a" 20 | 21 | 22 | ## Process input 23 | # Get full path of input and verify existence 24 | INPUT="$( readlink -f "$1" )" 25 | BASENAME="$( basename "$INPUT" )" 26 | FSP=${INPUT/.lsf/.fsp} 27 | 28 | cd $EXECDIR || exit 1 29 | [[ -f "$INPUT" ]] || { 30 | echo "'$1' not found, skipping" 31 | exit 1 32 | } 33 | 34 | [[ "${INPUT##*.}" == "lsf" ]] || { 35 | echo "'$INPUT' not a .lsf file, aborting" 36 | exit 1 37 | } 38 | 39 | # Make temporary script file 40 | TMPLSF="${INPUT/.lsf/_$RANDOM.lsf}" 41 | cp "$INPUT" "$TMPLSF" 42 | 43 | # Datafile to pass variables for analysis 44 | TMPLDF=${TMPLSF/.lsf/.ldf} 45 | 46 | # Set save location for file and environment to expected output 47 | sed -i -n '/\bsave(/!p;$a\save("'"$FSP"'")\;\nsavedata("'"$TMPLDF"'")\;\n' "$TMPLSF" 48 | 49 | 50 | ## Build temporary self-cleaning files to submit to pueue 51 | SHBUILD=${TMPLSF/.lsf/_build.sh} 52 | SHENGINE=${TMPLSF/.lsf/_engine.sh} 53 | SHANALYZE=${TMPLSF/.lsf/_analyze.sh} 54 | 55 | 56 | # Script to build .fsp file via CAD 57 | cat > $SHBUILD < $SHENGINE < $SHANALYZE <#${FSP}#;s##${TMPLDF}#" $ANALYSIS > "$TMPLSF" # Reusing previous $TMPLSF 94 | $XVFB -s "-screen 0 1600x1200x16" $CAD -nw -run "$TMPLSF" -exit 95 | rm "$TMPLSF" "$TMPLDF" # Clean up temporary files 96 | 97 | echo "Work on $BASENAME complete!" 98 | 99 | # Delete self 100 | rm $SHANALYZE 101 | EOT 102 | 103 | chmod a+x $SHBUILD $SHENGINE $SHANALYZE 104 | 105 | 106 | ## Execute: submit to pueue queue with chained dependencies 107 | # Build 108 | ID=$( pueue add -g $CADQ "$SHBUILD" | grep -Po '(?<=id )[0-9]+' ) 109 | 110 | # Add 'after $ID completes' queue for $SHENGINE 111 | ID=$( pueue add -a $ID -g $ENGQ "$SHENGINE" | grep -Po '(?<=id )[0-9]+' ) 112 | 113 | # Add 'after $ID completes' queue for $SHANALYZE 114 | pueue add -a $ID -g $CADQ "$SHANALYZE" 115 | 116 | -------------------------------------------------------------------------------- /remote-slurm/MQ_queue_mode.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2021 4 | # Run small Lumerical MODE job on slurm host 5 | # Input: single .lsf script to define structure and simulation 6 | 7 | 8 | ## Variable definitions 9 | export EXECDIR=~/lumerical/tmp/ 10 | 11 | # Queue commands 12 | THREADS=10 13 | QENG="srun --nodes=1 --cpus-per-task=$THREADS --time=2:00:00 --mem=10G -p batch --job-name=MODE" 14 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 15 | # Unable to determine cause, even with extensive Xvfb testing 16 | QCAD="nice -n 15" 17 | 18 | # Local pueue queue definitions 19 | CADQ="local" 20 | ENGQ="mode-engine" 21 | 22 | # Path definitions 23 | CAD="/sw/cnsi/lumerical/2021R1/bin/mode-solutions-app" 24 | ENG="/sw/cnsi/lumerical/2021R1/bin/fd-engine -t $THREADS" 25 | ANALYSIS="~/lumerical/lumanalysis_template.lsf" 26 | XVFB="~/lumerical/xvnc-run -a" 27 | 28 | 29 | ## Process input 30 | # Get full path of input and verify existence 31 | INPUT="$( readlink -f "$1" )" 32 | BASENAME="$( basename "$INPUT" )" 33 | LMS=${INPUT/.lsf/.lms} 34 | 35 | cd $EXECDIR || exit 1 36 | [[ -f "$INPUT" ]] || { 37 | echo "'$1' not found, skipping" 38 | exit 1 39 | } 40 | 41 | [[ "${INPUT##*.}" == "lsf" ]] || { 42 | echo "'$INPUT' not a .lsf file, aborting" 43 | exit 1 44 | } 45 | 46 | # Make temporary script file 47 | TMPLSF="${INPUT/.lsf/_$RANDOM.lsf}" 48 | cp "$INPUT" "$TMPLSF" 49 | 50 | # Datafile to pass variables for analysis 51 | TMPLDF=${TMPLSF/.lsf/.ldf} 52 | 53 | # Set save location for file and environment to expected output 54 | sed -i -n '/\bsave(/!p;$a\save("'"$LMS"'")\;\nsavedata("'"$TMPLDF"'")\;\n' "$TMPLSF" 55 | 56 | 57 | ## Build temporary self-cleaning files to submit to pueue 58 | SHBUILD=${TMPLSF/.lsf/_build.sh} 59 | SHENGINE=${TMPLSF/.lsf/_engine.sh} 60 | SHANALYZE=${TMPLSF/.lsf/_analyze.sh} 61 | 62 | 63 | # Script to build .lms file via CAD 64 | cat > $SHBUILD < $SHENGINE < $SHANALYZE <#${LMS}#;s##${TMPLDF}#" $ANALYSIS > "$TMPLSF" # Reusing previous $TMPLSF 104 | $QCAD $XVFB -s "-screen 0 1600x1200x16" $CAD -nw -run "$TMPLSF" -exit 105 | rm "$TMPLSF" "$TMPLDF" # Clean up temporary files 106 | 107 | echo "Work on $BASENAME complete!" 108 | 109 | # Delete self 110 | rm $SHANALYZE 111 | EOT 112 | 113 | chmod a+x $SHBUILD $SHENGINE $SHANALYZE 114 | 115 | 116 | ## Execute: submit to pueue queue with chained dependencies 117 | # Build 118 | ID=$( pueue add -g $CADQ "$SHBUILD" | grep -Po '(?<=id )[0-9]+' ) 119 | 120 | # Add 'after $ID completes' queue for $SHENGINE 121 | ID=$( pueue add -a $ID -g $ENGQ "$SHENGINE" | grep -Po '(?<=id )[0-9]+' ) 122 | 123 | # Add 'after $ID completes' queue for $SHANALYZE 124 | pueue add -a $ID -g $CADQ "$SHANALYZE" 125 | 126 | -------------------------------------------------------------------------------- /remote-slurm/MQ_queue_varfdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-01 4 | # Run small Lumerical varFDTD job on slurm host 5 | # Input: single .lsf script to define structure and simulation 6 | 7 | 8 | ## Variable definitions 9 | export EXECDIR=~/lumerical/tmp/ 10 | 11 | # Local pueue queue definitions 12 | CADQ="local" 13 | ENGQ="varfdtd-engine" 14 | 15 | # Path definitions 16 | CAD="/sw/cnsi/lumerical/2021R1/bin/mode-solutions-app" 17 | ENG="~/lumerical/Qrun_varfdtd.sh" # Separate script as it needs to determine memory and node requirements 18 | ANALYSIS="~/lumerical/lumanalysis_template.lsf" 19 | XVFB="~/lumerical/xvnc-run -a" 20 | 21 | 22 | ## Process input 23 | # Get full path of input and verify existence 24 | INPUT="$( readlink -f "$1" )" 25 | BASENAME="$( basename "$INPUT" )" 26 | LMS=${INPUT/.lsf/.lms} 27 | 28 | cd $EXECDIR || exit 1 29 | [[ -f "$INPUT" ]] || { 30 | echo "'$1' not found, skipping" 31 | exit 1 32 | } 33 | 34 | [[ "${INPUT##*.}" == "lsf" ]] || { 35 | echo "'$INPUT' not a .lsf file, aborting" 36 | exit 1 37 | } 38 | 39 | # Make temporary script file 40 | TMPLSF="${INPUT/.lsf/_$RANDOM.lsf}" 41 | cp "$INPUT" "$TMPLSF" 42 | 43 | # Datafile to pass variables for analysis 44 | TMPLDF=${TMPLSF/.lsf/.ldf} 45 | 46 | # Set save location for file and environment to expected output 47 | sed -i -n '/\bsave(/!p;$a\save("'"$LMS"'")\;\nsavedata("'"$TMPLDF"'")\;\n' "$TMPLSF" 48 | 49 | 50 | ## Build temporary self-cleaning files to submit to pueue 51 | SHBUILD=${TMPLSF/.lsf/_build.sh} 52 | SHENGINE=${TMPLSF/.lsf/_engine.sh} 53 | SHANALYZE=${TMPLSF/.lsf/_analyze.sh} 54 | 55 | 56 | # Script to build .lms file via CAD 57 | cat > $SHBUILD < $SHENGINE < $SHANALYZE <#${LMS}#;s##${TMPLDF}#" $ANALYSIS > "$TMPLSF" # Reusing previous $TMPLSF 94 | $XVFB -s "-screen 0 1600x1200x16" $CAD -nw -run "$TMPLSF" -exit 95 | rm "$TMPLSF" "$TMPLDF" # Clean up temporary files 96 | 97 | echo "Work on $BASENAME complete!" 98 | 99 | # Delete self 100 | rm $SHANALYZE 101 | EOT 102 | 103 | chmod a+x $SHBUILD $SHENGINE $SHANALYZE 104 | 105 | 106 | ## Execute: submit to pueue queue with chained dependencies 107 | # Build 108 | ID=$( pueue add -g $CADQ "$SHBUILD" | grep -Po '(?<=id )[0-9]+' ) 109 | 110 | # Add 'after $ID completes' queue for $SHENGINE 111 | ID=$( pueue add -a $ID -g $ENGQ "$SHENGINE" | grep -Po '(?<=id )[0-9]+' ) 112 | 113 | # Add 'after $ID completes' queue for $SHANALYZE 114 | pueue add -a $ID -g $CADQ "$SHANALYZE" 115 | 116 | -------------------------------------------------------------------------------- /remote-slurm/Qrun_device.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-01-04 4 | # Updated 2022-07-27 5 | # Run small Lumerical EME job on slurm host, to be used with remote dispatch 6 | # Input: ldev file with EME simulation 7 | # Set REFRESH=1 to refresh ldev 8 | 9 | 10 | ## Variable definitions 11 | export EXECDIR=~/lumerical/tmp/ 12 | 13 | # Queue commands 14 | THREADS=12 # Default 15 | NODES=2 # Default; cannot get expected runtime in advance 16 | # node52 seems to have trouble 2022-09-13 17 | QRUN="/usr/bin/srun --exclude=node52 --mpi=pmi2 --nodes=$NODES --cpus-per-task=$THREADS --time=10:00:00 --mem=12G -p batch --job-name=deviceEng" 18 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 19 | # Unable to determine cause, even with extensive Xvfb testing 20 | QCAD="nice -n 15" 21 | 22 | # Path definitions 23 | CAD="/sw/cnsi/lumerical/2021R1/bin/device-app" 24 | ENG="/sw/cnsi/lumerical/2021R1/bin/device-engine-mpich2nem" 25 | LSFREFRESH="~/lumerical/refresh.lsf" 26 | XVFB="~/lumerical/xvnc-run -a" 27 | 28 | 29 | ## Process input 30 | # Get full path of input and verify existence 31 | INPUT="$( readlink -f $1 )" 32 | BASENAME="$( basename "$INPUT" )" 33 | 34 | cd $EXECDIR || exit 1 35 | [[ -f "$INPUT" ]] || { 36 | echo "'$1' not found, skipping" 37 | exit 1 38 | } 39 | 40 | [[ "${INPUT##*.}" == "ldev" ]] || { 41 | echo "'$INPUT' not a .ldev file, aborting" 42 | exit 1 43 | } 44 | 45 | # Sleep to avoid race conditions in batch submissions 46 | sleep $(($RANDOM % 3)) 47 | 48 | [[ "$REFRESH" -eq 1 ]] && { 49 | # Make temporary refresh script 50 | TMPSCRIPT="${INPUT/.ldev/_refresh_$RANDOM.lsf}" 51 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 52 | 53 | # Load and refresh file with CAD to avoid cross-version bugs 54 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 55 | $QCAD $XVFB $CAD -nw -run "$TMPSCRIPT" -exit 56 | rm "$TMPSCRIPT" 57 | printf " done\n" 58 | } 59 | 60 | 61 | ## Submit job interactively 62 | echo "Running $(basename $ENG) on $BASENAME..." 63 | $QRUN $ENG -t $THREADS \"$INPUT\" || { 64 | echo "Error processing $BASENAME, aborting" 65 | exit 1 66 | } 67 | echo "Processed $BASENAME" 68 | rm "${INPUT/.ldev/_p0.log}" # Remove engine log file 69 | 70 | echo "Work on $BASENAME complete!" 71 | -------------------------------------------------------------------------------- /remote-slurm/Qrun_eme.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-01-04 4 | # Updated 2022-07-27 5 | # Run small Lumerical EME job on slurm host, to be used with remote dispatch 6 | # Input: lms file with EME simulation 7 | # Set REFRESH=1 to refresh lms 8 | 9 | 10 | ## Variable definitions 11 | EXECDIR="$(readlink -f ~/lumerical/tmp/)" 12 | 13 | # Queue commands 14 | THREADS=16 # CPUs per node (20 hyperthreaded cores per node on slurm-host, 32 threads per solver license) 15 | NODES=4 # Does not scale so well to large numbers 16 | QRUN="/usr/bin/srun --exclude=node19 --chdir=""$EXECDIR"" --mpi=pmi2 --nodes=$NODES --cpus-per-task=$THREADS --time=10:00:00 --mem=12G -p batch --job-name=emeEng" 17 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 18 | # Unable to determine cause, even with extensive Xvfb testing 19 | QCAD="nice -n 15" 20 | 21 | # Path definitions 22 | CAD="/sw/cnsi/lumerical-2021R1/bin/mode-solutions-app" 23 | ENG="/sw/cnsi/lumerical-2021R1/bin/eme-engine" 24 | LSFREFRESH="~/lumerical/refresh.lsf" 25 | XVFB="~/lumerical/xvnc-run -a" 26 | 27 | 28 | ## Process input 29 | # Get full path of input and verify existence 30 | INPUT="$( readlink -f $1 )" 31 | BASENAME="$( basename "$INPUT" )" 32 | 33 | cd $EXECDIR || exit 1 34 | [[ -f "$INPUT" ]] || { 35 | echo "'$1' not found, skipping" 36 | exit 1 37 | } 38 | 39 | [[ "${INPUT##*.}" == "lms" ]] || { 40 | echo "'$INPUT' not a .lms file, aborting" 41 | exit 1 42 | } 43 | 44 | # Sleep to avoid race conditions in batch submissions 45 | sleep $(($RANDOM % 3)) 46 | 47 | [[ "$REFRESH" -eq 1 ]] && { 48 | # Make temporary refresh script 49 | TMPSCRIPT="${INPUT/.lms/_refresh_$RANDOM.lsf}" 50 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 51 | 52 | # Load and refresh file with CAD to avoid cross-version bugs 53 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 54 | $QCAD $XVFB $CAD -nw -run "$TMPSCRIPT" -exit 55 | rm "$TMPSCRIPT" 56 | printf " done\n" 57 | } 58 | 59 | 60 | ## Submit job interactively 61 | echo "Running $(basename $ENG) on $BASENAME..." 62 | $QRUN $ENG -t $THREADS -n $NODES \"$INPUT\" || { 63 | echo "Error processing $BASENAME, aborting" 64 | exit 1 65 | } 66 | echo "Processed $BASENAME" 67 | rm "${INPUT/.lms/_p0.log}" # Remove engine log file 68 | 69 | echo "Work on $BASENAME complete!" 70 | -------------------------------------------------------------------------------- /remote-slurm/Qrun_fde.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-01-03 4 | # Updated 2022-07-27 5 | # Run small Lumerical FDE job on slurm host, to be used with remote dispatch 6 | # Input: lms file with FDE simulation 7 | # Set REFRESH=1 to refresh lms file 8 | 9 | 10 | ## Variable definitions 11 | EXECDIR="$(readlink -f ~/lumerical/tmp/)" 12 | 13 | # Queue commands 14 | THREADS=32 # CPUs per node (20 hyperthreaded cores per node on slurm-host, 32 threads per solver license) 15 | NODES=1 # Does not distribute 16 | QRUN="/usr/bin/srun --exclude=node19 --chdir=""$EXECDIR"" --mpi=pmi2 --nodes=$NODES --cpus-per-task=$THREADS --time=10:00:00 --mem=12G -p batch --job-name=fdeEng" 17 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 18 | # Unable to determine cause, even with extensive Xvfb testing 19 | QCAD="nice -n 15" 20 | 21 | # Path definitions 22 | CAD="/sw/cnsi/lumerical-2021R1/bin/mode-solutions-app" 23 | ENG="/sw/cnsi/lumerical-2021R1/bin/fd-engine" 24 | LSFREFRESH="~/lumerical/refresh.lsf" 25 | XVFB="~/lumerical/xvnc-run -a" 26 | 27 | 28 | ## Process input 29 | # Get full path of input and verify existence 30 | INPUT="$( readlink -f $1 )" 31 | BASENAME="$( basename "$INPUT" )" 32 | 33 | cd $EXECDIR || exit 1 34 | [[ -f "$INPUT" ]] || { 35 | echo "'$1' not found, skipping" 36 | exit 1 37 | } 38 | 39 | [[ "${INPUT##*.}" == "lms" ]] || { 40 | echo "'$INPUT' not a .lms file, aborting" 41 | exit 1 42 | } 43 | 44 | # Sleep to avoid race conditions in batch submissions 45 | sleep $(($RANDOM % 5)) 46 | 47 | [[ "$REFRESH" -eq 1 ]] && { 48 | # Make temporary refresh script 49 | TMPSCRIPT="${INPUT/.lms/_refresh_$RANDOM.lsf}" 50 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 51 | 52 | # Load and refresh file with CAD to avoid cross-version bugs 53 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 54 | $QCAD $XVFB $CAD -nw -run "$TMPSCRIPT" -exit 55 | rm "$TMPSCRIPT" 56 | printf " done\n" 57 | } 58 | 59 | 60 | ## Submit job interactively 61 | echo "Running $(basename $ENG) on $BASENAME..." 62 | $QRUN $ENG -t $THREADS \"$INPUT\" || { 63 | echo "Error processing $BASENAME, aborting" 64 | exit 1 65 | } 66 | echo "Processed $BASENAME" 67 | rm "${INPUT/.lms/_p0.log}" # Remove engine log file 68 | 69 | echo "Work on $BASENAME complete!" 70 | -------------------------------------------------------------------------------- /remote-slurm/Qrun_fdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Michael Nickerson 2022-01-04 3 | # Updated 2022-07-27 4 | # Run small Lumerical FDTD job on slurm-host, to be used with remote dispatch 5 | # Input: fsp file with FDTD simulation 6 | # Set REFRESH=1 to refresh fsp file 7 | 8 | #set -x 9 | 10 | ## Variable definitions 11 | EXECDIR="$(readlink -f ~/lumerical/tmp/)" 12 | 13 | # Queue commands 14 | THREADS=16 # CPUs per node (20 hyperthreaded cores per node on slurm-host, 32 threads per FDTD license) 15 | MAXNODES=4 # Does not scale so well to large numbers 16 | NODES=$MAXNODES 17 | QRUN="/usr/bin/srun --exclude=node19 --chdir=""$EXECDIR"" --mpi=pmi2 --nodes=$MAXNODES --cpus-per-task=$THREADS --time=10:00:00 --mem=12G -p batch --job-name=fdtdEng" 18 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 19 | # Unable to determine cause, even with extensive Xvfb testing 20 | QCAD="nice -n 15" 21 | 22 | # Path definitions 23 | CAD="/sw/cnsi/lumerical-2021R1/bin/fdtd-solutions-app" 24 | ENG="/sw/cnsi/lumerical-2021R1/bin/fdtd-engine-mpich2nem" 25 | LSFREFRESH="~/lumerical/refresh.lsf" 26 | XVFB="~/lumerical/xvnc-run -a" 27 | 28 | 29 | ## Process input 30 | # Get full path of input and verify existence 31 | INPUT="$( readlink -f $1 )" 32 | BASENAME="$( basename "$INPUT" )" 33 | 34 | cd $EXECDIR || exit 1 35 | [[ -f "$INPUT" ]] || { 36 | echo "'$1' not found, skipping" 37 | exit 1 38 | } 39 | 40 | [[ "${INPUT##*.}" == "fsp" ]] || { 41 | echo "'$INPUT' not a .fsp file, aborting" 42 | exit 1 43 | } 44 | 45 | # Sleep to avoid race conditions in batch submissions 46 | sleep $(($RANDOM % 5)) 47 | 48 | [[ "$REFRESH" -eq 1 ]] && { 49 | # Make temporary refresh script 50 | TMPSCRIPT="${INPUT/.fsp/_refresh_$RANDOM.lsf}" 51 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 52 | 53 | # Load and refresh file with CAD to avoid cross-version bugs 54 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 55 | $QCAD $XVFB $CAD -nw -run "$TMPSCRIPT" -exit 56 | rm "$TMPSCRIPT" 57 | printf " done\n" 58 | } 59 | 60 | 61 | ## Check memory and time requirements to decide submission options 62 | # Get memory and time requirements 63 | printf "Checking memory and CPU requirements..." 64 | MR="$($ENG -mr "$INPUT")" 65 | printf "\n" 66 | 67 | # Check memory 68 | # Available: 69 | # ~180 GB on slurm-host 'batch' 70 | # 1.4 TB on slurm-host 'largemem' 71 | MEM=$( grep -Po '(?<=memory=)\d*' <<< "$MR" ) 72 | MEM=$(( (MEM * 120 / 100)/1024 + 4 )) # Reasonable minimum and overhead 73 | echo " Need ~${MEM}GB memory" 74 | 75 | if [[ $MEM -gt 1300 ]]; then 76 | echo " Error, memory requirements (~${MEM}GB) not satisfiable!" 77 | exit 1 78 | elif [[ $MEM -gt 180 ]]; then 79 | echo " Using 'largemem' queue on slurm-host" 80 | QRUN=${QRUN//batch/largemem} # Different queue 81 | NODES=2 # Maximum 'largemem' nodes 82 | fi 83 | 84 | # Check time 85 | TIME=$( grep -Po '(?<=time_steps=)\d*' <<< "$MR" ) 86 | GRID=$( grep -Po '(?<=gridpoints=)\d*' <<< "$MR" ) 87 | [[ $GRID -lt 0 ]] && GRID=2147483647 # Signed 32-bit overflow 88 | 89 | # Estimated total time required from Lumerical templates, roughly calibrated on slurm-host 90 | TIME=$(( GRID * TIME / 75 / 3 / 1000000 / 60 )) # In minutes 91 | echo " Expected $TIME minutes CPU time" 92 | 93 | [[ $NODES -eq 2 ]] && { # If not defined otherwise in memory section 94 | NODES=$(( TIME/60 + 1 )) # Rough estimate of nodes required; ~60 minute jobs 95 | } 96 | NODES=$(( NODES < MAXNODES ? NODES : MAXNODES )) # Cap requested nodes 97 | echo " Using $NODES nodes, expected $((TIME/NODES)) minutes real execution time" 98 | 99 | TIME=$((TIME / NODES / 60 + 1)) # Expected execution hours 100 | [[ $TIME -gt 6 ]] && { 101 | TIME=$((TIME * 3/2)) # Request more time 102 | echo " Requesting $TIME hours of job time" 103 | } 104 | [[ $TIME -lt 10 ]] && TIME=10 # Minimum request 105 | 106 | # Update queue command from previously calculated memory, nodes, and time 107 | QRUN=${QRUN//mem=12/mem=$MEM} 108 | QRUN=${QRUN//nodes=$MAXNODES/nodes=$NODES} 109 | QRUN=${QRUN//time=10/time=$TIME} 110 | 111 | 112 | ## Submit job interactively 113 | echo "Running $(basename $ENG) on $BASENAME..." 114 | $QRUN $ENG -t $THREADS -n $NODES \"$INPUT\" || { 115 | echo "Error processing $BASENAME, aborting" 116 | exit 1 117 | } 118 | echo "Processed $BASENAME" 119 | rm "${INPUT/.fsp/_p0.log}" # Remove engine log file 120 | 121 | echo "Work on $BASENAME complete!" 122 | -------------------------------------------------------------------------------- /remote-slurm/Qrun_varfdtd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Michael Nickerson 2022-01-03 4 | # Updated 2022-07-27 5 | # Run small Lumerical varfdtd job on slurm host, to be used with remote dispatch 6 | # Input: lms file with varfdtd simulation 7 | # Set REFRESH=1 to refresh lms file 8 | 9 | 10 | ## Variable definitions 11 | EXECDIR="$(readlink -f ~/lumerical/tmp/)" 12 | 13 | # Queue commands 14 | THREADS=16 # CPUs per node (20 hyperthreaded cores per node on slurm-host, 32 threads per solver license) 15 | MAXNODES=4 # Does not scale so well to large numbers 16 | NODES=$MAXNODES 17 | QRUN="/usr/bin/srun --exclude=node19 --chdir=""$EXECDIR"" --mpi=pmi2 --nodes=$MAXNODES --cpus-per-task=$THREADS --time=10:00:00 --mem=12G -p batch --job-name=varfdtdEng" 18 | # Unfortunately on 'slurm-host', the nodes can't run the CAD applications without core dumping 19 | # Unable to determine cause, even with extensive Xvfb testing 20 | QCAD="nice -n 15" 21 | 22 | # Path definitions 23 | CAD="/sw/cnsi/lumerical-2021R1/bin/mode-solutions-app" 24 | ENG="/sw/cnsi/lumerical-2021R1/bin/varfdtd-engine-mpich2nem" 25 | LSFREFRESH="~/lumerical/refresh.lsf" 26 | XVFB="~/lumerical/xvnc-run -a" 27 | 28 | 29 | ## Process input 30 | # Get full path of input and verify existence 31 | INPUT="$( readlink -f $1 )" 32 | BASENAME="$( basename "$INPUT" )" 33 | 34 | cd $EXECDIR || exit 1 35 | [[ -f "$INPUT" ]] || { 36 | echo "'$1' not found, skipping" 37 | exit 1 38 | } 39 | 40 | [[ "${INPUT##*.}" == "lms" ]] || { 41 | echo "'$INPUT' not a .lms file, aborting" 42 | exit 1 43 | } 44 | 45 | # Sleep to avoid race conditions in batch submissions 46 | sleep $(($RANDOM % 5)) 47 | 48 | [[ "$REFRESH" -eq 1 ]] && { 49 | # Make temporary refresh script 50 | TMPSCRIPT="${INPUT/.lms/_refresh_$RANDOM.lsf}" 51 | sed "s##${INPUT}#" "$LSFREFRESH" > "$TMPSCRIPT" 52 | 53 | # Load and refresh file with CAD to avoid cross-version bugs 54 | printf "Refreshing %s to avoid cross-version bugs..." $BASENAME 55 | $QCAD $XVFB $CAD -nw -run "$TMPSCRIPT" -exit 56 | rm "$TMPSCRIPT" 57 | printf " done\n" 58 | } 59 | 60 | 61 | ## Check memory and time requirements to decide submission options 62 | # Get memory and time requirements 63 | printf "Checking memory and CPU requirements..." 64 | MR="$($ENG -mr "$INPUT")" 65 | printf "\n" 66 | 67 | # Check memory 68 | # Available: 69 | # 4x126GB, >10x64GB, >10x48GB on slurm-host 'batch' 70 | # 768GB on slurm-host 'largemem' 71 | MEM=$( grep -Po '(?<=memory=)\d*' <<< "$MR" ) 72 | MEM=$(( (MEM * 200 / 100)/1024 + 4 )) # Reasonable minimum and overhead 73 | echo " Need ~${MEM}GB memory" 74 | 75 | if [[ $MEM -gt 1300 ]]; then 76 | echo " Error, memory requirements (~${MEM}GB) not satisfiable!" 77 | exit 1 78 | elif [[ $MEM -gt 180 ]]; then 79 | echo " Using 'largemem' queue on slurm-host" 80 | QRUN=${QRUN//batch/largemem} # Different queue 81 | NODES=2 # Maximum 'largemem' nodes 82 | fi 83 | 84 | # Check time 85 | TIME=$( grep -Po '(?<=time_steps=)\d*' <<< "$MR" ) 86 | [[ $TIME -gt 0 ]] || TIME=500000 # On error, use typical value 87 | GRID=$( grep -Po '(?<=gridpoints=)\d*' <<< "$MR" ) 88 | [[ $GRID -lt 0 ]] && GRID=2147483647 # Signed 32-bit overflow 89 | [[ $GRIS -eq 0 ]] && GRID=10000000 # On error, use typical value 90 | 91 | # Estimated total time required from Lumerical templates, roughly calibrated on slurm-host 92 | TIME=$(( GRID * TIME / 75 / 1000000 / 60 )) # In minutes 93 | echo " Expected $TIME minutes CPU time" 94 | 95 | [[ $NODES -eq 2 ]] && { # If not defined otherwise in memory section 96 | NODES=$(( TIME/15 + 1 )) # Rough estimate of nodes required; ~15 minute jobs 97 | } 98 | NODES=$(( NODES < MAXNODES ? NODES : MAXNODES )) # Cap requested nodes 99 | echo " Using $NODES nodes, expected $((TIME/NODES)) minutes real execution time" 100 | 101 | TIME=$((TIME/NODES/60 + 1)) # Expected execution hours 102 | [[ $TIME -gt 5 ]] && { 103 | TIME=$((TIME * 3/2)) # Request more time 104 | echo " Requesting $TIME hours of job time" 105 | } 106 | [[ $TIME -lt 5 ]] && TIME=5 # Minimum request 107 | 108 | # Update queue command from previously calculated memory, nodes, and time 109 | QRUN=${QRUN//mem=12/mem=$MEM} 110 | QRUN=${QRUN//nodes=$MAXNODES/nodes=$NODES} 111 | QRUN=${QRUN//time=10/time=$TIME} 112 | 113 | 114 | ## Submit job interactively 115 | echo "Running $(basename $ENG) on $BASENAME..." 116 | $QRUN $ENG -t $THREADS -n $NODES \"$INPUT\" || { 117 | echo "Error processing $BASENAME, aborting" 118 | exit 1 119 | } 120 | echo "Processed $BASENAME" 121 | rm "${INPUT/.lms/_p0.log}" # Remove engine log file 122 | 123 | echo "Work on $BASENAME complete!" 124 | -------------------------------------------------------------------------------- /remote-slurm/dispatch-device.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to slurm, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@slurm-host 7 | SET slurmDIR=~/lumerical/tmp 8 | SET slurmRUN=~/lumerical/Qrun_device.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%slurmDIR%/%~nx1" 12 | %plink% -batch %login% %slurmRUN% "%slurmDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%slurmDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%slurmDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-slurm/dispatch-eme.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to slurm, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@slurm-host 7 | SET slurmDIR=~/lumerical/tmp 8 | SET slurmRUN=~/lumerical/Qrun_eme.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%slurmDIR%/%~nx1" 12 | %plink% -batch %login% %slurmRUN% "%slurmDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%slurmDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%slurmDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-slurm/dispatch-fde.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to slurm, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@slurm-host 7 | SET slurmDIR=~/lumerical/tmp 8 | SET slurmRUN=~/lumerical/Qrun_fde.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%slurmDIR%/%~nx1" 12 | %plink% -batch %login% %slurmRUN% "%slurmDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%slurmDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%slurmDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-slurm/dispatch-fdtd.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to slurm, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@slurm-host 7 | SET slurmDIR=~/lumerical/tmp 8 | SET slurmRUN=~/lumerical/Qrun_fdtd.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%slurmDIR%/%~nx1" 12 | %plink% -batch %login% %slurmRUN% "%slurmDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%slurmDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%slurmDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-slurm/dispatch-varfdtd.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM Dispatch file to slurm, intended to report progress 3 | 4 | SET plink=plink 5 | SET pscp=pscp 6 | SET login=user@slurm-host 7 | SET slurmDIR=~/lumerical/tmp 8 | SET slurmRUN=~/lumerical/Qrun_varfdtd.sh 9 | 10 | 11 | %pscp% -C -batch -q %1 %login%:"%slurmDIR%/%~nx1" 12 | %plink% -batch %login% %slurmRUN% "%slurmDIR%/%~nx1" 13 | %pscp% -C -batch -q %login%:"%slurmDIR%/%~nx1" %1 14 | %plink% -batch %login% rm "%slurmDIR%/%~n1.*" 15 | -------------------------------------------------------------------------------- /remote-slurm/lumanalysis_template.lsf: -------------------------------------------------------------------------------- 1 | addpath('/home/nickersonm/lumerical/common'); 2 | util_fCommon; 3 | 4 | cd(filedirectory(currentscriptname)); 5 | load(""); 6 | loaddata(""); 7 | 8 | if( layoutmode() > 0 ) { 9 | run; save; 10 | if( getnamednumber('FDE') > 0 ) { 11 | # eval() gets around the lack of 'nummodes()' in CHARGE and FDTD 12 | eval("if( nummodes() == 0 ) { findmodes; }"); 13 | } 14 | } 15 | 16 | lum_analyze; 17 | -------------------------------------------------------------------------------- /remote-slurm/pueue_remaining.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while true; do { 4 | echo "$(date +%F\ %X): $(pueue status | grep -e 'Queued' -e 'Running' | wc -l)" 5 | sleep 10 6 | } 7 | done 8 | -------------------------------------------------------------------------------- /remote-slurm/refresh.lsf: -------------------------------------------------------------------------------- 1 | load(""); 2 | switchtolayout; 3 | save; 4 | --------------------------------------------------------------------------------