Note

You can download this example as a Jupyter notebook or start it in interactive mode.

Simple electricity market examples

This example gradually builds up more and more complicated energy-only electricity markets in PyPSA, starting from a single bidding zone, going up to multiple bidding zones connected with transmission (NTCs) along with variable renewables and storage.

Preliminaries

Here libraries are imported and data is defined.

[1]:
import pypsa, numpy as np
[2]:
#marginal costs in EUR/MWh
marginal_costs = {"Wind" : 0,
                  "Hydro" : 0,
                  "Coal" : 30,
                  "Gas" : 60,
                  "Oil" : 80}

#power plant capacities (nominal powers in MW) in each country (not necessarily realistic)
power_plant_p_nom = {"South Africa" : {"Coal" : 35000,
                                       "Wind" : 3000,
                                       "Gas" : 8000,
                                       "Oil" : 2000
                                      },
                     "Mozambique" : {"Hydro" : 1200,
                                    },
                     "Swaziland" : {"Hydro" : 600,
                                    },
                    }

#transmission capacities in MW (not necessarily realistic)
transmission = {"South Africa" : {"Mozambique" : 500,
                                  "Swaziland" : 250},
                "Mozambique" : {"Swaziland" : 100}}

#country electrical loads in MW (not necessarily realistic)
loads = {"South Africa" : 42000,
         "Mozambique" : 650,
         "Swaziland" : 250}

Single bidding zone with fixed load, one period

In this example we consider a single market bidding zone, South Africa.

The inelastic load has essentially infinite marginal utility (or higher than the marginal cost of any generator).

[3]:
country = "South Africa"

network = pypsa.Network()

network.add("Bus",country)

for tech in power_plant_p_nom[country]:
    network.add("Generator",
                "{} {}".format(country,tech),
                bus=country,
                p_nom=power_plant_p_nom[country][tech],
                marginal_cost=marginal_costs[tech])


network.add("Load",
            "{} load".format(country),
            bus=country,
            p_set=loads[country])
[4]:
#Run optimisation to determine market dispatch
network.lopf()
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
WARNING:pyomo.solvers:Could not locate the 'glpsol' executable, which is required for solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_829/1894603646.py in <module>
      1 #Run optimisation to determine market dispatch
----> 2 network.lopf()

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'glpk'
[5]:
#print the load active power (P) consumption
network.loads_t.p
[5]:
now
[6]:
#print the generator active power (P) dispatch
network.generators_t.p
[6]:
now
[7]:
#print the clearing price (corresponding to gas)
network.buses_t.marginal_price
[7]:
now

Two bidding zones connected by transmission, one period

In this example we have bidirectional transmission capacity between two bidding zones. The power transfer is treated as controllable (like an A/NTC (Available/Net Transfer Capacity) or HVDC line). Note that in the physical grid, power flows passively according to the network impedances.

[8]:
network = pypsa.Network()

countries = ["Mozambique", "South Africa"]

for country in countries:

    network.add("Bus",country)

    for tech in power_plant_p_nom[country]:
        network.add("Generator",
                    "{} {}".format(country,tech),
                    bus=country,
                    p_nom=power_plant_p_nom[country][tech],
                    marginal_cost=marginal_costs[tech])


    network.add("Load",
                "{} load".format(country),
                bus=country,
                p_set=loads[country])

    #add transmission as controllable Link
    if country not in transmission:
        continue

    for other_country in countries:
        if other_country not in transmission[country]:
            continue

        #NB: Link is by default unidirectional, so have to set p_min_pu = -1
        #to allow bidirectional (i.e. also negative) flow
        network.add("Link",
                   "{} - {} link".format(country, other_country),
                   bus0=country,
                   bus1=other_country,
                   p_nom=transmission[country][other_country],
                   p_min_pu=-1)
[9]:
network.lopf()
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
WARNING:pyomo.solvers:Could not locate the 'glpsol' executable, which is required for solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_829/889609669.py in <module>
----> 1 network.lopf()

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'glpk'
[10]:
network.loads_t.p
[10]:
now
[11]:
network.generators_t.p
[11]:
now
[12]:
network.links_t.p0
[12]:
now
[13]:
#print the clearing price (corresponding to water in Mozambique and gas in SA)
network.buses_t.marginal_price
[13]:
now
[14]:
#link shadow prices
network.links_t.mu_lower
[14]:
now

