Different Pmp values from pvlib and PVMismatch

78 views
Skip to first unread message

Brais González Rodríguez

unread,
Jul 16, 2024, 8:32:23 AM (3 days ago) Jul 16
to pvlib-python
Hello,

I am conducting a study to compare the results obtained with PVMismatch (https://sunpower.github.io/PVMismatch/) and pvlib. After a detailed analysis of different scenarios, I have encountered many cases where I get results with significant differences. I am aware that PVMismatch uses the 2-diode model, while in pvlib I am using the single-diode model. Perhaps the entire difference comes from there, but it seems like a very large discrepancy, and I wanted to ask for advice here in case I am doing something wrong.

The module I am working with is a JAM72S20 440/MR, and the parameters I am using are as follows:

alpha_i_sc_percent = 0.044
beta_v_oc_percent = -0.272
gamma_p_mp_percent = -0.35
v_oc = 48.81
v_mp = 40.82
i_sc = 11.36
i_mp = 10.78
cells_in_series = 72
parallel_strings = 2


In the example I am presenting here, the irradiance value is 1071.1 W/m² and the module temperature is 75.5°C.

The code I am using in PVlib is as follows:

I_L_ref, I_o_ref, R_s, R_sh_ref, a_ref, Adjust = pvlib.ivtools.sdm.fit_cec_sam(
    celltype="monoSi",
    v_mp=v_mp,
    i_mp=i_mp,
    v_oc=v_oc,
    i_sc=i_sc,
    alpha_sc=i_sc*alpha_i_sc_percent/100,
    beta_voc=v_oc*beta_v_oc_percent/100,
    gamma_pmp=gamma_p_mp_percent,
    cells_in_series=cells_in_series,
    temp_ref=25,
)

photocurrent, saturation_current, resistance_series, resistance_shunt, nnsvth = pvlib.pvsystem.calcparams_cec(
    effective_irradiance=irradiance,
    temp_cell=temperature,
    alpha_sc=i_sc*alpha_i_sc_percent/100,
    a_ref=a_ref,
    I_L_ref=I_L_ref,
    I_o_ref=I_o_ref,
    R_sh_ref=R_sh_ref,
    R_s=R_s,
    Adjust=Adjust,
)

cell_iv = pvlib.pvsystem.singlediode(
    photocurrent=photocurrent,
    saturation_current=saturation_current,
    resistance_series=resistance_series,
    resistance_shunt=resistance_shunt,
    nNsVth=nnsvth,
)
cell_iv["p_mp"]


obtaining a Pmp of 386.0 W.

On the other hand, in PVMismatch, I am using the following code:

cell = pvmismatch.pvmismatch_lib.pvcell.PVcell(
    Rs=R_s/cells_in_series,
    Rsh=R_sh_ref/cells_in_series,
    Isat1_T0=I_o_ref/parallel_strings,
    Isc0_T0=i_sc/parallel_strings,
    Eg=1.121,
    alpha_Isc=alpha_i_sc_percent/100/parallel_strings,
    Tcell=temperature+273.15,
    Ee=irradiance/1000,
)

cell_pos = [[[{"crosstie": False, "idx": i} for i in range(cells_in_series)]]]
strings = []
cells = [cell] * cells_in_series

submodule = pvmismatch.PVmodule(cell_pos=cell_pos, pvcells=cells)
string = pvmismatch.PVstring(numberMods=1, pvmods=[submodule])
strings.append(string)

submodule = pvmismatch.PVmodule(cell_pos=cell_pos, pvcells=cells)
string = pvmismatch.PVstring(numberMods=1, pvmods=[submodule])
strings.append(string)

module = pvmismatch.PVsystem(pvstrs=strings)
module.Pmp


obtaining a Pmp of 113.7 W. Here, I had to define each of the cells in the module, dividing Rs and Rsh by the number of series cells (72) and Isat1_T0, Isc0_T0, and alpha_Isc by the number of parallel strings (2). Subsequently, I combined them into two strings in PVMismatch, which finally result in the final PVSystem.

I attach a Jupyter notebook with all the code in case it is useful. Additionally, it includes the computation of Pmp for a single cell with pvlib and PVMismatch, obtaining 2.77 W and 0.79 W, respectively. That is, in both cases (cell and module), the results I get with PVMismatch are about 3.5 times less than those with pvlib.

Any ideas on the reason(s) why this is happening? I have observed that with an irradiance of 1000 W/m² and a temperature of 25°C, the results I get with PVlib and PVMismatch are very similar. However, as soon as I deviate from these conditions, the results start to differ significantly, as in this case.

Thank you very much in advance for your help.
Brais

Brais González Rodríguez

unread,
Jul 16, 2024, 8:46:08 AM (3 days ago) Jul 16
to pvlib-python
Sorry, the attachment was not sent properly for some reason. You can view it here: https://colab.research.google.com/drive/18ySYsWxTtKuAupQ8xYp3QE6Ivrcxe2fw?usp=sharing

Anton Driesse

unread,
Jul 16, 2024, 8:47:00 AM (3 days ago) Jul 16
to pvlib-...@googlegroups.com

Try this:

Rsh=R_sh_ref*cells_in_series

INFORMACIÓN SOBRE PROTECCIÓN DE DATOS PERSONALES; Reglamento UE 2016/679 de Protección de Datos (RGPD) y Ley Orgánica 3/2018 de Protección de Datos Personales y garantía de los derechos digitales (LOPDGDD); RESPONSABLE DEL TRATAMIENTO: ieco Desarrollo Digital, S.L.; FINALIDADES DEL TRATAMIENTO: Inicio o mantenimiento de una relación mercantil, respuesta a consultas, solicitudes o peticiones; LEGITIMACIÓN PARA EL TRATAMIENTO: Consentimiento de la persona interesada (artículo 6.1,a del RGPD), relación contractual (artículo 6.1,b del RGPD) o Interés legítimo sobre el origen del contacto (artículo 6.1,f del RGPD); DESTINATARIOS: No se cederán datos a terceros, salvo Encargados del Tratamiento con los que se tenga una vinculación contractual para que puedan ejecutar correctamente la prestación del servicio (artículo 28 RGPD).; EJERCICIO DE DERECHOS: Se permite el ejercicio de los derechos de acceso, rectificación o supresión, la limitación del tratamiento, oposición y el derecho a la portabilidad de sus datos en le...@ieco.io; CONFIDENCIALIDAD: Este mensaje, y en su caso cualquier fichero anexo, se dirige únicamente a su destinatario/a, y puede contener información privilegiada y/o confidencial, siendo para uso exclusivo del mismo/a. Si no es Vd. el destinatario/a de referencia, se le notifica que la utilización, cesión a terceros, divulgación y/o copia sin autorización está prohibida. Si ha recibido este mensaje por error, le rogamos que nos lo comunique inmediatamente por esta misma vía y proceda a su destrucción.
--
You received this message because you are subscribed to the Google Groups "pvlib-python" group.
To unsubscribe from this group and stop receiving emails from it, send an email to pvlib-python...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/pvlib-python/e83f1740-16b6-45b5-a704-9aa9e42d67ffn%40googlegroups.com.
-- 
PV Performance Labs
Emmy-Noether-Str. 2
79110 Freiburg
Germany

+49-761-8973-5603 (Office, daytime)
+49-174-532-7677 (Mobile)

www.pvperformancelabs.com
Message has been deleted
Message has been deleted

cwh...@sandia.gov

unread,
Jul 16, 2024, 12:02:50 PM (3 days ago) Jul 16
to pvlib-python
Questions and comments:

1. Is each parallel string 72 cells in length, or are there 2 strings in parallel each with 36 cells? The single diode model functions in pvlib only model a series-connected string of cells, so the parameters you get back from 'fit_cec_sam' are for a module of 72 cells in series. If the module is 2 strings of 36 cells, then you would need to fit the model after dividing Imp, Isc and alpha_i_sc by 2, and setting cells_in_series=36. Then simulate the power by calcparams_cec and singlediode, and double the currents. Or, I think you can get close by pretending its a single string of 72 cells, unless you are planning to model situations with irradiance varying among the cells.

2. Fitting the CEC model to a single cell: this code starts with parameters fit for a 72-cell string and also dividing the currents by parallel_strings. I think that results in currents being too low by half.

3. For PVMismatch I think you need to specify an Isat2 parameter? For the second diode. I don't know if PVMismatch also has a variable diode factor or if it uses the theoretical 1 and 2. Things to check that may help close the gap. Its been a while since I looked at the PVMismatch code.

Cliff
Message has been deleted

Brais González Rodríguez

unread,
Jul 16, 2024, 1:00:36 PM (3 days ago) Jul 16
to pvlib-python
Thank you very much for the answer. Regarding point 1, the module consists of two parallel strings, each with 72 cells (144 cells in total in the module). I understand what you are saying, and I believe the corrected code would be as follows:


I_L_ref, I_o_ref, R_s, R_sh_ref, a_ref, Adjust = pvlib.ivtools.sdm.fit_cec_sam(
    celltype="monoSi",
    v_mp=v_mp,
    i_mp=i_mp/parallel_strings,
    v_oc=v_oc,
    i_sc=i_sc/parallel_strings,
    alpha_sc=(i_sc/parallel_strings)*alpha_i_sc_percent/100,

    beta_voc=v_oc*beta_v_oc_percent/100,
    gamma_pmp=gamma_p_mp_percent,
    cells_in_series=cells_in_series,
    temp_ref=25,
)

photocurrent, saturation_current, resistance_series, resistance_shunt, nnsvth = pvlib.pvsystem.calcparams_cec(
    effective_irradiance=irradiance,
    temp_cell=temperature,
    alpha_sc=(i_sc/parallel_strings)*alpha_i_sc_percent/100,

    a_ref=a_ref,
    I_L_ref=I_L_ref,
    I_o_ref=I_o_ref,
    R_sh_ref=R_sh_ref,
    R_s=R_s,
    Adjust=Adjust,
)

cell_iv = pvlib.pvsystem.singlediode(
    photocurrent=photocurrent,
    saturation_current=saturation_current,
    resistance_series=resistance_series,
    resistance_shunt=resistance_shunt,
    nNsVth=nnsvth,
)

cell_iv["p_mp"]*2

which returns the same 386.0 W, precisely for the reason you mentioned that I am considering the same irradiance for all the cells. However, I currently have a code that models each cell separately and then combines them based on whether they are connected in series or parallel (with that code, I get exactly the same result as modeling the entire module). I didn't attach it to avoid complicating the question since I am almost sure that the value returned by pvlib is correct and the one returned by PVMismatch is incorrect. My idea is to model situations where there are partial shadows on the module, so I do need to define the model at the cell level and then combine them.

Regarding point 2, I completely agree with what you mention, and after correcting it, I get a Pmp of the cell of 2.68 W (slightly lower than the previous 2.77 W). However, the difference with PVMismatch is still very large.

About point 3, indeed, PVMismatch requires specifying Isat2, which I left as default because I was unable to find it in the module specifications. The module in question is https://www.jasolar.com/uploadfile/2020/0619/20200619040220997.pdf and the specifications I have are as follows:

brand: JA Solar
family:
url: https://storage.googleapis.com/part-datasheets/df1e3aa600ea0e3a0ce1070ea63e84ec04cb8f95.pdf
version: 20200530A
product_warranty_years: 12
performance_warranty_curve:
  first_year_degradation_percent: 2
  yearly_linear_degradation_percent: 0.55
  years: 25
length: 2.120
width: 1.052
depth: 0.040
weight: 25.0
bifacial: false
frameless: false
back_color: white
frame_color: gray
cell_type: Mono
cells_in_series: 72
n_cells: 144
n_diodes: 3
layout: twin
t_ref: 25.0
alpha_i_sc_percent: 0.044
beta_v_oc_percent: -0.272
gamma_p_mp_percent: -0.35
maximum_system_voltage: 1000
maximum_series_fuse_rating: 20
models:
  - uuid: bdad4e2fef5d471dbfd9642c90075f3f
    model: JAM72S20-440/MR
    nominal_power: 440
    v_oc: 48.81
    v_mp: 40.82
    i_sc: 11.36
    i_mp: 10.78

Where could I obtain the value for Isat2?

Thanks again for the help.
Brais
Message has been deleted

Brais González Rodríguez

unread,
Jul 16, 2024, 1:00:55 PM (3 days ago) Jul 16
to pvlib-python
Thanks a lot for the suggestion. Unfortunately, it does not work, since the resulting module Pmp only changes from 113.7 W to 113.5 W. Anyway, why should I have to multiply the shunt resistance by the number of cells in series instead of divide it?

cwh...@sandia.gov

unread,
Jul 16, 2024, 1:14:42 PM (3 days ago) Jul 16
to pvlib-python
Where could I obtain the value for Isat2?

The only way to get Isat1 / Isat2 is to fit the two-diode model to the data. There's no public code to do that, AFAIK.

An alternative is to set Isa1 to be the I_o_ref you get from fit_cec_sam, then adjust Isat2 until the PVMismatch and pvlib Voc values agree. 

I noticed that, at fit_cec_sam, you are not dividing     gamma_pmp=gamma_p_mp_percent,
by 2, and I think you need to do that.

Even so, I don't think these adjustments would change the Pmp by a factor of roughly 3. I suspect there's something not right about the photocurrent. How do the Isc predictions compare?


On Tuesday, July 16, 2024 at 10:00:43 AM UTC-7 brai...@ieco.io wrote:
Thanks a lot for the suggestion. Unfortunately, it does not work, since the resulting module Pmp only changes from 113.7 W to 113.5 W. Anyway, why should I have to multiply the shunt resistance by the number of cells in series instead of divide it?

On Tuesday, July 16, 2024 at 2:47:00 PM UTC+2 anton....@pvperformancelabs.com wrote:

Mark Mikofski

unread,
Jul 16, 2024, 9:27:47 PM (3 days ago) Jul 16
to pvlib-python

There is a function in pvmismatch to generate 2-diode coefficients. It’s called gen_coeffs() check the documentation: https://sunpower.github.io/PVMismatch/api/contrib.html#module-pvmismatch.contrib.gen_coeffs


Check the discussions on how to generate coefficients for pvmismatch: https://github.com/SunPower/PVMismatch/discussions


Especially:

There are also some stack overflow questions: https://stackoverflow.com/search?q=Pvmismatch


You can also post questions in the discussion on GitHub, see link above. Note although communities overlap, pvmismatch is a sunpower/maxeon tool and is not officially supported by pvlib, although I bet you will still find kind folks who will try to support you in this forum like in this thread

Brais González Rodríguez

unread,
Jul 18, 2024, 4:37:15 AM (yesterday) Jul 18
to pvlib-python
Hello,

Thank you very much for your answer. Regarding gamma_pmp, dividing it by 2 changes the Pmp significantly, from 386.0 W to 426.1 W. This, as I understand it, is due to the fact that the cell temperature in this example is very high (75.5°C), and therefore the model is very sensitive to this change. Since it's a percentage, I'm not sure that it needs to be divided by 2, just as I don't divide alpha_i_sc_percent (but I do divide i_sc). Could you explain why it should be divided by 2?

Regarding the values I obtain, with pvlib they are as follows:

I_L_ref = 11.36
I_o_ref = 2.57e-11
photocurrent = 6.21
Pmp = 386.0
Voc = 41.7
Isc = 12.4

without dividing gamma_pmp by 2. If I divide gamma_pmp by 2, I get:

I_L_ref = 11.37
I_o_ref = 3.39e-14
photocurrent = 6.28
Pmp = 426.1
Voc = 44.8
Isc = 12.54.

The updated code is as follows:

# Pvlib module

I_L_ref, I_o_ref, R_s, R_sh_ref, a_ref, Adjust = pvlib.ivtools.sdm.fit_cec_sam(
    celltype="monoSi",
    v_mp=v_mp,
    i_mp=i_mp/parallel_strings,
    v_oc=v_oc,
    i_sc=i_sc/parallel_strings,
    alpha_sc=(i_sc/parallel_strings)*alpha_i_sc_percent/100,

    beta_voc=v_oc*beta_v_oc_percent/100,
    gamma_pmp=gamma_p_mp_percent,
    cells_in_series=cells_in_series,
    temp_ref=25,
)
print(I_L_ref*2)
print(I_o_ref*2)

photocurrent, saturation_current, resistance_series, resistance_shunt, nnsvth = pvlib.pvsystem.calcparams_cec(
    effective_irradiance=irradiance,
    temp_cell=temperature,
    alpha_sc=(i_sc/parallel_strings)*alpha_i_sc_percent/100,

    a_ref=a_ref,
    I_L_ref=I_L_ref,
    I_o_ref=I_o_ref,
    R_sh_ref=R_sh_ref,
    R_s=R_s,
    Adjust=Adjust,
)
print(photocurrent)

cell_iv = pvlib.pvsystem.singlediode(
    photocurrent=photocurrent,
    saturation_current=saturation_current,
    resistance_series=resistance_series,
    resistance_shunt=resistance_shunt,
    nNsVth=nnsvth,
)
print(cell_iv["p_mp"]*2)
print(cell_iv["v_oc"])
print(cell_iv["i_sc"]*2)

On the other hand, with PVMismatch, I get the following:

Pmp = 137.7
Voc = 39.0
Isc = 12.3

I have updated the code with the function that calculates the coefficients mentioned in the other post, and the code is as follows:

# Pvmismatch module
(isat1, isat2, rs, rsh), sol = gen_coeffs.gen_two_diode(
    isc = i_sc,
    voc = v_oc,
    imp = i_mp,
    vmp = v_mp,
    nseries = cells_in_series,
    nparallel = parallel_strings,
    tc = temperature
)
cell = pvmismatch.pvmismatch_lib.pvcell.PVcell(
    Rs=rs,
    Rsh=rsh,
    Isat1_T0=isat1,
    Isat2_T0=isat2,
    Isc0_T0=i_sc/parallel_strings,
    Eg=1.121,
    alpha_Isc=(alpha_i_sc_percent/100)/parallel_strings,

    Tcell=temperature+273.15,
    Ee=irradiance/1000,
)
cell_pos = [[[{"crosstie": False, "idx": i} for i in range(cells_in_series)]]]
strings = []
cells = [cell] * cells_in_series
submodule = pvmismatch.PVmodule(cell_pos=cell_pos, pvcells=cells)
string = pvmismatch.PVstring(numberMods=1, pvmods=[submodule])
strings.append(string)
submodule = pvmismatch.PVmodule(cell_pos=cell_pos, pvcells=cells)
string = pvmismatch.PVstring(numberMods=1, pvmods=[submodule])
strings.append(string)
module = pvmismatch.PVsystem(pvstrs=strings)
print(module.Pmp)
print(module.Voc)
print(module.Isc)

I have seen that if I set "crosstie: True" when defining cell_pos, the Pmp I obtain is 383.3 W, which is much closer to the one from pvlib. At the time, I checked what that parameter meant and it seemed correct to set it to False, but I might have been mistaken, so I will review it again.

Thank you very much for all the help!
Brais

Brais González Rodríguez

unread,
Jul 18, 2024, 4:38:07 AM (yesterday) Jul 18
to pvlib-python
Hello,

Thank you very much for all the information. In particular, thanks for showing me gen_coeffs() function, as I was unaware of its existence and it is extremely useful to me. On the other hand, I have noticed that I might have made an error in setting the crosstie parameter to False. I will carefully review the links you provided, as I will likely find the answer there.

Thanks again!
Brais

cwh...@sandia.gov

unread,
Jul 18, 2024, 11:39:24 AM (yesterday) Jul 18
to pvlib-python
>   Regarding gamma_pmp, ....  Since it's a percentage, I'm not sure that it needs to be divided by 2,  Could you explain why it should be divided by 2?

You are correct, the percent change in power for one string should be the same as the percent change for the module.

Cliff
Reply all
Reply to author
Forward
0 new messages