Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8698c7a
Add files via upload
JFrederico2022 Oct 3, 2026
f468215
Merge branch 'pvlib:main' into main
JFrederico2022 Oct 6, 2026
16beb52
Delete pvlib/ivtools/sdm/Villalva.ipynb
JFrederico2022 Oct 6, 2026
07654d1
Add files via upload
JFrederico2022 Oct 6, 2026
547f679
Update __init__.py
JFrederico2022 Oct 6, 2026
5f29b13
Update pvsystem.py
JFrederico2022 Oct 6, 2026
51fdd23
Add files via upload
JFrederico2022 Oct 6, 2026
eb878f6
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 7, 2026
386c004
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 7, 2026
308fe5f
Update villalva.py
JFrederico2022 Oct 7, 2026
e8b541e
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
994732b
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
1b6898d
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
1b638ac
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
0ed22d0
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
eb33c3e
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
c37846c
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 9, 2026
6ffbb12
Update pvlib/pvsystem.py
JFrederico2022 Oct 9, 2026
e3f6400
Update pvlib/ivtools/sdm/__init__.py
JFrederico2022 Oct 9, 2026
2ebcb2d
Update pvlib/pvsystem.py
JFrederico2022 Oct 9, 2026
9ea7601
Update villalva.py
JFrederico2022 Oct 9, 2026
5a7ea02
Add Villalva parameters to PV modeling documentation
JFrederico2022 Oct 9, 2026
d18881d
Revise references in pvsystem.py documentation
JFrederico2022 Oct 9, 2026
01f0585
Suppress E231 warnings in plot_villalva.py
JFrederico2022 Oct 9, 2026
09b970e
Add fit_villalva function to parameters documentation
JFrederico2022 Oct 9, 2026
6b62931
Fix return statement indentation in pvsystem.py
JFrederico2022 Oct 9, 2026
7307b50
Import fit_villalva function in __init__.py
JFrederico2022 Oct 9, 2026
af96cee
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 21, 2026
534df7c
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 21, 2026
a3f1f99
Update pvlib/pvsystem.py
JFrederico2022 Oct 21, 2026
07359d8
Update pvlib/pvsystem.py
JFrederico2022 Oct 21, 2026
05aa194
Update pvlib/pvsystem.py
JFrederico2022 Oct 21, 2026
6386ac6
Update pvlib/pvsystem.py
JFrederico2022 Oct 21, 2026
c0de33a
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 21, 2026
d397546
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 21, 2026
23514ad
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 21, 2026
7832f4f
Update pvlib/ivtools/sdm/villalva.py
JFrederico2022 Oct 22, 2026
d997727
Update pvlib/pvsystem.py
JFrederico2022 Oct 22, 2026
a2ea5f4
Add files via upload
JFrederico2022 Oct 23, 2026
7bd8710
Refine documentation for Villalva SDM parameters
JFrederico2022 Oct 23, 2026
f63ba4a
Implement test for calcparams_villalva function
JFrederico2022 Oct 23, 2026
bc3194c
Update title for Villalva model example
JFrederico2022 Oct 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
444 changes: 444 additions & 0 deletions docs/examples/iv-modeling/plot_villalva.py

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/sphinx/source/reference/pv_modeling/parameters.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Functions for fitting single diode models
ivtools.sdm.fit_pvsyst_iec61853_sandia_2025
ivtools.sdm.fit_desoto_sandia
ivtools.sdm.fit_desoto_batzelis
ivtools.sdm.fit_villalva

Functions for fitting the single diode equation

Expand Down
2 changes: 2 additions & 0 deletions docs/sphinx/source/reference/pv_modeling/sdm.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Functions relevant for single diode models.
pvsystem.calcparams_cec
pvsystem.calcparams_desoto
pvsystem.calcparams_pvsyst
pvsystem.calcparams_villalva
pvsystem.i_from_v
pvsystem.singlediode
pvsystem.v_from_i
Expand Down Expand Up @@ -39,3 +40,4 @@ Functions for fitting diode models
ivtools.sdm.fit_cec_sam
ivtools.sdm.fit_desoto
ivtools.sdm.fit_desoto_batzelis
ivtools.sdm.fit_villalva
5 changes: 5 additions & 0 deletions pvlib/ivtools/sdm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@
fit_pvsyst_iec61853_sandia_2025,
pvsyst_temperature_coeff,
)

