DevicePerformanceProfile

class DevicePerformanceProfile(configuration, equilibrium_methods=None, non_equilibrium_methods=None)

Class for performing timing and memory profiles of the different methods available for calculating the Green’s function and lesser Green’s function.

Parameters:
  • configuration (DeviceConfiguration) – The device configuration with an attached calculator to profile.

  • equilibrium_methods (GreensFunction | SparseGreensFunction | sequence of (GreensFunction | SparseGreensFunction)) – The methods benchmarked for the equilibrium calculation. If no methods should be benchmarked for the equilibrium calculation, an empty list can be specified.
    Default:: (GreensFunction, SparseGreensFunction)

  • non_equilibrium_methods – The methods benchmarked for the non-equilibrium calculation. If no methods should be benchmarked for the non-equilibrium calculation, an empty list can be specified.
    Default:: (GreensFunction, SparseGreensFunction)

equilibriumMethods()
Returns:

The equilibrium methods profiled.

Return type:

tuple of (GreensFunction | SparseGreensFunction)

memoryPeaks()

Get the peak memory usage across all processes for the different Green’s function methods. Every process returns the same global peak memory usage.

Returns:

The peak memory usages in MB.

Return type:

dict

memoryPeaksSingleProcess()

Get the peak memory usage for the different Green’s function methods. Each process returns its own local peak memory usage.

Returns:

The peak memory usages in MB.

Return type:

dict

memoryProfiles()

Get the memory usage over time for the different Green’s function methods. Each process returns its own local memory usage.

Returns:

The memory profiles in MB.

Return type:

dict

nlprint(stream=<_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>)

Print out the profiling report.

Parameters:

stream (file) – The stream to write to. This stream must support strings being written to it using ‘write’.
Default: sys.stdout

nonEquilibriumMethods()
Returns:

The non-equilibrium methods profiled.

Return type:

tuple of (GreensFunction | SparseGreensFunction)

timings()

Get the timing results for the different Green’s function methods.

Returns:

The timing results in seconds.

Return type:

dict

uniqueString()

Return a unique string representing the state of the object.

Notes

This profiler helps to pick the optimal values for the arguments equilibrium_method and non_equilibrium_method in DeviceAlgorithmParameters. These arguments select the algorithms used to calculate the Green’s function and lesser Green’s function.

For both equilibrium and non-equilibrium contour points the following methods are profiled:

For each method the elapsed time and memory usage are reported.

Note

The profile is generated by spawning a child process. Not all clusters support this.

Usage Example

Investigate the optimal methods to calculate the (lesser) Green’s function for a carbon chain with the following script.

# -------------------------------------------------------------
# Left electrode
# -------------------------------------------------------------
# Set up lattice
vector_a = [6.0, 0.0, 0.0]*Angstrom
vector_b = [0.0, 6.0, 0.0]*Angstrom
vector_c = [0.0, 0.0, 5.8]*Angstrom
left_electrode_lattice = UnitCell(vector_a, vector_b, vector_c)
# Define elements
left_electrode_elements = [Carbon, Carbon]
# Define coordinates
left_electrode_coordinates = [[ 3.  ,  3.  ,  1.45],
                              [ 3.  ,  3.  ,  4.35]]*Angstrom
# Set up configuration
left_electrode = BulkConfiguration(
    bravais_lattice=left_electrode_lattice,
    elements=left_electrode_elements,
    cartesian_coordinates=left_electrode_coordinates
    )
# -------------------------------------------------------------
# Right electrode
# -------------------------------------------------------------
# Set up lattice
vector_a = [6.0, 0.0, 0.0]*Angstrom
vector_b = [0.0, 6.0, 0.0]*Angstrom
vector_c = [0.0, 0.0, 5.8]*Angstrom
right_electrode_lattice = UnitCell(vector_a, vector_b, vector_c)
# Define elements
right_electrode_elements = [Carbon, Carbon]
# Define coordinates
right_electrode_coordinates = [[ 3.  ,  3.  ,  1.45],
                               [ 3.  ,  3.  ,  4.35]]*Angstrom
# Set up configuration
right_electrode = BulkConfiguration(
    bravais_lattice=right_electrode_lattice,
    elements=right_electrode_elements,
    cartesian_coordinates=right_electrode_coordinates
    )
# -------------------------------------------------------------
# Central region
# -------------------------------------------------------------
# Set up lattice
vector_a = [6.0, 0.0, 0.0]*Angstrom
vector_b = [0.0, 6.0, 0.0]*Angstrom
vector_c = [0.0, 0.0, 71.5]*Angstrom
central_region_lattice = UnitCell(vector_a, vector_b, vector_c)
# Define elements
central_region_elements = [Carbon, Carbon, Carbon, Carbon, Carbon, Carbon, Carbon, Carbon,
                           Carbon, Carbon, Carbon, Carbon, Carbon, Carbon, Carbon, Carbon,
                           Carbon, Carbon, Carbon, Carbon, Carbon, Carbon, Carbon, Carbon]