Three bidding zones connected by transmission, one period

In this example we have bidirectional transmission capacity between three bidding zones. The power transfer is treated as controllable (like an A/NTC (Available/Net Transfer Capacity) or HVDC line). Note that in the physical grid, power flows passively according to the network impedances.

[15]:
network = pypsa.Network()

countries = ["Swaziland", "Mozambique", "South Africa"]

for country in countries:

    network.add("Bus",country)

    for tech in power_plant_p_nom[country]:
        network.add("Generator",
                    "{} {}".format(country,tech),
                    bus=country,
                    p_nom=power_plant_p_nom[country][tech],
                    marginal_cost=marginal_costs[tech])


    network.add("Load",
                "{} load".format(country),
                bus=country,
                p_set=loads[country])

    #add transmission as controllable Link
    if country not in transmission:
        continue

    for other_country in countries:
        if other_country not in transmission[country]:
            continue

        #NB: Link is by default unidirectional, so have to set p_min_pu = -1
        #to allow bidirectional (i.e. also negative) flow
        network.add("Link",
                   "{} - {} link".format(country, other_country),
                   bus0=country,
                   bus1=other_country,
                   p_nom=transmission[country][other_country],
                   p_min_pu=-1)
[16]:
network.lopf()
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
WARNING:pyomo.solvers:Could not locate the 'glpsol' executable, which is required for solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_829/889609669.py in <module>
----> 1 network.lopf()

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'glpk'
[17]:
network.loads_t.p
[17]:
now
[18]:
network.generators_t.p
[18]:
now
[19]:
network.links_t.p0
[19]:
now
[20]:
#print the clearing price (corresponding to hydro in S and M, and gas in SA)
network.buses_t.marginal_price
[20]:
now
[21]:
#link shadow prices
network.links_t.mu_lower
[21]:
now

Single bidding zone with price-sensitive industrial load, one period

In this example we consider a single market bidding zone, South Africa.

Now there is a large industrial load with a marginal utility which is low enough to interact with the generation marginal cost.

[22]:
country = "South Africa"

network = pypsa.Network()

network.add("Bus",country)

for tech in power_plant_p_nom[country]:
    network.add("Generator",
                "{} {}".format(country,tech),
                bus=country,
                p_nom=power_plant_p_nom[country][tech],
                marginal_cost=marginal_costs[tech])

#standard high marginal utility consumers
network.add("Load",
            "{} load".format(country),
            bus=country,
            p_set=loads[country])

#add an industrial load as a dummy negative-dispatch generator with marginal utility of 70 EUR/MWh for 8000 MW
network.add("Generator",
            "{} industrial load".format(country),
            bus=country,
            p_max_pu=0,
            p_min_pu=-1,
            p_nom=8000,
            marginal_cost=70)
[23]:
network.lopf()
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
WARNING:pyomo.solvers:Could not locate the 'glpsol' executable, which is required for solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_829/889609669.py in <module>
----> 1 network.lopf()

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'glpk'
[24]:
network.loads_t.p
[24]:
now
[25]:
#NB only half of industrial load is served, because this maxes out
#Gas. Oil is too expensive with a marginal cost of 80 EUR/MWh
network.generators_t.p
[25]:
now
[26]:
network.buses_t.marginal_price
[26]:
now

Single bidding zone with fixed load, several periods

In this example we consider a single market bidding zone, South Africa.

We consider multiple time periods (labelled [0,1,2,3]) to represent variable wind generation.

[27]:
country = "South Africa"

network = pypsa.Network()

#snapshots labelled by [0,1,2,3]
network.set_snapshots(range(4))

network.add("Bus",country)

#p_max_pu is variable for wind
for tech in power_plant_p_nom[country]:
    network.add("Generator",
                "{} {}".format(country,tech),
                bus=country,
                p_nom=power_plant_p_nom[country][tech],
                marginal_cost=marginal_costs[tech],
                p_max_pu=([0.3,0.6,0.4,0.5] if tech == "Wind" else 1),
                )

#load which varies over the snapshots
network.add("Load",
            "{} load".format(country),
            bus=country,
            p_set=loads[country] + np.array([0,1000,3000,4000]))