from pvlib.ivtools.sdm.villalva import ( # noqa: F401
_villalva_params_at_rs,
fit_villalva,
)
Comment thread
JFrederico2022 marked this conversation as resolved.
273 changes: 273 additions & 0 deletions pvlib/ivtools/sdm/villalva.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
"""Functions for fitting the Villalva single-diode model."""

import numpy as np
import pandas as pd

from scipy import constants

from pvlib import pvsystem


def _villalva_params_at_rs(
resistance_series,
v_mp,
i_mp,
v_oc,
i_sc,
a_ref,
):
"""Calculate ``I_L``, ``I_o`` and ``R_sh`` for one candidate ``R_s``."""
exp_voc = np.expm1(v_oc / a_ref)
exp_vmp = np.expm1((v_mp + i_mp * resistance_series) / a_ref)

p_mp = v_mp * i_mp

# Algebraic combination of Villalva's equations:
#
# I_L = (R_sh + R_s) / R_sh * I_sc
#
# I_o = (I_L - V_oc / R_sh) / (exp(V_oc / a_ref) - 1)
#
# and the R_sh(R_s) relation obtained at the MPP.
a_term = (
v_mp * i_sc
- v_mp * i_sc * exp_vmp / exp_voc
- p_mp
)

b_term = (
v_mp * i_sc * resistance_series
- v_mp
* (i_sc * resistance_series - v_oc)
* exp_vmp
/ exp_voc
)

resistance_shunt = (
v_mp * (v_mp + i_mp * resistance_series) - b_term
) / a_term

photocurrent = (
(resistance_shunt + resistance_series)
/ resistance_shunt
* i_sc
)

saturation_current = (
photocurrent - v_oc / resistance_shunt
) / exp_voc

return photocurrent, saturation_current, resistance_shunt


def fit_villalva(
v_mp,
i_mp,
v_oc,
i_sc,
alpha_sc,
beta_voc,
cells_in_series,
diode_factor,
temp_ref=25.0,
irrad_ref=1000.0,
rs_step=1e-4,
rs_max=None,
):
r"""Fit Villalva single-diode model to datasheet values.

Parameter extraction for Villalva's single-diode model [1]_ is described in [2]_.
Parameter estimation increments the series resistance from zero. For each
candidate :math:`R_s`, the corresponding :math:`R_{sh}`, :math:`I_L`, and
:math:`I_0` are calculated and the maximum power of the resulting single-diode
equation is evaluated. The returned parameters solution minimizes the absolute
difference between the modeled maximum power and the datasheet maximum
power at reference conditions.

The dimensionless diode ideality factor :math:`n` is supplied by the user.
The modified ideality factor at reference conditions is calculated as

.. math::

a_{ref} = n N_s k T_{ref} / q.

Parameters
----------
v_mp : float
Maximum-power voltage at reference conditions. [V]
i_mp : float
Maximum-power current at reference conditions. [A]
v_oc : float
Open-circuit voltage at reference conditions. [V]
i_sc : float
Short-circuit current at reference conditions. [A]
alpha_sc : float
Short-circuit current temperature coefficient. [A/K]
beta_voc : float
Open-circuit voltage temperature coefficient. [V/K]
cells_in_series : int
Effective number of cells or junctions connected in series.
diode_factor : float
Dimensionless diode ideality factor. [unitless]
temp_ref : float, default 25
Reference cell temperature. [°C]
irrad_ref : float, default 1000
Reference irradiance. [Wm⁻²]
rs_step : float, default 1e-4
Increment used for the series-resistance sweep. [ohm]
rs_max : float, optional
Maximum series resistance considered. If ``None``, ``v_oc / i_sc``
is used. [ohm]

Returns
-------
params : dict
Fitted reference-condition Villalva parameters. The dictionary
contains ``I_L_ref``, ``I_o_ref``, ``R_s``, ``R_sh_ref``, ``a_ref``,
``alpha_sc``, ``beta_voc``, ``i_sc_ref``, ``v_oc_ref``,
``irrad_ref``, and ``temp_ref``.
history : pandas.DataFrame
Full fitting history for all valid values of ``R_s`` evaluated by the
algorithm.

Raises
------
ValueError
If the minimum shunt resistance is not positive.
RuntimeError
If no valid solution is found.

Notes
-----
The returned reference parameters are intended for use with
:py:func:`pvlib.pvsystem.calcparams_villalva`. I-V curve points can then
be calculated with :py:func:`pvlib.pvsystem.singlediode` or
:py:func:`pvlib.pvsystem.i_from_v`.

References
----------
.. [1] M. G. Villalva, J. R. Gazoli, and E. Ruppert Filho, "Comprehensive
approach to modeling and simulation of photovoltaic arrays," IEEE Trans.
Power Electron., vol. 24, no. 5, pp. 1198-1208, May 2009.
:doi:`10.1109/TPEL.2009.2013862`
.. [2] M. G. Villalva, "Conversor eletrônico de potência trifásico para
sistema fotovoltaico conectado à rede elétrica," Ph.D. dissertation,
Faculdade de Engenharia Elétrica e de Computação, Universidade Estadual
de Campinas (UNICAMP), Campinas, Brazil, 2010.
:doi:`10.47749/T/UNICAMP.2010.781324`
"""
Comment thread
JFrederico2022 marked this conversation as resolved.
temp_ref_k = temp_ref + 273.15

