Skip to content

Commit

Permalink
Merge pull request #87 from NREL/Kempe_Gap_Calc
Browse files Browse the repository at this point in the history
Kempe gap calc
  • Loading branch information
martin-springer authored May 6, 2024
2 parents dbde447 + c352954 commit a4ffcb8
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 741 deletions.
4 changes: 2 additions & 2 deletions pvdeg/data/xeff_demo.csv
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Source,Location ID,City,State,Country,Latitude,Longitude,Time Zone,Elevation,Local Time Zone,Clearsky DHI Units,Clearsky DNI Units,Clearsky GHI Units,Dew Point Units,DHI Units,DNI Units,GHI Units,Solar Zenith Angle Units,Temperature Units,Pressure Units,Relative Humidity Units,Precipitable Water Units,Wind Direction Units,Wind Speed Units,Cloud Type -15,Cloud Type 0,Cloud Type 1,Cloud Type 2,Cloud Type 3,Cloud Type 4,Cloud Type 5,Cloud Type 6,Cloud Type 7,Cloud Type 8,Cloud Type 9,Cloud Type 10,Cloud Type 11,Cloud Type 12,Fill Flag 0,Fill Flag 1,Fill Flag 2,Fill Flag 3,Fill Flag 4,Fill Flag 5,Surface Albedo Units,Version,Tilt,Azimuth,Wind_Height_m
Source,Location ID,City,State,Country,Latitude,Longitude,Time Zone,Elevation,Local Time Zone,Clearsky DHI Units,Clearsky DNI Units,Clearsky GHI Units,Dew Point Units,DHI Units,DNI Units,GHI Units,Solar Zenith Angle Units,Temperature Units,Pressure Units,Relative Humidity Units,Precipitable Water Units,Wind Direction Units,Wind Speed Units,Cloud Type -15,Cloud Type 0,Cloud Type 1,Cloud Type 2,Cloud Type 3,Cloud Type 4,Cloud Type 5,Cloud Type 6,Cloud Type 7,Cloud Type 8,Cloud Type 9,Cloud Type 10,Cloud Type 11,Cloud Type 12,Fill Flag 0,Fill Flag 1,Fill Flag 2,Fill Flag 3,Fill Flag 4,Fill Flag 5,Surface Albedo Units,Version,tilt,azimuth,wind_height
NSRDB,145809,-,-,-,39.73,-105.18,-7,1820,-7,w/m2,w/m2,w/m2,c,w/m2,w/m2,w/m2,Degree,c,mbar,%,cm,Degrees,m/s,N/A,Clear,Probably Clear,Fog,Water,Super-Cooled Water,Mixed,Opaque Ice,Cirrus,Overlapping,Overshooting,Unknown,Dust,Smoke,N/A,Missing Image,Low Irradiance,Exceeds Clearsky,Missing CLoud Properties,Rayleigh Violation,N/A,3.0.6,39.73,180,2
Year,Month,Day,Hour,Minute,DNI,DHI,GHI,Temperature,Dew Point,Wind Speed,Relative Humidity,Module_Temperature,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Year,Month,Day,Hour,Minute,DNI,DHI,GHI,Temperature,Dew Point,Wind Speed,Relative Humidity,module_temperature,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
1999,1,1,0,30,0,0,0,0,-5,1.8,79.39,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
1999,1,1,1,30,0,0,0,0,-4,1.7000000000000002,80.84,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
1999,1,1,2,30,0,0,0,0,-4,1.5,82.98,0,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
Expand Down
22 changes: 11 additions & 11 deletions pvdeg/degradation.py
Original file line number Diff line number Diff line change
Expand Up @@ -723,16 +723,12 @@ def degradation(

return degradation


# change it to take pd.DataFrame? instead of np.ndarray
@njit
def vecArrhenius(
poa_global : np.ndarray,
module_temp : np.ndarray,
ea : float,
x : float,
lnr0 : float
) -> float:

poa_global: np.ndarray, module_temp: np.ndarray, ea: float, x: float, lnr0: float
) -> float:
"""
Calculates degradation using :math:`R_D = R_0 * I^X * e^{\\frac{-Ea}{kT}}`
Expand All @@ -756,21 +752,25 @@ def vecArrhenius(
Returns
----------
degredation : float
Degradation Rate [%/h]
Degradation Rate [%/h]
"""

