Source code for solcore.spice.pv_module_solver

import numpy as np
from solcore.solar_cell_solver import solar_cell_solver
from solcore.spice import solve_circuit


[docs]def solve_pv_module(solar_cell, options, totalcells=25, bias_start=0, bias_end=75, bias_step=0.1, jscSigma=2e-4, shading=None): """ Calculate the IV curve of a PV module made of a certain number solar_cells connected in series in a single string. A certain dispersion to the distribution of photocurrents among cells and shadowing losses can be added to ahoeve more realistic results. :param solar_cell: A solar cell object containing all the junctions :param options: A state object with all the options needed to solve the solar cell IV :param totalcells: Total number of cells in a string :param bias_start: Initial voltage of the IV curve of the module :param bias_end: Final voltage of the IV curve of the module :param bias_step: Step for the bias :param jscSigma: Width of the distribution of short circuit currents around the ideal (eg: 0.02 = 2%). Default is set to 0.02% to aid convergence :param shading: Array containing the shading loses for each cell. It can be a 2D array (0 = no shading, 1 = full shading) :return: A tuple with the array of voltages, currents, a list with the Isc of each junction in each cell and the raw output from SPICE. """ # We first start by the solar cell as if it were a normal, isolated cell solar_cell_solver(solar_cell, 'iv', user_options=options) # We don't care about this IV curve, in principle, but we care about some of the parameters calculated, like jsc, # j01 or j02 if calculated from detailed balance. We extract those parameters from the cell totaljuncs = solar_cell.junctions area = solar_cell.area if hasattr(solar_cell, 'area') else 1 Rs = max(solar_cell.R_series / area, 1e-14) cell_temp = solar_cell.T - 273 Isc_array = np.zeros(totaljuncs) I01_array = np.zeros(totaljuncs) n1_array = np.zeros(totaljuncs) I02_array = np.zeros(totaljuncs) n2_array = np.zeros(totaljuncs) Eg_array = np.zeros(totaljuncs) rsh_array = np.zeros(totaljuncs) for i in range(totaljuncs): try: Isc_array[i] = solar_cell(i).jsc * area I01_array[i] = solar_cell(i).j01 * area n1_array[i] = solar_cell(i).n1 if hasattr(solar_cell(i), 'n1') else 1 I02_array[i] = solar_cell(i).j02 * area n2_array[i] = solar_cell(i).n2 if hasattr(solar_cell(i), 'n2') else 2 Eg_array[i] = solar_cell(i).Eg rsh_array[i] = min(solar_cell(i).R_shunt / area, 1e14) if hasattr(solar_cell(i), 'R_shunt') else 1e14 except AttributeError as err: raise AttributeError('ERROR in PV module: Junction is missing one essential argument. {}'.format(err)) if shading is not None: shade = shading.flatten() assert len(shade) == totalcells else: shade = np.zeros(totalcells) # Calculate the probability distribution for the cells # Since all cells will not generate the same current, some random scatter can be added to the Isc. # note that the same random value is applied to all sub-cells in a particular junction. jsc_random_normal = np.random.normal(0.0, jscSigma, size=totalcells) # Shift the distribution so it is always <=1 so Isc never exceeds the ideal value, # and also take into account shading jsc_random_normal = abs(1 - max(jsc_random_normal) * jscSigma + jscSigma * jsc_random_normal) * (1 - shade) # We choose to keep track of the sub-cell Isc values so that they can be plotted at the end. all_Isc_values = [] # Now we start to write the spice file spice_file = """ * SolCore spice Simulation \n.model bypassdiode d \n""" nodeCounter = 0 junctionCounter = 0 # We loop over all solar cells and all junctions within each solar cell for n in range(1, totalcells + 1): # Defines the lower connection for the bypass diode. lowerBypassConnection = nodeCounter temp_isc = [] # Loop through junctions writing the spice code for each junction in turn for j in range(totaljuncs): junctionCounter += 1 isc = Isc_array[j] * jsc_random_normal[n - 1] # Write the spice code for the junction to file spiceout = spice_junction(junctionCounter, nodeCounter, isc, I01_array[j], I02_array[j], n1_array[j], n2_array[j], Eg_array[j], rsh_array[j]) spice_file = spice_file + spiceout nodeCounter = nodeCounter + 1 temp_isc.append(isc) # Save the isc value for later plotting # Entire cell has been built all_Isc_values.append(temp_isc) # Add isc values to the main list. # Add the series resistance spiceout = 'r{0} {1} {2} {3}\n'.format(2 * junctionCounter - 1, nodeCounter+1, nodeCounter, Rs) nodeCounter = nodeCounter + 1 spice_file = spice_file + spiceout # Connect bypass diode. Connections are: lowerBypassConnection & nodeCounter spiceout = 'd{0} {1} {2} bypassdiode\n'.format(2 * junctionCounter + 1, lowerBypassConnection, nodeCounter) spice_file = spice_file + spiceout junctionCounter += 1 spice_file = spice_file + ".OPTIONS TNOM=25 TEMP=" + str(cell_temp) + "\n" spice_file = spice_file + "vbias " + str(nodeCounter) + " 0\n" spice_file = spice_file + ".dc vbias " + str(bias_start) + " " + str(bias_end) + " " + str(bias_step) + "\n" spice_file = spice_file + ".plot dc i(vbias)\n" spice_file = spice_file + ".end \n\n" # And the definition of the spice file is finished. Now it's time to solve the problem raw_data = solve_circuit(spice_file) voltage = [] current = [] raw_data_in_lines = raw_data.split('\n') for line in raw_data_in_lines: if len(line) > 2 and line[1] in '0123456789': splitstring = line.split() voltage.append(float(splitstring[0])) current.append(float(splitstring[1])) return voltage, current, all_Isc_values, raw_data
[docs]def spice_junction(jc, nc, isc, j01, j02, n1, n2, Eg, rsh): """ Creates the string representation in SPICE of the junciton defined by the input values. :param jc: junction counter :param nc: node counter :param isc: Short circuit current :param j01: Reverse saturation current corresponding to ideality factor n1 :param j02: Reverse saturation current corresponding to ideality factor n2 :param n1: Ideality factor, typically = 1 :param n2: Ideality factor, typically = 2 :param Eg: Bandgap of the material the junction is made off :param rsh: Shunt resistance in the junction :return: A string representation of the junciton in SPICE """ isource = 'i{0} {1} {2} dc {3}\n'.format(jc, nc, nc + 1, isc) d1 = 'd{0} {1} {2} diode{3} OFF\n'.format(2 * jc - 1, nc + 1, nc, 2 * jc - 1) d1deff = '.model diode{0} d(is={1},n={2},eg={3})\n'.format(2 * jc - 1, j01, n1, Eg) d2 = 'd{0} {1} {2} diode{3} OFF\n'.format(2 * jc, nc + 1, nc, 2 * jc) d2deff = '.model diode{0} d(is={1},n={2},eg={3})\n'.format(2 * jc, j02, n2, Eg) rshunt = 'r{0} {1} {2} {3}\n'.format(2 * jc, nc + 1, nc, rsh) junction = isource + d1 + d1deff + d2 + d2deff + rshunt return junction
if __name__ == '__main__': out = spice_junction(1, 1, 300, 1e-25, 1e-16, 1, 2, 1.4, 0.0001, 1e10) print(out)