[28]:
#specify that we consider all snapshots
network.lopf(network.snapshots)
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
WARNING:pyomo.solvers:Could not locate the 'glpsol' executable, which is required for solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_829/2405348393.py in <module>
      1 #specify that we consider all snapshots
----> 2 network.lopf(network.snapshots)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'glpk'
[29]:
network.loads_t.p
[29]:
snapshot
0
1
2
3
[30]:
network.generators_t.p
[30]:
snapshot
0
1
2
3
[31]:
network.buses_t.marginal_price
[31]:
snapshot
0
1
2
3

Single bidding zone with fixed load and storage, several periods

In this example we consider a single market bidding zone, South Africa.

We consider multiple time periods (labelled [0,1,2,3]) to represent variable wind generation. Storage is allowed to do price arbitrage to reduce oil consumption.

[32]:
country = "South Africa"

network = pypsa.Network()

#snapshots labelled by [0,1,2,3]
network.set_snapshots(range(4))

network.add("Bus",country)

#p_max_pu is variable for wind
for tech in power_plant_p_nom[country]:
    network.add("Generator",
                "{} {}".format(country,tech),
                bus=country,
                p_nom=power_plant_p_nom[country][tech],
                marginal_cost=marginal_costs[tech],
                p_max_pu=([0.3,0.6,0.4,0.5] if tech == "Wind" else 1),
                )

#load which varies over the snapshots
network.add("Load",
            "{} load".format(country),
            bus=country,
            p_set=loads[country] + np.array([0,1000,3000,4000]))

#storage unit to do price arbitrage
network.add("StorageUnit",
            "{} pumped hydro".format(country),
            bus=country,
            p_nom=1000,
            max_hours=6, #energy storage in terms of hours at full power
           )
[33]:
network.lopf(network.snapshots)
INFO:pypsa.opf:Performed preliminary steps
INFO:pypsa.opf:Building pyomo model using `kirchhoff` formulation
INFO:pypsa.opf:Solving model using glpk
WARNING:pyomo.solvers:Could not locate the 'glpsol' executable, which is required for solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
/tmp/ipykernel_829/416887474.py in <module>
----> 1 network.lopf(network.snapshots)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/components.py in lopf(self, snapshots, pyomo, solver_name, solver_options, solver_logfile, formulation, keep_files, extra_functionality, multi_investment_periods, **kwargs)
    646
    647         if pyomo:
--> 648             return network_lopf(self, **args)
    649         else:
    650             return network_lopf_lowmem(self, **args)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf(network, snapshots, solver_name, solver_io, skip_pre, extra_functionality, multi_investment_periods, solver_logfile, solver_options, keep_files, formulation, ptdf_tolerance, free_memory, extra_postprocessing)
   1663                               solver_logfile=solver_logfile, solver_options=solver_options,
   1664                               keep_files=keep_files, free_memory=free_memory,
-> 1665                               extra_postprocessing=extra_postprocessing)

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pypsa/opf.py in network_lopf_solve(network, snapshots, formulation, solver_options, solver_logfile, keep_files, free_memory, extra_postprocessing)
   1563             network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1564     else:
-> 1565         network.results = network.opt.solve(*args, suffixes=["dual"], keepfiles=keep_files, logfile=solver_logfile, options=solver_options)
   1566
   1567     if logger.isEnabledFor(logging.INFO):

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    510         """ Solve the problem """
    511
--> 512         self.available(exception_flag=True)
    513         #
    514         # If the inputs are models, then validate that they have been

~/checkouts/readthedocs.org/user_builds/pypsa-docs-staging/envs/latest/lib/python3.7/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    123             if exception_flag:
    124                 msg = "No executable found for solver '%s'"
--> 125                 raise ApplicationError(msg % self.name)
    126             return False
    127         return True

ApplicationError: No executable found for solver 'glpk'
[34]:
network.loads_t.p
[34]:
snapshot
0
1
2
3
[35]:
network.generators_t.p
[35]:
snapshot
0
1
2
3
[36]:
network.storage_units_t.p
[36]:
snapshot
0
1
2
3
[37]:
network.storage_units_t.state_of_charge
[37]:
snapshot
0
1
2
3
[38]:
network.buses_t.marginal_price
[38]:
snapshot
0
1
2
3