Calliope Urban Scale MILP Example Model

For more details on analysing input/output data, see the full urban scale example model

In [1]:
import calliope

# cufflinks allows for easy plotly plots to be 
# produced from a pandas DataFrame
import cufflinks

# We increase logging verbosity
calliope.set_log_level('INFO')
In [2]:
model = calliope.examples.milp()

# Note, we see the overrides that we have applied printed here, thanks to inreasing logging verbosity
[2018-04-20 09:51:37] INFO: Model: initialising
[2018-04-20 09:51:38] INFO: Override applied to model.name: Urban-scale example model -> Urban-scale example model with MILP
`techs.boiler.costs.monetary.energy_cap`:35 applied from override as new configuration
`techs.boiler.costs.monetary.purchase`:2000 applied from override as new configuration
`techs.chp.constraints.energy_cap_min_use`:0.2 applied from override as new configuration
`techs.chp.constraints.energy_cap_per_unit`:300 applied from override as new configuration
`techs.chp.constraints.units_max`:4 applied from override as new configuration
Override applied to techs.chp.costs.monetary.energy_cap: 750 -> 700
`techs.chp.costs.monetary.purchase`:40000 applied from override as new configuration
[2018-04-20 09:51:39] INFO: Model: preprocessing stage 1 (model_run)
[2018-04-20 09:51:41] INFO: Model: preprocessing stage 2 (model_data)
[2018-04-20 09:51:43] INFO: Model: preprocessing complete. Time since start: 0:00:05.708900
In [3]:
# Model inputs can be viewed at `model.inputs`. 
# Variables are indexed over any combination of `techs`, `locs`, `carriers`, `costs` and `timesteps`, 
# although `techs`, `locs`, and `carriers` are often concatenated. 
# e.g. `chp`, `X1`, `heat` -> `X1::chp::heat` 
model.inputs
Out[3]:
<xarray.Dataset>
Dimensions:                            (carrier_tiers: 3, carriers: 3, coordinates: 2, costs: 1, loc_carriers: 10, loc_tech_carriers_conversion_plus: 3, loc_techs: 26, loc_techs_area: 3, loc_techs_conversion: 2, loc_techs_conversion_plus: 1, loc_techs_export: 4, loc_techs_finite_resource: 9, loc_techs_investment_cost: 20, loc_techs_milp: 1, loc_techs_non_conversion: 23, loc_techs_om_cost: 7, loc_techs_supply_plus: 3, loc_techs_transmission: 10, locs: 4, techs: 9, timesteps: 48)
Coordinates:
  * loc_techs_finite_resource          (loc_techs_finite_resource) object 'X3::demand_heat' ...
  * costs                              (costs) object 'monetary'
  * loc_techs_area                     (loc_techs_area) object 'X3::pv' ...
  * loc_techs_non_conversion           (loc_techs_non_conversion) object 'X3::demand_heat' ...
  * timesteps                          (timesteps) datetime64[ns] 2005-07-01 ...
  * loc_techs                          (loc_techs) object 'X3::demand_heat' ...
  * carrier_tiers                      (carrier_tiers) <U5 'in' 'out' 'out_2'
  * loc_techs_milp                     (loc_techs_milp) object 'X1::chp'
  * loc_techs_conversion_plus          (loc_techs_conversion_plus) object 'X1::chp' ...
  * techs                              (techs) object 'supply_grid_power' ...
  * loc_tech_carriers_conversion_plus  (loc_tech_carriers_conversion_plus) object 'X1::chp::gas' ...
  * loc_techs_export                   (loc_techs_export) object 'X1::chp' ...
  * coordinates                        (coordinates) object 'x' 'y'
  * locs                               (locs) object 'X2' 'X3' 'N1' 'X1'
  * loc_techs_transmission             (loc_techs_transmission) object 'X2::heat_pipes:N1' ...
  * loc_carriers                       (loc_carriers) object 'X1::electricity' ...
  * carriers                           (carriers) object 'electricity' 'gas' ...
  * loc_techs_om_cost                  (loc_techs_om_cost) object 'X1::chp' ...
  * loc_techs_supply_plus              (loc_techs_supply_plus) object 'X3::pv' ...
  * loc_techs_investment_cost          (loc_techs_investment_cost) object 'X2::heat_pipes:N1' ...
  * loc_techs_conversion               (loc_techs_conversion) <U10 'X3::boiler' ...
