Skip to content
This repository has been archived by the owner on Sep 27, 2023. It is now read-only.

Adding XLSX converter for SPIDA / pole analysis #1331

Draft
wants to merge 26 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d9cd85b
Adding XLSX converter for SPIDA / pole analysis
aivanova5 Sep 13, 2023
f9cb6d3
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 13, 2023
adabad9
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 13, 2023
022cd1d
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 13, 2023
cca4499
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 13, 2023
8307129
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 13, 2023
f82aa65
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 13, 2023
298bd22
Update xlsx-spida2csv-geodata.py
aivanova5 Sep 18, 2023
97fb9bc
Merge branch 'develop' into develop-add-xlsx-converter
dchassin Sep 22, 2023
2bd1ef3
Update version.h
Jun 29, 2023
ab162fc
Add support for accessing ISO New England hourly market data (#1307)
dchassin Jul 3, 2023
3406e94
Add wheel install (#1310)
dchassin Jul 9, 2023
4c5344b
Add shell subcommand (#1312)
dchassin Jul 10, 2023
ae7f891
Update Geodata.md (#1314)
aivanova5 Jul 14, 2023
05b2a02
Fix create_meters.py implementation (#1321)
aivanova5 Jul 28, 2023
4719de9
Update README.md (#1315)
dchassin Jul 31, 2023
b6c8e43
Add group tool (#1319)
dchassin Jul 31, 2023
256a551
Updated pole.cpp (#1317)
AllisonBrand Sep 19, 2023
bbbbcb4
Update test_pole.csv (#1337)
dchassin Sep 22, 2023
c666f9d
Add headers and source to build target (#1336)
dchassin Sep 22, 2023
d00b29b
Add create_childs tool (#1335)
dchassin Sep 22, 2023
33a4de9
Merge branch 'develop' into develop-add-xlsx-converter
dchassin Sep 22, 2023
fde683e
Format update to converters for help command
aivanova5 Sep 22, 2023
67750e0
Merge branch 'develop-add-xlsx-converter' of https://github.com/aivan…
aivanova5 Sep 22, 2023
88af99a
Merge branch 'arras-energy:develop' into develop
aivanova5 Sep 22, 2023
d1a93b0
Merge branch 'develop' into develop-add-xlsx-converter
aivanova5 Sep 22, 2023
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
Prev Previous commit
Next Next commit
Add support for accessing ISO New England hourly market data (#1307)
Signed-off-by: David P. Chassin <[email protected]>
Signed-off-by: David P. Chassin <[email protected]>
  • Loading branch information
dchassin committed Sep 22, 2023
commit ab162fce17d7160e7ae031e9370cfcedf6612478
18 changes: 6 additions & 12 deletions cloud/websites/docs.gridlabd.us/index.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
<!DOCTYPE HTML />
<HTML>
<SCRIPT LANGUAGE="Javascript" SRC="_defaults.js"></SCRIPT>
<SCRIPT LANGUAGE="JavaScript">
document.writeln('<FRAMESET ROWS="' + (top_panel_height+20) + ',*" BORDER=2>');
console.info(window.location.search)
document.writeln('<FRAME SRC="_topbar.html" NORESIZE="noresize"></FRAME>');
document.writeln('<FRAMESET COLS="320,*" BORDER=3>');
document.writeln('<FRAME NAME="contents" SRC="_contents.html' + window.location.search + '"></FRAME>');
document.writeln('<FRAME NAME="page" SRC="_page.html' + window.location.search + '"></FRAME>');
document.writeln('</FRAMESET>');
document.writeln('</FRAMESET>');
</SCRIPT>

<HEAD>
<META HTTP-EQUIV="refresh" CONTENT="0,url=http://docs.gridlabd.us/index.html?owner=arras-energy&project=gridlabd" />
</HEAD>
<BODY>
Redirecting to <A HREF="http://docs.gridlabd.us/index.html?owner=arras-energy&project=gridlabd">http://docs.arras-energy.org/index.html?owner=arras-energy&project=gridlabd</A>...
</BODY>
</HTML>
1 change: 1 addition & 0 deletions tools/Makefile.mk
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dist_pkgdata_DATA += tools/gridlabd-editor.png
dist_pkgdata_DATA += tools/gridlabd-editor.py
dist_pkgdata_DATA += tools/insights.py
dist_pkgdata_DATA += tools/install.py
dist_pkgdata_DATA += tools/isone.py
dist_pkgdata_DATA += tools/market_data.py
dist_pkgdata_DATA += tools/mdb_info.py
dist_pkgdata_DATA += tools/market_model.py
Expand Down
2 changes: 2 additions & 0 deletions tools/autotest/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test_isone.csv
test_isone_opt.glm
21 changes: 21 additions & 0 deletions tools/autotest/test_isone.glm
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#python -m isone -d=smd/ME -s=2020-03-01 -e=2020-03-01 -c=test_isone.csv -g=test_isone_opt.glm
module tape
{
csv_header_type NAME;
}
#include "test_isone_opt.glm"
object recorder
{
parent isone;
file "test_isone_record.csv";
property "DA_Demand,RT_Demand,DA_LMP,DA_EC,DA_CC,DA_MLC,RT_LMP,RT_EC,RT_CC,RT_MLC,Dry_Bulb,Dew_Point";
interval 1h;
}
clock {
timezone ${ISONE_TIMEZONE};
starttime ${ISONE_STARTDATE};
stoptime ${ISONE_STOPDATE};
}
#ifexists "../test_isone_record.csv"
#on_exit 0 diff ../test_isone_record.csv test_isone_record.csv > gridlabd.diff
#endif
25 changes: 25 additions & 0 deletions tools/autotest/test_isone_record.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
timestamp,DA_Demand,RT_Demand,DA_LMP,DA_EC,DA_CC,DA_MLC,RT_LMP,RT_EC,RT_CC,RT_MLC,Dry_Bulb,Dew_Point
2020-03-01 00:00:00 EST,+847,+1177.36,+20.28,+20.34,-0.17,+0.11,+16.21,+16.07,+0,+0.14,+19,+5
2020-03-01 01:00:00 EST,+840.8,+1152.96,+19.16,+19.22,-0.15,+0.09,+16.33,+16.2,+0,+0.13,+18,+6
2020-03-01 02:00:00 EST,+855.1,+1138.74,+17.52,+17.62,-0.13,+0.03,+15.64,+15.56,+0,+0.08,+18,+5
2020-03-01 03:00:00 EST,+838.6,+1144.65,+17.73,+17.8,-0.14,+0.07,+15.21,+15.14,+0,+0.07,+18,+5
2020-03-01 04:00:00 EST,+852.2,+1167.67,+18.3,+18.38,-0.13,+0.05,+15.26,+15.19,+0,+0.07,+17,+5
2020-03-01 05:00:00 EST,+914.7,+1209.67,+19.12,+19.16,-0.13,+0.09,+13.21,+13.15,+0,+0.06,+17,+5
2020-03-01 06:00:00 EST,+912.1,+1277.46,+18.72,+18.72,-0.07,+0.07,+16.09,+15.94,+0,+0.15,+17,+5
2020-03-01 07:00:00 EST,+1017.2,+1337.71,+18.34,+18.11,-0.04,+0.27,+14.3,+14.1,+0,+0.2,+19,+6
2020-03-01 08:00:00 EST,+1122,+1401.55,+17.95,+17.59,+0,+0.36,+13.48,+13.28,+0,+0.2,+23,+7
2020-03-01 09:00:00 EST,+1027,+1411.46,+16.49,+16.31,-0.02,+0.2,+13.94,+13.74,+0,+0.2,+27,+8
2020-03-01 10:00:00 EST,+999.9,+1438.49,+14.8,+14.83,+0,-0.03,+15.96,+15.78,+0,+0.18,+28,+6
2020-03-01 11:00:00 EST,+956.6,+1425.79,+13.67,+13.79,-0.02,-0.1,+11.46,+11.35,+0,+0.11,+30,+7
2020-03-01 12:00:00 EST,+902.7,+1398.41,+12.82,+13.08,-0.13,-0.13,+12.5,+12.41,+0,+0.09,+32,+8
2020-03-01 13:00:00 EST,+896.3,+1370.73,+12.75,+13.02,-0.12,-0.15,+11.81,+11.75,+0,+0.06,+32,+7
2020-03-01 14:00:00 EST,+883.2,+1348.15,+12.66,+12.97,-0.12,-0.19,+12.61,+12.56,+0,+0.05,+34,+7
2020-03-01 15:00:00 EST,+1013.1,+1383.17,+13.49,+13.68,-0.12,-0.07,+14.08,+13.96,+0,+0.12,+35,+8
2020-03-01 16:00:00 EST,+1117.8,+1460.34,+16.51,+16.48,-0.11,+0.14,+21.35,+21.15,+0,+0.2,+34,+7
2020-03-01 17:00:00 EST,+1206.7,+1531.06,+19.58,+19.61,-0.13,+0.1,+21.93,+21.74,+0,+0.19,+32,+7
2020-03-01 18:00:00 EST,+1239,+1563.54,+25.89,+25.92,-0.18,+0.15,+30.69,+30.34,+0,+0.35,+29,+8
2020-03-01 19:00:00 EST,+1160,+1516.67,+20.82,+20.95,-0.18,+0.05,+24.05,+23.85,+0,+0.2,+27,+8
2020-03-01 20:00:00 EST,+1120.6,+1467.12,+17.55,+17.75,-0.13,-0.07,+21.78,+21.63,+0,+0.15,+26,+8
2020-03-01 21:00:00 EST,+984.8,+1361.46,+16.71,+16.87,-0.14,-0.02,+16.71,+16.57,+0,+0.14,+25,+8
2020-03-01 22:00:00 EST,+942.1,+1264.5,+16.54,+16.69,-0.16,+0.01,+18.14,+17.99,+0,+0.15,+24,+8
2020-03-01 23:00:00 EST,+872.2,+1218.99,+16.79,+16.94,-0.14,-0.01,+25.65,+25.27,+0,+0.38,+22,+8
288 changes: 288 additions & 0 deletions tools/isone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
"""ISO New England data access module

Syntax: gridlabd isone OPTIONS ...

Options:
-d|--dataset=DATASET/SUBSET Download data from the specified dataset
(and subset, default is `smd/ISO NE CA`)
-c|--csv=FILENAME Save CSV data to the specified file
(default is /dev/stdout)
-C|--CLASS=CLASSNAME Specify the GLM class name (default is
`isone`)
-e|--end=YEAR|STOPDATE Download data ending on the specified year
or date (last month default)
-f|--freshen Freshen the data cache
-g|--glm=FILENAME Save GLM data to the specified file
(default is /dev/null)
-l|--list Output a list of available sheets in dataset
-N|--NAME=OBJECTNAME Specify the GLM object name (default is
`isone`)
-s|--start=YEAR|STARTDATE Download data starting on the specified
year or date (last month by default)
-y|--year[=YEAR] Download data for the specified year
(current year by default)

Currently the only supported dataset is `smd`. The valid subsets depend on the
dataset and year. See https://www.iso-ne.com/isoexpress/web/reports/load-and-demand/-/tree/zone-info
for more information.
"""

import sys, os
import datetime
import pandas
import requests
import traceback

EXECNAME = os.path.splitext(os.path.basename(sys.argv[0]))[0]
DATASET = "smd"
CSVFILE = "/dev/stdout"
GLMFILE = None
CLASSNAME = "isone"
OBJNAME = "isone"
STARTDATE = None
STOPDATE = None
CACHEDIR = "/usr/local/opt/gridlabd/current/share/gridlabd/isone.d/" \
if not "GLD_ETC" in os.environ else os.path.join(os.environ['GLD_ETC'],"isone")
DATEFORMAT = "%Y-%m-%d"
FLOATFORMAT = "%.2f"
FRESHEN = False
VERBOSE = False
QUIET = False
DEBUG = False

E_OK = 0
E_SYNTAX = 1
E_INVALID = 2
E_FAILED = 3

def error(msg,code=None):
if not QUIET:
print(f"ERROR [{EXECNAME}]: {msg}",flush=True,file=sys.stderr)
if not code is None:
exit(code)

def verbose(msg):
if VERBOSE:
print(f"VERBOSE [{EXECNAME}]: {msg}",flush=True,file=sys.stderr)

def get_year(year=None,dataset=None):
"""Get data from a year
:param year: int - the starting date in DATEFORMAT
:return: pandas.DataFrame - the requested dataset
:return: list - list of available subsets (if dataset is None)
"""
if year == None:
year = datetime.datetime.now().year
verbose(f"setting to default year '{year}'")
assert(type(year)==int)
if dataset == None:
dataset = DATASET
verbose(f"setting to default dataset '{dataset}'")
specs = dataset.split("/")
cachefile = os.path.join(CACHEDIR,f"{specs[0]}_{year}.xlsx")
if FRESHEN or not os.path.exists(cachefile):
verbose(f"creating cache directory '{CACHEDIR}'")
os.makedirs(CACHEDIR,exist_ok=True)
try:
url = f"https://www.iso-ne.com/static-assets/documents/{year}/02/{year}_{specs[0]}_hourly.xlsx"
verbose(f"downloading data from '{url}'")
req = requests.get(url)
if req.status_code == 200:
verbose(f"saving data to '{cachefile}'")
with open(cachefile,"wb") as xls:
xls.write(req.content)
else:
error(f"unable to download data from '{url}' (HTTP code {req.status_code})",E_FAILED)
except Exception as err:
error(f"unable to get data from '{url}' ({err})",E_FAILED)
if len(specs) > 1:
verbose(f"loading '{specs[1]}' from '{cachefile}'")
return pandas.read_excel(cachefile,sheet_name=specs[1],index_col=[0,1],parse_dates=[0])
else:
verbose(f"loading data from '{cachefile}'")
return pandas.read_excel(cachefile,sheet_name=None)

def get_data(startdate=None,stopdate=None,dataset=None):
"""Get data from a date range
:param startdate: str - the starting date in DATEFORMAT
:param stopdate: str - the stopping date in DATEFORMAT
:param dataset: str - the dataset/subset specification
:return: pandas.DataFrame - the requested data
"""
if startdate == None:
startdate = datetime.datetime(year=datetime.datetime.now().year)
else:
startdate = datetime.datetime.strptime(startdate,DATEFORMAT)
if startdate == None:
stopdate = datetime.datetime.now()
else:
stopdate = datetime.datetime.strptime(stopdate,DATEFORMAT)
if startdate > stopdate:
error("startdate is after stopdate",E_INVALID)
data = []
for year in range(startdate.year,stopdate.year+1):
data.append(get_year(year,dataset))
data = pandas.concat(data)
maxdate = min(data.index.get_level_values(0).max(),stopdate)
return data.loc[pandas.date_range(startdate,maxdate)]

def fix_timestamps(df):
"""Fix timestamp in dataframe
:param data: pandas.DataFrame - data to fix
:return: pandas.DataFrame - fixed data
"""
df['timestamp'] = [x[0] + datetime.timedelta(hours = int(x[1])-1) for x in list(df.index)]
return df.set_index('timestamp')

if __name__ == "__main__":
if len(sys.argv) == 1:
for line in __doc__.split("\n"):
if line.startswith("Syntax: "):
print(line,file=sys.stderr)
exit(E_SYNTAX)
elif sys.argv[1] in ["-h","--help","help"]:
print(__doc__)
exit(E_OK)
for arg in sys.argv[1:]:
spec = arg.split("=")
if len(spec) == 1:
token = arg
value = None
elif len(spec) == 2:
token = spec[0]
value = spec[1]
else:
token = spec[0]
value = "=".join(spec[1:])

if token in ["-v","--verbose"]:
VERBOSE = True
elif token in ["-q","--quiet"]:
QUIET = True
elif token in ["--debug"]:
DEBUG = True

# -d|--dataset=DATASET/SUBSET Download data from the specified dataset
# (and subset, default is `smd/ISO NE CA`)
elif token in ["-d","--dataset"]:
if value:
DATASET = value
else:
error("dataset not specified",E_SYNTAX)

# -c|--csv=FILENAME Save CSV data to the specified file
# (default is /dev/stdout)
elif token in ["-c","--csv"]:
if value:
CSVFILE = value
else:
error("csvfile not specified",E_SYNTAX)

# -C|--CLASS=CLASSNAME Specify the GLM class name (default is
# `isone`)
elif token in ["-C","--CLASS"]:
if value:
CLASSNAME = value
else:
error("class name not specified",E_SYNTAX)

# -e|--end=YEAR|STOPDATE Download data ending on the specified year
# or date (last month default)
elif token in ["-e","--end"]:
if value:
try:
STOPDATE = int(value)
except:
STOPDATE = value

# -f|--freshen Freshen the data cache
elif token in ["-f","--freshen"]:
FRESHEN = True

# -g|--glm=FILENAME save GLM data to the specified file
# (default is /dev/null)
elif token in ["-g","--glm"]:
if value:
GLMFILE = value
else:
error("GLM name not specified",E_SYNTAX)

# -l|--list output a list of available datasets
elif token in ["-l","--list"]:
print("\n".join(get_year(value)),file=sys.stdout)
exit(E_OK)

# -N|--NAME=OBJECTNAME specify the GLM object name (default is
# `isone`)
elif token in ["-N","--NAME"]:
if value:
OBJNAME = value
else:
error("object name not specified",E_SYNTAX)

# -s|--start=YEAR|STARTDATE download data starting on the specified
# year or date (last month by default)
elif token in ["-s","--start"]:
if value:
try:
STARTDATE = str(int(value))
except:
STARTDATE = value

# -y|--year[=YEAR] download data for the specified year
# (current year by default)
elif token in ["-y","--year"]:
if value:
try:
STARTDATE = value + "-01-01"
STOPDATE = value + "-12-31"
except:
error(f"year '{value}' is invalid",E_INVALID)
else:
error("year not specified",E_SYNTAX)

# invalid argument
else:
error(f"argument '{arg}' is not recognized",E_SYNTAX)

try:
data = get_data(STARTDATE,STOPDATE)

if GLMFILE:
if CSVFILE.startswith("/dev/"):
error(f"CSV file '{CSVFILE}' must be a regular file",E_INVALID)

# classinfo = CLASSNAME.split(".")

properties = "\n ".join([f'double {x};' for x in data.columns])
with open(GLMFILE,"w") as glm:
glm.write(f"""// created by '{' '.join(sys.argv)}' at {datetime.datetime.now()}
class {CLASSNAME}
{{
{properties}
}}
module tape;
object {CLASSNAME}
{{
name {OBJNAME};
object player
{{
file "{CSVFILE}";
property "{','.join(data.columns)}";
}};
}}
#define ISONE_TIMEZONE=EST+5EDT
#define ISONE_STARTDATE={data.index.get_level_values(0).min()}
#define ISONE_STOPDATE={data.index.get_level_values(0).max()+datetime.timedelta(days=1)}
""")

fix_timestamps(data).to_csv(CSVFILE,float_format=FLOATFORMAT,header=(GLMFILE is None))

exit(E_OK)
except SystemExit as err:
exit(err.code)
except:
e_type,e_value,e_trace = sys.exc_info()
if DEBUG:
traceback.print_tb(e_trace)
error(f"{e_value} ({e_type.__name__} exception)",E_FAILED)