IO#

class nextnano_optimizers.io.IO(input_file_path: str, variable_names: list, target_output_paths: list, sweep: dict | None = None)#

Bases: object

Interface between the nextnano input file, optimization variables, and simulation output datafiles.

Parameters:
input_file_pathstr

Path to a nextnano input file.

variable_nameslist of str

Names of the input variables to be optimized. Each name must correspond to a variable defined in the input file.

target_output_pathslist of tuple of str

Paths to the output files to be loaded after each simulation. Each tuple contains the path components relative to the simulation output directory.

  • Example one output: [('bias_00000', 'potential.dat')]

  • Example 2 outputs: [('bias_00000', 'potential.dat'), ('Structure', 'AlloyConcentration.dat')]

sweepdict, default=None

Input variables to sweep over, independently of the optimization. Each dictionary key must correspond to a variable defined in the input file and must not appear in variable_names. Each dictionary value is a list of numerical values to sweep over.

For each optimization candidate, one simulation is run for every combination of sweep values. For example, {"source_bias": [-0.5, 0.0, 0.5]} results in three simulations per candidate.

When None, no sweep is performed and each candidate is simulated only once.

Attributes:
input_file_pathstr

Path to a nextnano input file.

input_variable_nameslist of str

Names of the input variables to be optimized.

target_outputlist of list or tuple

Paths to the output files to be loaded after each simulation.

sweepdict or None

Input variables to sweep over.

number_of_output_targetsint

Number of target output paths.

number_of_input_variablesint

Number of input variable names.

sweep_shapetuple of int

Number of values for each sweep variable. Empty tuple when there is no sweep. For example:

sweep = {"source_bias": [-0.5, 0.5], "well_width": [4, 5, 6]}
sweep_shape = (2, 3)
sweep_sizeint

Total number of sweep combinations, equal to the product of the elements of sweep_shape (1 when there is no sweep).

Raises:
ValueError

If input variables are invalid.

Examples

Create an IO instance with three optimization variables and two target output files:

>>> io = IO(
...     input_file_path="device.nnp",
...     variable_names=["well_width", "barrier_width", "doping"],
...     target_output_paths=[
...         ("bias_00000", "potential.dat"),
...         ("bias_00000", "quantum", "Gamma", "energies.dat"),
...     ],
... )

To additionally run each simulations at different temperatures for each candidate, configure a sweep:

>>> io = IO(
...     input_file_path="device.nnp",
...     variable_names=["well_width", "barrier_width", "doping"],
...     target_output_paths=[
...         ("bias_00000", "potential.dat"),
...         ("bias_00000", "quantum", "Gamma", "energies.dat"),
...     ],
...     sweep={"temperature": [200, 300, 400]},
... )
load_output_datafiles()#

Load the simulation output datafiles specified in target_output_paths.

Returns:
numpy.ndarray or list

The return structure depends on the input to the latest run_simulation call:

  • 1D input, no sweep: array of shape (k,).

  • 1D input, with sweep: array of shape (k, *sweep_shape).

  • 2D input, no sweep: list of N arrays of shape (k,).

  • 2D input, with sweep: list of N arrays of shape (k, *sweep_shape).

Here, k is the number of target output files and N is the number of optimization candidates. Each array contains nextnanopy.DataFile objects. For 2D inputs, failed candidates are represented by None instead of an array.

Raises:
RuntimeError

If run_simulation has not been called, or if the simulation for a single candidate failed.

Examples

Every example below assumes an IO with 2 output files in target_output_paths and 3 variables in variable_names. The shape of the returned datafiles depends on the input to the latest run_simulation call.

Load the output datafiles after running a simulation for one candidate:

>>> io.run_simulation([5.0, 3.0, 1e17])
>>> datafiles = io.load_output_datafiles()
>>> datafiles.shape
(2,)

Load the output datafiles for multiple candidates:

>>> io.run_simulation([[5.0, 3.0, 1e17], [6.0, 3.5, 2e17]])
>>> datafiles = io.load_output_datafiles()
>>> len(datafiles)
2
>>> datafiles[0].shape
(2,)

Load the output datafiles when io is configured with sweep={"temperature": [200, 300, 400]}:

>>> io.run_simulation([5.0, 3.0, 1e17])
>>> datafiles = io.load_output_datafiles()
>>> datafiles.shape
(2, 3)
run_simulation(input_variable_values, parallel_limit: int = 1, input_dir_path: str | None = None, output_dir_path: str | None = None, input_file_stem: str | None = None, clean_input_file: bool = False, start_index: int | None = None) → None#

Run nextnano simulations for the given input variable values.

A 1D input runs simulations for a single optimization candidate, while a 2D input runs simulations for multiple candidates. If a sweep is configured, sweep_size simulations are run for each candidate, one for each combination of sweep values.

Parameters:
input_variable_values1D or 2D iterable

Values of the variables specified in variable_names. A 1D input has a shape (m,), where m is the number of optimization variables. A 2D input has shape (N, m), where N is the number of candidates. Each row represents one candidate.

parallel_limitint, default=1

Maximum number of parallel simulations. 1 runs them serially.

input_dir_pathstr, default=None

Directory the generated input files are saved in. If None, they are saved next to the original input file.

output_dir_pathstr, default=None

Parent directory for the simulation output folders. Each simulation creates its own subfolder, named after the generated input file. If None, the directory is determined by the outputdirectory setting in the nextnanopy configuration.

input_file_stemstr, default=None

Filename stem for generated input files. The stem is the filename without its extension (e.g. "input_file", not "input_file.nnp"). If None, the original input filename stem is used.

clean_input_filebool, default=False

Whether to remove the generated input files after execution. Note: when running across multiple run_simulation calls in the same input_dir_path (e.g. successive evolution generations), keep this False so nextnanopy auto-increments input filenames and produces unique output folders. Setting True causes filename — and therefore output folder — collisions.

start_indexint, default=None

Starting index for numbering generated input files, appended to the filename stem as a five-digit number. For example for nnp files, input_file_stem="sim" and start_index=5 produce sim_00005.nnp, sim_00006.nnp input files and sim_00005, sim_00006 output folders, etc. If None, file naming is handled by nextnanopy.

Raises:
ValueError

If the shape of input_variable_values is incompatible with input_variable_names, or if input_variable_values is empty.

FileExistsError

If start_index is given and a name it produces is already taken in input_dir_path.

Examples

Run simulations for a single optimization candidate:

>>> io.run_simulation([5.0, 3.0, 1e17])

Run simulations for multiple optimization candidates:

>>> io.run_simulation([
...     [5.0, 3.0, 1e17],
...     [6.0, 3.5, 2e17],
...     [7.0, 4.0, 3e17],
... ])

Run simulations for multiple candidates in parallel, with at most two simulations running simultaneously:

>>> io.run_simulation([
...     [5.0, 3.0, 1e17],
...     [6.0, 3.5, 2e17],
...     [7.0, 4.0, 3e17],
... ], parallel_limit=2)