Using Gurobi to optimize distributed energy storage assets
Thank you for joining us. We will be starting shortly.

Welcome to the Webinar
Using Gurobi to optimize distributed energy storage assets

Speaker Introduction• Andrew Martinez, P.E.
• Data Scientist at Advanced Microgrid Solution (AMS)• Member Data Science and Optimization Team• M.S. in Mechanical Engineering, Stanford University; B.S. in MechanicalEngineering from Purdue University• Formally trained as a mechanical engineer with a specialization in energy
system. Began working on optimization models at the National Renewable
Energy Lab (NREL) to help answer energy policy questions. Extended appliedoptimization modeling knowledge to build microgrid models at consulting firm
before starting at AMS 3 years ago.

Copyright 2018, Gurobi Optimization, Inc.

3

Agenda• Overview of Advanced Microgrid Solutions• Problem statement• Why optimization• Key constraint formulation• Gurobi features we use• Python API (Python examples)• Variable and Constraint classes• SOS2 and linear expressions• Gurobi Server• Problem statistics• Solution in action

Copyright 2018, Gurobi Optimization, Inc.

4

Overview of AMS• AMS builds, helps finance, and operates energy
storage systems at host customer sites

Utility BillSavings• Host customer benefits include:••••

Guaranteed savings
Enhanced branding
Reduced GHG emissions
Improved demand response

Host Customer

ServiceFees

Build, Finance,& Operate

Battery StorageAMS• The utility receives backup capacity• Systems are financed through:

FirmCapacity• Utility contracts• Government incentives• Shared savings with host customer

Utility
Contract

Utility

ECONOMIC MODEL
Copyright 2018, Gurobi Optimization, Inc.

5

Electricity Grid is ChangingAMS won 90 MW Contracts to Build
Energy Storage Systems for Grid
Support in Southern California

Copyright 2018, Gurobi Optimization, Inc.

SAN ONOFRE

6

AMS Targets Load Clusters

AMS software platform enables
storage as a service
Copyright 2016, Gurobi Optimization, Inc.

7

Irvine Company | 250kW-1500kWh
Copyright 2018, Gurobi Optimization, Inc.

8

AMS Aggregates Capacity in Specific Regions

Automated performance optimization
through AMS software platform in the cloud
Copyright 2018, Gurobi Optimization, Inc.

9

Hybrid-Electric Buildings®
1

PEAK DEMAND MANAGEMENTCARBON MANAGEMENT

2LOAD SHIFTING

DEMAND RESPONSE

3UTILITY CAPACITY

WHOLESALE MARKET PRODUCTS

1000400%

1800
600

300%

2400200200%

POWER (kW)03-200100%-400-600

0:00

2:00

4:00

6:00

8:00

10:00

12:00

14:00

16:00

18:00

Pre-AMS Elec (kW)Post-AMS Elec (kW)Battery State (%)

Battery In, Out (kW)Copyright 2018, Gurobi Optimization, Inc.

20:00

22:00

0%

10Problem Statement
How do you operate a fleet of batteries to maximize value given multiple revenue streams such as
host customer contracts and utility contracts within the technology constraints of a battery system?
Utility Bill Savings

Host Customer

Host Customer

Host Customer

AMS
Utility
Utility Contract

ISO/RTO/TSO
Market Products

Copyright 2018, Gurobi Optimization, Inc.

11

Why Optimization?
• Might seem obvious but stepping through an example.

Copyright 2018, Gurobi Optimization, Inc.

12

Demand Charge Management - Simple
180160140

Set Demand Limit

Power, kW

120
100
8060
40
20000:00

04:00

08:00

12:00

16:00

20:00

00:00

Time
Copyright 2018, Gurobi Optimization, Inc.

13

Demand Charge Management - TOU
Mid-Peak180160On-Peak

Mid-PeakSet Demand Limits

140
Power, kW

120100806040
20000:00

04:00

08:00

12:00

16:00

20:00

00:00

Time
Copyright 2018, Gurobi Optimization, Inc.

14

Demand Charge Management - RTP
180

On-Peak
prices

Mid-PeakSet Demand Limits

145090

Mid-Peak
20160
140
Power, kW

120
100
80
60
40
20
000:00

04:00

08:00

12:00

16:00

20:00

00:00

Time
Copyright 2018, Gurobi Optimization, Inc.

15

Demand Charge Management - Dispatch
On-Peak
prices

Mid-PeakHow do you set the demand limits
180trying to balance the economics
whileof adispatch?
160

145090

Mid-Peak
20140

Dispatch callPower, kW

120
100
80
60
40
20000:00

04:00

08:00

12:00

16:00

20:00

00:00

Time
Copyright 2018, Gurobi Optimization, Inc.

16

Problem Formulation
Physical Constraints

