"""Collection of classes and functions for humidity calculations."""
import numpy as np
import pandas as pd
from numba import jit
import warnings
from pvdeg import temperature, spectral, decorators, utilities
# Constants
R_GAS = 0.00831446261815324 # Gas constant in kJ/(mol·K)
[docs]
def relative(temperature_air, dew_point):
"""Calculate ambient relative humidity from dry bulb air temperature and dew point.
References
----------
Alduchov, O. A., and R. E. Eskridge, 1996: Improved Magnus' form approximation of
saturation vapor pressure. J. Appl. Meteor., 35, 601-609.
August, E. F., 1828: Ueber die Berechnung der Expansivkraft des Wasserdunstes. Ann.
Phys. Chem., 13, 122-137.
Magnus, G., 1844: Versuche über die Spannkräfte des Wasserdampfs. Ann. Phys. Chem.,
61, 225-247.
Parameters
----------
temperature_air : pd.Series or float
Series or float of ambient air temperature. [°C]
dew_point : pd.Series or float
Series or float of dew point temperature. [°C]
Notes
-----
Passing NaN values in either ``temperature_air`` or ``dew_point`` at any index
position will return NaN values in the output at those same position(s) in
``relative_humidity``.
Returns
-------
relative_humidity : pd.Series or float
Series or float of ambient relative humidity. [%]
"""
if (
(isinstance(temperature_air, pd.Series) and temperature_air.isna().any())
or (isinstance(dew_point, pd.Series) and dew_point.isna().any())
or (isinstance(temperature_air, float) and pd.isna(temperature_air))
or (isinstance(dew_point, float) and pd.isna(dew_point))
):
warnings.warn(
"Input contains NaN values. Output will contain NaNs at those positions."
)
num = np.exp(17.625 * dew_point / (243.04 + dew_point))
den = np.exp(17.625 * temperature_air / (243.04 + temperature_air))
return 100 * num / den
# @jit
[docs]
def dew_yield(
elevation: float, dew_point: float, dry_bulb: float, wind_speed: float, n: float
):
"""Estimate the dew yield in [mm/day].
This may be useful for degradation modeling where the presence of water is a
factor. E.g. much greater surface conductivity on glass promoting potential
induced degradation (PID).
Calculation taken from: Beysens,
"Estimating dew yield worldwide from a few meteo data", Atmospheric Research 167
(2016) 146-155.
Parameters
----------
elevation : int
Site elevation [km]
dew_point : float
Dewpoint temperature in Celsius [°C]
dry_bulb : float
Air temperature "dry bulb temperature" [°C]
wind_speed : float
Air or windspeed measure [m/s]
n : float
Total sky cover(okta)
This is a quasi emperical scale from 0 to 8 used in meterology which corresponds
to 0-sky completely clear, to 8-sky completely cloudy. Does not account for
cloud type or thickness.
Returns
-------
dew_yield : float
Amount of dew yield in [mm/day]
"""
wind_speed_cut_off = 4.4
dew_yield = (1 / 12) * (
0.37
* (
1
+ (0.204323 * elevation)
- (0.0238893 * elevation**2)
- (18.0132 - (1.04963 * elevation**2) + (0.21891 * elevation**2))
* (10 ** (-3) * dew_point)
)
* ((((dew_point + 273.15) / 285) ** 4) * (1 - (n / 8)))
+ (0.06 * (dew_point - dry_bulb))
* (1 + 100 * (1 - np.exp(-((wind_speed / wind_speed_cut_off) ** 20))))
)
return dew_yield
[docs]
def water_saturation_pressure(temp, average=True):
"""Calculate the water saturation temperature or dew point for given vapor pressure.
Water saturation pressure (psat) model created from an emperical fit of
ln(psat) vs temperature using a 6th order polynomial fit. The fit produced
R²=0.999813. Calculation created by Michael Kempe, unpublished data.
The fit used data from -40°C to 200°C.
Parameters:
-----------
temp : series, float
The air temperature (dry bulb) as a time-indexed series [°C]
average : boolean, default = True
If true, return both water saturation pressure serires and the average water
saturation pressure (used for certain calcs)
Returns:
--------
water_saturation_pressure : array, float
Saturation point
avg_water_saturation_pressure : float, optional
Mean saturation point for the series given
"""
water_saturation_pressure = np.exp(
(3.2575315268e-13 * temp**6)
- (1.5680734584e-10 * temp**5)
+ (2.2213041913e-08 * temp**4)
+ (2.3720766595e-7 * temp**3)
- (4.0316963015e-04 * temp**2)
+ (7.9836323361e-02 * temp)
- (5.6983551678e-1)
)
if average:
return water_saturation_pressure, water_saturation_pressure.mean()
else:
return water_saturation_pressure
[docs]
def surface_relative(rh_ambient, temp_ambient, temp_module):
"""Calculate the relative humidity on a solar panel surface at the module
temperature.
Parameters
----------
rh_ambient : pd series, float
The ambient outdoor environmnet relative humidity [%].
temp_ambient : pd series, float
The ambient outdoor environmnet temperature [°C]
temp_module : pd series, float
The surface temperature of the solar panel module [°C]
Returns
--------
rh_Surface : float
The relative humidity of the surface of a solar module as a fraction or percent
depending on input.
"""
rh_Surface = rh_ambient * (
water_saturation_pressure(temp_ambient)[0]
/ water_saturation_pressure(temp_module)[0]
)
return rh_Surface
[docs]
def diffusivity_weighted_water(
rh_ambient,
temp_ambient,
temp_module,
So=None,
Eas=None,
Ead=None,
encapsulant="W001",
):
"""Calculate the diffusivity weighted average module surface RH.
Parameters
----------
rh_ambient : series (float)
Ambient outdoor relative humidity. [%] Example: 50 = 50%, NOT 0.5 = 50%
temp_ambient : series (float)
Ambient outdoor temperature [°C]
temp_module : pandas series (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
So : float
Encapsulant solubility prefactor in [g/cm³]
Will default to 1.81390702[g/cm³] which is the suggested value for EVA 'W001' if
not specified.
Eas : float
Encapsulant solubility activation energy in [kJ/mol]
Eas = 16.729 [kJ/mol] is the suggested value for EVA.
Ead : float
Encapsulant diffusivity activation energy in [kJ/mol]
Ead = 38.14 [kJ/mol] is the suggested value for EVA.
encapsulant : str
This is the code number for the encapsulant. The default is EVA 'W001'.
Return
------
diffuse_weighted_water : pandas series (float)
Average water content in equilibrium with the module surface, weighted
by the encapsulant diffusivity in [g/cm³].
"""
if So is None:
So = utilities.read_material_property(
key=encapsulant, parameters=["So"], pvdeg_file="H2Opermeation"
)["So"]
if Eas is None:
Eas = utilities.read_material_property(
key=encapsulant, parameters=["Eas"], pvdeg_file="H2Opermeation"
)["Eas"]
if Ead is None:
Ead = utilities.read_material_property(
key=encapsulant, parameters=["Ead"], pvdeg_file="H2Opermeation"
)["Ead"]
# Get the relative humidity of the surface
rh_surface = surface_relative(rh_ambient, temp_ambient, temp_module)
# Generate a series of the numerator values "prior to summation"
numerator = (
So
* np.exp(-(Eas / (R_GAS * (temp_module + 273.15))))
* rh_surface
* np.exp(-(Ead / (R_GAS * (temp_module + 273.15))))
)
# get the summation of the numerator
numerator = numerator.sum(axis=0, skipna=True)
denominator = np.exp(-(Ead / (R_GAS * (temp_module + 273.15))))
# get the summation of the denominator
denominator = denominator.sum(axis=0, skipna=True)
diffuse_weighted_water = (numerator / denominator) / 100
diffuse_weighted_water = (numerator / denominator) / 100
return diffuse_weighted_water
[docs]
def front_encapsulant(
rh_ambient,
temp_ambient,
temp_module,
So=None,
Eas=None,
Ead=None,
encapsulant="W001",
):
"""Calculate diffusivity weighted average Relative Humidity of the module surface.
Parameters
----------
rh_ambient : series (float)
Ambient outdoor relative humidity. [%] Example: 50 = 50%, NOT 0.5 = 50%
temp_ambient : series (float)
Ambient outdoor temperature [°C]
temp_module : pandas series (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
So : float
Encapsulant solubility prefactor in [g/cm³]
Will default to 1.81390702[g/cm³] which is the suggested value for EVA 001 if
not specified.
Eas : float
Encapsulant solubility activation energy in [kJ/mol]
Eas = 16.729 [kJ/mol] is the suggested value for EVA.
Ead : float
Encapsulant diffusivity activation energy in [kJ/mol]
Ead = 38.14 [kJ/mol] is the suggested value for EVA.
encapsulant : str
This is the code number for the encapsulant. The default is EVA 'W001'.
Return
------
front_encapsulant : pandas series (float)
Relative Humidity of the photovoltaic module frontside encapsulant. [%]
"""
if So is None:
So = utilities.read_material_property(
key=encapsulant, parameters=["So"], pvdeg_file="H2Opermeation"
)["So"]
if Eas is None:
Eas = utilities.read_material_property(
key=encapsulant, parameters=["Eas"], pvdeg_file="H2Opermeation"
)["Eas"]
if Ead is None:
Ead = utilities.read_material_property(
key=encapsulant, parameters=["Ead"], pvdeg_file="H2Opermeation"
)["Ead"]
diffuse_water = diffusivity_weighted_water(
rh_ambient=rh_ambient,
temp_ambient=temp_ambient,
temp_module=temp_module,
So=So,
Eas=Eas,
Ead=Ead,
)
front_encapsulant = (
diffuse_water / (So * np.exp(-(Eas / (R_GAS * (temp_module + 273.15)))))
) * 100
return front_encapsulant
[docs]
def csat(temp_module, So=None, Eas=None, encapsulant="W001"):
"""Return saturation of Water Concentration [g/cm³].
Calculation is used in determining Relative Humidity of Backside Solar Module
Encapsulant, and returns saturation of Water Concentration [g/cm³].
For most coding, it is better to just run the calculation insitu. The code is
here for completeness and for informational purposes.
Parameters
----------
temp_module : pandas series (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
So : float
Encapsulant solubility prefactor in [g/cm³]
Eas : float
Encapsulant solubility activation energy in [kJ/mol]
encapsulant : str
This is the code number for the encapsulant.
The default is EVA 'W001'.
Returns
-------
Csat : pandas series (float)
Saturation of Water Concentration [g/cm³]
"""
if So is None:
So = utilities.read_material_property(
key=encapsulant, parameters=["So"], pvdeg_file="H2Opermeation"
)["So"]
if Eas is None:
Eas = utilities.read_material_property(
key=encapsulant, parameters=["Eas"], pvdeg_file="H2Opermeation"
)["Eas"]
# Saturation of water concentration
Csat = So * np.exp(-(Eas / (R_GAS * (273.15 + temp_module))))
return Csat
[docs]
def ceq(Csat, rh_SurfaceOutside):
"""
Return Equilibration water concentration [g/cm³].
Calculation is used in determining Relative Humidity of Backside Solar Module
Encapsulant, and returns Equilibration water concentration [g/cm³].
For most coding, it is better to just run the calculation insitu. The code is
here for completeness and for informational purposes.
Parameters
----------
Csat : pandas series (float)
Saturation of Water Concentration [g/cm³]
rh_SurfaceOutside : pandas series (float)
The relative humidity of the surface of a solar module (%)
Returns
-------
Ceq : pandas series (float)
Equilibration water concentration [g/cm³]
"""
Ceq = Csat * (rh_SurfaceOutside / 100)
return Ceq
[docs]
def back_encapsulant_water_concentration(
temp_module=None,
rh_surface=None,
rh_ambient=None,
temp_ambient=None,
start=None,
Po_b=None,
Ea_p_b=None,
backsheet_thickness=None,
So_e=None,
Ea_s_e=None,
back_encap_thickness=None,
backsheet="W017",
encapsulant="W001",
output="rh",
):
"""Return water concentration in encapsulant.
Calculation is used in determining Relative Humidity of Backside Solar Module
Encapsulant. This function returns a numpy array of the Concentration of water
in the encapsulant at every time step.
This calculation uses a quasi-steady state approximation of the diffusion
equation to calculate the concentration of water in the encapsulant. For this,
it is assumed that the diffusion in the encapsulant is much larger than the
diffusion in the backsheet, and it ignores the transients in the backsheet.
Numba was used to isolate recursion requiring a for loop
Numba Functions are very fast because they compile and run in machine code but
can not use pandas dataframes.
Parameters
-----------
temp_module : pandas series (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
rh_surface : list (float)
The relative humidity of the surface of a solar module [%]
EXAMPLE: "50 = 50% NOT 0.5 = 50%"
if this parameter is not provided, it will be calculated using rh_ambient and
temp_ambient.
rh_ambient : series (float)
Ambient outdoor relative humidity. [%] Example: 50 = 50%, NOT 0.5 = 50%
If rh_surface is not provided, this parameter along with temp_ambient will be
used to calculate it.
temp_ambient : series (float)
Ambient outdoor temperature [°C]
If rh_surface is not provided, this parameter along with rh_ambient will be used
to calculate it.
start : float
Initial value of the Concentration of water in the encapsulant.
by default, the function will use half the equilibrium value as the first
value
Po_b : float
Water permeation rate prefactor [g·mm/m²/day].
The suggested value for PET W17 is Po = 1319534666.90318 [g·mm/m²/day].
Ea_p_b : float
Backsheet permeation activation energy [kJ/mol] .
For PET backsheet W017, Ea_p_b=55.4064573018373 [kJ/mol]
backsheet_thickness : float
Thickness of the backsheet [mm].
The suggested value for a PET backsheet_thickness=0.3.
So_e : float
Encapsulant solubility prefactor in [g/cm³]
So = 1.81390702[g/cm³] is the suggested value for EVA W001.
Ea_s_e : float
Encapsulant solubility activation energy in [kJ/mol]
Eas = 16.729[kJ/mol] is the suggested value for EVA W001.
back_encap_thickness : float
Thickness of the backside encapsulant [mm].
The suggested value for EVA encapsulant is 0.46mm
backsheet : str
This is the code number for the backsheet.
The default is PET 'W017'.
encapsulant : str
This is the code number for the encapsulant.
The default is EVA 'W001'.
output : str
The default is "rh" which is the relative humidity in the encapsulant
in [%], any other value, e.g. "Ce" will return the concentration in [g/cm³].
Returns
--------
Ce_list : Pandas series (float)
Concentration of water in the encapsulant at every time step in [g/cm³],
or the relative humidity in [%] depending on the output parameter.
"""
if rh_surface is None:
if rh_ambient is None or temp_ambient is None:
raise ValueError(
"If rh_surface is not provided, both rh_ambient and temp_ambient must"
"be provided."
)
# Get the relative humidity of the surface
rh_surface = surface_relative(
rh_ambient=rh_ambient, temp_ambient=temp_ambient, temp_module=temp_module
)
Ce_list = np.zeros(len(temp_module))
index_passthrough_variable = temp_module.index
if not isinstance(temp_module, np.ndarray):
temp_module = temp_module.to_numpy()
if not isinstance(rh_surface, np.ndarray):
rh_surface = rh_surface.to_numpy()
if Po_b is None:
Po_b = utilities.read_material_property(
key=backsheet, parameters=["Po"], pvdeg_file="H2Opermeation"
)["Po"]
if Ea_p_b is None:
Ea_p_b = utilities.read_material_property(
key=backsheet, parameters=["Eap"], pvdeg_file="H2Opermeation"
)["Eap"]
if backsheet_thickness is None:
try:
backsheet_thickness = utilities.read_material_property(
key=backsheet, parameters=["t"], pvdeg_file="H2Opermeation"
)["t"]
if backsheet_thickness is None:
raise ValueError()
except (KeyError, ValueError):
raise ValueError(
"backsheet_thickness must be specified as a float or "
"a backsheet material with a backsheet_thickness "
"available should be specified."
)
if So_e is None:
So_e = utilities.read_material_property(
key=encapsulant, parameters=["So"], pvdeg_file="H2Opermeation"
)["So"]
if Ea_s_e is None:
Ea_s_e = utilities.read_material_property(
key=encapsulant, parameters=["Eas"], pvdeg_file="H2Opermeation"
)["Eas"]
if back_encap_thickness is None:
try:
back_encap_thickness = utilities.read_material_property(
key=encapsulant, parameters=["t"], pvdeg_file="H2Opermeation"
)["t"]
if back_encap_thickness is None:
raise ValueError()
except (KeyError, ValueError):
raise ValueError(
"back_encap_thickness must be specified as a float or "
"an encapsulant material with a back_encap_thickness "
"available should be specified."
)
# Convert the parameters to the correct and convenient units
WVTRo = Po_b / 100 / 100 / 24 / backsheet_thickness
EaWVTR = Ea_p_b / R_GAS
So = So_e * back_encap_thickness / 10
Eas = Ea_s_e / R_GAS
# Ce is the initial start of concentration of water
if start is None:
Ce_start = (
So * np.exp(-(Eas / (temp_module[0] + 273.15))) * rh_surface[0] / 100 / 2
)
else:
Ce_start = start
Ce_list = _Ce(
WVTRo=WVTRo,
EaWVTR=EaWVTR,
temp_module=temp_module,
So=So,
Eas=Eas,
Ce_start=Ce_start,
rh_surface=rh_surface,
)
if output == "rh":
# Convert the concentration to relative humidity
Ce_list = 100 * (Ce_list / (So * np.exp(-(Eas / (temp_module + 273.15)))))
Ce_list = pd.Series(
Ce_list, index=index_passthrough_variable, name="RH_back_encapsulant"
)
else:
Ce_list = pd.Series(
Ce_list, index=index_passthrough_variable, name="Ce_back_encapsulant"
)
return Ce_list
@jit
def _Ce(
WVTRo,
EaWVTR,
temp_module,
So,
Eas,
Ce_start,
rh_surface,
):
"""
This is a helper function for the Ce function that is used to calculate the
concentration of water in the encapsulant.
Parameters
-----------
All the parameters must be Numba compilable types. I.e. numpy arrays or floats.
Returns
--------
Ce_list : Numba array (float)
Concentration of water in the encapsulant at every time step in [g/cm³].
"""
Ce = Ce_start
for i in range(1, len(rh_surface)):
Ce = Ce + (WVTRo * np.exp(-EaWVTR / (temp_module[i] + 273.15))) / (
So * np.exp(-Eas / (temp_module[i] + 273.15))
) * (rh_surface[i] / 100 * So * np.exp(-Eas / (temp_module[i] + 273.15)) - Ce)
return Ce
[docs]
@jit
def Ce_numba(
start,
temp_module,
rh_surface,
WVTRo=7970633554,
EaWVTR=55.0255,
So=1.81390702,
back_encap_thickness=0.5,
Eas=16.729,
):
"""Return water concentration in encapsulant.
Calculation is used in determining Relative Humidity of Backside Solar Module
Encapsulant. This function returns a numpy array of the Concentration of water
in the encapsulant at every time step.
Numba was used to isolate recursion requiring a for loop
Numba Functions compile and run in machine code but can not use pandas (Very fast).
Parameters
-----------
start : float
Initial value of the Concentration of water in the encapsulant
currently takes the first value produced from
the _ceq(Saturation of Water Concentration) as a point
of acceptable equilibrium
temp_module : pandas series (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
rh_Surface : list (float)
The relative humidity of the surface of a solar module [%]
EXAMPLE: "50 = 50% NOT .5 = 50%"
WVTRo : float
Water Vapor Transfer Rate prefactor [g/m2/day].
The suggested value for EVA is WVTRo = 7970633554(g/m2/day).
EaWVTR : float
Water Vapor Transfer Rate activation energy [kJ/mol] .
It is suggested to use 0.15[mm] thick PET as a default
for the backsheet and set EaWVTR=55.0255[kJ/mol]
So : float
Encapsulant solubility prefactor in [g/cm3]
So = 1.81390702(g/cm3) is the suggested value for EVA.
back_encap_thickness : float
Thickness of the backside encapsulant [mm].
The suggested value for EVA encapsulant is 0.5 mm
Eas : float
Encapsulant solubility activation energy in [kJ/mol]
Eas = 16.729[kJ/mol] is the suggested value for EVA.
Returns
--------
Ce_list : numpy array
Concentration of water in the encapsulant at every time step
"""
dataPoints = len(temp_module)
Ce_list = np.zeros(dataPoints)
for i in range(0, len(rh_surface)):
if i == 0:
# Ce = Initial start of concentration of water
Ce = start
else:
Ce = Ce_list[i - 1]
Ce = Ce + (
(
WVTRo
/ 100
/ 100
/ 24
* np.exp(-((EaWVTR) / (R_GAS * (temp_module[i] + 273.15))))
)
/ (
So
* back_encap_thickness
/ 10
* np.exp(-((Eas) / (R_GAS * (temp_module[i] + 273.15))))
)
* (
rh_surface[i]
/ 100
* So
* np.exp(-((Eas) / (R_GAS * (temp_module[i] + 273.15))))
- Ce
)
)
Ce_list[i] = Ce
return Ce_list
[docs]
def back_encapsulant(
rh_ambient,
temp_ambient,
temp_module,
WVTRo=7970633554,
EaWVTR=55.0255,
So=1.81390702,
back_encap_thickness=0.5,
Eas=16.729,
):
"""Return the relative humidity of backside module encapsulant.
Function to calculate the Relative Humidity of Backside Solar Module Encapsulant
and return a pandas series for each time step
Parameters
----------
rh_ambient : pandas series (float)
The ambient outdoor environmnet relative humidity in [%]
EXAMPLE: "50 = 50% NOT .5 = 50%"
temp_ambient : pandas series (float)
The ambient outdoor environmnet temperature in Celsius
temp_module : list (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
WVTRo : float
Water Vapor Transfer Rate prefactor [g/m2/day].
The suggested value for EVA is WVTRo = 7970633554[g/m2/day].
EaWVTR : float
Water Vapor Transfer Rate activation energy [kJ/mol] .
It is suggested to use 0.15[mm] thick PET as a default
for the backsheet and set EaWVTR=55.0255[kJ/mol]
So : float
Encapsulant solubility prefactor in [g/cm3]
So = 1.81390702[g/cm3] is the suggested value for EVA.
back_encap_thickness : float
Thickness of the backside encapsulant [mm].
The suggested value for EVA encapsulant is 0.5 mm.
Eas : float
Encapsulant solubility activation energy in [kJ/mol]
Eas = 16.729[kJ/mol] is the suggested value for EVA.
Returns
-------
back_encapsulant : pandas series (float)
Relative Humidity of backside solar module encapsulant [%]
"""
rh_surface = surface_relative(
rh_ambient=rh_ambient, temp_ambient=temp_ambient, temp_module=temp_module
)
Csat = csat(temp_module=temp_module, So=So, Eas=Eas)
Ceq = ceq(Csat=Csat, rh_SurfaceOutside=rh_surface)
start = Ceq.iloc[0]
# Need to convert these series to numpy arrays for numba function
temp_module_numba = temp_module.to_numpy()
rh_surface_numba = rh_surface.to_numpy()
Ce_nparray = Ce_numba(
start=start,
temp_module=temp_module_numba,
rh_surface=rh_surface_numba,
WVTRo=WVTRo,
EaWVTR=EaWVTR,
So=So,
back_encap_thickness=back_encap_thickness,
Eas=Eas,
)
back_encapsulant = 100 * (Ce_nparray / Csat)
return back_encapsulant
[docs]
def backsheet_from_encap(rh_back_encap, rh_surface_outside):
"""Calculate the Relative Humidity of solar module backsheet as timeseries.
Requires the relative humidity of the backside encapsulant and the outside surface
of the module.
Parameters
----------
rh_back_encap : pandas series (float)
Relative Humidity of Frontside Solar module Encapsulant. *See rh_back_encap()
rh_surface_outside : pandas series (float)
The relative humidity of the surface of a solar module.
*See surface_relative()
Returns
-------
RHbacksheet_series : pandas series (float)
Relative Humidity of Backside Backsheet of a Solar Module [%]
"""
RHbacksheet_series = (rh_back_encap + rh_surface_outside) / 2
return RHbacksheet_series
[docs]
def backsheet(
rh_ambient,
temp_ambient,
temp_module,
start=None,
Po_b=None,
Ea_p_b=None,
backsheet_thickness=None,
So_e=None,
Ea_s_e=None,
back_encap_thickness=None,
backsheet="W017",
encapsulant="W001",
):
"""
Calculate the relative humidity in a solar module backsheet as timeseries.
It assume a value that is the average of the RH of the backside encapsulant and the
outside surface of the module.
Parameters
----------
rh_ambient : pandas series (float)
The ambient outdoor environmnet relative humidity in (%)
EXAMPLE: "50 = 50% NOT .5 = 50%"
temp_ambient : pandas series (float)
The ambient outdoor environmnet temperature in Celsius
temp_module : list (float)
The surface temperature in Celsius of the solar panel module
"module temperature [°C]"
start : float
Initial value of the Concentration of water in the encapsulant.
by default, the function will use an equilibrium value as the first value
Po_b : float
Water permeation rate prefactor [g·mm/m²/day].
The suggested value for PET W17 is Po = 1319534666.90318 [g·mm/m²/day].
Ea_p_b : float
Backsheet permeation activation energy [kJ/mol] .
For PET backsheet W017, Ea_p_b=55.4064573018373 [kJ/mol]
backsheet_thickness : float
Thickness of the backsheet [mm].
The suggested value for a PET backsheet is 0.3 [mm]
So_e : float
Encapsulant solubility prefactor in [g/cm³]
So = 1.81390702[g/cm³] is the suggested value for EVA W001.
Ea_s_e : float
Encapsulant solubility activation energy in [kJ/mol]
Eas = 16.729[kJ/mol] is the suggested value for EVA W001.
back_encap_thickness : float
Thickness of the backside encapsulant [mm].
The suggested value for EVA encapsulant is 0.46 mm.
backsheet : str
This is the code number for the backsheet.
The default is PET 'W017'.
encapsulant : str
This is the code number for the encapsulant.
The default is EVA 'W001'.
Returns
--------
rh_backsheet : float series or array
relative humidity of the PV backsheet as a time-series [%]
"""
# Get the relative humidity of the surface
surface = surface_relative(
rh_ambient=rh_ambient, temp_ambient=temp_ambient, temp_module=temp_module
)
# Get the relative humidity of the back encapsulant
back_encapsulant = back_encapsulant_water_concentration(
rh_surface=surface,
# temp_ambient=temp_ambient,
# rh_ambient=rh_ambient,
temp_module=temp_module,
start=start,
Po_b=Po_b,
Ea_p_b=Ea_p_b,
backsheet_thickness=backsheet_thickness,
So_e=So_e,
Ea_s_e=Ea_s_e,
back_encap_thickness=back_encap_thickness,
backsheet=backsheet,
encapsulant=encapsulant,
output="rh",
)
return (back_encapsulant + surface) / 2
[docs]
@decorators.geospatial_quick_shape(
"timeseries",
["RH_surface_outside", "RH_front_encap", "RH_back_encap", "RH_backsheet"],
)
def module(
weather_df=None,
meta=None,
poa=None,
temp_module=None,
tilt=None,
azimuth=180,
sky_model="isotropic",
temp_model="sapm",
conf="open_rack_glass_glass",
wind_factor=0.33,
Po_b=None,
Ea_p_b=None,
backsheet_thickness=None,
So_e=None,
Ea_s_e=None,
Ea_d_e=None,
back_encap_thickness=None,
backsheet="W017",
encapsulant="W001",
**weather_kwargs,
):
"""Calculate the Relative Humidity of solar module backsheet from timeseries data.
Parameters
----------
weather_df : pd.DataFrame
Weather data for a single location.
meta : pd.DataFrame
Meta data for a single location.
poa : pd.Series, optional
Plane of array irradiance [W/m²]. If not provided, it will be calculated
temp_module : pd.Series, optional
Module temperature [°C]. If not provided, it will be calculated.
tilt : float, optional
Tilt angle of PV system relative to horizontal.
azimuth : float, optional
Azimuth angle of PV system relative to north.
sky_model : str, optional
Options: 'isotropic', 'klucher', 'haydavies', 'reindl', 'king', 'perez'.
temp_model : str, optional
Options: 'sapm', 'pvsyst', 'faiman', 'sandia'.
wind_factor : float, optional
Wind speed correction exponent to account for different wind speed measurement
heights between weather database (e.g. NSRDB) and the tempeature model
(e.g. SAPM). The NSRDB provides calculations at 2 m (i.e module height) but SAPM
uses a 10m height. It is recommended that a power-law relationship between
height and wind speed of 0.33 be used*. This results in a wind speed that is
1.7 times higher. It is acknowledged that this can vary significantly.
Po_b : float
Water permeation rate prefactor [g·mm/m²/day].
The suggested value for PET W17 is Po = 1319534666.90318 [g·mm/m²/day].
Ea_p_b : float
Backsheet permeation activation energy [kJ/mol].
backsheet_thickness : float
Thickness of the backsheet [mm].
The suggested value for a PET backsheet is 0.3mm.
So_e : float
Encapsulant solubility prefactor in [g/cm³]
Ea_s_e : float
Encapsulant solubility activation energy in [kJ/mol]
Ea_d_e : float
Encapsulant diffusivity activation energy in [kJ/mol]
back_encap_thickness : float
Thickness of the backside encapsulant [mm].
The suggested value for EVA encapsulant is 0.46mm.
backsheet : str
This is the code number for the backsheet.
The default is PET 'W017'.
encapsulant : str
This is the code number for the encapsulant.
The default is EVA 'W001'.
**weather_kwargs : keyword arguments
Additional keyword arguments passed to the weather data reader.
Returns
--------
rh_surface_outside : float pandas dataframe
relative humidity of the PV module surface as a time-series,
rh_front_encap: float pandas dataframe
relative humidity of the PV frontside encapsulant as a time-series,
rh_back_encap : float pandas dataframe
relative humidity of the PV backside encapsulant as a time-series,
Ce_back_encap : float pandas dataframe
concentration of water in the PV backside encapsulant as a time-series,
rh_backsheet : float pandas dataframe
relative humidity of the PV backsheet as a time-series
"""
# solar_position = spectral.solar_position(weather_df, meta)
# poa = spectral.poa_irradiance(weather_df, meta, solar_position, tilt, azimuth,
# sky_model)
# temp_module = temperature.module(weather_df, poa, temp_model, mount_type,
# wind_factor)
if poa is None:
poa = spectral.poa_irradiance(
weather_df=weather_df,
meta=meta,
tilt=tilt,
azimuth=azimuth,
sky_model=sky_model,
**weather_kwargs,
)
if temp_module is None:
temp_module = temperature.module(
weather_df=weather_df,
meta=meta,
poa=poa,
temp_model=temp_model,
conf=conf,
wind_factor=wind_factor,
**weather_kwargs,
)
rh_surface_outside = surface_relative(
rh_ambient=weather_df["relative_humidity"],
temp_ambient=weather_df["temp_air"],
temp_module=temp_module,
)
rh_front_encap = front_encapsulant(
rh_ambient=weather_df["relative_humidity"],
temp_ambient=weather_df["temp_air"],
temp_module=temp_module,
So=So_e,
Eas=Ea_s_e,
Ead=Ea_d_e,
encapsulant=encapsulant,
)
rh_back_encap = back_encapsulant_water_concentration(
temp_module=temp_module,
rh_surface=None,
rh_ambient=weather_df["relative_humidity"],
temp_ambient=weather_df["temp_air"],
Po_b=Po_b,
Ea_p_b=Ea_p_b,
backsheet_thickness=backsheet_thickness,
So_e=So_e,
Ea_s_e=Ea_s_e,
back_encap_thickness=back_encap_thickness,
backsheet=backsheet,
encapsulant=encapsulant,
output="rh",
)
Ce_back_encap = back_encapsulant_water_concentration(
temp_module=temp_module,
rh_surface=None,
rh_ambient=weather_df["relative_humidity"],
temp_ambient=weather_df["temp_air"],
Po_b=Po_b,
Ea_p_b=Ea_p_b,
backsheet_thickness=backsheet_thickness,
So_e=So_e,
Ea_s_e=Ea_s_e,
back_encap_thickness=back_encap_thickness,
backsheet=backsheet,
encapsulant=encapsulant,
output="Ce",
)
rh_backsheet = (rh_back_encap + rh_surface_outside) / 2
data = {
"RH_surface_outside": rh_surface_outside,
"RH_front_encap": rh_front_encap,
"RH_back_encap": rh_back_encap,
"Ce_back_encap": Ce_back_encap,
"RH_backsheet": rh_backsheet,
}
results = pd.DataFrame(data=data)
return results