Table of Contents
This chapter explains and documents the two plug-in tools that are currently available in Virtual NanoLab:
Custom Builders are very convenient tools for building specialize geometries, which depend parametrically on some parameters. The systems can be very complicated, but the key is that it should be possible to generate them with a small number of input parameters from the user. A typical example is a carbon nanotube; essentially you only need to specify the chiral indices (and perhaps the bond length) to generate the rather elaborate geometry of the tube.
VNL already comes with several built-in Custom Builders,
available via the Builders menu. But
it is also possible to write your own, and in this way extend
the functionality of the software. This can save a lot of time
if you are repeatedly generating similar geometries over and over again,
where only a particular detail − a distance, say,
or a chemical element, or some index − changes.
Such Custom Builder can also easily be shared with other users.
The following example, set up a Custom Builder script for building a
DeviceConfiguration consisting of a one-dimensional lithium
wire with a hydrogen molecule. You will be able to change the geometry through
specification of the relevant inter-atomic distances.
First, you will use an existing Custom Builder script, and then learn the details of writing one.
Below you see the entire Custom Builder script for setting up the
Li-H2-Li system. Save the script locally by clicking the
li-h2-li-builder.py link below the source code box.
def setupConfiguration(distance_li_li,
distance_h_h,
distance_li_h,
number_of_li):
# Create the electrode by repeating the system 4 times.
lattice = UnitCell([10.0,0.0,0.0]*Units.Ang,
[0.0, 10.0, 0.0]*Units.Ang,
[0.0, 0.0, distance_li_li]*Units.Ang)
coordinates = [[0.5, 0.5, 0.0]]
bulk = BulkConfiguration(lattice, [Lithium], fractional_coordinates=coordinates)
# Repeat and center.
electrode = bulk.repeat(1,1,4)
electrode = electrode.center()
# Setup the central region.
elements = [Lithium]*number_of_li + [Hydrogen, Hydrogen] + [Lithium]*number_of_li
# Setup all the z-coordinates.
coordinates_z = [ i*distance_li_li for i in range(number_of_li)]
coordinates_z += [ distance_li_li*(number_of_li-1) + distance_li_h, ]
coordinates_z += [ distance_li_li*(number_of_li-1)+ distance_li_h
+ distance_h_h, ]
coordinates_z += [ i*distance_li_li + 2*distance_li_h + distance_h_h
for i in range(number_of_li-1,2*number_of_li-1) ]
# Setup the full coordinates.
coordinates = [ (5.0, 5.0, z)*Units.Ang for z in coordinates_z]
# Create the lattice for the central region.
lattice = UnitCell([10.0,0.0,0.0]*Units.Ang,
[0.0, 10.0, 0.0]*Units.Ang,
[0.0, 0.0, coordinates_z[-1]+distance_li_li]*Units.Ang)
# Create the central region.
central_region = BulkConfiguration(lattice, elements, coordinates)
# Center the configuration.
central_region = central_region.center()
# Combine into a device.
return DeviceConfiguration(central_region, [electrode, electrode])
# Initialize builder
builder = Builder()
# Set the configuration generator
builder.setConfigurationGenerator(setupConfiguration)
builder.title('Li - H2 - Li')
builder.newGroup('Parameters')
builder.double('distance_li_li', 2.900, 'Li/Li distance (Ang)', min=0.1, max=1000)
builder.double('distance_h_h', 0.804, 'H/H distance (Ang)', min=0.01, max=1000)
builder.double('distance_li_h' , 2.336, 'Li/H distance (Ang)', min=0.01, max=1000)
builder.integer('number_of_li', 6, 'Screening Lithium', min=4, max=1000)
Now drop the script (e.g. from the file browser in VNL) onto the
Custom Builder icon
on the main VNL toolbar. The Custom Builder opens up,
ready to build Li-H2-Li devices.
The script consists of two main parts:
a function for generating a configuration based on some input parameters, and
commands for setting up a user dialog for defining the input parameters that define the configuration.
These two parts of the script will be explained in further detail in the follow sections.
The last portion of the script sets up the Custom Builder interface via the following steps:
Define the builder itself (technically speaking: instantiate the class).
# Initialize builder builder = Builder()
Specify the function (setupConfiguration(), in our case)
to be used by the builder for generating the configuration.
# Set the configuration generator
builder.setConfigurationGenerator(setupConfiguration)
builder.title('Li - H2 - Li')
Define the parameters that the user can change to control the generated geometry. For each of these parameters, a corresponding input field will be shown in the Custom Builder interface.
builder.newGroup('Parameters')
builder.double('distance_li_li', 2.900, 'Li/Li distance (Ang)', min=0.1, max=1000)
builder.double('distance_h_h', 0.804, 'H/H distance (Ang)', min=0.01, max=1000)
builder.double('distance_li_h' , 2.336, 'Li/H distance (Ang)', min=0.01, max=1000)
builder.integer('number_of_li', 6, 'Screening Lithium', min=4, max=1000)
The other essential part of the script is the function that generates the configuration. It takes the same parameters as defined for the builder interface as input:
def setupConfiguration(distance_li_li,
distance_h_h,
distance_li_h,
number_of_li):
Note how the names of the keyword arguments to the function (e.g. number_of_li)
correspond to the first argument (a string, in that case) to each input method (double or float)
in the user dialog setup.
Based on the input parameters, the script now sets up the configuration, and returns the configuration to the Custom Builder. The returned configuration will in each case be displayed in the 3D window in the Custom Builder.
return DeviceConfiguration(central_region, [electrode, electrode])
Valid returns are MoleculeConfiguration,
BulkConfiguration, and
DeviceConfiguration.
|
|
Tip |
|---|---|
|
To avoid error messages, the function should
return
Information to the user about
this or other errors (or general information) can be
displayed in the Log panel by using normal
|
Table 3: Input fields that can be used for setting up a Custom Builder.
| Command name | Description |
|---|---|
string(variable, value='', label=None)
|
A string variable. |
integer(variable, value=0, label=None, min=None, max=None)
|
An integer variable. The arguments min and
max are the minimum and maximum values of the variable.
|
double(variable, value=0.0, label=None, min=None, max=None)
|
A float (or double precision real number) variable. The arguments
min and max are the minimum and
maximum values of the variable.
|
boolean(variable, value=True, label=None)
|
A Boolean variable. |
atom(variable, value=Hydrogen, label=None)
|
A variable associated with a user-specified chemical element. |
choice(variable, value_map, default_key=None, label=None)
|
A variable associated with a selection of choices. The
value_map argument is a list of tuples holding the
keys and values pairs associated with a given choice. For example
[('a',1),('b',2),('c',3)] describes three keys
'a', 'b', and 'c'
associated with the respective values 1,
2, and 3. The
default_key argument is the key that is selected by
default.
|
title(title)
|
The title displayed in the Custom Builder window. The argument
title is a string holding the desired title.
|
help(value, label=None)
|
Provides a help entry in the GUI. The help text is supplied as HTML text in
the value argument.
|
filename(variable, default='drop', label=None)
|
Provides a drop zone where a file can be dropped. The name (path) of the
file name will be assigned to the variable.
|
Here
variable is the variable name, that should match
the keyword arguments to the configuration generator function
value is the default value of the input element
label is the label displayed next to the element in the
Custom Builder interface
The Custom Analyzer is a tool that can take input data and perform some analysis on it, and present the results in graphs or textual output in VNL. It provides access to the full range of ATK commands, and can be used to automate complex analysis procedures. Some examples of the usage of the Custom Analyzer are:
Calculate the current from a TransmissionSpectrum
Perform a calculation and subsequent analysis of a configuration
Calculate multi-poles of an ElectronDifferenceDensity
Plot the value of an ElectrostaticDifferencePotential along a line
The Custom Analyzer comes with a few built-in Analyzers already, available from the
Analyzer
menu in the top of the tool. In this section you will learn to write your own
Custom Analyzer to perform tailored analysis
through the use of NanoLanguage scripts.
You will now set up a Custom Analyzer to
perform an Extended Hückel calculation on a device configuration (to be provided by the user),
display the band structure of the electrodes,
plot the transmission spectrum of the device,
and display the molecular energy spectrum projection of the central region (MPSH analysis).
Below is shown the entire Custom Analyzer script. Save the script locally by clicking the link below.
def analyze(configuration, left_voltage, right_voltage, element,
non_scf, plot0, plot1, plot2, plot3):
# If it is empty, do nothing.
if configuration == None:
return
# First calculate the bandstructure.
numerical_accuracy_parameters = NumericalAccuracyParameters(
k_point_sampling=(1, 1, 100))
if non_scf:
iteration_control_parameters = NonSelfconsistent
else:
iteration_control_parameters = None
electrode_calculator = HuckelCalculator(
numerical_accuracy_parameters=numerical_accuracy_parameters,
iteration_control_parameters = iteration_control_parameters
)
electrode = configuration.electrodes()[0]
electrode.setCalculator(electrode_calculator)
electrode.update()
device_calculator = DeviceHuckelCalculator(
poisson_solver=FastFourierSolver(),
electrode_voltages=[left_voltage, right_voltage]*Units.Volt,
iteration_control_parameters = iteration_control_parameters
)
bandstructure = Bandstructure(electrode,
route=['G','Z'],
points_per_segment=100,
bands_above_fermi_level=10)
# Transmission spectrum.
configuration.setCalculator(device_calculator)
transmission = TransmissionSpectrum(configuration,
energies = numpy.linspace(-3,3,100)*Units.eV)
# MPSH
projection_list=ProjectionList(elements=[element])
mpsh = MolecularEnergySpectrum(configuration,
projection_list=projection_list,
energy_zero_parameter=FermiLevel)
# Potential
potential = ElectrostaticDifferencePotential(configuration)
shape = potential.shape()
veff = [potential[shape[0]/2,shape[1]/2,i].inUnitsOf(Units.Hartree)
for i in range(shape[2]) ]
dX, dY, dZ = potential.volumeElement().inUnitsOf(Ang)
dz = numpy.linalg.norm(dZ)
plot = Plot2D()
plot.setXLabel("Z-Axis (Ang)")
plot.setYLabel("Potential / Ha")
plot.addData([numpy.arange(shape[2])*dz, veff])
plot.setTitle("Difference Potential")
return mpsh, bandstructure, plot, transmission
# Initialize builder
analyzer = Builder()
analyzer.title('Huckel Analysis of DeviceConfiguration')
# Set the configuration generator
analyzer.setAnalyzerGenerator(analyze)
# Set up a Builder widget interface
analyzer.newGroup('Analysis')
analyzer.configuration('configuration', 'Device Configuration')
analyzer.double('left_voltage', 0, 'Left Voltage')
analyzer.double('right_voltage', 0, 'Right Voltage')
# Select the element.
analyzer.atom('element', Hydrogen, 'Element')
analyzer.boolean('non_scf', True, 'NonSelfconsistent?')
analyzer.plot('plot0', label='MPSH')
analyzer.plot('plot1', label='Bandstructure')
analyzer.plot('plot2', label='Potential')
analyzer.plot('plot3', label='Transmission')
Drop the script onto the Custom
Analyzer icon
on the main VNL toolbar.
The Custom Analyzer will open up, but no results are plotted
since you have not yet provided it with a device geometry to calculate.
Now drop the
Li-H2-Li system from the previous chapter onto the
DeviceConfiguration box. After the few seconds it takes
to perform the calculation, you should see the following:
|
The Custom Analyzer script is closely related to the Custom Builder script presented in the previous chapter. It consists of two main parts:
A function which, based on some input parameters, performs the analysis and sets up the plotting.
Commands for setting up the interface to specify those parameters.
In the following subsections, you will learn a little more detail of these two parts of the script.
The last section of the script sets up the Custom Analyzer parameters interface:
Instantiate the Custom Analyzer analyzer.
return mpsh, bandstructure, plot, transmission
Assign the function analyze(), which performs the analysis,
to the analyzer.
analyzer = Builder()
analyzer.title('Huckel Analysis of DeviceConfiguration')
Define the user parameters.
The key parameter is the configuration which is used for the
calculation. The analyzer.configuration() input field
gives a drop-zone where the geometry can be dropped.
In addition there are a few other parameters, similar to
those for the Custom Builder above.
# Set the configuration generator
analyzer.setAnalyzerGenerator(analyze)
# Set up a Builder widget interface
analyzer.newGroup('Analysis')
analyzer.configuration('configuration', 'Device Configuration')
analyzer.double('left_voltage', 0, 'Left Voltage')
analyzer.double('right_voltage', 0, 'Right Voltage')
Unique to the Custom Analyzer is the possibility to define one or
more plots which display the output data. In the present case
there are four separate graphs − in the specification they are labelled and the analyze() function
will provide the actual data for the plots.
# Select the element.
analyzer.atom('element', Hydrogen, 'Element')
analyzer.boolean('non_scf', True, 'NonSelfconsistent?')
analyzer.plot('plot0', label='MPSH')
As for the Custom Builder, the part of the script that performs the analysis must take the same parameters as defined for the interface, including the plots.
def analyze(configuration, left_voltage, right_voltage, element,
non_scf, plot0, plot1, plot2, plot3):
Based on the input parameters the script now sets up an Extended Hückel Calculation and perform some analysis.
The plotting in the Custom Analyzer is controlled by the return arguments; the function should return as many plot objects as there are plots defined (four, in this case).
plot.setYLabel("Potential / Ha")
plot.addData([numpy.arange(shape[2])*dz, veff])
The returned plot objects may be of two types: objects with built-in plots (see below) or explicit plots, where the user controls the data which is plotted. You will now set up such a plot for the effective potential.
The following lines of code calculates the effective potential through the center of the cell and stores it in a NumPy array.
mpsh = MolecularEnergySpectrum(configuration,
projection_list=projection_list,
energy_zero_parameter=FermiLevel)
# Potential
The potential is then inserted into a plot. The data itself is added using the
addData() method. The data is represented as a list with two
NumPy arrays describing x and y values.
for i in range(shape[2]) ]
dX, dY, dZ = potential.volumeElement().inUnitsOf(Ang)
dz = numpy.linalg.norm(dZ)
plot = Plot2D()
It is possible to add several data sets into the same plot by multiple calls of
the addData() method.
The following objects have built-in plotting:
In these cases the Analyzer uses the same type of plotting as VNL normally does if you click the Show Plot in the Result Viewer.
The methods for the Plot2D class are summarized in the table below.
Table 4: Syntax for the Plot2D class.
| Method name | Description |
|---|---|
setXLabel(label)
|
The string that should be used as the label on the x-axis (abscissa) of the plot. |
setYLabel(label)
|
The string that should be used as the label on the y-axis (ordinate) of the plot. |
setTitle(label)
|
A string holding the plot title. |
addData(data)
|
Add a new data object to the plot. The data object should be an N × M NumPy array, where the first row contains the M points of the x-axis (abscissa). The successive N-1 rows contain the M data points that should be plotted. |
Table 5: Commands used for setting up a GUI for the Custom Analyzer. All commands shown in Table 3 can also be used in a Custom Analyzer.
| Command name | Description |
|---|---|
plot(variable, label=None)
|
A variable associated with a 2D plot. The plot should be set in the
function defined with the setAnalyzerGenerator(). The
variable argument is the name assigned to the value
associated with the plot element. The label argument is
currently not used.
|