Before you can run NEMO, you need a configuration file. The default configuration file (nemo.cfg
) is installed with the NEMO package and can be copied into your working directory as a starting point. On Unix systems, this can be found at /usr/local/etc/nemo.cfg
. Alternatively, you can set the NEMORC
environment variable to point to a configuration file. See the "Configuration file" section below for more details on the format of this file.
NEMO can be driven by your own Python code. Some simple examples of how to do this appear below. First, we will create a simulation with a single combined cycle gas turbine (CCGT). The "NSW1:31" notation indicates that the generator is sited in polygon 31 in the NSW1 region.
In [1]:
import nemo
from nemo import scenarios
c = nemo.Context()
scenarios._one_ccgt(c)
print(c.generators)
Then run the simulation:
In [2]:
nemo.run(c)
print(c)
The CCGT is configured with a zero capacity. Hence, no electricity is served in the simulation (100% unserved energy) and the largest shortfall was 33,645 MW (33.6 GW). This figure corresponds to the peak demand in the simulated year.
Let's now do a run with two CCGTs (13.2 GW and 20 GW) such that almost of the demand is met except for a few hours of unserved energy:
In [3]:
c = nemo.Context()
c.generators[0].set_capacity(13.2)
nemo.run(c)
print(c)
If we print the unserved
attribute in the context, we can see when the six hours of unserved energy occurred and how large the shortfalls were:
In [4]:
print(c.unserved)
NEMO includes a utils.py
module that includes a plot
function to show the time sequential dispatch. The following example demonstrates its use:
In [5]:
from matplotlib.pyplot import ioff
from nemo import utils
ioff()
utils.plt.rcParams["figure.figsize"] = (12, 6) # 12" x 6" figure
utils.plot(c)
The previous plot is rather bunched up. Instead, you can also pass a pair of dates to the plot()
function to limit the range of dates shown. For example:
In [6]:
from matplotlib.pyplot import ioff
from datetime import datetime
ioff()
utils.plt.rcParams["figure.figsize"] = (12, 6) # 12" x 6" figure
utils.plot(c, xlim=[datetime(2010, 1, 5), datetime(2010, 1, 12)])
Writing NEMO in Python allows the simulation framework to be easily scripted using Python language constructs, such as for loops. Using the previous example, the following small script demonstrates how simulation runs can be automated:
In [7]:
c = nemo.Context()
scenarios._one_ccgt(c)
for i in range(0, 40):
c.generators[0].set_capacity(i)
nemo.run(c)
if c.unserved_energy() == 0:
break
print(c.generators)
Once the generator capacity reaches 34 GW, there is no unserved energy.
NEMO contains two types of scenarios: supply-side and demand-side scenarios. The supply-side scenario modifies the list of generators. For example:
In [8]:
c = nemo.Context()
scenarios.ccgt(c)
print(c.generators)
A list of the current supply-side scenarios (with descriptions) can be obtained by running evolve --list-scenarios
from the shell (without the leading !):
In [9]:
!python3 evolve --list-scenarios
Demand-side scenarios modify the electricity demand time series before the simulation runs. Demand-side scenarios behave like operators that can be combined in any combination to modify the demand as desired. These are:
For example, applying scale:-10
followed by shift:1000:16:12
will reduce the overall demand by 10% and then shift 1 MW of demand from 4pm to noon every day of the year.
NEMO uses a configuration file to give users control over where data such as demand time series are to be found. The location of the configuration file can be specified by setting the NEMORC environment variable. The configuration file format is similar to Windows INI files; it has sections (in brackets) and, within sections, key=value pairs.
The default configuration file is called nemo.cfg
. The keys currently recognised are:
Instead of running a single simulation, it is more interesting to use evolve
which drives an evolutionary algorithm to find the least cost portfolio that meets demand. There are many options which you can discover by running evolve --help
. Here is a simple example to find the least cost portfolio using the ccgt
scenario (all-gas scenario with CCGT and OCGT generation):
$ evolve -s ccgt
It is possible to distribute the workload across multiple CPUs and multiple computers. See the SCOOP documentation for more details. To run the same evolution, but using all of your locally available CPU cores, you need to load the SCOOP module like so:
$ python3 -m scoop evolve -s ccgt
At the end of a run, details of the least cost system are printed on the console: the capacity of each generator, the energy supplied, CO2 emissions, costs, and the average cost of generation in dollars per MWh. If you want to see a plot of the system dispatch, you need to use the replay.py
script described in the next section.
Many of the optimisation parameters can be controlled from the command line, requiring no changes to the source code. Typically, source code changes are only required to add new supply scenario functions or cost classes. The command line options for evolve
are documented as follows:
Short option | Long option | Description | Default |
---|---|---|---|
-h | --help | Show help and then exit | |
-c | --carbon-price | Carbon price in \$/tonne | 25 |
-d | --demand-modifier | Demand modifier | unchanged |
-g | --generations | Number of generations to run | 100 |
-o | --output | Filename of results output file (will overwrite) | results.json |
-r | --discount-rate | Discount rate | 0.05 |
-s | --supply-scenario | Generation mix scenario | re100 |
-t | --transmission | Include transmission costs | False |
-v | --verbose | Be verbose | False |
--bioenergy-limit | Limit on annual energy from bioenergy in TWh/year | 20 | |
--ccs-storage-costs | CCS storage costs in \$/tonne | 27 | |
--coal-price | Coal price in \$/GJ | 1.86 | |
--costs | Use different cost scenario | AETA2013-in2030-mid | |
--emissions-limit | Limit total emissions to N Mt/year | $\infty$ | |
--fossil-limit | Limit share of energy from fossil sources | 1.0 | |
--gas-price | Gas price in \$/GJ | 11 | |
--hydro-limit | Limit on annual energy from hydro in TWh/year | 12 | |
--lambda | CMA-ES lambda value | None (autodetect) | |
--list-scenarios | Print list of scenarios and exit | ||
--min-regional-generation | Minimum share of energy generated intra-region | 0.0 | |
--nsp-limit | Non-synchronous penetration limit | 0.75 | |
--reliability-std | Reliability standard (% unserved) | 0.002 | |
--seed | Seed for random number generator | None | |
--sigma | CMA-ES sigma value | 2.0 | |
--trace-file | Filename for evaluation trace | None | |
--version | Print version number and exit |
To avoid having to re-run a long optimisation just to examine the resulting system, it is possible to reproduce a single run using the results from an earlier optimisation. The evolve
script writes an output file at the end of the run (default filename results.json
). This file encodes all of the relevant information from the optimisation run so that the solution it found can be replayed easily and accurately by replay
.
The input file for replay
may consist of any number of scenarios and configurations to replay, one per line. Blank lines are ignored and comment lines (#
) are shown for information. Each non-comment line must contain a JSON record from the results file that evolve
writes. Typically the input file for replay
will just be the output file from evolve
unmodified. However, if you want multiple simulations to be replayed, this is easy to achieve by pasting multiple JSON strings into the file, one per line.
A run is replayed using replay
like so:
$ replay -f results.json -x
The -f
option specifies the name of the input data file (the default is results.json
) and the -x
option enables a graphical plot of the system dispatch that you can navigate using zoom in, zoom out and pan controls. By including the --spills
option, surplus energy in each hour will be plotted above the demand line in a lighter shade than the usual colour of the spilling generator. All command line options can be displayed using:
In [10]:
!python3 replay --help