Skip to content

Commit

Permalink
Merge pull request #327 from NREL/develop
Browse files Browse the repository at this point in the history
PV Fixes (master < develop)
  • Loading branch information
adfarth authored Jun 20, 2022
2 parents 120bff8 + e087122 commit f1bea98
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 43 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ Classify the change according to the following categories:
##### Removed
### Patches

# v2.0.3
### Minor Updates
##### Fixed
- In `src/pvwatts.py`, Updated lat-long coordinates if-statement used to determine whether the "nsrdb" dataset should be used to determine the PV prod factor. Accounts for recent updates to NSRDB data used by PVWatts (v6)
- Avoids overwriting user-entered PV azimuth (other than 180) for ground-mount systems in southern hemisphere
- Updates default azimuth to 0 for southern latitudes for all PV types (rather than just for ground-mount)

# v2.0.2
### Patches
- bug fix for 15/30 minute scenarios with URDB TOU demand rates
Expand Down
4 changes: 2 additions & 2 deletions reo/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def obj_create(self, bundle, **kwargs):
set_status(data, 'Optimizing...')
data['outputs']['Scenario']['Profile']['pre_setup_scenario_seconds'] = profiler.getDuration()
if bundle.request.META.get('HTTP_X_API_USER_ID') or False:
if bundle.request.META.get('HTTP_X_API_USER_ID') or '' == '6f09c972-8414-469b-b3e8-a78398874103':
if bundle.request.META.get('HTTP_X_API_USER_ID', '') == '6f09c972-8414-469b-b3e8-a78398874103':
data['outputs']['Scenario']['job_type'] = 'REopt Web Tool'
else:
data['outputs']['Scenario']['job_type'] = 'developer.nrel.gov'
Expand Down Expand Up @@ -338,7 +338,7 @@ def obj_create(self, bundle, **kwargs):
set_status(data, 'Optimizing...')
data['outputs']['Scenario']['Profile']['pre_setup_scenario_seconds'] = profiler.getDuration()
if bundle.request.META.get('HTTP_X_API_USER_ID') or False:
if bundle.request.META.get('HTTP_X_API_USER_ID') or '' == '6f09c972-8414-469b-b3e8-a78398874103':
if bundle.request.META.get('HTTP_X_API_USER_ID', '') == '6f09c972-8414-469b-b3e8-a78398874103':
data['outputs']['Scenario']['job_type'] = 'REopt Web Tool'
else:
data['outputs']['Scenario']['job_type'] = 'developer.nrel.gov'
Expand Down
2 changes: 1 addition & 1 deletion reo/nested_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,7 @@ def list_of_dict(input):
},
"prod_factor_series_kw": {
"type": "list_of_float",
"description": "Optional user-defined production factors. Entries have units of kWh/kW, representing the energy (kWh) output of a 1 kW system in each time step. Must be hourly (8,760 samples), 30 minute (17,520 samples), or 15 minute (35,040 samples)."
"description": "Optional user-defined production factors. Entries have units of kWh/kW, representing the AC energy (kWh) output of a 1 kW-DC system in each time step. Must be hourly (8,760 samples), 30 minute (17,520 samples), or 15 minute (35,040 samples)."
},
"can_net_meter": {
"type": "bool",
Expand Down
20 changes: 6 additions & 14 deletions reo/src/pvwatts.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def __init__(self,
module_type=0,
timeframe='hourly',
gcr=0.4,
dc_ac_ratio=1.1,
dc_ac_ratio=1.2,
inv_eff=0.96,
radius=100,
dataset="nsrdb",
Expand Down Expand Up @@ -130,16 +130,6 @@ def __init__(self,
self.verify = verify # used for testing
self.response = None
self.response = self.data # store response so don't hit API multiple times
if self.tilt is None:
# if the site is in the southern hemisphere, and no tilt has been specified,
# then set the tilt to the positive latitude value and change the azimuth to zero
if self.latitude < 0:
self.tilt = self.latitude * -1
self.azimuth = 0
else:
# if the site is in the norther hemisphere, and no tilt has been specified,
# then set the tilt to the latitude value
self.tilt = self.latitude

@property
def url(self):
Expand All @@ -155,9 +145,11 @@ def url(self):
def data(self):
if self.response is None:
# Check if point is beyond the bounds of the NRSDB dataset, if so use the international dataset and double the radius
if self.latitude < -59.5 or self.latitude > 60.01 or self.longitude > -22.37 or self.longitude < -179.58 :
self.dataset = 'intl'
self.radius = self.radius *2
if self.longitude < -179.5 or self.longitude > -21.0 or self.latitude < -21.5 or self.latitude > 60.0:
if self.longitude < 81.5 or self.longitude > 179.5 or self.latitude < -43.8 or self.latitude > 60.0 :
if self.longitude < 67.0 or self.longitude > 81.5 or self.latitude < -43.8 or self.latitude > 38.0:
self.dataset = 'intl'
self.radius = self.radius *2
resp = requests.get(self.url, verify=self.verify)
log.info("PVWatts API query successful.")
data = check_pvwatts_response_data(resp)
Expand Down
14 changes: 5 additions & 9 deletions reo/src/techs.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,22 +131,18 @@ def __init__(self, dfm, degradation_pct, time_steps_per_hour=1, acres_per_kw=6e-
self.pvwatts = None
self.sr_required_pct = kwargs.get("sr_required_pct")

# If site is in southern hemisphere and user has not changed from default azimuth of 180, update to 0 for all array types
if kwargs.get('latitude') < 0:
if self.azimuth == 180: # Assume user does not want array facing away from equator
self.azimuth = 0
# if user hasn't entered the tilt (default value is 0.537), tilt value gets assigned based on array_type
if self.tilt == 0.537:
if kwargs.get('array_type') == 0: # 0 are Ground Mount Fixed (Open Rack) arrays, we assume an optimal tilt
"""
start assuming the site is in the northern hemisphere, set the tilt to the latitude and leave the
default azimuth of 180 (unless otherwise specified)
"""
self.tilt = kwargs.get('latitude')
if kwargs.get('latitude') < 0:
"""
if the site is in the southern hemisphere, now set the tilt to the positive latitude value and
change the azimuth to 0. Also update kwargs going forward so they get saved to the database later
show up in final results
"""
self.tilt = -1 * self.tilt
self.azimuth = 0
self.tilt = abs(kwargs.get('latitude')) # if site is in southern hemisphere will set tilt to positive latitude value
else: # All other tilts come from lookup table included in the array_type_to_tilt_angle dictionary above
self.tilt = PV.array_type_to_tilt_angle[kwargs.get('array_type')]

Expand Down
52 changes: 35 additions & 17 deletions reo/tests/test_southern_hemisphere_latitude.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,39 +31,51 @@
from tastypie.test import ResourceTestCaseMixin
from django.test import TestCase
from reo.models import ModelManager

import numpy as np

class NegativeLatitudeTest(ResourceTestCaseMixin, TestCase):
REopt_tol = 1e-2

def setUp(self):
super(NegativeLatitudeTest, self).setUp()
self.reopt_base = '/v1/job/'
self.reopt_base = '/v2/job/'

def get_response(self, data):
return self.api_client.post(self.reopt_base, format='json', data=data)

def test_southern_hemisphere_latitude(self):
"""
Tests locations in the Southern Hemisphere where the tilt must be set to the latitude (array type 0 - Ground Mount Fixed (Open Rack)).
Tests locations in the Southern Hemisphere (Indonesia)
where the tilt must be set to the latitude (array type 0 - Ground Mount Fixed (Open Rack)).
In these cases we need to make the negative latitude positive and set the azimuth to 0.
Also tests that NSRDB HIMIWARI data for SE Asia is utilized to determine the PV prod factor.
:return:
"""

nested_data = {'Scenario':{
'Site': {
'latitude':-31.9505,
'longitude':115.8605,
'ElectricTariff': {'urdb_label':'5b78d2775457a3bf45af0aed'},
'LoadProfile': {'doe_reference_name': 'Hospital',
'annual_kwh':12000},
'Wind':{'max_kw':0},
'PV': {'array_type': 0}
}
}
}
nested_data = {"Scenario": {
"Site": {
"longitude": 130.1452734,
"latitude": -3.2384616,
"PV": {
"min_kw": 1000,
"max_kw": 1000,
'array_type': 0,
'degradation_pct': 0.0
},
"LoadProfile": {
"doe_reference_name": "FlatLoad",
"annual_kwh": 10000000.0,
"city": "LosAngeles"
},
"ElectricTariff": {
"urdb_label": "5ed6c1a15457a3367add15ae"
},
"Financial": {
"escalation_pct": 0.026,
"analysis_years": 25
}
}}}

resp = self.get_response(data=nested_data)
self.assertHttpCreated(resp)
Expand All @@ -72,13 +84,19 @@ def test_southern_hemisphere_latitude(self):
d = ModelManager.make_response(run_uuid=run_uuid)
messages = d['messages']

a = d["outputs"]["Scenario"]["Site"]["PV"]
pv_list = a['year_one_to_battery_series_kw'], a['year_one_to_load_series_kw'], a['year_one_to_grid_series_kw'], a['year_one_curtailed_production_series_kw']
tot_pv = np.sum(pv_list, axis=0)
total_pv = sum(tot_pv)

try:
self.assertEqual(d['inputs']['Scenario']['Site']['PV']['azimuth'], 0,
"Not adjusting azimuth for negative latitudes.")

self.assertEqual(d['inputs']['Scenario']['Site']['PV']['tilt'], 31.9505,
self.assertEqual(d['inputs']['Scenario']['Site']['PV']['tilt'], 3.2384616,
"Not adjusting tilt for negative latitudes"
)
self.assertAlmostEqual(total_pv, 1246915, delta=0.5) # checked against PVWatts online calculator

except Exception as e:
error_msg = None
Expand Down

0 comments on commit f1bea98

Please sign in to comment.