Economic Constraints- Battery Power and Energy- Host Customer Tariff Interpretation- Energy Balance- Utility Dispatch Performance- Net Export Rules- Market Product Prices

Objective Function
Maximizing Net Present Valueof owning and operating system

Copyright 2018, Gurobi Optimization, Inc.

17

Key Constraints - PhysicalPhysical Constraints•••

Battery Power and Energy
Energy Balance
Net Export Rules

site.battery_energy_capacity

Code example-- for loops (sites, intervals) --v.battery_energy[t]m.addConstr(v.battery_discharge_power[site_id, t] <=site.max_discharge_power)m.addConstr(v.battery_charge_power[site_id, t] <=site.max_charge_power)…
v.battery_energy[t+1]v.battery_disc[t]--if t < len(intervals)-m.addConstr(v.battery_energy[site_id, t+1] ==v.battery_energy[site_id, t] –v.battery_discharge_power[site_id, t] +v.battery_charge_power[site_id, t] )Zero SOE

tt+1

m.addConstr(v.battery_energy[site_id, t] >= 0)
m.addConstr(v.battery_energy[site_id, t] <=site.battery_energy_capacity)
Copyright 2018, Gurobi Optimization, Inc.

18

Key Constraints - Economic•

Dispatch Performance• Thresholds for performance•

Incentive Performance• Battery throughput incentive

On-peak

Mid-peak

Facility DemandTariff• Time of Use Energy andDemand Charges

Mid-peakOn-peak Demand•

Off-peakMid-peak Demand

Economic Constraints

Psuedo-Code Example
v.site_peak[bill_id, rate_index] >= v.post_site_load[site_id, t]# for t in {bill_period, rate_indices}v.demand_charge[bill_id, rate_index] == v.site_peak[bill_id, rate_index] * demand_rate[bill_id, rate_index]
v.energy_charge[bill_id, rate_index] == v.site_energy.sum(bill_id, t_active) * energy_rate[bill_id, rate_index]# where t_active in {bill_period, rate_indices}v.total_post_bill == v.demand_charge.sum() + v.energy_charge.sum()
Copyright 2018, Gurobi Optimization, Inc.

19

Gurobi Features• Python API• Variable class• Constraint class• Linear expression with SOS2 constraints• Compute server, intelligent queuing and prioritization

Apologies for the Python-centric examples to follow
Gets a little technical so I am assuming some Python and Gurobi experience
Copyright 2018, Gurobi Optimization, Inc.

20

Prerequisites (part 1)
Setting up the environment and modelfrom gurobipy import Env, GRB, Model, tupledict, LinExpr
def get_gurobi_env(timeout=600, job_priority=0):
return Env.ClientEnv(computeServer=COMPUTESERVER,
password=PASSWORD,
priority=job_priority,
timeout=timeout)
def optimize_portfolio(api_client, portfolio):m = Model(‘Portfolio’, env=get_gurobi_env(timeout, job_priority))v = initialize_variables(portfolio, m)
set_constraints(portfolio, m, v)
set_objective(m, v)
solve_model(portfolio, m)
persist_results(api_client, portfolio, v)

Orchestrating function which runs
through other functions to build andsolve model
Only inputs are portfolio object andAPI client for persisting data

* ClientEnv parameters changed slightly in >8.0, showing 8.0 parameters

Copyright 2018, Gurobi Optimization, Inc.

21

Prerequisites (part 2)
Helper objects and functionsclass Portfolio(object):def __init__(self)
self.sites={} # {site_id: SiteObject}Contains all the information that you
need to build the optimization model.

def add_sites(api_client, site_id)# adds sites to portfolio from APIclass Site(object):def __init__(self,max_c_p,max_d_p)
self.max_charge_power=max_c_p
self.max_discharge_power=max_d_p
self.interval_data=pd.DataFrame()

Contains all the information
associated with a site to help organize
information

def set_interval_data(api_client, start, end)

# pulls in interval data for siteCopyright 2018, Gurobi Optimization, Inc.

22

Variable Class
Define variables as tupledicts in a variable class
Allows for quick retrieval of resultant data and improves code readability
class Variables(object):def __init__(self):
self.battery_charge_power = tupledict()
self.battery_discharge_power = tupledict()

def initialize_variables(portfolio, m):v = Variables()for site_id, site in portfolio.sites.items():for interval in site.interval_data.interval_index:
v.battery_charge_power[site_id, interval] = m.addVar(lb=0, ub=site.max_charge_power)
return v

def persist_results(api_client, portfolio, v):--for loops (sites)-site.interval_data[‘battery_charge’] = [v.battery_charge_power[site_id, interval].x for interval insite.interval_data.interval_index]

Copyright 2018, Gurobi Optimization, Inc.

23

