-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: more wind power curves #53
Conversation
ad7a9df
to
ecb5402
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a bunch of comments, most of them are small. Nice tests!
Once the U and V components of the wind are converted to a non-directional | ||
wind speed magnitude, this speed is converted to power using wind turbine |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't most wind turbines only work with wind in a specific direction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe all modern turbines pivot to follow the wind.
wind speed. This distribution tends to boost the power produced at lower | ||
wind speeds (since the power curve in this region is convex) and lower the | ||
power produced at higher wind speeds (since the power curve in this region is | ||
concave as the turbine tops out, and shuts down at higher wind speeds). This | ||
tracks with the wind-farm level data shown in NREL's validation report. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
data_dir = path.abspath(path.join(path.dirname(__file__), '..', 'data')) | ||
Form860 = get_form_860(data_dir) | ||
powercurves_path = path.join(data_dir, 'PowerCurves.csv') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice. I believe this makes the paths work on both windows and mac?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
path.join
makes path-gluing work on all systems. path.abspath
takes any relative path (including ..
s) and transforms it to an absolute path.
if not path.isdir(data_dir): | ||
raise ValueError('data_dir is not a valid directory') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
if not isinstance(year, int): | ||
raise TypeError('year is not an int') | ||
|
||
form_860_filename = ''.join(['3_2_Wind_Y', str(year), '.csv']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hot tip: you can use a format string and it's way simpler: f'3_2_Wind_Y{year}.csv'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hadn't seen this before! Very handy.
except FileNotFoundError: | ||
regex_str = r'3_2_Wind_Y(\d{4}).csv' | ||
matching_years = [ | ||
re.match(regex_str, f).group(1) | ||
for f in os.listdir(data_dir) if re.match(regex_str, f)] | ||
err_msg = ' '.join(['form data for year', str(year), 'not found. ']) | ||
err_msg += 'Years with data: ' + ', '.join(matching_years) | ||
raise ValueError(err_msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's some stuff on glob if you want to check it out https://docs.python.org/3/library/glob.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I'm not grepping glob yet, but I think os + regex is a bit clearer for this application. I want to look through the directory, for filenames that match a pattern, and then extract part of that filename.
data_dir = path.abspath(path.join(path.dirname(__file__), '..', 'data')) | ||
Form860 = get_form_860(data_dir) | ||
powercurves_path = path.join(data_dir, 'PowerCurves.csv') | ||
PowerCurves = pd.read_csv(powercurves_path, index_col=0, header=None).T | ||
PowerCurves.set_index('Speed bin (m/s)', inplace=True) | ||
statepowercurves_path = path.join(data_dir, 'StatePowerCurves.csv') | ||
try: | ||
StatePowerCurves = pd.read_csv(statepowercurves_path, index_col=0) | ||
except FileNotFoundError: | ||
StatePowerCurves = build_state_curves(Form860, PowerCurves, rsd=0.4) | ||
StatePowerCurves.to_csv(statepowercurves_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed in person and we're planning on making this into two functions instead
cumulative_curve = np.zeros_like(curve_x) | ||
cumulative_capacity = 0 | ||
state_wind_farms = Form860[Form860['State'] == s] | ||
for i, f in enumerate(state_wind_farms.index): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't know enumerate()
was a thing! That's super useful. Also I think you might be able to operate over the entire dataframe at once instead of looping over it. Let me get back to you on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enumerate
is great. It beats the hell out of for i in range(len(foo)):
for s in states: | ||
x = state_curves.index | ||
y = np.zeros_like(x) | ||
for i, w in enumerate(x): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
starting to get hard to follow the variable names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
sample_points = np.logical_and((x > min_point),(x < max_point)) | ||
cdf_points = norm.cdf(x[sample_points], loc=w, scale=sd) | ||
pdf_points = np.concatenate((np.zeros(1), np.diff(cdf_points))) | ||
#pdf_points *= 1 / np.sum(pdf_points) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
forgot to delete this
Changes made based on the review by @merrielle. @rouille, I think you'll want to take a look at some of the changes I made here. The biggest one is that More tests added as well.
|
It is working. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 👍 This looks awesome. Great job with the changes and excellent tests!
if w == 0: | ||
xs = state_curves.index | ||
ys = np.zeros_like(xs) | ||
for i, x in enumerate(xs): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, this actually helps a lot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
w
for wind speed totally made sense in my head at the time...
This branch contains changes to the way wind profiles are generated that have been implemented in the latest profiles for Eastern. EIA Form 860 data are used to create state average turbine power curves (based on turbine models installed at real wind farms, the heights they're installed at, and the power curve for each turbine). When
power_curves.py
is imported, state power curves are build and saved if they are not already present in the expected directory.Where turbine power curves are not available, or there are no wind farms listed in EIA Form 860, the IEC Class 2 curve is used by default. Tests are contained in
test_power_curves.py
to ensure that these functions are working properly.Wind power profiles for farms are generated from state average turbine power curves based on a distribution of wind speeds throughout an hour (temporally) and throughout a farm (spatially). More information is given in the README.