Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 2 additions & 259 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,263 +145,6 @@ wt.region_from_loc(
wt.get_maps_json('co2_moer')
```

# Using the Optimizer Class
`WattTime.Optimizer` produces a power consumption schedule that minimizes carbon emissions subject to user and device constraints.
# Optimizer Package

The `WattTime.Optimizer` class requires 4 things:

- Watttime’s forecast of marginal emissions
- device capacity and energy needs
- region
- window start
- window end


| optimization\_method | ASAP | Charging curve | Time constraint | Contiguous |
| :---- | :---- | :---- | :---- | :---- |
| baseline | Yes | Constant | No | No |
| simple | No | Constant | No | No |
| sophisticated | No | Variable | Yes | No |
| contiguous | No | Variable | Yes | Intervals at fixed lengths |
| Variable contiguous | No | Variable | Yes | Intervals at variable lengths |
| auto | No | Chooses the fastest algorithm that can still process all inputs | | |

Click any of the thumbnails below to see the notebook that generated it.

1. Naive Smart device charging: needs 30 minutes to reach full charge, expected plug out time within the next 4 hours. Simple use case.
2. Requery: update usage plan every 20 minutes using new forecast for the next 4 hours. Simple use case with recalculation
3. Partial charging guarantee: charge 75% by 8am. User constraint
4. Data center workloads: estimated runtime is 2 hours and it needs to complete by 12pm Contiguous (single period, fixed length)
5. Dishwasher: needs to run over two usage intervals of lengths 80 min and 40 min. They must complete in that order. Contiguous (multiple periods, fixed length)
6. Compressor: needs to run 120 minutes over the next 12 hours; each cycle needs to be at least 20 minutes long, and any number of contiguous intervals (from one to six) is okay. Contiguous (multiple periods, variable length)

**Naive Smart Device Charging**
* Simple - uses the MOER forecast at window start to find the set of intervals that minimize emissions, and outputs a charge schedule based on that.

```py
from datetime import datetime, timedelta
import pandas as pd
from pytz import UTC
from watttime import WattTimeOptimizer
import os

username = os.getenv("WATTTIME_USER")
password = os.getenv("WATTTIME_PASSWORD")
wt_opt = WattTimeOptimizer(username, password)

# 12 hour charge window (720/60 = 12)
now = datetime.now(UTC)
window_start = now
window_end = now + timedelta(minutes=720)

usage_plan = wt_opt.get_optimal_usage_plan(
region="CAISO_NORTH",
usage_window_start=window_start,
usage_window_end=window_end,
usage_time_required_minutes=240,
usage_power_kw=12,
optimization_method="auto",
)

print(usage_plan.head())
print(usage_plan["usage"].tolist())
print(usage_plan.sum())
```

**Partial Charging Guarantee - Introducing Constraints**
* Sophisticated - total charge window 12 hours long, 75% charged by hour 8.

```py
from datetime import datetime, timedelta
import pandas as pd
from pytz import UTC
from watttime import WattTimeOptimizer
import os

username = os.getenv("WATTTIME_USER")
password = os.getenv("WATTTIME_PASSWORD")
wt_opt = WattTimeOptimizer(username, password)

# 12 hour charge window (720/60 = 12)
# Minute 480 is time context when the constraint, i.e. 75% charge, must be satisfied
# 75% of 240 (required charge expressed in minutes) is 180

now = datetime.now(UTC)
window_start = now
window_end = now + timedelta(minutes=720)
usage_time_required_minutes = 240
constraint_time = now + timedelta(minutes=480)
constraint_usage_time_required_minutes = 180
usage_power_kw = 12.0

# map the constraint to the time context
constraints = {constraint_time:constraint_usage_time_required_minutes}

usage_plan = wt_opt.get_optimal_usage_plan(
region="CAISO_NORTH",
usage_window_start=window_start,
usage_window_end=window_end,
usage_time_required_minutes=240,
usage_power_kw=12,
constraints=constraints,
optimization_method="auto",
)

print(usage_plan.head())
print(usage_plan["usage"].tolist())
print(usage_plan.sum())
```

**Variable Charging Curve - EV**
* Sophisticated - total charge window 12 hours long, 75% charged by hour 8.

```py
from datetime import datetime, timedelta
import pandas as pd
from pytz import UTC
from watttime import WattTimeOptimizer
import os

username = os.getenv("WATTTIME_USER")
password = os.getenv("WATTTIME_PASSWORD")
wt_opt = WattTimeOptimizer(username, password)

# 12 hour charge window (720/60 = 12)
# Minute 480 is time context when the constraint, i.e. 75% charge, must be satisfied
# 75% of 240 (required charge expressed in minutes) is 180

now = datetime.now(UTC)
window_start = now
window_end = now + timedelta(minutes=720)
variable_usage_power = ''

usage_plan = wt_opt.get_optimal_usage_plan(
region="CAISO_NORTH",
usage_window_start=window_start,
usage_window_end=window_end,
usage_time_required_minutes=240,
usage_power_kw=variable_usage_power,
constraints=constraints,
optimization_method="auto",
)

print(usage_plan.head())
print(usage_plan["usage"].tolist())
print(usage_plan.sum())
```


* **Data Center Workload**:
* Fixed Contiguous (single period, fixed length) - charging schedule to be composed of contiguous interval(s) of fixed length

```py
## AI model training - estimated runtime is 2 hours and it needs to complete within 12 hours

from datetime import datetime, timedelta
import pandas as pd
from pytz import UTC
from watttime.api import WattTimeOptimizer
import os

username = os.getenv("WATTTIME_USER")
password = os.getenv("WATTTIME_PASSWORD")
wt_opt = WattTimeOptimizer(username, password)

now = datetime.now(UTC)
window_start = now
window_end = now + timedelta(minutes=720)

usage_power_kw = 12.0
region = "CAISO_NORTH"

# by passing a single interval of 120 minutes to charge_per_interval, the Optimizer will know to fit call the fixed contigous modeling function.
usage_plan = wt_opt.get_optimal_usage_plan(
region=region,
usage_window_start=window_start,
usage_window_end=window_end,
usage_time_required_minutes=120,
usage_power_kw=12,
charge_per_interval=[120],
optimization_method="auto",
verbose = False
)

print(usage_plan.head())
print(usage_plan["usage"].tolist())
print(usage_plan.sum())
```

**Dishwasher**:
* Fixed Contiguous (multiple periods, fixed length) - runs over two usage intervals of lengths 80 min and 40 min. The order of the intervals is immutable.

```py
## Dishwasher - there are two cycles of length 80 min and 40 min each, and they must be completed in that order.

from datetime import datetime, timedelta
import pandas as pd
from pytz import UTC
from watttime import WattTimeOptimizer
import os

username = os.getenv("WATTTIME_USER")
password = os.getenv("WATTTIME_PASSWORD")
wt_opt = WattTimeOptimizer(username, password)

# Suppose that the time now is 12 midnight
now = datetime.now(UTC)
window_start = now
window_end = now + timedelta(minutes=720)

# Pass two values to charge_per_interval instead of one.
usage_plan = wt_opt.get_optimal_usage_plan(
region="CAISO_NORTH",
usage_window_start=window_start,
usage_window_end=window_end,
usage_time_required_minutes=120, # 80 + 40
usage_power_kw=12,
charge_per_interval=[80,40],
optimization_method="auto",
)

print(usage_plan.head())
print(usage_plan["usage"].tolist())
print(usage_plan.sum())
```

**Compressor**:
* Contiguous (multiple periods, variable length) - runs 120 minutes over the next 12 hours; each cycle needs to be at least 20 minutes long, and any number of intervals (from one to six) is okay.

```py
## Compressor - needs to run 120 minutes over the next 12 hours; each cycle needs to be at least 20 minutes long, and any number of contiguous intervals (from one to six) is okay.

from datetime import datetime, timedelta
import pandas as pd
from pytz import UTC
from watttime import WattTimeOptimizer
import os

username = os.getenv("WATTTIME_USER")
password = os.getenv("WATTTIME_PASSWORD")
wt_opt = WattTimeOptimizer(username, password)

# Suppose that the time now is 12 midnight
now = datetime.now(UTC)
window_start = now
window_end = now + timedelta(minutes=720)

usage_plan = wt_opt.get_optimal_usage_plan(
region="CAISO_NORTH",
usage_window_start=window_start,
usage_window_end=window_end,
usage_time_required_minutes=120,
usage_power_kw=12,
# Here _None_ implies that there is no upper bound, and replacing None by 120 would have the exact same effect.
charge_per_interval=[(20,None),(20,None),(20,None),(20,None),(20,None),(20,None)],
optimization_method="auto",
use_all_intervals=False
)

print(usage_plan.head())
print(usage_plan["usage"].tolist())
print(usage_plan.sum())
```
Insert link to Optimizer README.md and add a brief description of functionality
Loading