mask = poa_global >= 25
poa_global = poa_global[mask]
module_temp = module_temp[mask]

ea_scaled = ea / 8.31446261815324E-03
ea_scaled = ea / 8.31446261815324e-03
R0 = np.exp(lnr0)
poa_global_scaled = poa_global / 1000

degredation = 0
# refactor to list comprehension approach
for entry in range(len(poa_global_scaled)):
degredation += R0 * np.exp(-ea_scaled / (273.15 + module_temp[entry])) * np.power(poa_global_scaled[entry], x)
degredation += (
R0
* np.exp(-ea_scaled / (273.15 + module_temp[entry]))
* np.power(poa_global_scaled[entry], x)
)

return (degredation / len(poa_global))
return degredation / len(poa_global)
22 changes: 17 additions & 5 deletions pvdeg/spectral.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,24 @@ def poa_irradiance(

# TODO: change for handling HSAT tracking passed or requested
if tilt is None:
tilt = float(meta["latitude"])
try:
tilt = float(meta["tilt"])
except:
tilt = float(meta["latitude"])
print(
f"The array tilt angle was not provided, therefore the latitude tilt of {tilt:.1f} was used."
)
if azimuth is None: # Sets the default orientation to equator facing.
if float(meta["latitude"]) < 0:
azimuth = 0
else:
azimuth = 180
try:
azimuth = float(meta["azimuth"])
except:
if float(meta["latitude"]) < 0:
azimuth = 0
else:
azimuth = 180
print(
f"The array azimuth was not provided, therefore an azimuth of {azimuth:.1f} was used."
)

if sol_position is None:
sol_position = solar_position(weather_df, meta)
Expand Down
163 changes: 14 additions & 149 deletions pvdeg/standards.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
def eff_gap_parameters(
weather_df=None,
meta=None,
module_temp=None,
weather_kwarg=None,
sky_model="isotropic",
temp_model="sapm",
Expand All @@ -46,9 +45,10 @@ def eff_gap_parameters(
sky_model : str, optional
Options: 'isotropic', 'klucher', 'haydavies', 'reindl', 'king', 'perez'.
temp_model : str, optional
Options: 'sapm'. 'pvsyst' and 'faiman' will be added later.
Options: 'sapm'. 'pvsyst' and 'faiman' and others from PVlib.
Performs the calculation for the cell temperature.
conf_0 : str, optional Model for the high temperature module on the exponential decay curve.
conf_0 : str, optional
Model for the high temperature module on the exponential decay curve.
Default: 'insulated_back_glass_polymer'
conf_inf : str, optional
Model for the lowest temperature module on the exponential decay curve.
Expand Down Expand Up @@ -81,8 +81,6 @@ def eff_gap_parameters(
maximum achievable temperature.
T_inf : float
An array of temperature values for a module that is rack mounted, [°C].
T_measured : float
An array of values for the test module in the system, [°C] interest.
poa : float
An array of values for the plane of array irradiance, [W/m²]
Expand All @@ -98,17 +96,6 @@ def eff_gap_parameters(
elif weather_df is None:
weather_df, meta = weather.get(**weather_kwarg)

# if tilt == None:
# tilt = meta["latitude"]

# if azimuth == None: # Sets the default orientation to equator facing. MSP: Defaults are already set in temperature.py
# if float(meta["latitude"]) < 0:
# azimuth = 0
# else:
# azimuth = 180
# if "wind_height" not in meta.keys():
# wind_factor = 1

solar_position = spectral.solar_position(weather_df, meta)
poa = spectral.poa_irradiance(
weather_df,
Expand All @@ -134,13 +121,11 @@ def eff_gap_parameters(
conf=conf_inf,
wind_factor=wind_factor,
)
T_measured = module_temp
T_ambient = weather_df["temp_air"]

return T_0, T_inf, T_measured, T_ambient, poa
return T_0, T_inf, poa


def eff_gap(T_0, T_inf, T_measured, T_ambient, poa, x_0=6.5, poa_min=100, t_amb_min=0):
def eff_gap(T_0, T_inf, T_measured, T_ambient, poa, x_0=6.5, poa_min=400, t_amb_min=0):
"""
Calculate the effective standoff distance for rooftop mounded PV system
according to IEC TS 63126. The 98ᵗʰ percentile calculations for T_0 and T_inf are
Expand All @@ -159,10 +144,16 @@ def eff_gap(T_0, T_inf, T_measured, T_ambient, poa, x_0=6.5, poa_min=100, t_amb_
T_ambient : float
An array of values for the ambient temperature, [°C].
poa : float
An array of values for the plane of array irradiance, [W/m²]
An array of values for the plane of array irradiance, [W/m²].
x_0 : float, optional
Thermal decay constant [cm], [Kempe, PVSC Proceedings 2023].
According to edition 2 of IEC TS 63126 a value of 6.5 cm is recommended.
poa_min : float
The minimum value for which the data will be used, [W/m²].
400 W/m² is recommended.
t_amb_min : float
The minimum ambient temperature for which the calculation will be made, [°C].
A value of 0 °C is recommended to avoid data where snow might be present.
Returns
-------
Expand All @@ -186,7 +177,6 @@ def eff_gap(T_0, T_inf, T_measured, T_ambient, poa, x_0=6.5, poa_min=100, t_amb_
summ = summ + (T_0.iloc[i] - T_measured.iloc[i]) / (
T_0.iloc[i] - T_inf.iloc[i]
)

try:
x_eff = -x_0 * np.log(1 - summ / n)
except RuntimeWarning as e:
Expand Down Expand Up @@ -274,13 +264,6 @@ def standoff(
to IEC TS 63126, PVSC Proceedings 2023
"""

# if azimuth == None: # Sets the default orientation to equator facing.
# if float(meta["latitude"]) < 0:
# azimuth = 0
# else:
# azimuth = 180
# if "wind_height" not in meta.keys():
# wind_factor = 1
parameters = ["temp_air", "wind_speed", "dhi", "ghi", "dni"]

if isinstance(weather_df, dd.DataFrame):
Expand Down Expand Up @@ -377,14 +360,14 @@ def interpret_standoff(standoff_1=None, standoff_2=None):
else:
if T98_0 is not None:
Output = (
"The estimated temperature of an insulated-back module is "
"The estimated T₉₈ of an insulated-back module is "
+ "%.1f" % T98_0
+ "°C. \n"
)
if T98_inf is not None:
Output = (
Output
+ "The estimated temperature of an open-rack module is "
+ "The estimated T₉₈ of an open-rack module is "
+ "%.1f" % T98_inf
+ "°C. \n"
)
Expand Down Expand Up @@ -498,16 +481,6 @@ def T98_estimate(
"""

# if tilt == None:
# tilt = meta["latitude"]

# if azimuth == None: # Sets the default orientation to equator facing.
# if float(meta["latitude"]) < 0:
# azimuth = 0
# else:
# azimuth = 180
# if "wind_height" not in meta.keys():
# wind_factor = 1
parameters = ["temp_air", "wind_speed", "dhi", "ghi", "dni"]

if isinstance(weather_df, dd.DataFrame):
Expand Down Expand Up @@ -598,111 +571,3 @@ def standoff_x(
).x[0]

return temp_df


# def run_calc_standoff(
# project_points,
# out_dir,
# tag,
# #weather_db,
# #weather_satellite,
# #weather_names,
# max_workers=None,
# tilt=None,
# azimuth=180,
# sky_model='isotropic',
# temp_model='sapm',
# module_type='glass_polymer',
# level=1,
# x_0=6.1,
# wind_speed_factor=1
# ):

# """
# parallelization utilizing gaps #TODO: write docstring
# """

# #inputs
# weather_arg = {}
# #weather_arg['satellite'] = weather_satellite
# #weather_arg['names'] = weather_names
# weather_arg['NREL_HPC'] = True #TODO: add argument or auto detect
# weather_arg['attributes'] = [
# 'air_temperature',
# 'wind_speed',
# 'dhi',
# 'ghi',
# 'dni',
# 'relative_humidity'
# ]

# all_fields = ['x', 'T98_0', 'T98_inf']

# out_fp = Path(out_dir) / f"out_standoff{tag}.h5"
# shapes = {n : (len(project_points), ) for n in all_fields}
# attrs = {'x' : {'units': 'cm'},
# 'T98_0' : {'units': 'Celsius'},
# 'T98_inf' : {'units': 'Celsius'}}
# chunks = {n : None for n in all_fields}
# dtypes = {n : "float32" for n in all_fields}

# # #TODO: is there a better way to add the meta data?
# # nsrdb_fnames, hsds = weather.get_NSRDB_fnames(
# # weather_arg['satellite'],
# # weather_arg['names'],
# # weather_arg['NREL_HPC'])

# # with NSRDBX(nsrdb_fnames[0], hsds=hsds) as f:
# # meta = f.meta[f.meta.index.isin(project_points.gids)]

# Outputs.init_h5(
# out_fp,
# all_fields,
# shapes,
# attrs,
# chunks,
# dtypes,
# #meta=meta.reset_index()
# meta=project_points.df
# )

# future_to_point = {}
# with ProcessPoolExecutor(max_workers=max_workers) as executor:
# for idx, point in project_points.df.iterrows():
# database = point.weather_db
# gid = idx #int(point.gid)
# df_weather_kwargs = point.drop('weather_db', inplace=False).filter(like='weather_')
# df_weather_kwargs.index = df_weather_kwargs.index.map(
# lambda arg: arg.lstrip('weather_'))
# weather_kwarg = weather_arg | df_weather_kwargs.to_dict()

# weather_df, meta = weather.load(
# database = database,
# id = gid,
# #satellite = point.satellite, #TODO: check input
# **weather_kwarg)
# future = executor.submit(
# calc_standoff,
# weather_df,
# meta,
# tilt,
# azimuth,
# sky_model,
# temp_model,
# module_type,
# level,
# x_0,
# wind_speed_factor
# )
# future_to_point[future] = gid

# with Outputs(out_fp, mode="a") as out:
# for future in as_completed(future_to_point):
# result = future.result()
# gid = future_to_point.pop(future)

# #ind = project_points.index(gid)
# for dset, data in result.items():
# out[dset, idx] = np.array([data])

# return out_fp.as_posix()
18 changes: 13 additions & 5 deletions pvdeg/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ def read(file_in, file_type, map_variables=True, **kwargs):
map_weather(weather_df)
map_meta(meta)

if weather_df.index.tzinfo is None:
tz = "Etc/GMT%+d" % -meta["tz"]
weather_df = weather_df.tz_localize(tz)

return weather_df, meta


Expand Down Expand Up @@ -192,6 +196,13 @@ def csv_read(filename):
metadata_values = file1.readline().split(",")
metadata_values[-1] = metadata_values[-1].strip() # strip trailing newline
meta = dict(zip(metadata_fields, metadata_values))
for (
key
) in meta: # converts everything to a float that is possible to convert to a float
try:
meta[key] = float(meta[key])
except:
pass
# get the column headers
columns = file1.readline().split(",")
columns[-1] = columns[-1].strip() # strip trailing newline
Expand Down Expand Up @@ -226,11 +237,7 @@ def csv_read(filename):
dtidx = print(
"Your data file should have columns for Year, Month, Day, and Hour"
)
try:
tz = "Etc/GMT%+d" % -meta["tz"]
weather_df.index = pd.DatetimeIndex(dtidx.tz_localize(tz))
except:
weather_df.index = pd.DatetimeIndex(dtidx)
weather_df.index = pd.DatetimeIndex(dtidx)
file1.close()

return weather_df, meta
Expand All @@ -252,6 +259,7 @@ def map_meta(meta):
"Elevation": "altitude",
"Local Time Zone": "tz",
"Time Zone": "tz",
"timezone": "tz",
"Dew Point": "dew_point",
"Longitude": "longitude",
"Latitude": "latitude",
Expand Down
Loading

0 comments on commit a4ffcb8

Please sign in to comment.