-
Notifications
You must be signed in to change notification settings - Fork 1.1k
sam iotools #1371 #1556
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
Open
shirubana
wants to merge
20
commits into
pvlib:main
Choose a base branch
from
shirubana:issue1371
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
sam iotools #1371 #1556
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
100d1f4
sam iotools #1371
shirubana dfd5aa9
add to reference
shirubana 1de291b
update the latest whatsnew that I found
shirubana 2de5a37
stickler
shirubana 19b05bd
stickler take 2
shirubana c573344
stickler take 3
shirubana d9672e9
codecov fix
shirubana 98407a6
back with the stickler
shirubana ebded83
Anton's suggestion
shirubana 82a24c9
update samp.py
shirubana 9d3c87b
pvlib.iotools.read_psm3 updates for SAM
shirubana 5d72491
updates williamhobbs
shirubana 82de598
propagate new write_sam name
shirubana 747acc8
silently cursing the stickler in spanish..
shirubana 1b684e0
again
shirubana f366fcf
i don't get it
shirubana 0dfa847
Update pvlib/tests/iotools/test_sam.py
shirubana 492e373
loop data
shirubana e4356f9
asdf
shirubana 34f2f9c
DNI and DHI typo
shirubana File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,178 @@ | ||
| """Functions for reading and writing SAM data files.""" | ||
|
|
||
| import pandas as pd | ||
|
|
||
|
|
||
| def saveSAM_WeatherFile(data, metadata, savefile='SAM_WeatherFile.csv', | ||
| standardSAM=True, includeminute=False): | ||
| """ | ||
| Saves dataframe with weather data from pvlib format on SAM-friendly format. | ||
|
|
||
| Parameters | ||
| ----------- | ||
| data : pandas.DataFrame | ||
| timeseries data in PVLib format. Should be TZ converted (not UTC). | ||
| Ideally it is one sequential year data; if not suggested to use | ||
| standardSAM = False. | ||
| metdata : dictionary | ||
| Dictionary with 'latitude', 'longitude', 'elevation', 'source', | ||
| and 'TZ' for timezone. | ||
| savefile : str | ||
| Name of file to save output as. | ||
| standardSAM : boolean | ||
| This checks the dataframe to avoid having a leap day, then averages it | ||
| to SAM style (closed to the right), | ||
| and fills the years so it starst on YEAR/1/1 0:0 and ends on | ||
| YEAR/12/31 23:00. | ||
| includeminute ; Bool | ||
| For hourly data, if SAM input does not have Minutes, it calculates the | ||
| sun position 30 minutes prior to the hour (i.e. 12 timestamp means sun | ||
| position at 11:30). | ||
| If minutes are included, it will calculate the sun position at the time | ||
| of the timestamp (12:00 at 12:00) | ||
| Set to true if resolution of data is sub-hourly. | ||
|
|
||
| Returns | ||
| ------- | ||
| Nothing, it just writes the file. | ||
|
|
||
| """ | ||
|
|
||
| def _is_leap_and_29Feb(s): | ||
| ''' Creates a mask to help remove Leap Years. Obtained from: | ||
| https://stackoverflow.com/questions/34966422/remove-leap-year-day- | ||
| from-pandas-dataframe/34966636 | ||
| ''' | ||
| return (s.index.year % 4 == 0) & \ | ||
| ((s.index.year % 100 != 0) | (s.index.year % 400 == 0)) & \ | ||
| (s.index.month == 2) & (s.index.day == 29) | ||
|
|
||
| def _averageSAMStyle(df, interval='60T', closed='right', label='right'): | ||
| ''' Averages subhourly data into hourly data in SAM's expected format. | ||
| ''' | ||
| df = df.resample(interval, closed=closed, label=label).mean() | ||
| return df | ||
|
|
||
| def _fillYearSAMStyle(df, freq='60T'): | ||
| ''' Fills year | ||
| ''' | ||
| # add zeros for the rest of the year | ||
| # add a timepoint at the end of the year | ||
| # apply correct TZ info (if applicable) | ||
| tzinfo = df.index.tzinfo | ||
| starttime = pd.to_datetime('%s-%s-%s %s:%s' % (df.index.year[0], 1, 1, | ||
| 0, 0) | ||
| ).tz_localize(tzinfo) | ||
| endtime = pd.to_datetime('%s-%s-%s %s:%s' % (df.index.year[-1], 12, 31, | ||
| 23, 60-int(freq[:-1])) | ||
| ).tz_localize(tzinfo) | ||
|
|
||
| df2 = _averageSAMStyle(df, freq) | ||
| df2.iloc[0] = 0 # set first datapt to zero to forward fill w zeros | ||
| df2.iloc[-1] = 0 # set last datapt to zero to forward fill w zeros | ||
| df2.loc[starttime] = 0 | ||
| df2.loc[endtime] = 0 | ||
| df2 = df2.resample(freq).ffill() | ||
| return df2 | ||
|
|
||
| # Modify this to cut into different years. Right now handles partial year | ||
| # and sub-hourly interval. | ||
| if standardSAM: | ||
| filterdatesLeapYear = ~(_is_leap_and_29Feb(data)) | ||
| data = data[filterdatesLeapYear] | ||
| data = _fillYearSAMStyle(data) | ||
|
|
||
| # metadata | ||
| latitude = metadata['latitude'] | ||
| longitude = metadata['longitude'] | ||
| elevation = metadata['elevation'] | ||
| timezone_offset = metadata['TZ'] | ||
| source = metadata['source'] | ||
|
|
||
| # make a header | ||
| header = '\n'.join(['Source,Latitude,Longitude,Time Zone,Elevation', | ||
| source + ',' + str(latitude) + ',' + str(longitude) | ||
| + ',' + str(timezone_offset) + ',' + | ||
| str(elevation)])+'\n' | ||
|
|
||
| savedata = pd.DataFrame({'Year': data.index.year, | ||
| 'Month': data.index.month, | ||
| 'Day': data.index.day, | ||
| 'Hour': data.index.hour}) | ||
|
|
||
| if includeminute: | ||
| savedata['Minute'] = data.index.minute | ||
|
|
||
| windspeed = list(data.wind_speed) | ||
| temp_amb = list(data.temp_air) | ||
| savedata['Wspd'] = windspeed | ||
| savedata['Tdry'] = temp_amb | ||
|
|
||
| if 'dni' in data: | ||
| dni = list(data.dni) | ||
| savedata['DHI'] = dni | ||
|
|
||
| if 'dhi' in data: | ||
| dhi = list(data.dhi) | ||
| savedata['DNI'] = dhi | ||
|
|
||
| if 'ghi' in data: | ||
| ghi = list(data.ghi) | ||
| savedata['GHI'] = ghi | ||
|
|
||
| if 'poa' in data: | ||
| poa = list(data.poa) | ||
| savedata['POA'] = poa | ||
|
|
||
| if 'albedo' in data: | ||
| albedo = list(data.albedo) | ||
| savedata['Albedo'] = albedo | ||
|
|
||
| # Not elegant but seems to work for the standardSAM format | ||
| if standardSAM and savedata.Albedo.iloc[0] == 0: | ||
| savedata.loc[savedata.index[0], 'Albedo'] = savedata.loc[ | ||
| savedata.index[1]]['Albedo'] | ||
| savedata.loc[savedata.index[-1], 'Albedo'] = savedata.loc[ | ||
| savedata.index[-2]]['Albedo'] | ||
|
|
||
| with open(savefile, 'w', newline='') as ict: | ||
| # Write the header lines, including the index variable for | ||
| # the last one if you're letting Pandas produce that for you. | ||
| # (see above). | ||
| for line in header: | ||
| ict.write(line) | ||
|
|
||
| savedata.to_csv(ict, index=False) | ||
|
|
||
|
|
||
| def tz_convert(df, tz_convert_val, metadata=None): | ||
| """ | ||
| Support function to convert metdata to a different local timezone. | ||
| Particularly for GIS weather files which are returned in UTC by default. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| df : DataFrame | ||
| A dataframe in UTC timezone | ||
| tz_convert_val : int | ||
| Convert timezone to this fixed value, following ISO standard | ||
| (negative values indicating West of UTC.) | ||
| Returns: metdata, metadata | ||
|
|
||
|
|
||
| Returns | ||
| ------- | ||
| df : DataFrame | ||
| Dataframe in the converted local timezone. | ||
| metadata : dict | ||
| Adds (or updates) the existing Timezone in the metadata dictionary | ||
|
|
||
| """ | ||
| import pytz | ||
| if (type(tz_convert_val) == int) | (type(tz_convert_val) == float): | ||
| df = df.tz_convert(pytz.FixedOffset(tz_convert_val*60)) | ||
|
|
||
| if metadata is not None: | ||
| metadata['TZ'] = tz_convert_val | ||
| return df, metadata | ||
| return df | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| """ | ||
| test the SAM IO tools | ||
| """ | ||
| from pvlib.iotools import get_pvgis_tmy | ||
| from pvlib.iotools import saveSAM_WeatherFile, tz_convert | ||
| from ..conftest import DATA_DIR | ||
| import numpy as np | ||
|
|
||
| # PVGIS Hourly tests | ||
| # The test files are actual files from PVGIS where the data section have been | ||
| # reduced to only a few lines | ||
| testfile_radiation_csv = DATA_DIR / \ | ||
| 'pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv' | ||
|
|
||
|
|
||
| def test_saveSAM_WeatherFile(): | ||
| data, months_selected, inputs, metadata = get_pvgis_tmy(latitude=33, | ||
| longitude=-110, | ||
| map_variables=True) | ||
| metadata = {'latitude': inputs['location']['latitude'], | ||
| 'longitude': inputs['location']['longitude'], | ||
| 'elevation': inputs['location']['elevation'], | ||
| 'source': 'User-generated'} | ||
| data, metadata = tz_convert(data, tz_convert_val=-7, metadata=metadata) | ||
| data['albedo'] = 0.2 | ||
| coerce_year = 2021 | ||
| data['poa'] = np.nan | ||
| data.index = data.index.map(lambda dt: dt.replace(year=coerce_year)) | ||
| saveSAM_WeatherFile(data, metadata, savefile='test_SAMWeatherFile.csv', | ||
| standardSAM=True, includeminute=True) |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.