Lumerical FDTD's scripting language is a powerful tool for automating simulation setup and post-processing. Almost all of the actions that can be performed through the graphical user interface can be accomplished through scripting.
This part of the tutorial provides a quick introduction to Lumerical's scripting language. As an example, the simulation from the previous part of this tutorial will be recreated, this time entirely set up and run using Lumerical's scripting language. A more thorough introduction to Lumerical's scripting language can be found in the online Lumerical Scripting course.
This is the second part of a three part tutorial. The other parts of the tutorial can be found on these pages:
- Getting Started with Lumerical FDTD for Photonic Integrated Circuits - Part 1
- Getting Started with Lumerical FDTD for Photonic Integrated Circuits - Part 3
Introduction to Lumerical Scripting
Lumerical's scripting language has many of the basic features of other scripting languages, like variables, arithmetic operators, flow control statements, and functions.
The scripting language consists of operators (+, =, &, etc.), keywords (if, for, function, etc.) and commands. The commands perform various operations, for example modifying the current simulation file, running simulations, mathematical calculations, or printing data to a text file.
For a list of script commands see Lumerical scripting language - Alphabetical list or Lumerical scripting language - By category.
Variables and Data Types
The scripting language has access to named data called variables.
The variables have various data types like matrices, datasets, strings, cells, and structs. Matrices and datasets were described in the previous part of this example. Cells are groups of ordered data, indexed by integers starting from one. Structs are groups of key-value pairs.
Syntax
Lumerical's scripting language uses a MATLAB-like syntax.
Variables are assigned values using the "=" operator. Statements are ended with semicolons. Comments are preceded with "#".
For example, to assign the value 1 to the variable x:
x = 1; # Assigns 1 to x
Strings can be printed to the Script Prompt with the ? operator followed by the string.
?"This is printed to the Script Prompt";
Commands can be used by writing the command name followed by a semicolon to end the statement. If the command has arguments the command name is followed by the arguments in parentheses, separated by commas.
run; # Command with no arguments
run("FDTD"); # Command with argumentsSome commands will return values when executed. These returned values can be used in mathematical expressions, assigned to variables, and passed other commands as arguments if the returned values are the appropriate data type.
set("y," get("x")); # Result of command used as an argument to another
x_span = get("x max") - get("x min"); # Result of commands assigned to variablesScripting Environments
Lumerical's scripting language is used in various environments. Statements can be executed one by one in the main scripting environment in the Script Prompt or grouped together into a script file with the *.lsf extension. When the script file is executed, each line in the script will be executed sequentially.
Lumerical FDTD has a built-in Script File Editor for editing and running scripts, with useful functionality like syntax highlighting and a debugger. The variables accessible to the main scripting environment can be seen in the Script Workspace.
Simulation object results are not automatically accessible to the main scripting environment. They can be added to the Script Workspace using commands like getresult and getdata.
For more information on using the Script Prompt and Script File Editor see Tips for Script Prompt and Script File Editor.
Script commands executed in the main scripting environment act on the current simulation file. This means they can be used in conjunction with the graphical interface, using both the graphical interface and script commands if desired. For example, a simulation could be set up and run through the graphical interface then the results could be processed and visualized using the scripting language.
Other script environments include Structure Group setup scripts, Analysis Group setup and analysis scripts, and figure of merit scripts for the particle swarm optimization tool. These other environments have restricted command lists and do not have access to variables in the main Script Workspace.
Adding Simulation Objects
Actions in the graphical interface typically have an equivalent script command. For example, in the graphical interface, objects can be added to the simulation by pressing buttons in the toolbar. The script equivalents are commands starting with "add" like addfdtd to add an FDTD solver or addport to add a port. Executing the addfdtd command has the exact same result as pressing the FDTD button in the toolbar. See the "Adding objects" category on Lumerical scripting language - By category to see the available options for add commands.
Modifying Simulation Objects
Simulation object properties can be modified using the set and setnamed commands. The set commands acts on the currently selected object, while setnamed acts on the object whose name is passed as an argument.
The set command sets the value of the object property in the first argument to the value passed as the second argument. For example, the x span of a selected FDTD solver object can set to 10 microns using the command
set("x span", 10e-6);Note that the scripting language always uses SI units, regardless of what units are being used by the graphical interface.
The property names will match the names shown in the edit dialog for the simulation object in most cases. The properties for the currently selected object can be printed to the Script Prompt by entering "?set;".
This method works because the set command returns a list of properties as a string when it is executed with no arguments. This string is then printed to the Script Prompt with the ? operator.
The properties of an added object can be set as it is added by passing a struct containing the property names as keys and the new property values as the struct values to the command as an argument. For example, the x and x span properties of an FDTD object can be set as it is added using like this:
addfdtd({"x": 1e-6, "x span": 10e-6})Commands for selecting singular or multiple objects at a time as well as the available set commands can be found in the "Manipulating objects" category on Lumerical scripting language - By category.
Example: Scripting Setup
The simulation in the first part of this example can be replicated through the scripting language. The script commands listed here can be copied and pasted into the Script Prompt then executed by pressing Enter. Multiple lines can be copied into the Script Prompt at once. The results of the script commands can then be seen in the graphical interface.
The script file wg_bend_example.lsf attached to this page includes the entire example script.
Preliminary Setup
To start the script, the newproject command creates a blank simulation file and the clear command clears the variables in the Script Workspace (save the currently open simulation first if needed!):
newproject; clear;
These commands ensure that each time the script is run it starts from the same blank simulation file.
Next, several variables are defined to make setting up the simulation easier. Comments are added to clarify the meaning of the variables:
bend_radius = 3e-6; # radius of waveguide bend waveguide_width = 0.5e-6; # width of waveguides waveguide_height = 0.22e-6; # height of waveguide waveguide_mode_buffer = 2e-6; # distance from edge of waveguide to PMLs
Adding Simulation Objects
The Ring object can be added to the simulation using the addring command, and the waveguide Rectangle objects can be added with the addrect command. The object properties are immediately set by passing a struct with the property names and values to the add commands. Arithmetic expressions using variables are used to make setting the object properties easier.
addring({"name": "waveguide bend",
"z span": waveguide_height,
"outer radius": bend_radius + 0.5*waveguide_width,
"inner radius": bend_radius - 0.5*waveguide_width,
"theta stop": 90,
"material": "Si (Silicon) - Palik"});
addrect({"name": "input waveguide",
"x max": 0, "x min": -2e-6,
"y": bend_radius, "y span": waveguide_width,
"z": 0, "z span": waveguide_height,
"material": "Si (Silicon) - Palik"});
addrect({"name": "output waveguide",
"x": bend_radius, "x span": waveguide_width,
"y max": 0, "y min": -2e-6,
"z": 0, "z span": waveguide_height,
"material": "Si (Silicon) - Palik"});Next the FDTD object is added to the simulation using the addfdtd command:
addfdtd({"background material": "SiO2 (Glass) - Palik",
"x min": -1e-6, "x max": bend_radius + 0.5*waveguide_width + waveguide_mode_buffer,
"y min": -1e-6, "y max": bend_radius + 0.5*waveguide_width + waveguide_mode_buffer,
"z": 0, "z span": waveguide_height + 2*waveguide_mode_buffer,
"z min bc": "Symmetric"});The ports are added to the simulation using the addport command:
addport({"x": -0.5e-6,
"y": bend_radius, "y span": waveguide_width + 2*waveguide_mode_buffer,
"z": 0, "z span": waveguide_height + 2*waveguide_mode_buffer,
"number of field profile samples": 3});
addport({"injection axis": "y-axis",
"x": bend_radius, "x span": waveguide_width + 2*waveguide_mode_buffer,
"y": -0.5e-6,
"z": 0, "z span": waveguide_height + 2*waveguide_mode_buffer,
"number of field profile samples": 3});Setting Port Group Properties
The port group monitor frequency point setting can be set by selecting the port group with the select command and using the set command:
select("FDTD::ports");
set("monitor frequency points", 100);The argument of the select command is the name of the object to be selected. The "::" is the scoping operator. It is used because the "ports" port group is below the FDTD solver in the Objects Tree hierarchy. It is similar to the "/" in a file path on a computer.
Also, like file paths, objects can be specified using a relative scope or a global scope. The select command in this example uses a relative scope, where the name is given relative to the current group scope, which is a location in the Objects Tree. The current group scope can be seen in the bottom of the Script Prompt, or printed using the groupscope command and the ‘?' operator.
The group scope can also be set using the groupscope command. The default group scope is "::model". There is rarely a reason to change the groupscope.
The global scope name can be specified by starting the name with "::model" (since the model group is always the root of the Objects Tree). The global scope name for the ports group would be "::model::FDTD::ports".
Once the port group is selected, its "monitor frequency points" property is set to 100 with the set command.
Setting Global Source Properties
Global source properties are set with the setglobalsource command:
setglobalsource("wavelength start", 1.5e-6);
setglobalsource("wavelength stop", 1.6e-6);Running the Simulation
To run the simulation, first save the simulation file with the save command, passing the name of the file as an argument:
save("wg_bend_example_from_script.fsp");The file name can be passed with a global or relative file path. The current working directory of Lumerical FDTD can be seen at the bottom of the graphical interface, or printed to the Script Prompt with the pwd command and ‘?' operator.
The simulation is then run with the run command:
run;
This will run the simulation with the resources set up in the resource configuration. If necessary, the resources of the Resource Configuration can be modified using the setresource command.
Visualizing Results
Once the simulation has finished running, the results stored in the simulation objects can be moved to the Script Workspace using the getresult command:
S21 = getresult("FDTD::ports::port 2", "S");The above command stores the "S" result of the port 2 object in the variable S21. This result is a dataset, as can be seen by its icon in the Script Workspace.
The S parameters stored in this dataset can be plotted as a function of wavelength with the plot command:
plot(S21.lambda*1e6, abs(S21.S)^2, "Wavelength (um)", "Transmission (normalized)");
In this command, the "lambda" (wavelength) parameter and the S (S-parameter) attribute of the S21 dataset are accessed using the . (dataset dot) operator. They can also be accessed using the getattribute and getparameter commands.
The first argument of the plot command is the variable along the x-axis, while the second argument is the y-axis variable. The third argument is the x-axis label and the fourth is the y-axis label.
The wavelength is multiplied by 1e6 to convert it from meters to micrometers. The absolute value of the S-parameters is calculated with the abs command, which is then squared with "^2" to get |S21|^2.
This part of the tutorial is now complete. The next part of the tutorial can be found on this page: Getting Started with Lumerical FDTD for Photonic Integrated Circuits - Part 3.
Full Example Script
The full script for this example is given below:
# Preliminary Setup
newproject;
clear;
bend_radius = 3e-6; # radius of waveguide bend
waveguide_width = 0.5e-6; # width of waveguides
waveguide_height = 0.22e-6; # height of waveguide
waveguide_mode_buffer = 2e-6; # distance from edge of waveguide to PMLs
# Adding Simulation Objects
addring({"name": "waveguide bend",
"z span": waveguide_height,
"outer radius": bend_radius + 0.5*waveguide_width,
"inner radius": bend_radius - 0.5*waveguide_width,
"theta stop": 90,
"material": "Si (Silicon) - Palik"});
addrect({"name": "input waveguide",
"x max": 0, "x min": -2e-6,
"y": bend_radius, "y span": waveguide_width,
"z": 0, "z span": waveguide_height,
"material": "Si (Silicon) - Palik"});
addrect({"name": "output waveguide",
"x": bend_radius, "x span": waveguide_width,
"y max": 0, "y min": -2e-6,
"z": 0, "z span": waveguide_height,
"material": "Si (Silicon) - Palik"});
addfdtd({"background material": "SiO2 (Glass) - Palik",
"x min": -1e-6, "x max": bend_radius + 0.5*waveguide_width + waveguide_mode_buffer,
"y min": -1e-6, "y max": bend_radius + 0.5*waveguide_width + waveguide_mode_buffer,
"z": 0, "z span": waveguide_height + 2*waveguide_mode_buffer,
"z min bc": "Symmetric"});
addport({"x": -0.5e-6,
"y": bend_radius, "y span": waveguide_width + 2*waveguide_mode_buffer,
"z": 0, "z span": waveguide_height + 2*waveguide_mode_buffer,
"number of field profile samples": 3});
addport({"injection axis": "y-axis",
"x": bend_radius, "x span": waveguide_width + 2*waveguide_mode_buffer,
"y": -0.5e-6,
"z": 0, "z span": waveguide_height + 2*waveguide_mode_buffer,
"number of field profile samples": 3});
# Setting Port Group Properties
select("FDTD::ports");
set("monitor frequency points", 100);
# Setting Global Source Properties
setglobalsource("wavelength start", 1.5e-6);
setglobalsource("wavelength stop", 1.6e-6);
# Running the Simulation
save("wg_bend_example_from_script.fsp");
run;
# Visualizing Results
S21 = getresult("FDTD::ports::port 2", "S");
plot(S21.lambda*1e6, abs(S21.S)^2, "Wavelength (um)", "Transmission (normalized)");