# Define coordinates
central_region_coordinates = [[  3.  ,   3.  ,   1.45],
                              [  3.  ,   3.  ,   4.35],
                              [  3.  ,   3.  ,   7.25],
                              [  3.  ,   3.  ,  10.15],
                              [  3.  ,   3.  ,  13.05],
                              [  3.  ,   3.  ,  15.95],
                              [  3.  ,   3.  ,  18.85],
                              [  3.  ,   3.  ,  21.75],
                              [  3.  ,   3.  ,  24.65],
                              [  3.  ,   3.  ,  27.55],
                              [  3.  ,   3.  ,  30.45],
                              [  3.  ,   3.  ,  33.35],
                              [  3.  ,   3.  ,  38.15],
                              [  3.  ,   3.  ,  41.05],
                              [  3.  ,   3.  ,  43.95],
                              [  3.  ,   3.  ,  46.85],
                              [  3.  ,   3.  ,  49.75],
                              [  3.  ,   3.  ,  52.65],
                              [  3.  ,   3.  ,  55.55],
                              [  3.  ,   3.  ,  58.45],
                              [  3.  ,   3.  ,  61.35],
                              [  3.  ,   3.  ,  64.25],
                              [  3.  ,   3.  ,  67.15],
                              [  3.  ,   3.  ,  70.05]]*Angstrom
# Set up configuration
central_region = BulkConfiguration(
    bravais_lattice=central_region_lattice,
    elements=central_region_elements,
    cartesian_coordinates=central_region_coordinates
    )
device_configuration = DeviceConfiguration(
    central_region,
    [left_electrode, right_electrode]
    )
# -------------------------------------------------------------
# Calculator
# -------------------------------------------------------------
#----------------------------------------
# Basis Set
#----------------------------------------
basis_set = [
    LDABasis.Carbon_SingleZeta,
    ]

#----------------------------------------
# Exchange-Correlation
#----------------------------------------
exchange_correlation = NCLDA.PZ

#----------------------------------------
# Poisson Solver Settings
#----------------------------------------
left_electrode_poisson_solver = FastFourier2DSolver(
    boundary_conditions=[[PeriodicBoundaryCondition,PeriodicBoundaryCondition],
                         [PeriodicBoundaryCondition,PeriodicBoundaryCondition],
                         [PeriodicBoundaryCondition,PeriodicBoundaryCondition]]
    )
right_electrode_poisson_solver = FastFourier2DSolver(
    boundary_conditions=[[PeriodicBoundaryCondition,PeriodicBoundaryCondition],
                         [PeriodicBoundaryCondition,PeriodicBoundaryCondition],
                         [PeriodicBoundaryCondition,PeriodicBoundaryCondition]]
    )

# Use the special noncollinear mixing scheme
iteration_control_parameters = IterationControlParameters(
    algorithm=PulayMixer(noncollinear_mixing=True)
    )

#----------------------------------------
# Electrode Calculators
#----------------------------------------
left_electrode_calculator = LCAOCalculator(
    basis_set=basis_set,
    exchange_correlation=exchange_correlation,
    poisson_solver=left_electrode_poisson_solver,
    )
right_electrode_calculator = LCAOCalculator(
    basis_set=basis_set,
    exchange_correlation=exchange_correlation,
    poisson_solver=right_electrode_poisson_solver,
    )

#----------------------------------------
# Device Calculator
#----------------------------------------
calculator = DeviceLCAOCalculator(
    basis_set=basis_set,
    exchange_correlation=exchange_correlation,
    iteration_control_parameters = iteration_control_parameters,
    electrode_calculators=
        [left_electrode_calculator, right_electrode_calculator],
    )

device_configuration.setCalculator(calculator)

# Create the performance profile and print a report.
device_performance_profile = DevicePerformanceProfile(device_configuration)
nlprint(device_performance_profile)

device_performance_profile.py

Here is an example of the output you might get by running with 2 MPI processes:

+------------------------------------------------------------------------------+
| Device Performance Profile (2 processes)                                     |
+------------------------------------------------------------------------------+
| Contour point timing (s):                                                    |
|                                   EQ        NEQ                              |
| GreensFunction                  0.01       0.01                              |
| SparseGreensFunction            0.03       0.02                              |
|                                                                              |
| Fastest EQ method (by 5.1 times): GreensFunction                             |
| Fastest NEQ method (by 2.9 times): GreensFunction                            |
+------------------------------------------------------------------------------+
| Peak memory usage/process (MB):                                              |
|                                   EQ        NEQ                              |
| GreensFunction                292.73     295.70                              |
| SparseGreensFunction          295.27     301.52                              |
|                                                                              |
| Most memory-efficient EQ method (by 1.0 times): GreensFunction               |
| Most memory-efficient NEQ method (by 1.0 times): GreensFunction              |
+------------------------------------------------------------------------------+

We can see that, in this case, GreensFunction is faster for calculating both equilibrium and non-equilibrium contour points. In terms of memory efficienty, the two methods are almost identical.

It is important to note that the timings given are for a single contour point run in parallel across all MPI processes. In a real device calculation many contour points are used, and the best performance is obtained when running with 1 process per contour point. It is therefore appropriate to run DevicePerformanceProfile with the same number of processes that will be specified in processes_per_contour_point.