# a_ref = a * Ns * kT/q
a_ref = (
diode_factor
* cells_in_series
* constants.k
* temp_ref_k
/ constants.e
)

p_mp_ref = v_mp * i_mp

# Villalva minimum shunt resistance.
r_sh_min = v_mp / (i_sc - i_mp) - (v_oc - v_mp) / i_mp

if r_sh_min <= 0:
raise ValueError("Villalva R_sh_min must be positive.")

if rs_max is None:
rs_max = v_oc / i_sc

rs_values = np.arange(0.0, rs_max + 0.5 * rs_step, rs_step)

# Calculate parameters for all candidate series resistances.
with np.errstate(divide="ignore", invalid="ignore", over="ignore"):
photocurrent, saturation_current, resistance_shunt = (
_villalva_params_at_rs(
rs_values,
v_mp,
i_mp,
v_oc,
i_sc,
a_ref,
)
)

# Exclude non-finite and physically invalid parameters.
valid = (
np.isfinite(photocurrent)
& np.isfinite(saturation_current)
& np.isfinite(resistance_shunt)
& (resistance_shunt >= r_sh_min)
& (photocurrent > 0)
& (saturation_current > 0)
)

if not np.any(valid):
raise RuntimeError("No valid Villalva solution was found.")

rs_values = rs_values[valid]
photocurrent = photocurrent[valid]
saturation_current = saturation_current[valid]
resistance_shunt = resistance_shunt[valid]

# Calculate the maximum power point for all valid candidates.
with np.errstate(divide="ignore", invalid="ignore", over="ignore"):
mpp = pvsystem.max_power_point(
photocurrent=photocurrent,
saturation_current=saturation_current,
resistance_series=rs_values,
resistance_shunt=resistance_shunt,
nNsVth=a_ref,
method="brentq",
)

v_mp_model = np.asarray(mpp["v_mp"])
i_mp_model = np.asarray(mpp["i_mp"])
p_mp_model = np.asarray(mpp["p_mp"])

power_error = p_mp_model - p_mp_ref

# Exclude non-finite maximum power point results.
valid_mpp = (
np.isfinite(v_mp_model)
& np.isfinite(i_mp_model)
& np.isfinite(p_mp_model)
& np.isfinite(power_error)
)

if not np.any(valid_mpp):
raise RuntimeError("No valid Villalva solution was found.")

history = pd.DataFrame(
{
"R_s": rs_values[valid_mpp],
"R_sh_ref": resistance_shunt[valid_mpp],
"I_L_ref": photocurrent[valid_mpp],
"I_o_ref": saturation_current[valid_mpp],
"v_mp_model": v_mp_model[valid_mpp],
"i_mp_model": i_mp_model[valid_mpp],
"p_mp_model": p_mp_model[valid_mpp],
"p_mp_ref": p_mp_ref,
"power_error": power_error[valid_mpp],
"abs_power_error": np.abs(power_error[valid_mpp]),
}
)

best = history.loc[history["abs_power_error"].idxmin()]

params = {
"I_L_ref": float(best["I_L_ref"]),
"I_o_ref": float(best["I_o_ref"]),
"R_s": float(best["R_s"]),
"R_sh_ref": float(best["R_sh_ref"]),
"a_ref": float(a_ref),
"alpha_sc": alpha_sc,
"beta_voc": beta_voc,
"i_sc_ref": i_sc,
"v_oc_ref": v_oc,
"irrad_ref": irrad_ref,
"temp_ref": temp_ref,
}

return params, history
Loading