Skip to content

Commit

Permalink
Merge pull request #100 from GreenScheduler/fix-off-by-one
Browse files Browse the repository at this point in the history
fix off by one
  • Loading branch information
abhidg authored Jun 9, 2024
2 parents ad68095 + 92d7608 commit 8853312
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 169 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
cache: 'pip'
- name: Install dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pip
python3 -m pip install '.[test]'
- name: Lint with flake8
run: |
Expand Down
2 changes: 1 addition & 1 deletion cats/CI_api_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ def invalid_code(r: dict) -> bool:
"carbonintensity.org.uk": APIInterface(
get_request_url=ciuk_request_url,
parse_response_data=ciuk_parse_response_data,
max_duration=2880, # 48h
max_duration=2850, # 48h - 30min so that carbon intensity is defined over the last interval
),
}
8 changes: 4 additions & 4 deletions cats/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import logging
import sys
from collections.abc import Mapping
from typing import Optional, Any
from typing import Any, Optional

import requests
import yaml
Expand All @@ -24,9 +24,9 @@
__all__ = ["get_runtime_config"]


def get_runtime_config(args) -> tuple[APIInterface, str, int,
Optional[list[tuple[int, float]]],
Optional[float]]:
def get_runtime_config(
args,
) -> tuple[APIInterface, str, int, Optional[list[tuple[int, float]]], Optional[float]]:
"""Return the runtime cats configuration from list of command line
arguments and content of configuration file.
Expand Down
35 changes: 22 additions & 13 deletions cats/forecast.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class CarbonIntensityPointEstimate:
value: float # the first attribute is used automatically for sorting methods
datetime: datetime

def __repr__(self):
return f"{self.datetime.isoformat()}\t{self.value}"


@dataclass(order=True)
class CarbonIntensityAverageEstimate:
Expand Down Expand Up @@ -62,10 +65,10 @@ def bisect_right(data, t):
#
def bisect_left(data, t):
for i, d in enumerate(data):
if d.datetime >= t:
return i
if d.datetime + self.data_stepsize >= t:
return i + 1

self.ndata = bisect_left(self.data, self.end) + 1
self.ndata = bisect_left(self.data, self.end) # window size

def __getitem__(self, index: int) -> CarbonIntensityAverageEstimate:
"""Return the average of timeseries data from index over the
Expand All @@ -84,24 +87,30 @@ def __getitem__(self, index: int) -> CarbonIntensityAverageEstimate:
# intensity is interpolated between the first (index) and
# second data point (index + 1) in the window. The ending
# intensity value is interpolated between the last and
# penultimate data points in he window.
# penultimate data points in the window.
window_start = self.start + index * self.data_stepsize
window_end = self.end + index * self.data_stepsize

# lbound: carbon intensity point estimate at window start
lbound = self.interp(
self.data[index],
self.data[index + 1],
when=window_start,
)
rbound = self.interp(
self.data[index + self.ndata - 2],
self.data[index + self.ndata - 1],
when=window_end,
)
# rbound: carbon intensity point estimate at window end
# Handle case when last data point exactly matches last carbon intensity,
# so there is no further data point to interpolate from.
if index + self.ndata == len(self.data):
rbound = self.data[-1]
else:
rbound = self.interp(
self.data[index + self.ndata - 1],
self.data[index + self.ndata],
when=window_end,
)
# window_data <- [lbound] + [...bulk...] + [rbound] where
# lbound and rbound are interpolated intensity values.
window_data = (
[lbound] + self.data[index + 1 : index + self.ndata - 1] + [rbound]
)
window_data = [lbound] + self.data[index + 1 : index + self.ndata] + [rbound]
acc = [
0.5 * (a.value + b.value) * (b.datetime - a.datetime).total_seconds()
for a, b in zip(window_data[:-1], window_data[1:])
Expand Down Expand Up @@ -138,4 +147,4 @@ def __iter__(self):
yield self.__getitem__(index)

def __len__(self):
return len(self.data) - self.ndata
return len(self.data) - self.ndata + 1
Loading

0 comments on commit 8853312

Please sign in to comment.