Data variables:
    resource_area_max                  (loc_techs_area) int32 1500 1500 1500
    energy_cap_max                     (loc_techs) float64 nan nan 2e+03 ...
    energy_cap_per_unit                (loc_techs) float64 nan nan nan nan ...
    energy_con                         (loc_techs) float64 1.0 1.0 1.0 1.0 ...
    resource_area_per_energy_cap       (loc_techs_area) int32 7 7 7
    resource_eff                       (loc_techs_finite_resource) float64 nan ...
    resource_unit                      (loc_techs_finite_resource) <U5 'power' ...
    energy_prod                        (loc_techs) float64 nan nan 1.0 1.0 ...
    lifetime                           (loc_techs) float64 nan nan 25.0 25.0 ...
    force_resource                     (loc_techs_finite_resource) bool True ...
    parasitic_eff                      (loc_techs_supply_plus) float64 0.85 ...
    export_carrier                     (loc_techs_export) <U11 'electricity' ...
    energy_cap_min_use                 (loc_techs) float64 nan nan nan nan ...
    energy_eff                         (loc_techs) float64 nan nan 0.9269 ...
    resource                           (loc_techs_finite_resource, timesteps) float64 -0.0156 ...
    units_max                          (loc_techs_milp) int32 4
    reserve_margin                     (carriers) float64 nan nan nan
    cost_om_prod                       (costs, loc_techs_om_cost) float64 0.004 ...
    cost_energy_cap                    (costs, loc_techs_investment_cost) float64 0.9 ...
    cost_purchase                      (costs, loc_techs_investment_cost) float64 nan ...
    cost_om_annual                     (costs, loc_techs_om_cost) float64 nan ...
    cost_om_con                        (costs, loc_techs_om_cost) float64 nan ...
    cost_export                        (costs, loc_techs_om_cost, timesteps) float64 -0.03173 ...
    cost_depreciation_rate             (costs, loc_techs_investment_cost) float64 0.1102 ...
    distance                           (loc_techs_transmission) float64 3.0 ...
    lookup_remotes                     (loc_techs_transmission) <U18 'N1::heat_pipes:X2' ...
    available_area                     (locs) float64 1.3e+03 900.0 nan 500.0
    loc_coordinates                    (coordinates, locs) int32 8 5 5 2 7 3 7 7
    colors                             (techs) <U7 '#C5ABE3' '#8E2999' ...
    inheritance                        (techs) <U29 'supply' 'conversion' ...
    names                              (techs) <U29 'National grid import' ...
    carrier_ratios                     (carrier_tiers, loc_tech_carriers_conversion_plus) float64 1.0 ...
    carrier_ratios_min                 (carrier_tiers, loc_techs_conversion_plus) float64 1.0 ...
    lookup_loc_carriers                (loc_carriers) <U175 'X1::supply_grid_power::electricity,X1::power_lines:X2::electricity,X1::pv::electricity,X1::demand_electricity::electricity,X1::power_lines:X3::electricity,X1::chp::electricity' ...
    lookup_loc_techs                   (loc_techs_non_conversion) <U35 'X3::demand_heat::heat' ...
    lookup_loc_techs_conversion        (carrier_tiers, loc_techs_conversion) object 'X3::boiler::gas' ...
    lookup_loc_techs_conversion_plus   (carrier_tiers, loc_techs_conversion_plus) object 'X1::chp::gas' ...
    lookup_primary_loc_tech_carriers   (loc_techs_conversion_plus) <U20 'X1::chp::electricity' ...
    lookup_loc_techs_export            (loc_techs_export) <U20 'X1::chp::electricity' ...
    lookup_loc_techs_area              (locs) <U6 'X2::pv' 'X3::pv' '' 'X1::pv'
    timestep_resolution                (timesteps) float64 1.0 1.0 1.0 1.0 ...
    timestep_weights                   (timesteps) float64 1.0 1.0 1.0 1.0 ...
    max_demand_timesteps               (carriers) datetime64[ns] 2005-07-01T08:00:00 ...
