Skip to content

Commit

Permalink
Bifacial option in Mermoud Lejeune module model (#622)
Browse files Browse the repository at this point in the history
* feat(): Expose bifacial model for mlm module model

* test: test values from mlm bifacial model

* fix: add nominal module efficiency calculation for Mermoud Lejuene model

* fix: handle divide by 0 in mlm iam calculation

* test: fixed performance ratio tests now that nominal module efficiency is calculated

Co-authored-by: Casey Zak <[email protected]>
  • Loading branch information
allenlawrence94 and Casey Zak authored Nov 9, 2021
1 parent 8e9dcb0 commit ae16711
Show file tree
Hide file tree
Showing 6 changed files with 9,270 additions and 14 deletions.
33 changes: 22 additions & 11 deletions shared/lib_mlmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,19 +186,31 @@ bool mlmodel_module_t::operator() (pvinput_t &input, double T_C, double opvoltag
}

// Total effective irradiance
double S;
double S_front, S_total, S_eff_front, S_eff_total;
if(input.radmode != 3){ // Skip module cover effects if using POA reference cell data
S = (f_IAM_beam * input.Ibeam + f_IAM_diff * input.Idiff + groundRelfectionFraction * f_IAM_gnd * input.Ignd) * f_AM;
S_front = input.Ibeam + input.Idiff + input.Ignd;
S_total = S_front + input.Irear; // Note the rear irradiance has already taken bifaciality into consideration
S_eff_front = (f_IAM_beam * input.Ibeam + f_IAM_diff * input.Idiff + groundRelfectionFraction * f_IAM_gnd * input.Ignd) * f_AM;
S_eff_total = S_eff_front + input.Irear * f_AM;
if (S_front > 1e-8) { // TODO: should sunup catch this?
out.AOIModifier = S_eff_front / S_front;
}
else {
out.AOIModifier = 1.0;
}
}
else if(input.usePOAFromWF){ // Check if decomposed POA is required, if not use weather file POA directly
S = input.poaIrr;
S_total = S_eff_total = input.poaIrr;
out.AOIModifier = 1.0;
}
else { // Otherwise use decomposed POA
S = (f_IAM_beam * input.Ibeam + f_IAM_diff * input.Idiff + groundRelfectionFraction * f_IAM_gnd * input.Ignd) * f_AM;
S_total = input.poaIrr;
S_eff_total = input.Ibeam + input.Idiff + input.Ignd + input.Irear;
out.AOIModifier = 1.0;
}

// Single diode model acc. to [1]
if (S >= 1)
if (S_eff_total >= 1)
{
double n=0.0, a=0.0, I_L=0.0, I_0=0.0, R_sh=0.0, I_sc=0.0;
double V_oc = V_oc_ref; // V_oc_ref as initial guess
Expand All @@ -217,16 +229,16 @@ bool mlmodel_module_t::operator() (pvinput_t &input, double T_C, double opvoltag
for (int i = 1; i <= iterations; i = i + 1) {
if (T_mode == T_MODE_FAIMAN) {
// T_cell = input.Tdry + (T_c_fa_alpha * G_total * (1 - eff)) / (T_c_fa_U0 + input.Wspd * T_c_fa_U1);
T_cell = input.Tdry + (T_c_fa_alpha * S * (1 - eff)) / (T_c_fa_U0 + input.Wspd * T_c_fa_U1);
T_cell = input.Tdry + (T_c_fa_alpha * S_eff_total* (1 - eff)) / (T_c_fa_U0 + input.Wspd * T_c_fa_U1);
}

n = n_0 + mu_n * (T_cell - T_ref);
a = N_series * k * (T_cell + T_0) * n / q;
I_L = (S / S_ref) * (I_Lref + alpha_isc * (T_cell - T_ref));
//I_L = (S / S_ref) * (I_Lref + alpha_isc / N_parallel * (T_cell - T_ref));
I_L = (S_eff_total/ S_ref) * (I_Lref + alpha_isc * (T_cell - T_ref));
//I_L = (S_eff_total/ S_ref) * (I_Lref + alpha_isc / N_parallel * (T_cell - T_ref));
I_0 = I_0ref * pow(((T_cell + T_0) / (T_ref + T_0)), 3) * exp((q * E_g) / (n * k) * (1 / (T_ref + T_0) - 1 / (T_cell + T_0)));

R_sh = R_shref + (R_sh0 - R_shref) * exp(-R_shexp * (S / S_ref));
R_sh = R_shref + (R_sh0 - R_shref) * exp(-R_shexp * (S_eff_total/ S_ref));

V_oc = openvoltage_5par_rec(V_oc, a, I_L, I_0, R_sh, D2MuTau, Vbi);
I_sc = I_L / (1 + R_s / R_sh);
Expand All @@ -243,7 +255,7 @@ bool mlmodel_module_t::operator() (pvinput_t &input, double T_C, double opvoltag
else I = current_5par_rec(V, 0.9*I_L, a, I_L, I_0, R_s, R_sh, D2MuTau, Vbi);
P = V*I;
}
eff = P / ((Width * Length) * (input.Ibeam + input.Idiff + input.Ignd));
eff = P / ((Width * Length) * S_total);
}

out.Power = P;
Expand All @@ -253,7 +265,6 @@ bool mlmodel_module_t::operator() (pvinput_t &input, double T_C, double opvoltag
out.Voc_oper = V_oc;
out.Isc_oper = I_sc;
out.CellTemp = T_cell;
out.AOIModifier = S / (input.Ibeam + input.Idiff + input.Ignd);
}

return out.Power >= 0;
Expand Down
4 changes: 4 additions & 0 deletions shared/lib_pv_io_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,10 @@ Module_IO::Module_IO(compute_module* cm, std::string cmName, double dcLoss)
else if (modulePowerModel == MODULE_PVYIELD)
{
// Mermoud/Lejeune single-diode model
isBifacial = cm->as_boolean("mlm_is_bifacial");
bifaciality = cm->as_double("mlm_bifaciality");
bifacialTransmissionFactor = cm->as_double("mlm_bifacial_transmission_factor");
groundClearanceHeight = cm->as_double("mlm_bifacial_ground_clearance_height");
size_t elementCount1 = 0;
size_t elementCount2 = 0;
ssc_number_t* arrayIncAngle = 0;
Expand Down
12 changes: 12 additions & 0 deletions ssc/cmod_pvsamv1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@ static var_info _cm_vtab_pvsamv1[] = {
{ SSC_INPUT, SSC_ARRAY, "mlm_IAM_c_cs_incAngle", "Spline IAM - Incidence angles", "deg", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },
{ SSC_INPUT, SSC_ARRAY, "mlm_IAM_c_cs_iamValue", "Spline IAM - IAM values", "-", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },
{ SSC_INPUT, SSC_NUMBER, "mlm_groundRelfectionFraction", "Ground reflection fraction", "-", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },
{ SSC_INPUT, SSC_NUMBER, "mlm_is_bifacial", "Modules are bifacial", "0/1", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },
{ SSC_INPUT, SSC_NUMBER, "mlm_bifacial_transmission_factor", "Bifacial transmission factor", "0-1", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },
{ SSC_INPUT, SSC_NUMBER, "mlm_bifaciality", "Bifaciality factor", "%", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },
{ SSC_INPUT, SSC_NUMBER, "mlm_bifacial_ground_clearance_height", "Module ground clearance height", "m", "", "Mermoud Lejeune Single Diode Model", "module_model=5", "", "" },


// inverter model
{ SSC_INPUT, SSC_NUMBER, "inverter_model", "Inverter model specifier", "", "0=cec,1=datasheet,2=partload,3=coefficientgenerator,4=PVYield", "Inverter", "*", "INTEGER,MIN=0,MAX=4", "" },
Expand Down Expand Up @@ -2997,6 +3002,13 @@ double cm_pvsamv1::module_eff(int mod_type)
eff = 100.0 * ((vmp * imp) / area) / 1000.0;
}
break;
case 5: // Mermoud Lejeune
{
double area = as_double("mlm_Length") * as_double("mlm_Width");
double vmp = as_double("mlm_V_mp_ref");
double imp = as_double("mlm_I_mp_ref");
eff = 100.0 * ((vmp * imp) / area) / 1000.0;
}
}

if (eff == 0.0) eff = -1;
Expand Down
Loading

0 comments on commit ae16711

Please sign in to comment.