Wind Energy: pass file path (instead of dict) to _compute_density_harvested_fields to prevent excessive debug logging

This commit is contained in:
Emily Davis 2025-01-24 13:33:04 -07:00
parent edebed084b
commit 63a3a91a58
3 changed files with 41 additions and 52 deletions

View File

@ -42,6 +42,10 @@ Unreleased Changes
* Updated the documentation for the ``mean_t_air`` attribute of the
``buildings_with_stats.shp`` output to clarify how the value is
calculated. https://github.com/natcap/invest/issues/1746
* Wind Energy
* Fixed a bug that could cause the Workbench to crash when running the Wind
Energy model with ``Taskgraph`` logging set to ``DEBUG`` (`InVEST #1497
<https://github.com/natcap/invest/issues/1497>`_).
3.14.3 (2024-12-19)
-------------------

View File

@ -694,14 +694,6 @@ def execute(args):
number_of_turbines = int(args['number_of_turbines'])
# Create a list of the biophysical parameters we are looking for from the
# input csv files
biophysical_params = [
'cut_in_wspd', 'cut_out_wspd', 'rated_wspd', 'hub_height',
'turbine_rated_pwr', 'air_density', 'exponent_power_curve',
'air_density_coefficient', 'loss_parameter'
]
# Read the biophysical turbine parameters into a dictionary
turbine_dict = validation.get_validated_dataframe(
args['turbine_parameters_path'],
@ -753,31 +745,13 @@ def execute(args):
for time_step in range(int(time) + 1):
price_list.append(wind_price * (1 + change_rate)**(time_step))
# Hub Height to use for setting Weibull parameters
hub_height = parameters_dict['hub_height']
LOGGER.debug('hub_height : %s', hub_height)
# Read the wind energy data into a dictionary
LOGGER.info('Reading in Wind Data into a dictionary')
wind_point_df = validation.get_validated_dataframe(
args['wind_data_path'], **MODEL_SPEC['args']['wind_data_path'])
wind_point_df.columns = wind_point_df.columns.str.upper()
# Calculate scale value at new hub height given reference values.
# See equation 3 in users guide
wind_point_df.rename(columns={'LAM': 'REF_LAM'}, inplace=True)
wind_point_df['LAM'] = wind_point_df.apply(
lambda row: row.REF_LAM * (hub_height / row.REF)**_ALPHA, axis=1)
wind_point_df.drop(['REF'], axis=1) # REF is not needed after calculation
wind_data = wind_point_df.to_dict('index') # so keys will be 0, 1, 2, ...
# Compute Wind Density and Harvested Wind Energy, adding the values to the
# points to the dictionary, and pickle the dictionary
# Compute Wind Density and Harvested Wind Energy,
# and pickle the resulting dictionary
wind_data_pickle_path = os.path.join(
inter_dir, 'wind_data%s.pickle' % suffix)
compute_density_harvested_task = task_graph.add_task(
func=_compute_density_harvested_fields,
args=(wind_data, parameters_dict, number_of_turbines,
args=(args['wind_data_path'], parameters_dict, number_of_turbines,
wind_data_pickle_path),
target_path_list=[wind_data_pickle_path],
task_name='compute_density_harvested_fields')
@ -1911,14 +1885,12 @@ def _create_distance_raster(base_raster_path, base_vector_path,
def _compute_density_harvested_fields(
wind_dict, parameters_dict, number_of_turbines,
wind_data_path, parameters_dict, number_of_turbines,
target_pickle_path):
"""Compute the density and harvested energy based on scale and shape keys.
Args:
wind_dict (dict): a dictionary whose values are a dictionary with
keys ``LAM``, ``LATI``, ``K``, ``LONG``, ``REF_LAM``, and ``REF``,
and numbers indicating their corresponding values.
wind_data_path (str): path to wind data input.
parameters_dict (dict): a dictionary where the 'parameter_list'
strings are the keys that have values pulled from bio-parameters
@ -1928,13 +1900,30 @@ def _compute_density_harvested_fields(
for the wind farm.
target_pickle_path (str): a path to the pickle file that has
wind_dict_copy, a modified dictionary with new fields computed
from the existing fields and bio-parameters.
wind_dict_copy, a modified dictionary of wind data with additional
fields computed from the existing fields and bio-parameters.
Returns:
None
"""
# Hub Height to use for setting Weibull parameters
hub_height = parameters_dict['hub_height']
LOGGER.debug('hub_height : %s', hub_height)
# Read the wind energy data into a dictionary
LOGGER.info('Reading in Wind Data into a dictionary')
wind_point_df = validation.get_validated_dataframe(
wind_data_path, **MODEL_SPEC['args']['wind_data_path'])
wind_point_df.columns = wind_point_df.columns.str.upper()
# Calculate scale value at new hub height given reference values.
# See equation 3 in users guide
wind_point_df.rename(columns={'LAM': 'REF_LAM'}, inplace=True)
wind_point_df['LAM'] = wind_point_df.apply(
lambda row: row.REF_LAM * (hub_height / row.REF)**_ALPHA, axis=1)
wind_point_df.drop(['REF'], axis=1) # REF is not needed after calculation
wind_dict = wind_point_df.to_dict('index') # so keys will be 0, 1, 2, ...
wind_dict_copy = wind_dict.copy()
# The rated power is expressed in units of MW but the harvested energy
@ -1952,9 +1941,6 @@ def _compute_density_harvested_fields(
air_density_coef = parameters_dict['air_density_coefficient']
losses = parameters_dict['loss_parameter']
# Hub Height to use for setting Weibull parameters
hub_height = parameters_dict['hub_height']
# Compute the mean air density, given by CKs formulas
mean_air_density = air_density_standard - air_density_coef * hub_height

View File

@ -274,7 +274,7 @@ class WindEnergyUnitTests(unittest.TestCase):
from natcap.invest import wind_energy
srs = osr.SpatialReference()
srs.ImportFromEPSG(3157) #UTM Zone 10N
srs.ImportFromEPSG(3157) # UTM Zone 10N
projection_wkt = srs.ExportToWkt()
origin = (443723.127327877911739, 4956546.905980412848294)
pos_x = origin[0]
@ -284,7 +284,7 @@ class WindEnergyUnitTests(unittest.TestCase):
fields = {'id': ogr.OFTReal}
attrs = [{'id': 1}]
# Square polygon that will overlap the 4 pixels of the raster in the
# Square polygon that will overlap the 4 pixels of the raster in the
# upper left corner
poly_geometry = [box(pos_x, pos_y - 17, pos_x + 17, pos_y)]
poly_vector_path = os.path.join(
@ -306,7 +306,7 @@ class WindEnergyUnitTests(unittest.TestCase):
dist_raster_path = os.path.join(self.workspace_dir, 'dist.tif')
# Call function to test given testing inputs
wind_energy._create_distance_raster(
base_raster_path, poly_vector_path, dist_raster_path,
base_raster_path, poly_vector_path, dist_raster_path,
self.workspace_dir)
# Compare the results
@ -348,11 +348,9 @@ class WindEnergyUnitTests(unittest.TestCase):
price_list = [0.10, 0.10, 0.10, 0.10, 0.10]
srs = osr.SpatialReference()
srs.ImportFromEPSG(3157) #UTM Zone 10N
srs.ImportFromEPSG(3157) # UTM Zone 10N
projection_wkt = srs.ExportToWkt()
origin = (443723.127327877911739, 4956546.905980412848294)
pos_x = origin[0]
pos_y = origin[1]
# Create harvested raster
harvest_val = 1000000
@ -360,8 +358,8 @@ class WindEnergyUnitTests(unittest.TestCase):
[[harvest_val, harvest_val + 1e5, harvest_val + 2e5,
harvest_val + 3e5, harvest_val + 4e5],
[harvest_val, harvest_val + 1e5, harvest_val + 2e5,
harvest_val + 3e5, harvest_val + 4e5],
], dtype=numpy.float32)
harvest_val + 3e5, harvest_val + 4e5]],
dtype=numpy.float32)
base_harvest_path = os.path.join(self.workspace_dir, 'harvest_raster.tif')
# Create raster to use for testing input
pygeoprocessing.numpy_array_to_raster(
@ -386,9 +384,9 @@ class WindEnergyUnitTests(unittest.TestCase):
# Compare the results that were "eye" tested.
desired_npv_array = numpy.array(
[[309332320.0, 348331200.0, 387330020.0, 426328930.0,
465327800.0],
465327800.0],
[309332320.0, 348331200.0, 387330020.0, 426328930.0,
465327800.0]], dtype=numpy.float32)
465327800.0]], dtype=numpy.float32)
actual_npv_array = pygeoprocessing.raster_to_numpy_array(
target_npv_raster_path)
numpy.testing.assert_allclose(actual_npv_array, desired_npv_array)
@ -402,6 +400,7 @@ class WindEnergyUnitTests(unittest.TestCase):
numpy.testing.assert_allclose(
actual_levelized_array, desired_levelized_array)
class WindEnergyRegressionTests(unittest.TestCase):
"""Regression tests for the Wind Energy module."""
@ -428,8 +427,8 @@ class WindEnergyRegressionTests(unittest.TestCase):
SAMPLE_DATA, 'global_wind_energy_parameters.csv'),
'turbine_parameters_path': os.path.join(
SAMPLE_DATA, '3_6_turbine.csv'),
'number_of_turbines': '80', # pass str to test casting
'min_depth': '3', # pass str to test casting
'number_of_turbines': '80', # pass str to test casting
'min_depth': '3', # pass str to test casting
'max_depth': 180,
'n_workers': -1
}
@ -534,13 +533,13 @@ class WindEnergyRegressionTests(unittest.TestCase):
args['max_distance'] = 200000
args['valuation_container'] = True
args['foundation_cost'] = 2000000
args['discount_rate'] = '0.07' # pass str to test casting
args['discount_rate'] = '0.07' # pass str to test casting
# Test that only grid points are provided in grid_points_path
args['grid_points_path'] = os.path.join(
SAMPLE_DATA, 'resampled_grid_pts.csv')
args['price_table'] = False
args['wind_price'] = 0.187
args['rate_change'] = '0.2' # pass str to test casting
args['rate_change'] = '0.2' # pass str to test casting
wind_energy.execute(args)