Attributes:
    model.calliope_version:       0.6.0
    model.name:                   Urban-scale example model with MILP
    model.subset_time:            ['2005-07-01', '2005-07-02']
    model.timeseries_dateformat:  %Y-%m-%d %H:%M:%S
    run.backend:                  pyomo
    run.bigM:                     1000000.0
    run.ensure_feasibility:       True
    run.mode:                     plan
    run.objective:                cost_minimization
    run.solver:                   glpk
    run.zero_threshold:           1e-10
    calliope_version:             0.6.0-dev
    defaults:                     available_area: null\ncarrier_ratios: {}\nc...
    allow_operate_mode:           1
In [4]:
# Individual data variables can be accessed easily, `to_pandas()` reformats the data to look nicer
# Here we look at one of the MILP overrides that we have added, the fixed `purchase` cost
model.inputs.cost_purchase.to_pandas().dropna(axis=1)
Out[4]:
loc_techs_investment_cost X3::boiler X2::boiler X1::chp
costs
monetary 2000.0 2000.0 40000.0
In [5]:
# Solve the model. Results are loaded into `model.results`. 
# By including logging (see package importing), we can see the timing of parts of the run, as well as the solver's log
model.run()
[2018-04-20 09:51:45] INFO: Backend: starting model run
[2018-04-20 09:51:47] INFO: Backend: model generated. Time since start: 0:00:09.997301
[2018-04-20 09:51:47] INFO: Backend: sending model to solver
[2018-04-20 09:51:52] INFO: Backend: solver finished running. Time since start: 0:00:14.669407
[2018-04-20 09:51:52] INFO: Backend: loaded results
[2018-04-20 09:51:52] INFO: Backend: generated solution array. Time since start: 0:00:14.950685
[2018-04-20 09:51:52] INFO: Postprocessing: started
[2018-04-20 09:51:53] INFO: Postprocessing: All values < 1e-10 set to 0 in unmet_demand
[2018-04-20 09:51:53] INFO: Postprocessing: Model was feasible, deleting unmet_demand variable
[2018-04-20 09:51:53] INFO: Postprocessing: ended. Time since start: 0:00:15.778898
In [6]:
# Model results are held in the same structure as model inputs. 
# The results consist of the optimal values for all decision variables, including capacities and carrier flow
# There are also results, like system capacity factor and levelised costs, which are calculated in postprocessing
# before being added to the results Dataset

model.results
Out[6]:
<xarray.Dataset>
Dimensions:                     (carriers: 3, costs: 1, loc_tech_carriers_con: 19, loc_tech_carriers_export: 4, loc_tech_carriers_prod: 21, loc_techs: 26, loc_techs_area: 3, loc_techs_cost: 20, loc_techs_investment_cost: 20, loc_techs_milp: 1, loc_techs_om_cost: 7, loc_techs_purchase: 2, loc_techs_supply_plus: 3, techs: 9, timesteps: 48)
Coordinates:
  * loc_tech_carriers_export    (loc_tech_carriers_export) object 'X1::pv::electricity' ...
  * costs                       (costs) object 'monetary'
  * loc_techs_cost              (loc_techs_cost) object 'X2::heat_pipes:N1' ...
  * loc_techs_area              (loc_techs_area) object 'X3::pv' 'X2::pv' ...
  * timesteps                   (timesteps) datetime64[ns] 2005-07-01 ...
  * loc_techs                   (loc_techs) object 'X3::demand_heat' ...
  * loc_techs_milp              (loc_techs_milp) object 'X1::chp'
  * techs                       (techs) object 'supply_grid_power' 'boiler' ...
  * loc_tech_carriers_prod      (loc_tech_carriers_prod) object 'X1::power_lines:X2::electricity' ...
  * loc_techs_purchase          (loc_techs_purchase) object 'X3::boiler' ...
  * carriers                    (carriers) <U11 'electricity' 'gas' 'heat'
  * loc_techs_om_cost           (loc_techs_om_cost) object 'X1::chp' ...
  * loc_techs_supply_plus       (loc_techs_supply_plus) object 'X3::pv' ...
  * loc_techs_investment_cost   (loc_techs_investment_cost) object 'X2::heat_pipes:N1' ...
  * loc_tech_carriers_con       (loc_tech_carriers_con) object 'N1::heat_pipes:X2::heat' ...
