@@ -807,7 +807,8 @@ def sapm_celltemp(irrad, wind, temp, model='open_rack_cell_glassback'):
807
807
return pd .DataFrame ({'temp_cell' : temp_cell , 'temp_module' : temp_module })
808
808
809
809
810
- def singlediode (module , IL , I0 , Rs , Rsh , nNsVth , ** kwargs ):
810
+ def singlediode (module , photocurrent , saturation_current ,
811
+ resistance_series , resistance_shunt , nNsVth ):
811
812
'''
812
813
Solve the single-diode model to obtain a photovoltaic IV curve.
813
814
@@ -822,65 +823,56 @@ def singlediode(module, IL, I0, Rs, Rsh, nNsVth, **kwargs):
822
823
are described later. Returns a DataFrame which contains
823
824
the 5 points on the I-V curve specified in SAND2004-3535 [3].
824
825
If all IL, I0, Rs, Rsh, and nNsVth are scalar, a single curve
825
- will be returned, if any are DataFrames (of the same length), multiple IV
826
+ will be returned, if any are Series (of the same length), multiple IV
826
827
curves will be calculated.
827
828
828
829
The input parameters can be calculated using calcparams_desoto from
829
- meterological data.
830
+ meteorological data.
830
831
831
832
Parameters
832
833
----------
833
834
module : DataFrame
834
835
A DataFrame defining the SAPM performance parameters.
835
836
836
- IL : float or DataFrame
837
+ photocurrent : float or Series
837
838
Light-generated current (photocurrent) in amperes under desired IV
838
- curve conditions.
839
+ curve conditions. Often abbreviated ``I_L``.
839
840
840
- I0 : float or DataFrame
841
+ saturation_current : float or Series
841
842
Diode saturation current in amperes under desired IV curve
842
- conditions.
843
+ conditions. Often abbreviated ``I_0``.
843
844
844
- Rs : float or DataFrame
845
- Series resistance in ohms under desired IV curve conditions.
845
+ resistance_series : float or Series
846
+ Series resistance in ohms under desired IV curve conditions.
847
+ Often abbreviated ``Rs``.
846
848
847
- Rsh : float or DataFrame
848
- Shunt resistance in ohms under desired IV curve conditions. May
849
- be a scalar or DataFrame, but DataFrames must be of same length as all
850
- other input DataFrames.
849
+ resistance_shunt : float or Series
850
+ Shunt resistance in ohms under desired IV curve conditions.
851
+ Often abbreviated ``Rsh``.
851
852
852
- nNsVth : float or DataFrame
853
+ nNsVth : float or Series
853
854
The product of three components. 1) The usual diode ideal
854
855
factor (n), 2) the number of cells in series (Ns), and 3) the cell
855
856
thermal voltage under the desired IV curve conditions (Vth).
856
857
The thermal voltage of the cell (in volts) may be calculated as
857
- k*Tcell/q, where k is Boltzmann's constant (J/K), Tcell is the
858
- temperature of the p-n junction in Kelvin, and q is the elementary
859
- charge of an electron (coulombs).
860
-
861
- Other Parameters
862
- ----------------
863
- NumPoints : integer
864
- Number of points in the desired IV curve (optional). Must be a finite
865
- scalar value. Non-integer values will be rounded to the next highest
866
- integer (ceil). If ceil(NumPoints) is < 2, no IV curves will be produced
867
- (i.e. Result.V and Result.I will not be generated). The default
868
- value is 0, resulting in no calculation of IV points other than
869
- those specified in [3].
858
+ ``k*temp_cell/q``, where k is Boltzmann's constant (J/K),
859
+ temp_cell is the temperature of the p-n junction in Kelvin,
860
+ and q is the charge of an electron (coulombs).
870
861
871
862
Returns
872
863
-------
873
- A DataFrame with the following fields. All fields have the
874
- same number of rows as the largest input DataFrame:
864
+ If ``photocurrent`` is a Series, a DataFrame with the following columns.
865
+ All columns have the same number of rows as the largest input DataFrame.
875
866
876
- * Result.Isc - short circuit current in amperes.
877
- * Result.Voc - open circuit voltage in volts.
878
- * Result.Imp - current at maximum power point in amperes.
879
- * Result.Vmp - voltage at maximum power point in volts.
880
- * Result.Pmp - power at maximum power point in watts.
881
- * Result.Ix - current, in amperes, at V = 0.5*Voc.
882
- * Result.Ixx - current, in amperes, at V = 0.5*(Voc+Vmp).
883
-
867
+ If ``photocurrent`` is a scalar, a dict with the following keys.
868
+
869
+ * i_sc - short circuit current in amperes.
870
+ * v_oc - open circuit voltage in volts.
871
+ * i_mp - current at maximum power point in amperes.
872
+ * v_mp - voltage at maximum power point in volts.
873
+ * p_mp - power at maximum power point in watts.
874
+ * i_x - current, in amperes, at ``v = 0.5*v_oc``.
875
+ * i_xx - current, in amperes, at ``V = 0.5*(v_oc+v_mp)``.
884
876
885
877
Notes
886
878
-----
@@ -905,39 +897,37 @@ def singlediode(module, IL, I0, Rs, Rsh, nNsVth, **kwargs):
905
897
sapm
906
898
calcparams_desoto
907
899
'''
900
+ pvl_logger .debug ('pvsystem.singlediode' )
901
+
902
+ # Find short circuit current using Lambert W
903
+ i_sc = i_from_v (resistance_shunt , resistance_series , nNsVth , 0.01 ,
904
+ saturation_current , photocurrent )
905
+
906
+ params = {'r_sh' : resistance_shunt ,
907
+ 'r_s' : resistance_series ,
908
+ 'nNsVth' : nNsVth ,
909
+ 'i_0' : saturation_current ,
910
+ 'i_l' : photocurrent }
911
+
912
+ __ , v_oc = _golden_sect_DataFrame (params , 0 , module ['V_oc_ref' ]* 1.6 ,
913
+ _v_oc_optfcn )
908
914
909
- # Find Isc using Lambert W
910
- Isc = I_from_V (Rsh = Rsh , Rs = Rs , nNsVth = nNsVth , V = 0.01 , I0 = I0 , IL = IL )
911
-
912
- # If passed a dataframe, output a dataframe, if passed a list or scalar,
913
- # return a dict
914
- if isinstance (Rsh , pd .Series ):
915
- DFOut = pd .DataFrame ({'Isc' : Isc })
916
- DFOut .index = Rsh .index
917
- else :
918
- DFOut = {'Isc' : Isc }
919
-
920
- DFOut ['Rsh' ] = Rsh
921
- DFOut ['Rs' ] = Rs
922
- DFOut ['nNsVth' ] = nNsVth
923
- DFOut ['I0' ] = I0
924
- DFOut ['IL' ] = IL
925
-
926
- __ , Voc_return = _golden_sect_DataFrame (DFOut , 0 , module .V_oc_ref * 1.6 ,
927
- _Voc_optfcn )
928
- Voc = Voc_return .copy ()
929
-
930
- Pmp , Vmax = _golden_sect_DataFrame (DFOut , 0 , module .V_oc_ref * 1.14 ,
931
- _pwr_optfcn )
932
- Imax = I_from_V (Rsh = Rsh , Rs = Rs , nNsVth = nNsVth , V = Vmax , I0 = I0 , IL = IL )
915
+ p_mp , v_mp = _golden_sect_DataFrame (params , 0 , module ['V_oc_ref' ]* 1.14 ,
916
+ _pwr_optfcn )
917
+
933
918
# Invert the Power-Current curve. Find the current where the inverted power
934
- # is minimized. This is Imax. Start the optimization at Voc/2
919
+ # is minimized. This is i_mp. Start the optimization at v_oc/2
920
+ i_mp = i_from_v (resistance_shunt , resistance_series , nNsVth , v_mp ,
921
+ saturation_current , photocurrent )
935
922
936
923
# Find Ix and Ixx using Lambert W
937
- Ix = I_from_V (Rsh = Rsh , Rs = Rs , nNsVth = nNsVth , V = .5 * Voc , I0 = I0 , IL = IL )
938
- Ixx = I_from_V (Rsh = Rsh , Rs = Rs , nNsVth = nNsVth , V = 0.5 * (Voc + Vmax ), I0 = I0 ,
939
- IL = IL )
924
+ i_x = i_from_v (resistance_shunt , resistance_series , nNsVth ,
925
+ 0.5 * v_oc , saturation_current , photocurrent )
940
926
927
+ i_xx = i_from_v (resistance_shunt , resistance_series , nNsVth ,
928
+ 0.5 * (v_oc + v_mp ), saturation_current , photocurrent )
929
+
930
+ # @wholmgren: need to move this stuff to a different function
941
931
# If the user says they want a curve of with number of points equal to
942
932
# NumPoints (must be >=2), then create a voltage array where voltage is
943
933
# zero in the first column, and Voc in the last column. Number of columns
@@ -952,30 +942,37 @@ def singlediode(module, IL, I0, Rs, Rsh, nNsVth, **kwargs):
952
942
# Result.I = I_from_V(Rsh*s, Rs*s, nNsVth*s, Result.V, I0*s, IL*s);
953
943
# end
954
944
955
- DFOut ['Imp' ] = Imax
956
- DFOut ['Voc' ] = Voc
957
- DFOut ['Vmp' ] = Vmax
958
- DFOut ['Pmp' ] = Pmp
959
- DFOut ['Ix' ] = Ix
960
- DFOut ['Ixx' ] = Ixx
945
+ dfout = {}
946
+ dfout ['i_sc' ] = i_sc
947
+ dfout ['i_mp' ] = i_mp
948
+ dfout ['v_oc' ] = v_oc
949
+ dfout ['v_mp' ] = v_mp
950
+ dfout ['p_mp' ] = p_mp
951
+ dfout ['i_x' ] = i_x
952
+ dfout ['i_xx' ] = i_xx
953
+
954
+ try :
955
+ dfout = pd .DataFrame (dfout , index = photocurrent .index )
956
+ except AttributeError :
957
+ pass
961
958
962
- return DFOut
959
+ return dfout
963
960
964
961
965
962
# Created April,2014
966
963
# Author: Rob Andrews, Calama Consulting
967
964
968
- def _golden_sect_DataFrame (df , VL , VH , func ):
965
+ def _golden_sect_DataFrame (params , VL , VH , func ):
969
966
'''
970
967
Vectorized golden section search for finding MPPT
971
968
from a dataframe timeseries.
972
969
973
970
Parameters
974
971
----------
975
- df : DataFrame
976
- Dataframe containing a timeseries of inputs to the function
977
- to be optimized.
978
- Each row should represent an independant optimization
972
+ params : dict
973
+ Dictionary containing scalars or arrays
974
+ of inputs to the function to be optimized.
975
+ Each row should represent an independent optimization.
979
976
980
977
VL: float
981
978
Lower bound of the optimization
@@ -984,7 +981,7 @@ def _golden_sect_DataFrame(df, VL, VH, func):
984
981
Upper bound of the optimization
985
982
986
983
func: function
987
- Function to be optimized must be in the form f(dataframe , x)
984
+ Function to be optimized must be in the form f(array-like , x)
988
985
989
986
Returns
990
987
-------
@@ -998,77 +995,122 @@ def _golden_sect_DataFrame(df, VL, VH, func):
998
995
-----
999
996
This funtion will find the MAXIMUM of a function
1000
997
'''
1001
-
1002
- df ['VH' ]= VH
1003
- df ['VL' ]= VL
998
+
999
+ df = params
1000
+ df ['VH' ] = VH
1001
+ df ['VL' ] = VL
1004
1002
1005
- err = df ['VH' ]- df ['VL' ]
1006
- errflag = True
1007
- iterations = 0
1003
+ err = df ['VH' ] - df ['VL' ]
1004
+ errflag = True
1005
+ iterations = 0
1006
+
1008
1007
while errflag :
1009
1008
1010
- phi = (np .sqrt (5 )- 1 )/ 2 * (df ['VH' ]- df ['VL' ])
1011
- df ['V1' ]= df ['VL' ]+ phi
1012
- df ['V2' ]= df ['VH' ]- phi
1009
+ phi = (np .sqrt (5 )- 1 )/ 2 * (df ['VH' ]- df ['VL' ])
1010
+ df ['V1' ] = df ['VL' ] + phi
1011
+ df ['V2' ] = df ['VH' ] - phi
1013
1012
1014
- df ['f1' ]= func (df ,'V1' )
1015
- df ['f2' ]= func (df ,'V2' )
1016
- df ['SW_Flag' ]= df ['f1' ]> df ['f2' ]
1013
+ df ['f1' ] = func (df , 'V1' )
1014
+ df ['f2' ] = func (df , 'V2' )
1015
+ df ['SW_Flag' ] = df ['f1' ] > df ['f2' ]
1017
1016
1018
- df ['VL' ]= df ['V2' ]* df ['SW_Flag' ]+ df ['VL' ]* (~ df ['SW_Flag' ])
1019
- df ['VH' ]= df ['V1' ]* ~ df ['SW_Flag' ]+ df ['VH' ]* (df ['SW_Flag' ])
1017
+ df ['VL' ] = df ['V2' ]* df ['SW_Flag' ] + df ['VL' ]* (~ df ['SW_Flag' ])
1018
+ df ['VH' ] = df ['V1' ]* ~ df ['SW_Flag' ] + df ['VH' ]* (df ['SW_Flag' ])
1020
1019
1021
- err = ( df ['V1' ]- df ['V2' ])
1022
- if isinstance ( df , pd . DataFrame ) :
1023
- errflag = all (abs (err )> .01 )
1024
- else :
1025
- errflag = (abs (err )> .01 )
1020
+ err = df ['V1' ] - df ['V2' ]
1021
+ try :
1022
+ errflag = (abs (err )> .01 ). all ( )
1023
+ except ValueError :
1024
+ errflag = (abs (err )> .01 )
1026
1025
1027
- iterations = iterations + 1
1026
+ iterations += 1
1028
1027
1029
- if iterations > 50 :
1028
+ if iterations > 50 :
1030
1029
raise Exception ("EXCEPTION:iterations exeeded maximum (50)" )
1031
1030
1032
- return func (df ,'V1' ) , df ['V1' ]
1031
+ return func (df , 'V1' ), df ['V1' ]
1033
1032
1034
1033
1035
1034
def _pwr_optfcn (df , loc ):
1036
1035
'''
1037
- Function to find power from I_from_V .
1036
+ Function to find power from ``i_from_v`` .
1038
1037
'''
1039
1038
1040
- I = I_from_V ( Rsh = df ['Rsh ' ], Rs = df ['Rs ' ], nNsVth = df ['nNsVth' ], V = df [ loc ],
1041
- I0 = df ['I0 ' ], IL = df ['IL ' ])
1039
+ I = i_from_v ( df ['r_sh ' ], df ['r_s ' ], df ['nNsVth' ],
1040
+ df [loc ], df [ 'i_0 ' ], df ['i_l ' ])
1042
1041
return I * df [loc ]
1043
1042
1044
1043
1045
- def _Voc_optfcn (df , loc ):
1044
+ def _v_oc_optfcn (df , loc ):
1046
1045
'''
1047
- Function to find V_oc from I_from_V .
1046
+ Function to find the open circuit voltage from ``i_from_v`` .
1048
1047
'''
1049
- I = - abs (I_from_V ( Rsh = df ['Rsh ' ], Rs = df ['Rs ' ], nNsVth = df ['nNsVth' ],
1050
- V = df [loc ], I0 = df ['I0 ' ], IL = df ['IL ' ]))
1048
+ I = - abs (i_from_v ( df ['r_sh ' ], df ['r_s ' ], df ['nNsVth' ],
1049
+ df [loc ], df ['i_0 ' ], df ['i_l ' ]))
1051
1050
return I
1052
1051
1053
1052
1054
- def I_from_V (Rsh , Rs , nNsVth , V , I0 , IL ):
1053
+ def i_from_v (resistance_shunt , resistance_series , nNsVth , voltage ,
1054
+ saturation_current , photocurrent ):
1055
1055
'''
1056
- Calculates I from V per Eq 2 Jain and Kapoor 2004
1057
- uses Lambert W implemented in wapr_vec.m
1058
- Rsh, nVth, V, I0, IL can all be DataFrames
1059
- Rs can be a DataFrame, but should be a scalar.
1056
+ Calculates current from voltage per Eq 2 Jain and Kapoor 2004 [1].
1057
+
1058
+ Parameters
1059
+ ----------
1060
+ resistance_series : float or Series
1061
+ Series resistance in ohms under desired IV curve conditions.
1062
+ Often abbreviated ``Rs``.
1063
+
1064
+ resistance_shunt : float or Series
1065
+ Shunt resistance in ohms under desired IV curve conditions.
1066
+ Often abbreviated ``Rsh``.
1067
+
1068
+ saturation_current : float or Series
1069
+ Diode saturation current in amperes under desired IV curve
1070
+ conditions. Often abbreviated ``I_0``.
1071
+
1072
+ nNsVth : float or Series
1073
+ The product of three components. 1) The usual diode ideal
1074
+ factor (n), 2) the number of cells in series (Ns), and 3) the cell
1075
+ thermal voltage under the desired IV curve conditions (Vth).
1076
+ The thermal voltage of the cell (in volts) may be calculated as
1077
+ ``k*temp_cell/q``, where k is Boltzmann's constant (J/K),
1078
+ temp_cell is the temperature of the p-n junction in Kelvin,
1079
+ and q is the charge of an electron (coulombs).
1080
+
1081
+ photocurrent : float or Series
1082
+ Light-generated current (photocurrent) in amperes under desired IV
1083
+ curve conditions. Often abbreviated ``I_L``.
1084
+
1085
+ Returns
1086
+ -------
1087
+ current : np.array
1088
+
1089
+ References
1090
+ ----------
1091
+ [1] A. Jain, A. Kapoor, "Exact analytical solutions of the parameters of
1092
+ real solar cells using Lambert W-function", Solar Energy Materials
1093
+ and Solar Cells, 81 (2004) 269-277.
1060
1094
'''
1061
1095
try :
1062
1096
from scipy .special import lambertw
1063
1097
except ImportError :
1064
1098
raise ImportError ('This function requires scipy' )
1065
1099
1066
- argW = (Rs * I0 * Rsh * np .exp (Rsh * (Rs * (IL + I0 )+ V ) /
1067
- (nNsVth * (Rs + Rsh ))) / (nNsVth * (Rs + Rsh )) )
1068
- inputterm = lambertw (argW )
1100
+ Rsh = resistance_shunt
1101
+ Rs = resistance_series
1102
+ I0 = saturation_current
1103
+ IL = photocurrent
1104
+ V = voltage
1105
+
1106
+ argW = (Rs * I0 * Rsh *
1107
+ np .exp ( Rsh * (Rs * (IL + I0 )+ V ) / (nNsVth * (Rs + Rsh )) ) /
1108
+ (nNsVth * (Rs + Rsh )) )
1109
+ lambertwterm = lambertw (argW )
1110
+ pvl_logger .debug ('argW: {}, lambertwterm{}' .format (argW , lambertwterm ))
1069
1111
1070
1112
# Eqn. 4 in Jain and Kapoor, 2004
1071
- I = - V / (Rs + Rsh ) - (nNsVth / Rs ) * inputterm + Rsh * (IL + I0 )/ (Rs + Rsh )
1113
+ I = - V / (Rs + Rsh ) - (nNsVth / Rs )* lambertwterm + Rsh * (IL + I0 )/ (Rs + Rsh )
1072
1114
1073
1115
return I .real
1074
1116
0 commit comments