diff --git a/.gitmodules b/.gitmodules index e69de29bb..d892d12f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "EVI-EnLitePy"] + path = EVI-EnLitePy + url = https://github.nrel.gov/AVCI/EVI-EnLitePy.git + branch = server_version diff --git a/Dockerfile b/Dockerfile index f013a9acd..6c61f95de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM reopt/py38 +FROM python:3.11 # Install NREL root certs for machines running on NREL's network. ARG NREL_ROOT_CERT_URL_ROOT="" @@ -9,12 +9,17 @@ ENV SRC_DIR=/opt/reopt/reo/src ENV LD_LIBRARY_PATH="/opt/reopt/reo/src:${LD_LIBRARY_PATH}" # Copy all code -ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONDONTWRITEBYTECODE=1 COPY . /opt/reopt # Install python packages WORKDIR /opt/reopt RUN ["pip", "install", "-r", "requirements.txt"] +# Install EVI-EnLitePy using pip (recommended best practice) +RUN if [ -d "/opt/reopt/EVI-EnLitePy" ] && [ "$(ls -A /opt/reopt/EVI-EnLitePy)" ]; then \ + cd /opt/reopt/EVI-EnLitePy && pip install -e .; \ +fi + EXPOSE 8000 ENTRYPOINT ["/bin/bash", "-c"] diff --git a/EVI-EnLitePy b/EVI-EnLitePy new file mode 160000 index 000000000..0f37872b0 --- /dev/null +++ b/EVI-EnLitePy @@ -0,0 +1 @@ +Subproject commit 0f37872b01827d8e0954c6d725e8dbcc04dcc354 diff --git a/docker-compose.yml b/docker-compose.yml index 80cdda5b5..859635a1a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: build: context: . dockerfile: Dockerfile - image: base-api-image + image: new-base-api-image command: > "celery -A reopt_api worker -l info" environment: @@ -39,7 +39,7 @@ services: build: context: . dockerfile: Dockerfile - image: base-api-image + image: new-base-api-image command: > "python manage.py migrate && /opt/reopt/bin/wait-for-it.bash -t 0 julia:8081 -- python manage.py runserver 0.0.0.0:8000" diff --git a/load_builder/ensite.json b/load_builder/ensite.json new file mode 100644 index 000000000..8a899692e --- /dev/null +++ b/load_builder/ensite.json @@ -0,0 +1,603 @@ +{ + "simConfig": { + "tStart": 0, + "tEnd": 604800, + "dowStart": 0 + }, + "hubConfig": { + "nodes": { + "grid": { + "type": "Grid", + "pMax": 300000.0, + "pMin": 0, + "eff": 0.99 + }, + "evse1": { + "type": "EVSE", + "pMax": 300000.0, + "pMin": 0, + "eff": 0.98, + "nEVPort": 2, + "pci": [ + "LD", + "LD" + ] + }, + "evse2": { + "type": "EVSE", + "pMax": 300000.0, + "pMin": 0, + "eff": 0.98, + "nEVPort": 2, + "pci": "LD" + }, + "evse3": { + "type": "EVSE", + "pMax": 300000.0, + "pMin": 0, + "eff": 0.98, + "nEVPort": 2, + "pci": "LD" + }, + "evse4": { + "type": "EVSE", + "pMax": 300000.0, + "pMin": 0, + "eff": 0.98, + "nEVPort": 2, + "pci": "MD/HD" + }, + "evse5": { + "type": "EVSE", + "pMax": 300000.0, + "pMin": 0, + "eff": 0.98, + "nEVPort": 2, + "pci": "MD/HD" + }, + "building_load": { + "type": "CPL", + "time": [ + 0, + 900, + 1800, + 2700, + 3600, + 4500, + 5400, + 6300, + 7200, + 8100, + 9000, + 9900, + 10800, + 11700, + 12600, + 13500, + 14400, + 15300, + 16200, + 17100, + 18000, + 18900, + 19800, + 20700, + 21600, + 22500, + 23400, + 24300, + 25200, + 26100, + 27000, + 27900, + 28800, + 29700, + 30600, + 31500, + 32400, + 33300, + 34200, + 35100, + 36000, + 36900, + 37800, + 38700, + 39600, + 40500, + 41400, + 42300, + 43200, + 44100, + 45000, + 45900, + 46800, + 47700, + 48600, + 49500, + 50400, + 51300, + 52200, + 53100, + 54000, + 54900, + 55800, + 56700, + 57600, + 58500, + 59400, + 60300, + 61200, + 62100, + 63000, + 63900, + 64800, + 65700, + 66600, + 67500, + 68400, + 69300, + 70200, + 71100, + 72000, + 72900, + 73800, + 74700, + 75600, + 76500, + 77400, + 78300, + 79200, + 80100, + 81000, + 81900, + 82800, + 83700, + 84600, + 85500, + 86400, + 87300, + 88200, + 89100, + 90000, + 90900, + 91800, + 92700, + 93600, + 94500, + 95400, + 96300, + 97200, + 98100, + 99000, + 99900, + 100800, + 101700, + 102600, + 103500, + 104400, + 105300, + 106200, + 107100, + 108000, + 108900, + 109800, + 110700, + 111600, + 112500, + 113400, + 114300, + 115200, + 116100, + 117000, + 117900, + 118800, + 119700, + 120600, + 121500, + 122400, + 123300, + 124200, + 125100, + 126000, + 126900, + 127800, + 128700, + 129600, + 130500, + 131400, + 132300, + 133200, + 134100, + 135000, + 135900, + 136800, + 137700, + 138600, + 139500, + 140400, + 141300, + 142200, + 143100, + 144000, + 144900, + 145800, + 146700, + 147600, + 148500, + 149400, + 150300, + 151200, + 152100, + 153000, + 153900, + 154800, + 155700, + 156600, + 157500, + 158400, + 159300, + 160200, + 161100, + 162000, + 162900, + 163800, + 164700, + 165600, + 166500, + 167400, + 168300, + 169200, + 170100, + 171000, + 171900, + 172800 + ], + "power": [ + 0, + 115768.90557614488, + 111052.02340350732, + 106243.96540927405, + 101248.76581765672, + 96570.04466587743, + 93198.17823814304, + 90826.32916441964, + 87023.57942474863, + 83436.24449431658, + 81655.80725341121, + 80351.78355011741, + 78391.01470690272, + 76524.89719584149, + 75849.64007460905, + 75171.95377484719, + 74762.94970004473, + 74785.98133219888, + 74974.87833325299, + 75034.88245524601, + 75053.78539510825, + 75462.00915896866, + 76211.98052987862, + 77151.15330971242, + 78713.83055741635, + 80697.72125438195, + 83229.71074938202, + 86422.01973010179, + 91701.66953882159, + 96826.09974563181, + 100536.59047897511, + 98627.50125113038, + 101026.77928593893, + 113397.30768915478, + 120789.37878468739, + 125086.23067937214, + 138968.87451142137, + 149728.1466181706, + 154689.7620241398, + 162414.35875756864, + 172040.3208539825, + 181616.36019767198, + 188361.01006918508, + 193894.3227290636, + 199348.66172264077, + 204691.88380812074, + 211477.3931988581, + 219553.8144975878, + 227734.307890666, + 235623.86863660946, + 241252.81855464025, + 245748.68819021765, + 250113.82821523494, + 253684.50561679932, + 256906.64040314284, + 259566.53136180423, + 262138.29058731813, + 265587.8329082571, + 268022.07549065276, + 268831.3090258474, + 268046.3556785552, + 266687.2767598618, + 265407.7785675803, + 263901.6050685742, + 262238.631827651, + 259864.17945746586, + 259258.14426325096, + 260351.58518913263, + 260781.2744725022, + 260556.6113717029, + 260612.13738030978, + 260900.1793592792, + 260937.2231589168, + 260812.33700178415, + 258894.05801698912, + 256387.57735312398, + 252556.31076981314, + 247663.10940524103, + 244336.46921034344, + 241006.16187443602, + 229296.17149419116, + 218036.09911564825, + 213202.7845893712, + 207379.7079880955, + 201109.48916701777, + 195030.73111149587, + 188231.49321122604, + 181211.80601803545, + 174912.1254591292, + 168300.37541429105, + 161171.66767080297, + 159829.78547531943, + 155668.44684056749, + 147021.56004675277, + 140195.87205118494, + 132990.5537835949, + 125932.22211296459, + 119607.3938765824, + 114224.16119668349, + 108861.86410024502, + 103633.475821886, + 98855.20786692914, + 95319.3349150407, + 92669.2333608828, + 88636.37784744859, + 84625.87644647704, + 81987.95286556207, + 80257.94930263511, + 78025.3995160709, + 76333.90547973095, + 76404.27574800745, + 76149.30380447183, + 75283.44017603615, + 75060.94051033257, + 75373.68350340766, + 75378.24621716273, + 75511.34395530022, + 75733.84757209556, + 75959.57567428125, + 76754.41482547301, + 77732.24620558122, + 78601.31828930757, + 79863.87527191175, + 81645.1725729833, + 84839.87213999625, + 87497.74808954235, + 89847.10387480202, + 86621.91703097537, + 84866.33821827677, + 92744.99438738922, + 97866.32436802566, + 100442.49273944512, + 112207.93503517946, + 120191.37581769569, + 122510.3959391192, + 127966.47696577231, + 132511.31787877544, + 137214.93411374232, + 141265.0820730139, + 144706.10886189333, + 148362.4079961007, + 152096.62428724568, + 157752.47514897128, + 164580.07293775745, + 172594.16516825437, + 180988.5032815957, + 186457.95187991243, + 191574.73033201674, + 196665.25157405154, + 200116.9263952046, + 203445.69327931653, + 206353.23093329868, + 209344.86404155273, + 213477.5083782941, + 216364.45305298414, + 217472.23160771147, + 217784.5467271467, + 217966.88711359314, + 217654.3283523812, + 217435.56651799893, + 217033.88100754542, + 215240.0497811758, + 215956.1555711758, + 218573.78801257262, + 220305.6386701705, + 222489.12700566195, + 224959.58847458754, + 227719.01635537934, + 230726.10187793532, + 232996.42429362237, + 233730.3298875959, + 233519.48010681197, + 231517.3818674315, + 228752.41772804878, + 227481.0983919819, + 226052.31017154508, + 216331.94551423888, + 206790.52351717363, + 203604.6489288222, + 199183.91481633333, + 194448.6273390925, + 189280.3790565904, + 182981.8908821766, + 177064.2090301336, + 171202.26802169654, + 164947.3577182609, + 159196.5478214874, + 158350.694914731, + 156127.8238934354, + 149344.6406372949, + 142506.54723537544, + 135308.83708899393, + 128517.20039204955 + ] + }, + "BESS": { + "type": "ESS", + "eCap": 1000000.0, + "eff": 0.98, + "pMax": 300000.0, + "pMin": 0, + "socMin": 0.2, + "socMax": 0.95, + "socInit": 0.75 + } + }, + "ems": { + "type": "basic", + "pChgCap": 1800000.0, + "pap": "FCFS+SMX", + "xSM": 0.5 + } + }, + "evInfo": { + "stochasticModel": 1, + "chgDurMax": 7200, + "evTypes": { + "LD": { + "type": "LD", + "eCap": 60000.0, + "pChgMax": 300000.0, + "count": [ + 5, + 10, + 15, + 20, + 25, + 30, + 35 + ], + "targetFinalSOCPMF": { + "val": [ + 0.75, + 0.8, + 0.9 + ], + "prob": [ + 0.5, + 0.3, + 0.2 + ] + }, + "energyDemandPMF": { + "val": [ + 0.6, + 0.7, + 0.8 + ], + "prob": [ + 0.5, + 0.3, + 0.2 + ] + }, + "arrivalTimePMF": { + "0-6": { + "val": [ + 3600, + 14400, + 43200 + ], + "prob": [ + 0.5, + 0.3, + 0.2 + ], + "dur": 7200 + } + }, + "departureTimePMF": null + }, + "HD": { + "type": "HD", + "eCap": 200000.0, + "pChgMax": 600000.0, + "count": [ + 35, + 30, + 25, + 20, + 15, + 10, + 5 + ], + "targetFinalSOCPMF": { + "val": [ + 0.75, + 0.8, + 0.9 + ], + "prob": [ + 0.5, + 0.3, + 0.2 + ] + }, + "energyDemandPMF": { + "val": [ + 0.6, + 0.7, + 0.8 + ], + "prob": [ + 0.5, + 0.3, + 0.2 + ] + }, + "arrivalTimePMF": { + "1-5": { + "val": [ + 5400.0, + 16200.0, + 57600 + ], + "prob": [ + 0.5, + 0.3, + 0.2 + ], + "dur": 7200 + }, + "0,6": { + "val": [ + 0 + ], + "prob": [ + 1 + ], + "dur": 7200 + } + }, + "departureTimePMF": null + } + } + }, + "resultsConfig": { + "timeseriesDt": 3600, + "powerMetricsDt": 900, + "resultFieldOptions": { + "nodes": null, + "portUsage": true, + "queueLength": true, + "nodeStats": true, + "evStats": true + } + } +} \ No newline at end of file diff --git a/load_builder/urls.py b/load_builder/urls.py index f31cfc794..5e707b5e5 100644 --- a/load_builder/urls.py +++ b/load_builder/urls.py @@ -5,4 +5,5 @@ urlpatterns = [ re_path(r'^load_builder/?$', views.load_builder), + re_path(r'^ensite/?$', views.ensite_view), ] \ No newline at end of file diff --git a/load_builder/views.py b/load_builder/views.py index 58614936e..d537b0b01 100644 --- a/load_builder/views.py +++ b/load_builder/views.py @@ -6,6 +6,30 @@ import numpy as np from django.http import JsonResponse from reo.exceptions import UnexpectedError +import os + +def ensite_view(request): + """ + Attempts to import ensitepy and return the SystemSimulator class. + If the package is not available, returns None. + """ + try: + from ensitepy.simextension import enlitepyapi + + inputs_path = os.path.join(os.getcwd(), "load_builder", "ensite.json") + with open(inputs_path, 'r') as f: + enlitepy_json = json.load(f) + # print(enlitepy_json) + # import remote_pdb + # remote_pdb.set_trace(host='0.0.0.0', port=4444) + results = enlitepyapi.run(enlitepy_json) + + return JsonResponse({"message": "imported ensitepy and simextension successfully"}) + except Exception as e: + exc_type, exc_value, exc_traceback = sys.exc_info() + err = UnexpectedError(exc_type, exc_value, exc_traceback, task='ensite') + # err.save_to_db() + return JsonResponse({"Error": err.message}, status=500) def check_load_builder_inputs(loads_table): diff --git a/reo/migrations/0154_rename_messagemodel_run_uuid_reo_message_run_uui_a803e6_idx_and_more.py b/reo/migrations/0154_rename_messagemodel_run_uuid_reo_message_run_uui_a803e6_idx_and_more.py new file mode 100644 index 000000000..33e56df73 --- /dev/null +++ b/reo/migrations/0154_rename_messagemodel_run_uuid_reo_message_run_uui_a803e6_idx_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.5 on 2025-09-01 16:14 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('reo', '0153_merge_20230329_1652'), + ] + + operations = [ + migrations.RenameIndex( + model_name='messagemodel', + new_name='reo_message_run_uui_a803e6_idx', + old_fields=('run_uuid',), + ), + migrations.RenameIndex( + model_name='pvmodel', + new_name='reo_pvmodel_run_uui_5bdbcb_idx', + old_fields=('run_uuid',), + ), + ] diff --git a/reo/models.py b/reo/models.py index 6865c0233..940ef378e 100644 --- a/reo/models.py +++ b/reo/models.py @@ -489,8 +489,10 @@ def create(cls, **kwargs): class PVModel(models.Model): - class Meta(): - index_together = [['run_uuid']] + class Meta: + indexes = [ + models.Index(fields=["run_uuid"]), + ] #Inputs run_uuid = models.UUIDField(unique=False) @@ -1160,8 +1162,10 @@ class MessageModel(models.Model): } } """ - class Meta(): - index_together = [['run_uuid']] + class Meta: + indexes = [ + models.Index(fields=["run_uuid"]), + ] message_type = models.TextField(null=True, blank=True, default='') message = models.TextField(null=True, blank=True, default='') diff --git a/reo/process_results.py b/reo/process_results.py index 6bb08bc7b..d78263cfc 100644 --- a/reo/process_results.py +++ b/reo/process_results.py @@ -9,7 +9,7 @@ from reo.exceptions import REoptError, UnexpectedError from reo.models import ModelManager, PVModel, FinancialModel, WindModel, AbsorptionChillerModel from reo.src.profiler import Profiler -from reo.src.emissions_calculator import EmissionsCalculator +# from reo.src.emissions_calculator import EmissionsCalculator from reo.utilities import annuity, TONHOUR_TO_KWHT, MMBTU_TO_KWH, GAL_DIESEL_TO_KWH from reo.nested_inputs import macrs_five_year, macrs_seven_year from reo.src.proforma_metrics import calculate_proforma_metrics diff --git a/reo/src/emissions_calculator.py b/reo/src/emissions_calculator.py index 3a748ee16..51ef1eb7b 100644 --- a/reo/src/emissions_calculator.py +++ b/reo/src/emissions_calculator.py @@ -1,192 +1,192 @@ -import os -import json -import logging -log = logging.getLogger(__name__) -import geopandas as gpd -import pandas as pd -import numpy as np -from functools import partial -import pyproj -from reo.src.pyeasiur import * +# import os +# import json +# import logging +# log = logging.getLogger(__name__) +# import geopandas as gpd +# import pandas as pd +# import numpy as np +# from functools import partial +# import pyproj +# from reo.src.pyeasiur import * -from shapely import geometry as g -from shapely.ops import transform +# from shapely import geometry as g +# from shapely.ops import transform -class EmissionsCalculator: +# class EmissionsCalculator: - def __init__(self, latitude=None, longitude=None, pollutant=None, **kwargs): - """ - :param latitude: float - :param longitude: float - :param kwargs: - """ - self._region_lookup = None - self.library_path = os.path.join('reo', 'src', 'data') - self.latitude = float(latitude) if latitude is not None else None - self.longitude = float(longitude) if longitude is not None else None - self.pollutant = pollutant - self._region_abbr = None - self._emmissions_profile = None - self._transmission_and_distribution_losses = None - self.meters_to_region = None - self.time_steps_per_hour = kwargs.get('time_steps_per_hour') or 1 - proj102008 = pyproj.Proj("+proj=aea +lat_1=20 +lat_2=60 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs") - self.project4326_to_102008 = partial(pyproj.transform,pyproj.Proj("epsg:4326"),proj102008) +# def __init__(self, latitude=None, longitude=None, pollutant=None, **kwargs): +# """ +# :param latitude: float +# :param longitude: float +# :param kwargs: +# """ +# self._region_lookup = None +# self.library_path = os.path.join('reo', 'src', 'data') +# self.latitude = float(latitude) if latitude is not None else None +# self.longitude = float(longitude) if longitude is not None else None +# self.pollutant = pollutant +# self._region_abbr = None +# self._emmissions_profile = None +# self._transmission_and_distribution_losses = None +# self.meters_to_region = None +# self.time_steps_per_hour = kwargs.get('time_steps_per_hour') or 1 +# proj102008 = pyproj.Proj("+proj=aea +lat_1=20 +lat_2=60 +lat_0=40 +lon_0=-96 +x_0=0 +y_0=0 +datum=NAD83 +units=m +no_defs") +# self.project4326_to_102008 = partial(pyproj.transform,pyproj.Proj("epsg:4326"),proj102008) - @property - def region(self): - lookup = { 'AK':'Alaska', - 'CA':'California', - 'EMW':'Great Lakes / Atlantic', - 'NE': 'Northeast', - 'NW':'Northwest', - 'RM':'Rocky Mountains', - 'SC':'Lower Midwest', - 'SE': 'Southeast', - 'SW':'Southwest', - 'TX':'Texas', - 'WMW':'Upper Midwest', - 'HI':'Hawaii (except Oahu)', - 'HI-Oahu':'Hawaii (Oahu)' } +# @property +# def region(self): +# lookup = { 'AK':'Alaska', +# 'CA':'California', +# 'EMW':'Great Lakes / Atlantic', +# 'NE': 'Northeast', +# 'NW':'Northwest', +# 'RM':'Rocky Mountains', +# 'SC':'Lower Midwest', +# 'SE': 'Southeast', +# 'SW':'Southwest', +# 'TX':'Texas', +# 'WMW':'Upper Midwest', +# 'HI':'Hawaii (except Oahu)', +# 'HI-Oahu':'Hawaii (Oahu)' } - if self.region_abbr is not None: - return lookup[self.region_abbr] - return None +# if self.region_abbr is not None: +# return lookup[self.region_abbr] +# return None - @property - def region_abbr(self): - if self._region_abbr is None: - gdf = gpd.read_file(os.path.join(self.library_path,'avert_4326.shp')) +# @property +# def region_abbr(self): +# if self._region_abbr is None: +# gdf = gpd.read_file(os.path.join(self.library_path,'avert_4326.shp')) - gdf_query = gdf[gdf.geometry.intersects(g.Point(self.longitude, self.latitude))] - if not gdf_query.empty: - self.meters_to_region = 0 - self._region_abbr = gdf_query.AVERT.values[0] +# gdf_query = gdf[gdf.geometry.intersects(g.Point(self.longitude, self.latitude))] +# if not gdf_query.empty: +# self.meters_to_region = 0 +# self._region_abbr = gdf_query.AVERT.values[0] - if self._region_abbr is None: - gdf = gpd.read_file(os.path.join(self.library_path,'avert_102008.shp')) - try: - lookup = transform(self.project4326_to_102008, g.Point(self.latitude, self.longitude)) # switched lat and long here - except: - raise AttributeError("Could not look up AVERT emissions region from point ({},{}). Location is\ - likely invalid or well outside continental US, AK and HI".format(self.longitude, self.latitude)) - distances_meter = gdf.geometry.apply(lambda x : x.distance(lookup)).values - min_idx = list(distances_meter).index(min(distances_meter)) - self._region_abbr = gdf.loc[min_idx,'AVERT'] - self.meters_to_region = int(round(min(distances_meter))) - if self.meters_to_region > 8046: - raise AttributeError('Your site location ({},{}) is more than 5 miles from the ' - 'nearest emission region. Cannot calculate emissions.'.format(self.longitude, self.latitude)) - return self._region_abbr +# if self._region_abbr is None: +# gdf = gpd.read_file(os.path.join(self.library_path,'avert_102008.shp')) +# try: +# lookup = transform(self.project4326_to_102008, g.Point(self.latitude, self.longitude)) # switched lat and long here +# except: +# raise AttributeError("Could not look up AVERT emissions region from point ({},{}). Location is\ +# likely invalid or well outside continental US, AK and HI".format(self.longitude, self.latitude)) +# distances_meter = gdf.geometry.apply(lambda x : x.distance(lookup)).values +# min_idx = list(distances_meter).index(min(distances_meter)) +# self._region_abbr = gdf.loc[min_idx,'AVERT'] +# self.meters_to_region = int(round(min(distances_meter))) +# if self.meters_to_region > 8046: +# raise AttributeError('Your site location ({},{}) is more than 5 miles from the ' +# 'nearest emission region. Cannot calculate emissions.'.format(self.longitude, self.latitude)) +# return self._region_abbr - @property - def emissions_series(self): - if self._emmissions_profile is None: - df = pd.read_csv(os.path.join(self.library_path,'AVERT_hourly_emissions_{}.csv'.format(self.pollutant)), dtype='float64', float_precision='high') - if self.region_abbr in df.columns: - self._emmissions_profile = list(df[self.region_abbr].round(6).values) - if self.time_steps_per_hour > 1: - self._emmissions_profile = list(np.concatenate([[i] * self.time_steps_per_hour for i in self._emmissions_profile])) - else: - raise AttributeError("Emissions error. Cannnot find hourly emmissions for region {} ({},{}) \ - ".format(self.region, self.latitude,self.longitude)) - return self._emmissions_profile +# @property +# def emissions_series(self): +# if self._emmissions_profile is None: +# df = pd.read_csv(os.path.join(self.library_path,'AVERT_hourly_emissions_{}.csv'.format(self.pollutant)), dtype='float64', float_precision='high') +# if self.region_abbr in df.columns: +# self._emmissions_profile = list(df[self.region_abbr].round(6).values) +# if self.time_steps_per_hour > 1: +# self._emmissions_profile = list(np.concatenate([[i] * self.time_steps_per_hour for i in self._emmissions_profile])) +# else: +# raise AttributeError("Emissions error. Cannnot find hourly emmissions for region {} ({},{}) \ +# ".format(self.region, self.latitude,self.longitude)) +# return self._emmissions_profile -class EASIURCalculator: +# class EASIURCalculator: - def __init__(self, latitude=None, longitude=None, **kwargs): - """ - :param latitude: float - :param longitude: float - :param kwargs: - """ - self.library_path = os.path.join('reo', 'src', 'data') - self.latitude = float(latitude) if latitude is not None else None - self.longitude = float(longitude) if longitude is not None else None - self.inflation = kwargs.get('inflation') or None - self._grid_costs_per_tonne = None - self._onsite_costs_per_tonne = None - self._escalation_rates = None +# def __init__(self, latitude=None, longitude=None, **kwargs): +# """ +# :param latitude: float +# :param longitude: float +# :param kwargs: +# """ +# self.library_path = os.path.join('reo', 'src', 'data') +# self.latitude = float(latitude) if latitude is not None else None +# self.longitude = float(longitude) if longitude is not None else None +# self.inflation = kwargs.get('inflation') or None +# self._grid_costs_per_tonne = None +# self._onsite_costs_per_tonne = None +# self._escalation_rates = None - @property - def grid_costs(self): - if self._grid_costs_per_tonne is None: - # Assumption: grid emissions occur at site at 150m above ground; - EASIUR_150m = get_EASIUR2005('p150', pop_year=2020, income_year=2020, dollar_year=2010) # For keys in EASIUR: EASIUR_150m_pop2020_inc2020_dol2010.keys() +# @property +# def grid_costs(self): +# if self._grid_costs_per_tonne is None: +# # Assumption: grid emissions occur at site at 150m above ground; +# EASIUR_150m = get_EASIUR2005('p150', pop_year=2020, income_year=2020, dollar_year=2010) # For keys in EASIUR: EASIUR_150m_pop2020_inc2020_dol2010.keys() - # convert lon, lat to CAMx grid (x, y), specify datum. default is NAD83 - # Note: x, y returned from g2l follows the CAMx grid convention. - # x and y start from 1, not zero. (x) ranges (1, ..., 148) and (y) ranges (1, ..., 112) - x, y = g2l(self.longitude, self.latitude, datum='NAD83') - x = int(round(x)) - y = int(round(y)) +# # convert lon, lat to CAMx grid (x, y), specify datum. default is NAD83 +# # Note: x, y returned from g2l follows the CAMx grid convention. +# # x and y start from 1, not zero. (x) ranges (1, ..., 148) and (y) ranges (1, ..., 112) +# x, y = g2l(self.longitude, self.latitude, datum='NAD83') +# x = int(round(x)) +# y = int(round(y)) - # Convert from 2010$ to 2020$ (source: https://www.in2013dollars.com/us/inflation/2010?amount=100) - convert_2010_2020_usd = 1.246 - try: - self._grid_costs_per_tonne = { - 'NOx': EASIUR_150m['NOX_Annual'][x - 1, y - 1] * convert_2010_2020_usd, - 'SO2': EASIUR_150m['SO2_Annual'][x - 1, y - 1] * convert_2010_2020_usd, - 'PM25': EASIUR_150m['PEC_Annual'][x - 1, y - 1] * convert_2010_2020_usd, - } - except: - raise AttributeError("Could not look up EASIUR health costs from point ({},{}). Location is \ - likely invalid or outside the CAMx grid".format(self.latitude, self.longitude)) - return self._grid_costs_per_tonne +# # Convert from 2010$ to 2020$ (source: https://www.in2013dollars.com/us/inflation/2010?amount=100) +# convert_2010_2020_usd = 1.246 +# try: +# self._grid_costs_per_tonne = { +# 'NOx': EASIUR_150m['NOX_Annual'][x - 1, y - 1] * convert_2010_2020_usd, +# 'SO2': EASIUR_150m['SO2_Annual'][x - 1, y - 1] * convert_2010_2020_usd, +# 'PM25': EASIUR_150m['PEC_Annual'][x - 1, y - 1] * convert_2010_2020_usd, +# } +# except: +# raise AttributeError("Could not look up EASIUR health costs from point ({},{}). Location is \ +# likely invalid or outside the CAMx grid".format(self.latitude, self.longitude)) +# return self._grid_costs_per_tonne - @property - def onsite_costs(self): - if self._onsite_costs_per_tonne is None: - # Assumption: on-site fuelburn emissions occur at site at 0m above ground; - EASIUR_0m = get_EASIUR2005('area', pop_year=2020, income_year=2020, dollar_year=2010) # For keys in EASIUR: EASIUR_150m_pop2020_inc2020_dol2010.keys() +# @property +# def onsite_costs(self): +# if self._onsite_costs_per_tonne is None: +# # Assumption: on-site fuelburn emissions occur at site at 0m above ground; +# EASIUR_0m = get_EASIUR2005('area', pop_year=2020, income_year=2020, dollar_year=2010) # For keys in EASIUR: EASIUR_150m_pop2020_inc2020_dol2010.keys() - # convert lon, lat to CAMx grid (x, y), specify datum. default is NAD83 - # Note: x, y returned from g2l follows the CAMx grid convention. - # x and y start from 1, not zero. (x) ranges (1, ..., 148) and (y) ranges (1, ..., 112) - x, y = g2l(self.longitude, self.latitude, datum='NAD83') - x = int(round(x)) - y = int(round(y)) +# # convert lon, lat to CAMx grid (x, y), specify datum. default is NAD83 +# # Note: x, y returned from g2l follows the CAMx grid convention. +# # x and y start from 1, not zero. (x) ranges (1, ..., 148) and (y) ranges (1, ..., 112) +# x, y = g2l(self.longitude, self.latitude, datum='NAD83') +# x = int(round(x)) +# y = int(round(y)) - # Convert from 2010$ to 2020$ (source: https://www.in2013dollars.com/us/inflation/2010?amount=100) - convert_2010_2020_usd = 1.246 - try: - self._onsite_costs_per_tonne = { - 'NOx': EASIUR_0m['NOX_Annual'][x - 1, y - 1] * convert_2010_2020_usd, - 'SO2': EASIUR_0m['SO2_Annual'][x - 1, y - 1] * convert_2010_2020_usd, - 'PM25': EASIUR_0m['PEC_Annual'][x - 1, y - 1] * convert_2010_2020_usd, - } - except: - raise AttributeError("Could not look up EASIUR health costs from point ({},{}). Location is \ - likely invalid or outside the CAMx grid".format(self.latitude, self.longitude)) - return self._onsite_costs_per_tonne +# # Convert from 2010$ to 2020$ (source: https://www.in2013dollars.com/us/inflation/2010?amount=100) +# convert_2010_2020_usd = 1.246 +# try: +# self._onsite_costs_per_tonne = { +# 'NOx': EASIUR_0m['NOX_Annual'][x - 1, y - 1] * convert_2010_2020_usd, +# 'SO2': EASIUR_0m['SO2_Annual'][x - 1, y - 1] * convert_2010_2020_usd, +# 'PM25': EASIUR_0m['PEC_Annual'][x - 1, y - 1] * convert_2010_2020_usd, +# } +# except: +# raise AttributeError("Could not look up EASIUR health costs from point ({},{}). Location is \ +# likely invalid or outside the CAMx grid".format(self.latitude, self.longitude)) +# return self._onsite_costs_per_tonne - @property - def escalation_rates(self): - if self._escalation_rates is None: - EASIUR_150m_yr2020 = get_EASIUR2005('p150', pop_year=2020, income_year=2020, dollar_year=2010) - EASIUR_150m_yr2024 = get_EASIUR2005('p150', pop_year=2024, income_year=2024, dollar_year=2010) +# @property +# def escalation_rates(self): +# if self._escalation_rates is None: +# EASIUR_150m_yr2020 = get_EASIUR2005('p150', pop_year=2020, income_year=2020, dollar_year=2010) +# EASIUR_150m_yr2024 = get_EASIUR2005('p150', pop_year=2024, income_year=2024, dollar_year=2010) - # convert lon, lat to CAMx grid (x, y), specify datum. default is NAD83 - x, y = g2l(self.longitude, self.latitude, datum='NAD83') - x = int(round(x)) - y = int(round(y)) +# # convert lon, lat to CAMx grid (x, y), specify datum. default is NAD83 +# x, y = g2l(self.longitude, self.latitude, datum='NAD83') +# x = int(round(x)) +# y = int(round(y)) - try: - # real compound annual growth rate - cagr_real = { - 'NOx': (EASIUR_150m_yr2024['NOX_Annual'][x - 1, y - 1]/EASIUR_150m_yr2020['NOX_Annual'][x - 1, y - 1])**(1/4)-1, - 'SO2': (EASIUR_150m_yr2024['SO2_Annual'][x - 1, y - 1]/EASIUR_150m_yr2020['SO2_Annual'][x - 1, y - 1])**(1/4)-1, - 'PM25': (EASIUR_150m_yr2024['PEC_Annual'][x - 1, y - 1]/EASIUR_150m_yr2020['PEC_Annual'][x - 1, y - 1])**(1/4)-1, - } - # nominal compound annual growth rate (real + inflation) - self._escalation_rates = { - 'NOx': cagr_real['NOx'] + self.inflation, - 'SO2': cagr_real['SO2'] + self.inflation, - 'PM25': cagr_real['PM25'] + self.inflation, - } - except: - raise AttributeError("Could not look up EASIUR health costs from point ({},{}). Location is \ - likely invalid or outside the CAMx grid".format(self.latitude, self.longitude)) - return self._escalation_rates +# try: +# # real compound annual growth rate +# cagr_real = { +# 'NOx': (EASIUR_150m_yr2024['NOX_Annual'][x - 1, y - 1]/EASIUR_150m_yr2020['NOX_Annual'][x - 1, y - 1])**(1/4)-1, +# 'SO2': (EASIUR_150m_yr2024['SO2_Annual'][x - 1, y - 1]/EASIUR_150m_yr2020['SO2_Annual'][x - 1, y - 1])**(1/4)-1, +# 'PM25': (EASIUR_150m_yr2024['PEC_Annual'][x - 1, y - 1]/EASIUR_150m_yr2020['PEC_Annual'][x - 1, y - 1])**(1/4)-1, +# } +# # nominal compound annual growth rate (real + inflation) +# self._escalation_rates = { +# 'NOx': cagr_real['NOx'] + self.inflation, +# 'SO2': cagr_real['SO2'] + self.inflation, +# 'PM25': cagr_real['PM25'] + self.inflation, +# } +# except: +# raise AttributeError("Could not look up EASIUR health costs from point ({},{}). Location is \ +# likely invalid or outside the CAMx grid".format(self.latitude, self.longitude)) +# return self._escalation_rates diff --git a/reo/src/pyeasiur.py b/reo/src/pyeasiur.py index 41908d00f..ef87c4753 100644 --- a/reo/src/pyeasiur.py +++ b/reo/src/pyeasiur.py @@ -1,263 +1,263 @@ -""" -sample Python codes for EASIUR and APSCA -""" - -import csv -import deepdish -import h5py -import numpy as np -import pyproj -import os - -# print(f"This module uses the following packages") -# print(f"deepdish: {deepdish.__version__}") -# print(f"h5py : {h5py.__version__}") -# print(f"numpy : {np.__version__}") -# print(f"pyproj : {pyproj.__version__}") - -library_path = os.path.join('reo', 'src', 'data') - -# Income Growth Adjustment factors from BenMAP -MorIncomeGrowthAdj = { - 1990: 1.000000, - 1991: 0.992025, - 1992: 0.998182, - 1993: 1.003087, - 1994: 1.012843, - 1995: 1.016989, - 1996: 1.024362, - 1997: 1.034171, - 1998: 1.038842, - 1999: 1.042804, - 2000: 1.038542, - 2001: 1.043834, - 2002: 1.049992, - 2003: 1.056232, - 2004: 1.062572, - 2005: 1.068587, - 2006: 1.074681, - 2007: 1.080843, - 2008: 1.087068, - 2009: 1.093349, - 2010: 1.099688, - 2011: 1.111515, - 2012: 1.122895, - 2013: 1.133857, - 2014: 1.144425, - 2015: 1.154627, - 2016: 1.164482, - 2017: 1.174010, - 2018: 1.183233, - 2019: 1.192168, - 2020: 1.200834, - 2021: 1.209226, - 2022: 1.217341, - 2023: 1.225191, - 2024: 1.232790, -} - -# GDP deflator from BenMAP -GDP_deflator = { - 1980: 0.478513, - 1981: 0.527875, - 1982: 0.560395, - 1983: 0.578397, - 1984: 0.603368, - 1985: 0.624855, - 1986: 0.636469, - 1987: 0.659698, - 1988: 0.686992, - 1989: 0.720093, - 1990: 0.759001, - 1991: 0.790941, - 1992: 0.814750, - 1993: 0.839141, - 1994: 0.860627, - 1995: 0.885017, - 1996: 0.911150, - 1997: 0.932056, - 1998: 0.946574, - 1999: 0.967480, - 2000: 1.000000, - 2001: 1.028455, - 2002: 1.044715, - 2003: 1.068525, - 2004: 1.096980, - 2005: 1.134146, - 2006: 1.170732, - 2007: 1.204077, - 2008: 1.250308, - 2009: 1.245860, - 2010: 1.266295, -} - -# pyproj constant -# LCP_US = pyproj.Proj("+proj=lcc +no_defs +a=6370000.0m +b=6370000.0m \ -# +lon_0=97w +lat_0=40n +lat_1=33n +lat_2=45n \ -# +x_0=2736000.0m +y_0=2088000.0m +towgs84=0,0,0") -# GEO_SPHEROID = pyproj.Proj("+proj=lonlat +towgs84=0,0,0 +a=6370000.0m +no_defs") -LCP_US = pyproj.Proj( - "+proj=lcc +no_defs +a=6370000.0 +b=6370000.0 " - "+lon_0=97w +lat_0=40n +lat_1=33n +lat_2=45n " - "+x_0=2736000.0 +y_0=2088000.0 +to_wgs=0,0,0 +units=m" -) -# GEO_SPHEROID = pyproj.Proj("+proj=lonlat +towgs84=0,0,0 +a=6370000.0 +no_defs") -DATUM_NAD83 = pyproj.Proj("epsg:4269 +proj=longlat +ellps=GRS80 +datum=NAD83 +no_defs +towgs84=0,0,0") -DATUM_WGS84 = pyproj.Proj("epsg:4326 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0") - - -def get_EASIUR2005(stack, pop_year=2005, income_year=2005, dollar_year=2010): - """Returns EASIUR for a given `stack` height in a dict. - - Args: - stack: area, p150, p300 - pop_year: population year - income_year: income level (1990 to 2024) - dollar_year: dollar year (1980 to 2010) - """ - - if stack not in ["area", "p150", "p300"]: - print("stack should be one of 'area', 'p150', 'p300'") - return False - - file_2005 = "sc_8.6MVSL_" + stack + "_pop2005.hdf5" - - #ret_map = deepdish.io.load("data/EASIUR_Data/" + file_2005) - ret_map = deepdish.io.load(os.path.join(library_path, 'EASIUR_Data', file_2005)) - - if pop_year != 2005: - filename = "sc_growth_rate_pop2005_pop2040_" + stack + ".hdf5" - map_rate = deepdish.io.load(os.path.join(library_path, 'EASIUR_Data', filename)) - - for k, v in map_rate.items(): - ret_map[k] = ret_map[k] * (v ** (pop_year - 2005)) - - if income_year != 2005: - try: - adj = MorIncomeGrowthAdj[income_year] / MorIncomeGrowthAdj[2005] - for k, v in ret_map.items(): - ret_map[k] = v * adj - except KeyError: - print("income year must be between 1990 to 2024") - return False - - if dollar_year != 2010: - try: - adj = GDP_deflator[dollar_year] / GDP_deflator[2010] - for k, v in ret_map.items(): - ret_map[k] = v * adj - except KeyError: - print("Dollar year must be between 1980 to 2010") - return False - - return ret_map - - -def get_pop_inc(year, min_age=30): - """Returns population and incidence (or mortality) rate of `min_age` or older for a given `year`.""" - - remainder = int(year) % 5 - if remainder == 0: - return get_pop_inc_raw(year, min_age) - else: - # linear interpolation - p1, i1 = get_pop_inc_raw(year - remainder, min_age) - p2, i2 = get_pop_inc_raw(year - remainder + 5, min_age) - pop = (p1 * (5 - remainder) + p2 * remainder) / 5.0 - inc = (i1 * (5 - remainder) + i2 * remainder) / 5.0 - return pop, inc - - -def get_pop_inc_raw(year, min_age=30): - """Returns population and incidence (or mortality) rate of `min_age` or older for a given `year`. - - Raw data (CSV files) were derived from BenMAP - """ - -# with open("data/EASIUR_Data/PopInc/popinc" + str(year) + ".CSV", newline="") as f: - with open(os.path.join(library_path, 'EASIUR_Data/PopInc/popinc{}.CSV'.format(str(year))), newline="") as f: +# """ +# sample Python codes for EASIUR and APSCA +# """ + +# import csv +# import deepdish +# import h5py +# import numpy as np +# import pyproj +# import os + +# # print(f"This module uses the following packages") +# # print(f"deepdish: {deepdish.__version__}") +# # print(f"h5py : {h5py.__version__}") +# # print(f"numpy : {np.__version__}") +# # print(f"pyproj : {pyproj.__version__}") + +# library_path = os.path.join('reo', 'src', 'data') + +# # Income Growth Adjustment factors from BenMAP +# MorIncomeGrowthAdj = { +# 1990: 1.000000, +# 1991: 0.992025, +# 1992: 0.998182, +# 1993: 1.003087, +# 1994: 1.012843, +# 1995: 1.016989, +# 1996: 1.024362, +# 1997: 1.034171, +# 1998: 1.038842, +# 1999: 1.042804, +# 2000: 1.038542, +# 2001: 1.043834, +# 2002: 1.049992, +# 2003: 1.056232, +# 2004: 1.062572, +# 2005: 1.068587, +# 2006: 1.074681, +# 2007: 1.080843, +# 2008: 1.087068, +# 2009: 1.093349, +# 2010: 1.099688, +# 2011: 1.111515, +# 2012: 1.122895, +# 2013: 1.133857, +# 2014: 1.144425, +# 2015: 1.154627, +# 2016: 1.164482, +# 2017: 1.174010, +# 2018: 1.183233, +# 2019: 1.192168, +# 2020: 1.200834, +# 2021: 1.209226, +# 2022: 1.217341, +# 2023: 1.225191, +# 2024: 1.232790, +# } + +# # GDP deflator from BenMAP +# GDP_deflator = { +# 1980: 0.478513, +# 1981: 0.527875, +# 1982: 0.560395, +# 1983: 0.578397, +# 1984: 0.603368, +# 1985: 0.624855, +# 1986: 0.636469, +# 1987: 0.659698, +# 1988: 0.686992, +# 1989: 0.720093, +# 1990: 0.759001, +# 1991: 0.790941, +# 1992: 0.814750, +# 1993: 0.839141, +# 1994: 0.860627, +# 1995: 0.885017, +# 1996: 0.911150, +# 1997: 0.932056, +# 1998: 0.946574, +# 1999: 0.967480, +# 2000: 1.000000, +# 2001: 1.028455, +# 2002: 1.044715, +# 2003: 1.068525, +# 2004: 1.096980, +# 2005: 1.134146, +# 2006: 1.170732, +# 2007: 1.204077, +# 2008: 1.250308, +# 2009: 1.245860, +# 2010: 1.266295, +# } + +# # pyproj constant +# # LCP_US = pyproj.Proj("+proj=lcc +no_defs +a=6370000.0m +b=6370000.0m \ +# # +lon_0=97w +lat_0=40n +lat_1=33n +lat_2=45n \ +# # +x_0=2736000.0m +y_0=2088000.0m +towgs84=0,0,0") +# # GEO_SPHEROID = pyproj.Proj("+proj=lonlat +towgs84=0,0,0 +a=6370000.0m +no_defs") +# LCP_US = pyproj.Proj( +# "+proj=lcc +no_defs +a=6370000.0 +b=6370000.0 " +# "+lon_0=97w +lat_0=40n +lat_1=33n +lat_2=45n " +# "+x_0=2736000.0 +y_0=2088000.0 +to_wgs=0,0,0 +units=m" +# ) +# # GEO_SPHEROID = pyproj.Proj("+proj=lonlat +towgs84=0,0,0 +a=6370000.0 +no_defs") +# DATUM_NAD83 = pyproj.Proj("epsg:4269 +proj=longlat +ellps=GRS80 +datum=NAD83 +no_defs +towgs84=0,0,0") +# DATUM_WGS84 = pyproj.Proj("epsg:4326 +proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs +towgs84=0,0,0") + + +# def get_EASIUR2005(stack, pop_year=2005, income_year=2005, dollar_year=2010): +# """Returns EASIUR for a given `stack` height in a dict. + +# Args: +# stack: area, p150, p300 +# pop_year: population year +# income_year: income level (1990 to 2024) +# dollar_year: dollar year (1980 to 2010) +# """ + +# if stack not in ["area", "p150", "p300"]: +# print("stack should be one of 'area', 'p150', 'p300'") +# return False + +# file_2005 = "sc_8.6MVSL_" + stack + "_pop2005.hdf5" + +# #ret_map = deepdish.io.load("data/EASIUR_Data/" + file_2005) +# ret_map = deepdish.io.load(os.path.join(library_path, 'EASIUR_Data', file_2005)) + +# if pop_year != 2005: +# filename = "sc_growth_rate_pop2005_pop2040_" + stack + ".hdf5" +# map_rate = deepdish.io.load(os.path.join(library_path, 'EASIUR_Data', filename)) + +# for k, v in map_rate.items(): +# ret_map[k] = ret_map[k] * (v ** (pop_year - 2005)) + +# if income_year != 2005: +# try: +# adj = MorIncomeGrowthAdj[income_year] / MorIncomeGrowthAdj[2005] +# for k, v in ret_map.items(): +# ret_map[k] = v * adj +# except KeyError: +# print("income year must be between 1990 to 2024") +# return False + +# if dollar_year != 2010: +# try: +# adj = GDP_deflator[dollar_year] / GDP_deflator[2010] +# for k, v in ret_map.items(): +# ret_map[k] = v * adj +# except KeyError: +# print("Dollar year must be between 1980 to 2010") +# return False + +# return ret_map + + +# def get_pop_inc(year, min_age=30): +# """Returns population and incidence (or mortality) rate of `min_age` or older for a given `year`.""" + +# remainder = int(year) % 5 +# if remainder == 0: +# return get_pop_inc_raw(year, min_age) +# else: +# # linear interpolation +# p1, i1 = get_pop_inc_raw(year - remainder, min_age) +# p2, i2 = get_pop_inc_raw(year - remainder + 5, min_age) +# pop = (p1 * (5 - remainder) + p2 * remainder) / 5.0 +# inc = (i1 * (5 - remainder) + i2 * remainder) / 5.0 +# return pop, inc + + +# def get_pop_inc_raw(year, min_age=30): +# """Returns population and incidence (or mortality) rate of `min_age` or older for a given `year`. + +# Raw data (CSV files) were derived from BenMAP +# """ + +# # with open("data/EASIUR_Data/PopInc/popinc" + str(year) + ".CSV", newline="") as f: +# with open(os.path.join(library_path, 'EASIUR_Data/PopInc/popinc{}.CSV'.format(str(year))), newline="") as f: - pop = np.zeros((148, 112)) - inc = np.zeros((148, 112)) - popinc_csv = csv.reader(f) - header = next(popinc_csv) - - if "Col" in header[0]: # Col has a strange char - header[0] = "Col" - - xys = set() - for row in popinc_csv: - row_d = dict(zip(header, row)) - if int(row_d["Start Age"]) == min_age: - x = int(row_d["Col"]) - 1 - y = int(row_d["Row"]) - 1 - if (x, y) in xys: - # just to check - print("Duplicate?", x, y) - xys.add((x, y)) - p = float(row_d["Population"]) - i = float(row_d["Baseline"]) - # print float(row_d['Point Estimate'])/i, row_d['Percent of Baseline'] - pop[x, y] = p - inc[x, y] = i / p - return pop, inc - - -def get_avg_plume(x, y, spec, stack="area", season="Q0"): - """Returns an Average Plume in a 148x112 array - - Args: - x, y: source location - spec: species (PEC, SO2, NOX, NH3) - stack: stack height (area, p150, p300) - season: season (Q0 for Annual, Q1 for Jan-Mar, Q2 for Apr-Jun, Q3 for Jul-Sep, Q4 for Oct-Dec) - - Returns: - an average plume in a 148x112 array - """ - - h5f = "".join( - ["data/EASIUR_Data/AveragePlumes_181x181/avgplumes_", season, "_", str(stack) + ".hdf5"] - ) - with h5py.File(h5f) as f: - return f[spec][x, y] - - -def get_avg_plume_stack(x, y, stkht, spec, season="Q0"): - """Returns an Average Plume in a 148x112 array""" - - stkht = float(stkht) - - # linear interpolation - if stkht < 150: - ap1 = get_avg_plume(x, y, spec, stack="area", season=season) - ap2 = get_avg_plume(x, y, spec, stack="p150", season=season) - return ap1 * (1 - stkht / 150.0) + ap2 * stkht / 150.0 - elif stkht < 300: - ap2 = get_avg_plume(x, y, spec, stack="p150", season=season) - ap3 = get_avg_plume(x, y, spec, stack="p300", season=season) - return ap2 * (1 - (stkht - 150.0) / 150.0) + ap3 * (stkht - 150.0) / 150.0 - else: - ap3 = get_avg_plume(x, y, spec, stack="p300", season=season) - return ap3 - - -def l2g(x, y, inverse=False, datum="NAD83"): - """Convert LCP (x, y) in CAMx 148x112 grid to Geodetic (lon, lat)""" - - if datum == "NAD83": - datum = DATUM_NAD83 - elif datum == "WGS84": - datum = DATUM_WGS84 - - if inverse: - return np.array(pyproj.transform(datum, LCP_US, x, y)) / 36000.0 + np.array( - [1, 1] - ) - else: - return pyproj.transform(LCP_US, datum, (x - 1) * 36e3, (y - 1) * 36e3) - - -def g2l(lon, lat, datum="NAD83"): - """Convert Geodetic (lon, lat) to LCP (x, y) in CAMx 148x112 grid""" - return l2g(lon, lat, True, datum) +# pop = np.zeros((148, 112)) +# inc = np.zeros((148, 112)) +# popinc_csv = csv.reader(f) +# header = next(popinc_csv) + +# if "Col" in header[0]: # Col has a strange char +# header[0] = "Col" + +# xys = set() +# for row in popinc_csv: +# row_d = dict(zip(header, row)) +# if int(row_d["Start Age"]) == min_age: +# x = int(row_d["Col"]) - 1 +# y = int(row_d["Row"]) - 1 +# if (x, y) in xys: +# # just to check +# print("Duplicate?", x, y) +# xys.add((x, y)) +# p = float(row_d["Population"]) +# i = float(row_d["Baseline"]) +# # print float(row_d['Point Estimate'])/i, row_d['Percent of Baseline'] +# pop[x, y] = p +# inc[x, y] = i / p +# return pop, inc + + +# def get_avg_plume(x, y, spec, stack="area", season="Q0"): +# """Returns an Average Plume in a 148x112 array + +# Args: +# x, y: source location +# spec: species (PEC, SO2, NOX, NH3) +# stack: stack height (area, p150, p300) +# season: season (Q0 for Annual, Q1 for Jan-Mar, Q2 for Apr-Jun, Q3 for Jul-Sep, Q4 for Oct-Dec) + +# Returns: +# an average plume in a 148x112 array +# """ + +# h5f = "".join( +# ["data/EASIUR_Data/AveragePlumes_181x181/avgplumes_", season, "_", str(stack) + ".hdf5"] +# ) +# with h5py.File(h5f) as f: +# return f[spec][x, y] + + +# def get_avg_plume_stack(x, y, stkht, spec, season="Q0"): +# """Returns an Average Plume in a 148x112 array""" + +# stkht = float(stkht) + +# # linear interpolation +# if stkht < 150: +# ap1 = get_avg_plume(x, y, spec, stack="area", season=season) +# ap2 = get_avg_plume(x, y, spec, stack="p150", season=season) +# return ap1 * (1 - stkht / 150.0) + ap2 * stkht / 150.0 +# elif stkht < 300: +# ap2 = get_avg_plume(x, y, spec, stack="p150", season=season) +# ap3 = get_avg_plume(x, y, spec, stack="p300", season=season) +# return ap2 * (1 - (stkht - 150.0) / 150.0) + ap3 * (stkht - 150.0) / 150.0 +# else: +# ap3 = get_avg_plume(x, y, spec, stack="p300", season=season) +# return ap3 + + +# def l2g(x, y, inverse=False, datum="NAD83"): +# """Convert LCP (x, y) in CAMx 148x112 grid to Geodetic (lon, lat)""" + +# if datum == "NAD83": +# datum = DATUM_NAD83 +# elif datum == "WGS84": +# datum = DATUM_WGS84 + +# if inverse: +# return np.array(pyproj.transform(datum, LCP_US, x, y)) / 36000.0 + np.array( +# [1, 1] +# ) +# else: +# return pyproj.transform(LCP_US, datum, (x - 1) * 36e3, (y - 1) * 36e3) + + +# def g2l(lon, lat, datum="NAD83"): +# """Convert Geodetic (lon, lat) to LCP (x, y) in CAMx 148x112 grid""" +# return l2g(lon, lat, True, datum) diff --git a/reo/validators.py b/reo/validators.py index e126c8a39..96f2d8e57 100755 --- a/reo/validators.py +++ b/reo/validators.py @@ -12,9 +12,9 @@ import re import uuid from reo.src.techs import Generator, Boiler, CHP, AbsorptionChiller, SteamTurbine -from reo.src.emissions_calculator import EmissionsCalculator, EASIURCalculator +# from reo.src.emissions_calculator import EmissionsCalculator, EASIURCalculator from reo.utilities import generate_year_profile_hourly, get_climate_zone_and_nearest_city -from reo.src.pyeasiur import * +# from reo.src.pyeasiur import * from reo.src.load_profile import BuiltInProfile hard_problems_csv = os.path.join('reo', 'hard_problems.csv') @@ -1355,10 +1355,10 @@ def check_special_cases(self, object_name_path, template_values=None, real_value pollutant = 'SO2' elif 'PM25' in key_name: pollutant = 'PM25' - ec = EmissionsCalculator( latitude=self.input_dict['Scenario']['Site']['latitude'], - longitude=self.input_dict['Scenario']['Site']['longitude'], - pollutant = pollutant, - time_steps_per_hour = ts_per_hour) + # ec = EmissionsCalculator( latitude=self.input_dict['Scenario']['Site']['latitude'], + # longitude=self.input_dict['Scenario']['Site']['longitude'], + # pollutant = pollutant, + # time_steps_per_hour = ts_per_hour) # If user applies CO2 emissions constraint or includes CO2 costs in objective must_include_CO2 = self.input_dict['Scenario']['include_climate_in_objective'] or ('co2_emissions_reduction_min_pct' in self.input_dict['Scenario']['Site']) \ or ('co2_emissions_reduction_max_pct' in self.input_dict['Scenario']['Site']) @@ -1366,8 +1366,9 @@ def check_special_cases(self, object_name_path, template_values=None, real_value must_include_health = self.input_dict['Scenario']['include_health_in_objective'] emissions_series = None try: - emissions_series = ec.emissions_series - emissions_region = ec.region + pass + # emissions_series = ec.emissions_series + # emissions_region = ec.region except AttributeError as e: # Emissions warning is a specific type of warning that we check for and display to the users when it occurs # If emissions are not required to do a run it tells the user why we could not get an emission series diff --git a/reo/views.py b/reo/views.py index a76bedfaa..2d859c10e 100644 --- a/reo/views.py +++ b/reo/views.py @@ -18,7 +18,7 @@ import logging log = logging.getLogger(__name__) from reo.src.techs import Generator, CHP, AbsorptionChiller, Boiler, SteamTurbine -from reo.src.emissions_calculator import EmissionsCalculator, EASIURCalculator +# from reo.src.emissions_calculator import EmissionsCalculator, EASIURCalculator from django.http import HttpResponse from django.template import loader import pandas as pd @@ -183,23 +183,24 @@ def emissions_profile(request): latitude = float(request.GET['latitude']) # need float to convert unicode longitude = float(request.GET['longitude']) - ec_CO2 = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='CO2') - ec_NOx = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='NOx') - ec_SO2 = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='SO2') - ec_PM25 = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='PM25') + # ec_CO2 = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='CO2') + # ec_NOx = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='NOx') + # ec_SO2 = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='SO2') + # ec_PM25 = EmissionsCalculator(latitude=latitude,longitude=longitude, pollutant='PM25') try: - response = JsonResponse({ - 'region_abbr': ec_CO2.region_abbr, - 'region': ec_CO2.region, - 'emissions_factor_series_lb_CO2_per_kwh': ec_CO2.emissions_series, - 'emissions_factor_series_lb_NOx_per_kwh': ec_NOx.emissions_series, - 'emissions_factor_series_lb_SO2_per_kwh': ec_SO2.emissions_series, - 'emissions_factor_series_lb_PM25_per_kwh': ec_PM25.emissions_series, - 'units': 'Pounds emission species per kWh', - 'description': 'Regional hourly grid emissions factors for applicable EPA AVERT region.', - 'meters_to_region': ec_CO2.meters_to_region - }) + response = JsonResponse({}) + # response = JsonResponse({ + # 'region_abbr': ec_CO2.region_abbr, + # 'region': ec_CO2.region, + # 'emissions_factor_series_lb_CO2_per_kwh': ec_CO2.emissions_series, + # 'emissions_factor_series_lb_NOx_per_kwh': ec_NOx.emissions_series, + # 'emissions_factor_series_lb_SO2_per_kwh': ec_SO2.emissions_series, + # 'emissions_factor_series_lb_PM25_per_kwh': ec_PM25.emissions_series, + # 'units': 'Pounds emission species per kWh', + # 'description': 'Regional hourly grid emissions factors for applicable EPA AVERT region.', + # 'meters_to_region': ec_CO2.meters_to_region + # }) return response except AttributeError as e: return JsonResponse({"Error": str(e.args[0])}, status=500) diff --git a/reoptjl/validators.py b/reoptjl/validators.py index a65a60b02..d5f19dca0 100644 --- a/reoptjl/validators.py +++ b/reoptjl/validators.py @@ -649,9 +649,9 @@ def validate_time_series(series: list, time_steps_per_hour: int) -> Tuple[list, if time_steps_per_hour < time_steps_per_hour_in_series: resampling_msg = f"Downsampled to match time_steps_per_hour via average." - index = pd.date_range('1/1/2000', periods=n, freq=f'{int(60/time_steps_per_hour_in_series)}T') + index = pd.date_range('1/1/2000', periods=n, freq=f'{int(60/time_steps_per_hour_in_series)}min') s = pd.Series(series, index=index) - s = s.resample(f'{int(60/time_steps_per_hour)}T').mean() + s = s.resample(f'{int(60/time_steps_per_hour)}min').mean() return s.tolist(), resampling_msg, "" # time_steps_per_hour > time_steps_per_hour_in_series diff --git a/requirements.txt b/requirements.txt index e6b1af133..dbfe1c1ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,119 +1,158 @@ -# python 3.8 +# Python 3.11 adal==1.2.7 -amqp==5.0.9 -asgiref==3.4.1 -attrs==21.4.0 -autopep8==1.6.0 -backports.zoneinfo==0.2.1 -bandit==1.7.1 -billiard==3.6.4.0 -cachetools==4.2.4 -celery==5.2.3 -certifi==2022.12.7 -cffi==1.15.0 -chardet==4.0.0 -charset-normalizer==2.0.10 -click==8.0.3 -click-didyoumean==0.3.0 -click-plugins==1.1.1 -click-repl==0.2.0 +amqp==5.3.1 +asgiref==3.9.1 +asttokens==3.0.0 +attrs==25.3.0 +autopep8==2.3.2 +azure-core==1.35.0 +backcall==0.2.0 +bandit==1.8.6 +beautifulsoup4==4.13.5 +billiard==4.2.1 +bleach==6.2.0 +blosc2==3.7.2 +cachetools==5.5.2 +celery==5.5.3 +certifi==2025.8.3 +cffi==1.17.1 +chardet==5.2.0 +charset-normalizer==3.4.3 +click==8.2.1 +click-didyoumean==0.3.1 +click-plugins==1.1.1.2 +click-repl==0.3.0 cligj==0.7.2 -colorama==0.4.4 -CoolProp==6.4.1 -cryptography==36.0.1 -Cython==0.29.26 -decorator==5.1.1 +colorama==0.4.6 +coolprop==7.0.0 +cryptography==45.0.6 +Cython==3.1.3 +decorator==5.2.1 deepdish==0.3.7 -Deprecated==1.2.13 -Django==4.0.7 -django-celery-results==2.4.0 -django-extensions==3.1.5 -django-picklefield==3.0.1 -django-tastypie==0.14.4 +defusedxml==0.7.1 +Deprecated==1.2.18 +Django==4.2 +django-extensions==4.1 +django-picklefield==3.3 +django-tastypie==0.15.1 +django_celery_results==2.6.0 docopt==0.6.2 -et-xmlfile==1.1.0 -Fiona==1.8.20 -future==0.18.3 -geopandas==0.10.2 -gitdb==4.0.9 -GitPython==3.1.30 -google-api-core==2.4.0 -google-api-python-client==2.36.0 -google-auth==1.35.0 -google-auth-httplib2==0.1.0 -google-auth-oauthlib==0.4.6 -googleapis-common-protos==1.54.0 -greenlet==1.1.2 -gunicorn==20.1.0 -h5py==3.6.0 -h5pyd==0.9.2 -httplib2==0.20.2 -idna==3.3 -importlib-metadata==4.10.1 -ipython-genutils==0.2.0 -isodate==0.6.1 +et_xmlfile==2.0.0 +executing==2.2.0 +fastjsonschema==2.21.2 +fiona==1.10.1 +future==1.0.0 +geopandas==1.1.1 +gitdb==4.0.12 +GitPython==3.1.45 +google-api-core==2.25.1 +google-api-python-client==2.179.0 +google-auth==2.40.3 +google-auth-httplib2==0.2.0 +google-auth-oauthlib==1.2.2 +googleapis-common-protos==1.70.0 +greenlet==3.2.4 +gunicorn==23.0.0 +h5py==3.14.0 +h5pyd==0.23.0 +httplib2==0.30.0 +idna==3.10 +importlib_metadata==8.7.0 +isodate==0.7.2 jdcal==1.4.1 -kombu==5.2.3 -lxml==4.9.1 -more-itertools==8.12.0 -msrest==0.6.21 -msrestazure==0.6.4 -munch==2.5.0 -numexpr==2.8.1 -numpy==1.22.1 +jedi==0.19.2 +Jinja2==3.1.6 +jsonschema==4.25.1 +jsonschema-specifications==2025.4.1 +kombu==5.5.4 +lxml==6.0.1 +markdown-it-py==4.0.0 +MarkupSafe==3.0.2 +mdurl==0.1.2 +mistune==3.1.4 +more-itertools==10.7.0 +msgpack==1.1.1 +msrest==0.7.1 +msrestazure==0.6.4.post1 +munch==4.0.0 +nbclient==0.10.2 +nbconvert==7.16.6 +nbformat==5.10.4 +ndindex==1.10.0 +numexpr==2.11.0 +numpy==2.3.2 numpy-financial==1.0.0 -oauthlib==3.2.2 -openpyxl==3.0.9 -packaging==21.3 -pandas==1.3.5 -pathlib2==2.3.6 -pbr==5.8.0 -pexpect==4.8.0 -pipreqs==0.4.11 -prompt-toolkit==3.0.24 -protobuf==3.19.5 -psutil==5.9.0 -psycopg2==2.9.3 +oauthlib==3.3.1 +openpyxl==3.1.5 +packaging==25.0 +pandas==2.3.2 +pandocfilters==1.5.1 +parso==0.8.5 +pathlib2==2.3.7.post1 +pbr==7.0.1 +pexpect==4.9.0 +pickleshare==0.7.5 +pipreqs==0.5.0 +platformdirs==4.4.0 +prompt_toolkit==3.0.52 +proto-plus==1.26.1 +protobuf==6.32.0 +psutil==7.0.0 +psycopg2==2.9.10 ptyprocess==0.7.0 -pyasn1==0.4.8 -pyasn1-modules==0.2.8 -pycodestyle==2.8.0 -pycparser==2.21 -Pygments==2.11.2 -PyJWT==2.4.0 -pyparsing==3.0.6 -pyproj==3.3.0 -python-dateutil==2.8.2 -python-mimeparse==1.6.0 -pytz==2021.3 +pure_eval==0.2.3 +py-cpuinfo==9.0.0 +pyasn1==0.6.1 +pyasn1_modules==0.4.2 +pycodestyle==2.14.0 +pycparser==2.22 +pydantic==2.11.7 +pydantic_core==2.33.2 +Pygments==2.19.2 +PyJWT==2.10.1 +pyogrio==0.11.1 +pyparsing==3.2.3 +pyproj==3.7.2 +python-dateutil==2.9.0.post0 +python-mimeparse==2.0.0 +pytz==2025.2 pytz-deprecation-shim==0.1.0.post0 -PyYAML==6.0 -redis==4.1.1 -requests==2.27.1 -requests-oauthlib==1.3.0 -requests-unixsocket==0.3.0 -rollbar==0.16.2 -rsa==4.8 -scipy==1.7.3 -Shapely==1.8.0 -six==1.16.0 -smmap==5.0.0 -SQLAlchemy==1.4.30 -sqlparse==0.4.2 -stevedore==3.5.0 -tables==3.7.0 +PyYAML==6.0.2 +pyzmq==27.0.2 +redis==6.4.0 +referencing==0.36.2 +requests==2.32.5 +requests-oauthlib==2.0.0 +requests-unixsocket==0.4.1 +rich==14.1.0 +rollbar==1.3.0 +rpds-py==0.27.1 +rsa==4.9.1 +scipy==1.16.1 +shapely==2.1.1 +six==1.17.0 +smmap==5.0.2 +soupsieve==2.8 +SQLAlchemy==2.0.43 +sqlparse==0.5.3 +stack-data==0.6.3 +stevedore==5.5.0 +tables==3.10.2 +tinycss2==1.4.0 toml==0.10.2 -traitlets==5.1.1 -typing_extensions==4.0.1 -tzdata==2021.5 -tzlocal==4.1 -uritemplate==4.1.1 -urllib3==1.26.8 -vine==5.0.0 -wcwidth==0.2.5 -wrapt==1.13.3 -xlsxwriter==3.1.9 -xlrd==2.0.1 +tornado==6.5.2 +traitlets==5.14.3 +typing_extensions==4.15.0 +tzdata==2025.2 +tzlocal==5.3.1 +uritemplate==4.2.0 +urllib3==2.5.0 +vine==5.1.0 +wcwidth==0.2.13 +webencodings==0.5.1 +wrapt==1.17.3 +xlrd==2.0.2 +xlsxwriter==3.2.5 yarg==0.1.9 -zipp==3.7.0 +zipp==3.23.0