Data variables:
    energy_cap                  (loc_techs) float64 9.398 365.1 211.0 10.4 ...
    carrier_prod                (loc_tech_carriers_prod, timesteps) float64 0.0 ...
    carrier_con                 (loc_tech_carriers_con, timesteps) float64 -69.84 ...
    cost                        (costs, loc_techs_cost) float64 0.05733 ...
    resource_area               (loc_techs_area) float64 350.0 0.0 0.0
    resource_con                (loc_techs_supply_plus, timesteps) float64 0.0 ...
    resource_cap                (loc_techs_supply_plus) float64 38.95 0.0 0.0
    carrier_export              (loc_tech_carriers_export, timesteps) float64 0.0 ...
    cost_var                    (costs, loc_techs_om_cost, timesteps) float64 0.4643 ...
    cost_investment             (costs, loc_techs_investment_cost) float64 0.05733 ...
    purchased                   (loc_techs_purchase) float64 0.0 1.0
    units                       (loc_techs_milp) float64 1.0
    operating_units             (loc_techs_milp, timesteps) float64 1.0 1.0 ...
    capacity_factor             (loc_tech_carriers_prod, timesteps) float64 0.0 ...
    systemwide_capacity_factor  (techs, carriers) float64 nan nan nan 0.0 ...
    systemwide_levelised_cost   (carriers, techs, costs) float64 nan inf nan ...
    total_levelised_cost        (carriers, costs) float64 0.0431 0.03331 0.02516
Attributes:
    model.calliope_version:       0.6.0
    model.name:                   Urban-scale example model with MILP
    model.subset_time:            ['2005-07-01', '2005-07-02']
    model.timeseries_dateformat:  %Y-%m-%d %H:%M:%S
    run.backend:                  pyomo
    run.bigM:                     1000000.0
    run.ensure_feasibility:       True
    run.mode:                     plan
    run.objective:                cost_minimization
    run.solver:                   glpk
    run.zero_threshold:           1e-10
    calliope_version:             0.6.0-dev
    defaults:                     available_area: null\ncarrier_ratios: {}\nc...
    allow_operate_mode:           1
    termination_condition:        optimal
    solution_time:                7.707835
    time_finished:                2018-04-20 09:51:52
In [7]:
# We can sum operating units of CHP over all locations and turn the result into a pandas DataFrame
df_units = model.get_formatted_array('operating_units').sum('locs').to_pandas().T

#The information about the dataframe tells us about the amount of data it holds in the index and in each column
df_units.info()
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 48 entries, 2005-07-01 00:00:00 to 2005-07-02 23:00:00
Data columns (total 1 columns):
chp    48 non-null float64
dtypes: float64(1)
memory usage: 768.0 bytes
In [8]:
# Using .head() to see the first few rows of operating units

df_units.head()
Out[8]:
techs chp
timesteps
2005-07-01 00:00:00 1.0
2005-07-01 01:00:00 1.0
2005-07-01 02:00:00 1.0
2005-07-01 03:00:00 1.0
2005-07-01 04:00:00 1.0
In [9]:
# We can plot this by using the timeseries plotting functionality.
# The top-left dropdown gives us the chance to scroll through other timeseries data too.

model.plot.timeseries()
In [10]:
# plot.capacities gives a graphical view of the non-timeseries variables, both input and output

# Note, because we fix unit size, CHP now has a maximum capacity of 300kW, 
# compared to 260kW in the non-MILP case

model.plot.capacity()

See the Calliope documentation for more details on setting up and running a Calliope model.