From 06abfaa2da07439060bba293eabeb22e51512758 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Tue, 20 Jun 2023 14:13:18 -0400 Subject: [PATCH 01/27] Added mismatch.py * adds function that combines curves (in series) * adds function that prepares inputs for combine_curves --- pvlib/ivtools/mismatch.py | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 pvlib/ivtools/mismatch.py diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py new file mode 100644 index 0000000000..aeb14743d7 --- /dev/null +++ b/pvlib/ivtools/mismatch.py @@ -0,0 +1,76 @@ +import pvlib +import numpy as np + + +def prepare_curves(params, num_pts): + # params is an n x 5 array where each row corresponds to one of + # the n curves' five defining parameters (photocurrent, saturation + # current, series resistance, shunt resistance, nNsVth) + # num_pts is the number of points you want calculated for each curve + # (this will also be the number of points in the aggregate curve) + + # in case params is a list containing scalars, add a dimension + if len(np.shape(params)) == 1: + params = params[np.newaxis,:] + + # get range of currents from 0 to max_isc + max_isc = np.max(pvlib.singlediode.bishop88_i_from_v(0.0, *params.T)) + currents = np.linspace(0, max_isc, num=num_pts, endpoint=True) + + # prepare inputs for bishop88 + bishop_inputs = np.array([[currents[idx]]*len(params) for idx in + range(num_pts)]) + # each row of bishop_inputs contains n copies of a single current + # value, where n is the number of curves being added together + # there is a row for each current value + + # get voltages for each curve + # transpose result so each row contains voltages for a single curve + voltages = pvlib.singlediode.bishop88_v_from_i(bishop_inputs, *params.T).T + + return currents, voltages + + +def combine_curves(currents, voltages, breakdown_voltage=-0.5): + # currents is a 1D array that contains a range from 0 to max_isc + # voltages is a 2D array where each row corresponds to the + # associated voltages of a single curve (should be n by + # len(currents), where n is the number of curves we're summing over) + # breakdown_voltage is the asymptote we use left of the y-axis + + currents = np.asarray(currents) + voltages = np.asarray(voltages) + assert currents.ndim == 1 + assert voltages.ndim == 2 + + # any voltages in array that are smaller than breakdown_voltage are + # clipped to be breakdown_voltage + clipped_voltages = np.clip(voltages, a_min=breakdown_voltage, a_max=None) + + # for each current, add the associated voltages of all the curves + # in our setup, this means summing each column of the voltage array + combined_voltages = np.sum(clipped_voltages, axis=0) + + # combined_voltages should now have same shape as currents + assert np.shape(combined_voltages) == np.shape(currents) + + # find max power point (in the dataset) + powers = currents*combined_voltages + mpp_idx = np.argmax(powers) + vmp = combined_voltages[mpp_idx] + imp = currents[mpp_idx] + pmp = powers[mpp_idx] + + # get isc + # np.interp requires second argument is increasing + assert np.all(np.diff(combined_voltages) < 0) + # FIXME not sure if I can guarantee that combined_voltages is + # decreasing for all inputs + isc = np.interp(0., np.flip(combined_voltages), np.flip(currents)) + + # get voc + voc = combined_voltages[0] + + return {'i_sc': isc, 'v_oc': voc, 'i_mp': imp, 'v_mp': vmp, 'p_mp': pmp, + 'i': currents, 'v': combined_voltages} + From 3159bc1e6a4f0a394f3044e7bdd302a78e15b4ac Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Wed, 21 Jun 2023 14:08:02 -0400 Subject: [PATCH 02/27] Make method in bishop functions explicit --- pvlib/ivtools/mismatch.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index aeb14743d7..6a3bc1c172 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -14,7 +14,8 @@ def prepare_curves(params, num_pts): params = params[np.newaxis,:] # get range of currents from 0 to max_isc - max_isc = np.max(pvlib.singlediode.bishop88_i_from_v(0.0, *params.T)) + max_isc = np.max(pvlib.singlediode.bishop88_i_from_v(0.0, *params.T, + method='newton') currents = np.linspace(0, max_isc, num=num_pts, endpoint=True) # prepare inputs for bishop88 @@ -26,7 +27,8 @@ def prepare_curves(params, num_pts): # get voltages for each curve # transpose result so each row contains voltages for a single curve - voltages = pvlib.singlediode.bishop88_v_from_i(bishop_inputs, *params.T).T + voltages = pvlib.singlediode.bishop88_v_from_i(bishop_inputs, *params.T, + method='newton').T return currents, voltages From 76efbacd20234b1ac7a8e918ca074b1c59518233 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Wed, 21 Jun 2023 14:13:36 -0400 Subject: [PATCH 03/27] Move clipping to prepare_curves --- pvlib/ivtools/mismatch.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 6a3bc1c172..ecc11db388 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -2,12 +2,13 @@ import numpy as np -def prepare_curves(params, num_pts): +def prepare_curves(params, num_pts, breakdown_voltage=-0.5): # params is an n x 5 array where each row corresponds to one of # the n curves' five defining parameters (photocurrent, saturation # current, series resistance, shunt resistance, nNsVth) # num_pts is the number of points you want calculated for each curve # (this will also be the number of points in the aggregate curve) + # breakdown_voltage is the asymptote we use left of the y-axis # in case params is a list containing scalars, add a dimension if len(np.shape(params)) == 1: @@ -30,25 +31,24 @@ def prepare_curves(params, num_pts): voltages = pvlib.singlediode.bishop88_v_from_i(bishop_inputs, *params.T, method='newton').T - return currents, voltages + # any voltages in array that are smaller than breakdown_voltage are + # clipped to be breakdown_voltage + clipped_voltages = np.clip(voltages, a_min=breakdown_voltage, a_max=None) + + return currents, clipped_voltages -def combine_curves(currents, voltages, breakdown_voltage=-0.5): +def combine_curves(currents, voltages): # currents is a 1D array that contains a range from 0 to max_isc # voltages is a 2D array where each row corresponds to the # associated voltages of a single curve (should be n by # len(currents), where n is the number of curves we're summing over) - # breakdown_voltage is the asymptote we use left of the y-axis currents = np.asarray(currents) voltages = np.asarray(voltages) assert currents.ndim == 1 assert voltages.ndim == 2 - # any voltages in array that are smaller than breakdown_voltage are - # clipped to be breakdown_voltage - clipped_voltages = np.clip(voltages, a_min=breakdown_voltage, a_max=None) - # for each current, add the associated voltages of all the curves # in our setup, this means summing each column of the voltage array combined_voltages = np.sum(clipped_voltages, axis=0) From 586de8f7ea985c42596caad2d2a39da9fd063148 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Wed, 21 Jun 2023 14:27:18 -0400 Subject: [PATCH 04/27] Change assert to ValueError --- pvlib/ivtools/mismatch.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index ecc11db388..fd29dfcd6b 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -63,11 +63,14 @@ def combine_curves(currents, voltages): imp = currents[mpp_idx] pmp = powers[mpp_idx] + # we're assuming voltages are decreasing, so combined_voltages + # should also be decreasing + if not np.all(np.diff(combined_voltages) < 0): + raise ValueError("Each row of voltages must be decreasing.") + # get isc - # np.interp requires second argument is increasing - assert np.all(np.diff(combined_voltages) < 0) - # FIXME not sure if I can guarantee that combined_voltages is - # decreasing for all inputs + # np.interp requires second argument is increasing, so flip + # combined_voltages and currents isc = np.interp(0., np.flip(combined_voltages), np.flip(currents)) # get voc From 3038f8697c332933dc974db727b91188f4613632 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Thu, 22 Jun 2023 13:25:15 -0400 Subject: [PATCH 05/27] Added docstrings --- pvlib/ivtools/mismatch.py | 122 +++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index fd29dfcd6b..f6100ca166 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -1,8 +1,71 @@ +""" +The `mismatch` module contains functions for combining curves in +series using the single diode model. +""" + import pvlib import numpy as np def prepare_curves(params, num_pts, breakdown_voltage=-0.5): + """ + Calculates currents and voltages on IV curves with the given + parameters, using the single diode equation. + + Parameters + ---------- + params : array-like + An array of parameters representing a set of :math:`n` IV + curves. The array should contain :math:`n` rows and five + columns. Each row contains the five parameters needed for a + single curve. The parameters should be in the following order: + + photocurrent : numeric + photo-generated current :math:`I_{L}` [A] + + saturation_current : numeric + diode reverse saturation current :math:`I_{0}` [A] + + resistance_series : numeric + series resistance :math:`R_{s}` [ohms] + + resistance_shunt : numeric + shunt resistance :math:`R_{sh}` [ohms] + + nNsVth : numeric + product of thermal voltage :math:`V_{th}` [V], diode + ideality factor :math:`n`, and number of series cells + :math:`N_{s}` [V] + + num_pts : int + Number of points to compute for each IV curve. + + breakdown_voltage : float + Vertical asymptote to use left of the y-axis. Any voltages that + are smaller than `breakdown_voltage` will be overwritten by it. + + Returns + ------- + tuple + currents : np.ndarray + A 1D array of current values. Has shape (`num_pts`,). + + voltages : np.ndarray + A 2D array of voltage values, where each row corresponds to + a single IV curve. Has shape (:math:`n`, `num_pts`), where + :math:`n` is the number of IV curves passed in. + + Notes + ----- + This function assumes a simplified reverse bias model. When using + :func:`pvlib.pvsystem.bishop88_v_from_i`, `breakdown_factor` is left + at the default value, which excludes the reverse bias term from the + model. Instead, any returned voltages that are less than + `breakdown_voltage` are replaced by `breakdown_voltage`, yielding a + vertical asymptote at `breakdown_voltage`. + + + """ # params is an n x 5 array where each row corresponds to one of # the n curves' five defining parameters (photocurrent, saturation # current, series resistance, shunt resistance, nNsVth) @@ -33,12 +96,62 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): # any voltages in array that are smaller than breakdown_voltage are # clipped to be breakdown_voltage - clipped_voltages = np.clip(voltages, a_min=breakdown_voltage, a_max=None) + voltages = np.clip(voltages, a_min=breakdown_voltage, a_max=None) - return currents, clipped_voltages + return currents, voltages def combine_curves(currents, voltages): + """ + Combines IV curves in series. + + Parameters + ---------- + currents : array-like + A 1D array-like object. Its first element must be zero, and it + should be increasing. + + voltages : array-like + A 2D array-like object. Each row corresponds to a single IV + curve and contains the voltages for that curve that are + associated to elements of `currents`. Each row must be + decreasing. + + Returns + ------- + dict + Contains the following keys: + i_sc : scalar + short circuit current of combined curve [A] + + v_oc : scalar + open circuit voltage of combined curve [V] + + i_mp : scalar + current at maximum power point of combined curve [A] + + v_mp : scalar + voltage at maximum power point of combined curve [V] + + p_mp : scalar + power at maximum power point of combined curve [W] + + i : np.ndarray + currents of combined curve [A] + + v : np.ndarray + voltages of combined curve [V] + + Notes + ----- + If the combined curve does not cross the y-axis, then the last (and + hence largest) current is returned for short circuit current. + + The maximum power point that is returned is the maximum power point + of the dataset. Its accuracy will improve as more points are passed + in. + + """ # currents is a 1D array that contains a range from 0 to max_isc # voltages is a 2D array where each row corresponds to the # associated voltages of a single curve (should be n by @@ -66,13 +179,16 @@ def combine_curves(currents, voltages): # we're assuming voltages are decreasing, so combined_voltages # should also be decreasing if not np.all(np.diff(combined_voltages) < 0): - raise ValueError("Each row of voltages must be decreasing.") + raise ValueError("Each row of voltages array must be decreasing.") # get isc # np.interp requires second argument is increasing, so flip # combined_voltages and currents isc = np.interp(0., np.flip(combined_voltages), np.flip(currents)) + # the first element of currents must be zero + if currents[0] != 0: + raise ValueError("First element of currents array must be zero.") # get voc voc = combined_voltages[0] From c0e57da7a68a4bd35d715acfce33c7577bf25607 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Thu, 22 Jun 2023 13:42:54 -0400 Subject: [PATCH 06/27] Fixed missing parenthesis --- pvlib/ivtools/mismatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index f6100ca166..5e03f99891 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -79,7 +79,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): # get range of currents from 0 to max_isc max_isc = np.max(pvlib.singlediode.bishop88_i_from_v(0.0, *params.T, - method='newton') + method='newton')) currents = np.linspace(0, max_isc, num=num_pts, endpoint=True) # prepare inputs for bishop88 From 0670a6f3f03ed07e0bb5e80c88e84903443ad6b7 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Thu, 22 Jun 2023 13:54:23 -0400 Subject: [PATCH 07/27] Updated comments, fixed typo --- pvlib/ivtools/mismatch.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 5e03f99891..023da5d38b 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -64,14 +64,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): `breakdown_voltage` are replaced by `breakdown_voltage`, yielding a vertical asymptote at `breakdown_voltage`. - """ - # params is an n x 5 array where each row corresponds to one of - # the n curves' five defining parameters (photocurrent, saturation - # current, series resistance, shunt resistance, nNsVth) - # num_pts is the number of points you want calculated for each curve - # (this will also be the number of points in the aggregate curve) - # breakdown_voltage is the asymptote we use left of the y-axis # in case params is a list containing scalars, add a dimension if len(np.shape(params)) == 1: @@ -90,6 +83,8 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): # there is a row for each current value # get voltages for each curve + # (note: expecting to vectorize for both the inputted currents and + # the inputted curve parameters) # transpose result so each row contains voltages for a single curve voltages = pvlib.singlediode.bishop88_v_from_i(bishop_inputs, *params.T, method='newton').T @@ -152,10 +147,6 @@ def combine_curves(currents, voltages): in. """ - # currents is a 1D array that contains a range from 0 to max_isc - # voltages is a 2D array where each row corresponds to the - # associated voltages of a single curve (should be n by - # len(currents), where n is the number of curves we're summing over) currents = np.asarray(currents) voltages = np.asarray(voltages) @@ -180,7 +171,6 @@ def combine_curves(currents, voltages): # should also be decreasing if not np.all(np.diff(combined_voltages) < 0): raise ValueError("Each row of voltages array must be decreasing.") - # get isc # np.interp requires second argument is increasing, so flip # combined_voltages and currents From a3a135b2118e8af4e2603496d402d8c483819de6 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Thu, 22 Jun 2023 14:02:36 -0400 Subject: [PATCH 08/27] Updated imports --- pvlib/ivtools/mismatch.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 023da5d38b..d9472c14b3 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -3,8 +3,8 @@ series using the single diode model. """ -import pvlib import numpy as np +from pvlib.singlediode import bishop88_i_from_v, bishop88_v_from_i def prepare_curves(params, num_pts, breakdown_voltage=-0.5): @@ -58,11 +58,11 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): Notes ----- This function assumes a simplified reverse bias model. When using - :func:`pvlib.pvsystem.bishop88_v_from_i`, `breakdown_factor` is left - at the default value, which excludes the reverse bias term from the - model. Instead, any returned voltages that are less than + :func:`pvlib.singlediode.bishop88_v_from_i`, `breakdown_factor` is + left at the default value, which excludes the reverse bias term from + the model. Instead, any returned voltages that are less than `breakdown_voltage` are replaced by `breakdown_voltage`, yielding a - vertical asymptote at `breakdown_voltage`. + vertical line at `breakdown_voltage`. """ From c27f558734c8f38e220b0a7a07196dd4a6c537b0 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Thu, 22 Jun 2023 14:33:22 -0400 Subject: [PATCH 09/27] Updated .rst file --- docs/sphinx/source/reference/pv_modeling/sdm.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/sphinx/source/reference/pv_modeling/sdm.rst b/docs/sphinx/source/reference/pv_modeling/sdm.rst index bfd5103ebe..9561ce3f1a 100644 --- a/docs/sphinx/source/reference/pv_modeling/sdm.rst +++ b/docs/sphinx/source/reference/pv_modeling/sdm.rst @@ -18,6 +18,14 @@ Functions relevant for single diode models. pvsystem.max_power_point ivtools.sdm.pvsyst_temperature_coeff +Functions for combining IV curves in series. + +.. autosummary:: + :toctree: ../generated/ + + ivtools.mismatch.prepare_curves + ivtools.mismatch.combine_curves + Low-level functions for solving the single diode equation. .. autosummary:: From 5dfda1504f8b11cffb60a53c8f5a3b9574343f7e Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Thu, 22 Jun 2023 14:53:50 -0400 Subject: [PATCH 10/27] Updated docstrings --- pvlib/ivtools/mismatch.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index d9472c14b3..56d705b241 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -10,7 +10,8 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): """ Calculates currents and voltages on IV curves with the given - parameters, using the single diode equation. + parameters, using the single diode equation. Returns values + in format needed for inputs to :func:`combine_curves`. Parameters ---------- @@ -42,27 +43,27 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): breakdown_voltage : float Vertical asymptote to use left of the y-axis. Any voltages that - are smaller than `breakdown_voltage` will be overwritten by it. + are smaller than ``breakdown_voltage`` will be replaced by it. Returns ------- tuple currents : np.ndarray - A 1D array of current values. Has shape (`num_pts`,). + A 1D array of current values. Has shape (``num_pts``,). voltages : np.ndarray A 2D array of voltage values, where each row corresponds to - a single IV curve. Has shape (:math:`n`, `num_pts`), where + a single IV curve. Has shape (:math:`n`, ``num_pts``), where :math:`n` is the number of IV curves passed in. Notes ----- This function assumes a simplified reverse bias model. When using - :func:`pvlib.singlediode.bishop88_v_from_i`, `breakdown_factor` is + :func:`pvlib.singlediode.bishop88_v_from_i`, ``breakdown_factor`` is left at the default value, which excludes the reverse bias term from the model. Instead, any returned voltages that are less than - `breakdown_voltage` are replaced by `breakdown_voltage`, yielding a - vertical line at `breakdown_voltage`. + ``breakdown_voltage`` are replaced by it, yielding a vertical line + at ``breakdown_voltage``. """ @@ -109,7 +110,7 @@ def combine_curves(currents, voltages): voltages : array-like A 2D array-like object. Each row corresponds to a single IV curve and contains the voltages for that curve that are - associated to elements of `currents`. Each row must be + associated to elements of ``currents``. Each row must be decreasing. Returns From 2f11a165d92cc266fcfe6bfebf72ceaeb04b311f Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Fri, 23 Jun 2023 09:44:21 -0400 Subject: [PATCH 11/27] Update docstring of prepare_curves Co-authored-by: Cliff Hansen --- pvlib/ivtools/mismatch.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 56d705b241..43d5f92d04 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -10,8 +10,13 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): """ Calculates currents and voltages on IV curves with the given - parameters, using the single diode equation. Returns values - in format needed for inputs to :func:`combine_curves`. + parameters, using the single diode equation, and a simple + model for reverse bias behavior. + + The current values are linearly spaced from 0 to the maximum Isc for all curves. + All curves have the same current values. + + Returns values in format needed for inputs to :func:`combine_curves`. Parameters ---------- From 3effafe1b214cc4ce7861af303d7860da4805e6e Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Fri, 23 Jun 2023 09:45:10 -0400 Subject: [PATCH 12/27] Update docstring of combine_curves Co-authored-by: Cliff Hansen --- pvlib/ivtools/mismatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 43d5f92d04..37ea57382f 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -66,7 +66,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): This function assumes a simplified reverse bias model. When using :func:`pvlib.singlediode.bishop88_v_from_i`, ``breakdown_factor`` is left at the default value, which excludes the reverse bias term from - the model. Instead, any returned voltages that are less than + the calculation. Instead, any returned voltages that are less than ``breakdown_voltage`` are replaced by it, yielding a vertical line at ``breakdown_voltage``. From b96958191d7e602ce93fa296eb1c7c104ccbe9f9 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 23 Jun 2023 10:51:04 -0400 Subject: [PATCH 13/27] Fixed spacing and clipped_voltage typo --- pvlib/ivtools/mismatch.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 37ea57382f..a6ee07610b 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -12,11 +12,12 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): Calculates currents and voltages on IV curves with the given parameters, using the single diode equation, and a simple model for reverse bias behavior. - - The current values are linearly spaced from 0 to the maximum Isc for all curves. - All curves have the same current values. - - Returns values in format needed for inputs to :func:`combine_curves`. + + The current values are linearly spaced from 0 to the maximum Isc for + all curves. All curves have the same current values. + + Returns values in format needed for inputs to + :func:`combine_curves`. Parameters ---------- @@ -161,7 +162,7 @@ def combine_curves(currents, voltages): # for each current, add the associated voltages of all the curves # in our setup, this means summing each column of the voltage array - combined_voltages = np.sum(clipped_voltages, axis=0) + combined_voltages = np.sum(voltages, axis=0) # combined_voltages should now have same shape as currents assert np.shape(combined_voltages) == np.shape(currents) From 7ab461cecf1abc6ac372950292c7b60cc389bca6 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 23 Jun 2023 11:11:08 -0400 Subject: [PATCH 14/27] Reverse order of currents array --- pvlib/ivtools/mismatch.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index a6ee07610b..f217aa473e 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -13,8 +13,8 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): parameters, using the single diode equation, and a simple model for reverse bias behavior. - The current values are linearly spaced from 0 to the maximum Isc for - all curves. All curves have the same current values. + The current values are linearly spaced from the maximum Isc for all + curves to 0. All curves have the same current values. Returns values in format needed for inputs to :func:`combine_curves`. @@ -80,7 +80,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): # get range of currents from 0 to max_isc max_isc = np.max(pvlib.singlediode.bishop88_i_from_v(0.0, *params.T, method='newton')) - currents = np.linspace(0, max_isc, num=num_pts, endpoint=True) + currents = np.linspace(max_isc, 0, num=num_pts, endpoint=True) # prepare inputs for bishop88 bishop_inputs = np.array([[currents[idx]]*len(params) for idx in @@ -110,14 +110,14 @@ def combine_curves(currents, voltages): Parameters ---------- currents : array-like - A 1D array-like object. Its first element must be zero, and it - should be increasing. + A 1D array-like object. Its last element must be zero, and it + should be decreasing. voltages : array-like A 2D array-like object. Each row corresponds to a single IV curve and contains the voltages for that curve that are associated to elements of ``currents``. Each row must be - decreasing. + increasing. Returns ------- @@ -146,7 +146,7 @@ def combine_curves(currents, voltages): Notes ----- - If the combined curve does not cross the y-axis, then the last (and + If the combined curve does not cross the y-axis, then the first (and hence largest) current is returned for short circuit current. The maximum power point that is returned is the maximum power point @@ -174,20 +174,20 @@ def combine_curves(currents, voltages): imp = currents[mpp_idx] pmp = powers[mpp_idx] - # we're assuming voltages are decreasing, so combined_voltages - # should also be decreasing - if not np.all(np.diff(combined_voltages) < 0): - raise ValueError("Each row of voltages array must be decreasing.") + # we're assuming voltages are increasing, so combined_voltages + # should also be increasing + if not np.all(np.diff(combined_voltages) > 0): + raise ValueError("Each row of voltages array must be increasing.") # get isc - # np.interp requires second argument is increasing, so flip - # combined_voltages and currents - isc = np.interp(0., np.flip(combined_voltages), np.flip(currents)) + # note that np.interp requires second argument is increasing (which + # we just checked) + isc = np.interp(0., combined_voltages, currents) - # the first element of currents must be zero - if currents[0] != 0: - raise ValueError("First element of currents array must be zero.") + # the last element of currents must be zero + if currents[-1] != 0: + raise ValueError("Last element of currents array must be zero.") # get voc - voc = combined_voltages[0] + voc = combined_voltages[-1] return {'i_sc': isc, 'v_oc': voc, 'i_mp': imp, 'v_mp': vmp, 'p_mp': pmp, 'i': currents, 'v': combined_voltages} From d87f9df2ea7073602dff2444ddd9a0920bf934f9 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 23 Jun 2023 14:02:55 -0400 Subject: [PATCH 15/27] Add tests --- pvlib/ivtools/mismatch.py | 6 +- pvlib/tests/ivtools/test_mismatch.py | 125 +++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 pvlib/tests/ivtools/test_mismatch.py diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index f217aa473e..89f6afdff4 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -78,8 +78,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): params = params[np.newaxis,:] # get range of currents from 0 to max_isc - max_isc = np.max(pvlib.singlediode.bishop88_i_from_v(0.0, *params.T, - method='newton')) + max_isc = np.max(bishop88_i_from_v(0.0, *params.T, method='newton')) currents = np.linspace(max_isc, 0, num=num_pts, endpoint=True) # prepare inputs for bishop88 @@ -93,8 +92,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): # (note: expecting to vectorize for both the inputted currents and # the inputted curve parameters) # transpose result so each row contains voltages for a single curve - voltages = pvlib.singlediode.bishop88_v_from_i(bishop_inputs, *params.T, - method='newton').T + voltages = bishop88_v_from_i(bishop_inputs, *params.T, method='newton').T # any voltages in array that are smaller than breakdown_voltage are # clipped to be breakdown_voltage diff --git a/pvlib/tests/ivtools/test_mismatch.py b/pvlib/tests/ivtools/test_mismatch.py new file mode 100644 index 0000000000..c39f83cef6 --- /dev/null +++ b/pvlib/tests/ivtools/test_mismatch.py @@ -0,0 +1,125 @@ + +import pytest +import numpy as np +import pandas as pd +from pvlib.ivtools.mismatch import prepare_curves, combine_curves + + +# inputs to test combine_curves +@pytest.mark.parametrize('currents, voltages, expected', [ + # different iscs and vocs + ( + np.array([2, 1, 0.]), + np.array([[0., 7.5, 8], [-1, 9.5, 10]]), + {'i_sc': 1.944444444444, 'v_oc': 18, 'i_mp': 1, 'v_mp': 17, + 'p_mp': 17, + 'i': np.array([2, 1, 0.]), + 'v': np.array([-1, 17, 18]) + } + ), + + # pandas inputs + ( + pd.Series([2, 1, 0.]), + pd.DataFrame([[0., 7.5, 8], [-1, 9.5, 10]]), + {'i_sc': 1.944444444444, 'v_oc': 18, 'i_mp': 1, 'v_mp': 17, + 'p_mp': 17, + 'i': np.array([2, 1, 0.]), + 'v': np.array([-1, 17, 18]) + } + ), + + # check that isc is actually largest current if curve doesn't + # cross y-axis + ( + np.array([1, 0.5, 0]), + np.array([[0., 1.75, 2], [0.5, 1, 1.5], [0.25, 0.75, 1]]), + {'i_sc': 1, 'v_oc': 4.5, 'i_mp': 0.5, 'v_mp': 3.5, 'p_mp': 1.75, + 'i': np.array([1, 0.5, 0.]), + 'v': np.array([0.75, 3.5, 4.5]) + } + ), + + # same curve twice + ( + np.array([1, 0.9, 0.]), + np.array([[0., 0.5, 1], [0, 0.5, 1]]), + {'i_sc': 1, 'v_oc': 2, 'i_mp': 0.9, 'v_mp': 1, 'p_mp': 0.9, + 'i': np.array([1, 0.9, 0.]), + 'v': np.array([0, 1, 2]) + } + ) + ]) + + +def test_combine(currents, voltages, expected): + out = combine_curves(currents, voltages) + + # check that outputted dictionary is close to expected + for k, v in expected.items(): + assert np.all(np.isclose(out[k], v)) + + +# inputs to test prepare_curves +@pytest.mark.parametrize('num_pts, breakdown_voltage, params, expected', [ + # standard use + # also tests vectorization of both the inputted currents and the + # curve parameters (when calling bishop88_v_from_i) + ( + 3, -0.5, + np.array([[1, 3e-08, 1., 300, 1.868364353685363], + [5, 1e-09, 0.1, 3000, 2.404825405733636]]), + ( + np.array([4.99983334, 2.49991667, 0.]), + np.array([[-0.5, -0.5, 3.21521313e+01], + [-2.52464716e-13, 5.17727056e+01, 5.36976290e+01]]) + ) + ), + + # different breakdown_voltage from default + ( + 3, -2, + np.array([[1, 3e-08, 1., 300, 1.868364353685363], + [5, 1e-09, 0.1, 3000, 2.404825405733636]]), + ( + np.array([4.99983334, 2.49991667, 0.]), + np.array([[-2, -2, 3.21521313e+01], + [-2.52464716e-13, 5.17727056e+01, 5.36976290e+01]]) + ) + ), + + # pandas input +# ( +# 3, -0.5, +# pd.DataFrame([[1, 3e-08, 1., 300, 1.868364353685363], +# [5, 1e-09, 0.1, 3000, 2.404825405733636]]), +# ( +# np.array([4.99983334, 2.49991667, 0.]), +# np.array([[-0.5, -0.5, 3.21521313e+01], +# [-2.52464716e-13, 5.17727056e+01, 5.36976290e+01]]) +# ) +# ) + + # params is just a list (no dimension) +# ( +# 5, -0.5, +# [1, 3e-08, 1., 300, 1.868364353685363], +# ( +# np.array([0.99667772, 0.74750829, 0.49833886, 0.24916943, 0.]), +# np.array([2.42028619e-14, 2.81472975e+01, 3.01512748e+01, +# 3.12974337e+01, 3.21521313e+01]) +# ) +# ) + ]) + + +def test_prepare_curves(params, num_pts, breakdown_voltage, expected): + out = prepare_curves(params, num_pts, breakdown_voltage) + + # check that outputted currents array is close to expected + assert np.all(np.isclose(out[0], expected[0])) + + # check that outputted voltages array is close to expected + assert np.all(np.isclose(out[1], expected[1])) + + From cf955a30706ed652e4ee49606fa53d0da167c8ae Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 23 Jun 2023 14:08:01 -0400 Subject: [PATCH 16/27] Fixed types, updated tests --- pvlib/ivtools/mismatch.py | 3 ++- pvlib/tests/ivtools/test_mismatch.py | 38 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 89f6afdff4..a713ca0b5f 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -73,8 +73,9 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): """ + params = np.asarray(params) # in case params is a list containing scalars, add a dimension - if len(np.shape(params)) == 1: + if params.ndim == 1: params = params[np.newaxis,:] # get range of currents from 0 to max_isc diff --git a/pvlib/tests/ivtools/test_mismatch.py b/pvlib/tests/ivtools/test_mismatch.py index c39f83cef6..1b1734ee29 100644 --- a/pvlib/tests/ivtools/test_mismatch.py +++ b/pvlib/tests/ivtools/test_mismatch.py @@ -89,27 +89,27 @@ def test_combine(currents, voltages, expected): ), # pandas input -# ( -# 3, -0.5, -# pd.DataFrame([[1, 3e-08, 1., 300, 1.868364353685363], -# [5, 1e-09, 0.1, 3000, 2.404825405733636]]), -# ( -# np.array([4.99983334, 2.49991667, 0.]), -# np.array([[-0.5, -0.5, 3.21521313e+01], -# [-2.52464716e-13, 5.17727056e+01, 5.36976290e+01]]) -# ) -# ) + ( + 3, -0.5, + pd.DataFrame([[1, 3e-08, 1., 300, 1.868364353685363], + [5, 1e-09, 0.1, 3000, 2.404825405733636]]), + ( + np.array([4.99983334, 2.49991667, 0.]), + np.array([[-0.5, -0.5, 3.21521313e+01], + [-2.52464716e-13, 5.17727056e+01, 5.36976290e+01]]) + ) + ), # params is just a list (no dimension) -# ( -# 5, -0.5, -# [1, 3e-08, 1., 300, 1.868364353685363], -# ( -# np.array([0.99667772, 0.74750829, 0.49833886, 0.24916943, 0.]), -# np.array([2.42028619e-14, 2.81472975e+01, 3.01512748e+01, -# 3.12974337e+01, 3.21521313e+01]) -# ) -# ) + ( + 5, -0.5, + [1, 3e-08, 1., 300, 1.868364353685363], + ( + np.array([0.99667772, 0.74750829, 0.49833886, 0.24916943, 0.]), + np.array([2.42028619e-14, 2.81472975e+01, 3.01512748e+01, + 3.12974337e+01, 3.21521313e+01]) + ) + ) ]) From a014b2a1ea67a62011665d769a92919ad4336a73 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 23 Jun 2023 15:17:16 -0400 Subject: [PATCH 17/27] Updated whatsnew --- docs/sphinx/source/whatsnew/v0.10.0.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index f7ad2b7d7c..177f5f8f49 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -28,6 +28,10 @@ Enhancements ~~~~~~~~~~~~ * Added `map_variables` parameter to :py:func:`pvlib.iotools.read_srml` and :py:func:`pvlib.iotools.read_srml_month_from_solardat` (:pull:`1773`) +* Added a new module :py:mod:`pvlib.ivtools.mismatch` to contain functions for + combining IV curves. Added functions + :py:func:`pvlib.ivtools.mismatch.prepare_curves` and + :py:func:`pvlib.ivtools.mismatch.combine_curves`. (:pull:`1781`) Bug fixes ~~~~~~~~~ From 1f5914a10be112365daa99dd91a3d8057bd397ee Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 23 Jun 2023 15:20:27 -0400 Subject: [PATCH 18/27] Added contributor --- docs/sphinx/source/whatsnew/v0.10.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index 177f5f8f49..bb40813a67 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -56,3 +56,4 @@ Contributors ~~~~~~~~~~~~ * Taos Transue (:ghuser:`reepoi`) * Adam R. Jensen (:ghuser:`AdamRJensen`) +* Abigail Jones (:ghuser:`ajonesr`) From 94e923cd9a7f5af1cfcd691bcb378a7731ee9b67 Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:13:43 -0400 Subject: [PATCH 19/27] Update prepare_curves docstring Co-authored-by: Cliff Hansen --- pvlib/ivtools/mismatch.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index a713ca0b5f..4eb7628873 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -64,11 +64,12 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): Notes ----- - This function assumes a simplified reverse bias model. When using - :func:`pvlib.singlediode.bishop88_v_from_i`, ``breakdown_factor`` is - left at the default value, which excludes the reverse bias term from - the calculation. Instead, any returned voltages that are less than - ``breakdown_voltage`` are replaced by it, yielding a vertical line + This function assumes a simplified reverse bias model. IV curves are + solved using :func:`pvlib.singlediode.bishop88_v_from_i` with + ``breakdown_factor`` left at the default value of 0., which excludes + the reverse bias term from the calculation. Here, voltages that are + less than ``breakdown_voltage`` are set equal to ``breakdown_voltage`` + effectively modeling the reverse bias curve by a vertical line at ``breakdown_voltage``. """ From 31d118855a90ded681d465ae6495cb6a9bc8455d Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:14:26 -0400 Subject: [PATCH 20/27] Update prepare_curves docstring Co-authored-by: Cliff Hansen --- pvlib/ivtools/mismatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 4eb7628873..20b57df930 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -16,7 +16,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): The current values are linearly spaced from the maximum Isc for all curves to 0. All curves have the same current values. - Returns values in format needed for inputs to + Returns currents and voltages in the format needed for input to :func:`combine_curves`. Parameters From 0880f247d84e331cba512fa729ef779007c95f84 Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:17:06 -0400 Subject: [PATCH 21/27] Update prepare_curves docstring Co-authored-by: Cliff Hansen --- pvlib/ivtools/mismatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools/mismatch.py b/pvlib/ivtools/mismatch.py index 20b57df930..fc00198b76 100644 --- a/pvlib/ivtools/mismatch.py +++ b/pvlib/ivtools/mismatch.py @@ -25,7 +25,7 @@ def prepare_curves(params, num_pts, breakdown_voltage=-0.5): An array of parameters representing a set of :math:`n` IV curves. The array should contain :math:`n` rows and five columns. Each row contains the five parameters needed for a - single curve. The parameters should be in the following order: + single curve in the following order: photocurrent : numeric photo-generated current :math:`I_{L}` [A] From e87b4aaf33ae6f8871ff1ea59c455296d8a76241 Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:17:21 -0400 Subject: [PATCH 22/27] Update spacing Co-authored-by: Cliff Hansen --- pvlib/tests/ivtools/test_mismatch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pvlib/tests/ivtools/test_mismatch.py b/pvlib/tests/ivtools/test_mismatch.py index 1b1734ee29..6a152bd1f9 100644 --- a/pvlib/tests/ivtools/test_mismatch.py +++ b/pvlib/tests/ivtools/test_mismatch.py @@ -50,8 +50,6 @@ } ) ]) - - def test_combine(currents, voltages, expected): out = combine_curves(currents, voltages) From 057cadac98a8c2b3c030dbfc3092b33b9fa3776e Mon Sep 17 00:00:00 2001 From: ajonesr <105310033+ajonesr@users.noreply.github.com> Date: Mon, 26 Jun 2023 14:17:33 -0400 Subject: [PATCH 23/27] Update spacing Co-authored-by: Cliff Hansen --- pvlib/tests/ivtools/test_mismatch.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pvlib/tests/ivtools/test_mismatch.py b/pvlib/tests/ivtools/test_mismatch.py index 6a152bd1f9..975f0aaece 100644 --- a/pvlib/tests/ivtools/test_mismatch.py +++ b/pvlib/tests/ivtools/test_mismatch.py @@ -109,8 +109,6 @@ def test_combine(currents, voltages, expected): ) ) ]) - - def test_prepare_curves(params, num_pts, breakdown_voltage, expected): out = prepare_curves(params, num_pts, breakdown_voltage) From 606de91a3ccac9982e9945a194474ae7e6413fc2 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Mon, 26 Jun 2023 14:38:34 -0400 Subject: [PATCH 24/27] Add tests for ValueErrors --- pvlib/tests/ivtools/test_mismatch.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pvlib/tests/ivtools/test_mismatch.py b/pvlib/tests/ivtools/test_mismatch.py index 975f0aaece..91049dd1ab 100644 --- a/pvlib/tests/ivtools/test_mismatch.py +++ b/pvlib/tests/ivtools/test_mismatch.py @@ -58,6 +58,16 @@ def test_combine(currents, voltages, expected): assert np.all(np.isclose(out[k], v)) +def test_combine_curves_args_fail(): + # voltages are not increasing + with pytest.raises(ValueError): + combine_curves([1,0], [[2,1],[0.1, 1]]) + + # currents don't end at zero + with pytest.raises(ValueError): + combine_curves([1.1,0.1], [[1,2],[3,4]]) + + # inputs to test prepare_curves @pytest.mark.parametrize('num_pts, breakdown_voltage, params, expected', [ # standard use From ded4548ce2670c7ff397487771c1329bfc240b1a Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Mon, 26 Jun 2023 15:49:11 -0400 Subject: [PATCH 25/27] Update init --- pvlib/ivtools/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvlib/ivtools/__init__.py b/pvlib/ivtools/__init__.py index de897e2989..c6edaf6eff 100644 --- a/pvlib/ivtools/__init__.py +++ b/pvlib/ivtools/__init__.py @@ -4,4 +4,4 @@ """ -from pvlib.ivtools import sde, sdm, utils # noqa: F401 +from pvlib.ivtools import mismatch, sde, sdm, utils # noqa: F401 From 52b745587cf5ae2a5356f330cf2d00cbd3e31889 Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Mon, 10 Jul 2023 14:43:13 -0500 Subject: [PATCH 26/27] Added example --- docs/examples/iv-modeling/plot_mismatch.py | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/examples/iv-modeling/plot_mismatch.py diff --git a/docs/examples/iv-modeling/plot_mismatch.py b/docs/examples/iv-modeling/plot_mismatch.py new file mode 100644 index 0000000000..48351aeb5e --- /dev/null +++ b/docs/examples/iv-modeling/plot_mismatch.py @@ -0,0 +1,45 @@ +""" +Calculating a combined IV curve +=============================== + +Example of combining IV curves in series, using the single diode model. +""" + +import numpy as np +import matplotlib.pyplot as plt +from scipy.constants import Boltzmann, elementary_charge + +from pvlib.ivtools.mismatch import prepare_curves, combine_curves + + +# set up parameter array +# the parameters should be in the order: photocurrent, saturation +# current, series resistance, shunt resistance, and n*Vth*Ns +vth = 298.15 * Boltzmann / elementary_charge + +# example array of parameters +params = np.array([[1.0, 3e-08, 1.0, 300, 1.3*vth*72], + [3, 3e-08, 0.1, 300, 1.01*vth*72], + [2, 5e-10, 0.1, 300, 1.1*vth*72]]) + +# prepare inputs for combine_curves +brk_voltage = -1.5 +currents, voltages_array = prepare_curves(params, num_pts=100, + breakdown_voltage=brk_voltage) + +# compute combined curve +combined_curve_dict = combine_curves(currents, voltages_array) + +# plot all curves and combined curve +for v in voltages_array: + plt.plot(v, currents) + +plt.plot(combined_curve_dict['v'], combined_curve_dict['i'], + label="Combined curve") + +plt.xlabel("Voltage [V]") +plt.ylabel("Current [A]") +plt.vlines(brk_voltage, ymin=0.0, ymax=combined_curve_dict['i_sc'], ls='--', + color='k', label="Breakdown voltage") +plt.legend() +plt.show() From 8cca7b7298bce3ee12bcc7d3b7b0fc8dc662a71c Mon Sep 17 00:00:00 2001 From: Abigail Jones Date: Fri, 14 Jul 2023 10:32:47 -0500 Subject: [PATCH 27/27] Update whatsnew, example --- docs/examples/iv-modeling/plot_mismatch.py | 39 ++++++++++++++++++---- docs/sphinx/source/whatsnew/v0.10.0.rst | 5 --- docs/sphinx/source/whatsnew/v0.10.2.rst | 5 +++ 3 files changed, 38 insertions(+), 11 deletions(-) diff --git a/docs/examples/iv-modeling/plot_mismatch.py b/docs/examples/iv-modeling/plot_mismatch.py index 48351aeb5e..f6f38ef5f5 100644 --- a/docs/examples/iv-modeling/plot_mismatch.py +++ b/docs/examples/iv-modeling/plot_mismatch.py @@ -2,9 +2,30 @@ Calculating a combined IV curve =============================== -Example of combining IV curves in series, using the single diode model. +Here we show how to use pvlib to combine IV curves in series. + +Differences in weather (irradiance or temperature) or module condition can +cause two modules (or cells) to produce different current-voltage (IV) +characteristics or curves. Series-connected modules produce a string-level IV +curve, which can be obtained by combining the curves for the individual +modules. Combining the curves involves modeling IV curves at negative voltage, +because some modules (cells) in the series circuit will produce more +photocurrent than others but the combined current cannot exceed that of the +lowest-current module (cell). """ +# %% +# +# pvlib provides two functions to combine series-connected curves: +# +#* :py:func:`pvlib.ivtools.mismatch.prepare_curves` uses parameters for the +# single diode equation and a simple model for negative voltage behavior to +# compute IV curves at a common set of currents +# +#* :py:func:`pvlib.ivtools.mismatch.combine_curves` produces the combined IV +# curve from a set of IV curves with common current values. + + import numpy as np import matplotlib.pyplot as plt from scipy.constants import Boltzmann, elementary_charge @@ -13,11 +34,13 @@ # set up parameter array + # the parameters should be in the order: photocurrent, saturation # current, series resistance, shunt resistance, and n*Vth*Ns -vth = 298.15 * Boltzmann / elementary_charge +# these are the parameters for the single diode function # example array of parameters +vth = 298.15 * Boltzmann / elementary_charge params = np.array([[1.0, 3e-08, 1.0, 300, 1.3*vth*72], [3, 3e-08, 0.1, 300, 1.01*vth*72], [2, 5e-10, 0.1, 300, 1.1*vth*72]]) @@ -31,15 +54,19 @@ combined_curve_dict = combine_curves(currents, voltages_array) # plot all curves and combined curve -for v in voltages_array: - plt.plot(v, currents) +for idx in range(len(voltages_array)): + v = voltages_array[idx] + plt.plot(v, currents, label=f"Panel {idx+1}") plt.plot(combined_curve_dict['v'], combined_curve_dict['i'], label="Combined curve") +# plot vertical line at breakdown voltage (used in simplified +# reverse bias model) +plt.vlines(brk_voltage, ymin=0.0, ymax=combined_curve_dict['i_sc'], ls='--', + color='k', linewidth=1, label="Breakdown voltage") + plt.xlabel("Voltage [V]") plt.ylabel("Current [A]") -plt.vlines(brk_voltage, ymin=0.0, ymax=combined_curve_dict['i_sc'], ls='--', - color='k', label="Breakdown voltage") plt.legend() plt.show() diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index e0476bfaf6..8e26c96fc8 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -92,10 +92,6 @@ Enhancements tolerance and number of iterations can be set. (:issue:`1249`, :pull:`1764`) * Improved `ModelChainResult.__repr__` (:pull:`1236`) -* Added a new module :py:mod:`pvlib.ivtools.mismatch` to contain functions for - combining IV curves. Added functions - :py:func:`pvlib.ivtools.mismatch.prepare_curves` and - :py:func:`pvlib.ivtools.mismatch.combine_curves`. (:pull:`1781`) * Improved ``ModelChainResult.__repr__`` (:pull:`1236`) * Exposes several functions useful for bifacial and shading calculations (:pull:`1666`): @@ -169,7 +165,6 @@ Contributors * Adam R. Jensen (:ghuser:`AdamRJensen`) * Echedey Luis (:ghuser:`echedey-ls`) * Cliff Hansen (:ghuser:`cwhanse`) -* Abigail Jones (:ghuser:`ajonesr`) * Cédric Leroy (:ghuser:`cedricleroy`) * Jean-Baptiste Pasquier (:ghuser:`pasquierjb`) * Mark Mikofski (:ghuser:`mikofski`) diff --git a/docs/sphinx/source/whatsnew/v0.10.2.rst b/docs/sphinx/source/whatsnew/v0.10.2.rst index f95f0bd93d..9f3876df91 100644 --- a/docs/sphinx/source/whatsnew/v0.10.2.rst +++ b/docs/sphinx/source/whatsnew/v0.10.2.rst @@ -15,6 +15,10 @@ Enhancements :py:func:`pvlib.iotools.get_pvgis_hourly`, :py:func:`pvlib.iotools.get_cams`, :py:func:`pvlib.iotools.get_bsrn`, and :py:func:`pvlib.iotools.read_midc_raw_data_from_nrel`. (:pull:`1800`) +* Added a new module :py:mod:`pvlib.ivtools.mismatch` to contain functions for + combining IV curves. Added functions + :py:func:`pvlib.ivtools.mismatch.prepare_curves` and + :py:func:`pvlib.ivtools.mismatch.combine_curves`. (:pull:`1781`) Bug fixes @@ -36,3 +40,4 @@ Requirements Contributors ~~~~~~~~~~~~ * Adam R. Jensen (:ghuser:`AdamRJensen`) +* Abigail Jones (:ghuser:`ajonesr`)