Constraint Class
Helpful when you want to change a particular constraint but don’t want to rebuild theentire model
class Constraints(object):def __init__(self):# battery no charge constraint
self.no_charge_period = tupledict()def calc_no_charge_sensitivites(portfolio, m, v)
c = Constraints()--for loops (sensitivities, sites, intervals in no charge period)-c.no_charge_period[site_id, interval] = m.addConstr(v.battery_charge_power[site_id, interval] == 0)
solve_model(portfolio, m)
persist_sensitivity_data(portfolio, v)
m.remove(c.no_charge_period[site_id, interval])
c.no_charge_period.clean()# remove constraints so they can be recreated in loop

# clean the tupledict to remove any previous objects

Copyright 2018, Gurobi Optimization, Inc.

24

SOS2 with Linear Expression
Allows for quickly and concisely creating piecewise linear constraints--for loops (events)--

revenue_bins

mu = [v.mu[i] for i in range(len(revenue_bins))]# create linear expressions to model revenue as a function of total
output during an eventm.addConstr(v.total_output[event_id] v.post_site_load.sum(‘*’, intervals_in_event)) == 0)

revenuem.addConstr(v.mu.sum('*') == 1)
m.addSOS(GRB.SOS_TYPE2, mu)

total_output

m.addConstr(v.total_output[event_id] LinExpr((revenue_bins[‘total_output']).tolist(), mu) == 0)
m.addConstr(v.event_revenue[event_id] LinExpr(revenue_meter_bins['revenue'].tolist(), mu) == 0)

Copyright 2018, Gurobi Optimization, Inc.

25

Context Wrapper
Allows you to store pre and post optimization data so you can diagnose problems
with the exact data that went into and came out of optimization@portfolio_context
def optimize_portfolio(api_client, portfolio):m = Model('Portfolio', env=get_gurobi_env(job_priority=job_priority))v = initialize_variables(portfolio, m)
set_constraints(portfolio, m, v)
set_objective(m, v)
solve_model(portfolio, m)
persist_results(api_client, portfolio, v)

Orchestrating function

def portfolio_context(f):@wraps(f)def wrapper(armada_api, portfolio, **kwargs):
create_optimization(armada_api, portfolio) # creates record in optimization table
send_context(portfolio, 'pre’) # pickles and sends the pre optimization portfolio object to s3
try:
return f(armada_api, portfolio, **kwargs)
finally:
send_context(portfolio, 'post’) # pickles and sends the post opt portfolio object to s3
return wrapper* wraps is imported from functools and is used to keep arguments and name of the original function when wrapped

Copyright 2018, Gurobi Optimization, Inc.

26

System Architecture• Use Docker to containerize all applications• API, Optimization jobs, ETL jobs, etc.

• Currently, using Elastic Beanstalk but moving overto Kubernetes orchestration

WebWeb• Maintain a dedicated EC2 instance on AWS hosting
Gurobi Server• Jobs are created on auto-scaling workers

Load Balancer
WorkerWorkerWorker• Prioritize jobs based on creator• Production• Pre-sales• DevelopmentJobs are prioritizedand queued• Currently, have to manual increase EC2 size.

GurobiServer• Intending to move to an autoscaling architecture

Copyright 2018, Gurobi Optimization, Inc.

27

Problem Statistics• Variables•

Largest source of variables are indexed by (site, [15 minute] interval)• charging/discharging, state of energy, grid usage (importing/exporting)• 17 variables total (per site, interval) * 50 sites * 700 intervals per week
yields ~600k variables• Constraints•

Large source of constraints generated for bill calculation• Optimization periods include 1-2 bills per site
• Each bill dependent upon tariffs comprised of 10-20 rates•Additional piecewise non-convex revenue functions, while fewer, necessitate binary variables

Copyright 2018, Gurobi Optimization, Inc.

28

Solution
Current
Battery
SOC

Historical
Data

Load
Forecast

Weather
Forecast

TariffInformation

Optimize
Schedule

MarketPrice
Forecast

Send Schedule
to Batteries

Utility
Dispatch
Likelihood

Repeat every 15 minutes

Copyright 2018, Gurobi Optimization, Inc.

29

Solution in Action
Demand Management

Utility Dispatches

Copyright 2018, Gurobi Optimization, Inc.

30

Solution Action
4hr dispatch on one of the 5MW contracts, 11 systems used to meet demand

Copyright 2018, Gurobi Optimization, Inc.

31

Questions
Please use the Question box on your GoToWebinar panel
to submit questions

Next Steps• If you haven’t already done so, please register for an account at www.gurobi.com.
• For questions about Gurobi pricing contact sales@gurobi.com or sales@gurobi.de.
• A recording of this webinar, including the slides, will be available in one week.
• Join us for future live webinar sessions! Check www.gurobi.com/events.