Source code for pvcracks.pvspice_lite.pvspice_tools

# -*- coding: utf-8 -*-
"""
Created on Thu Mar  7 15:18:35 2024

Build pv module netlists, run ngspice and read in output.

@author: nrjost, bbyford
"""

[docs]def run_ngspice(ngpsice_path, circuit_file): """ Run ngspice using path and netlist/circuit Parameters ---------- ngpsice_path : str path to ngspice, example: 'C:/Spice64/bin/ngspice.exe' circuit_file : str path to netlist, example: 'C:/Spice64/test/solar_circuit.cir' Returns ------- Nothing the output is saved to the path given in module_to_netlist Notes ------ To install ngspice follow the instructions here: https://ngspice.sourceforge.io/index.html """ import subprocess ngspice_command = [ngpsice_path, "-b", circuit_file] try: subprocess.run(ngspice_command, stdout=subprocess.DEVNULL); except subprocess.CalledProcessError as e: pass
[docs]def ngpsice_read_voltage_current_modules(path): """ Read the volatage and current from the ngspice output file Parameters ---------- path : str path where output of spice simulation file is saved Returns ------- voltage : float, array voltage values current : float, array current values df_iv : float, pandas DataFrame IV values Notes ------- If halfcut is true output IV for a halfcut module interconnection, esle for a series interconnection with full size cells This code can be further modified to show current and voltage of specific components such as the bypass diodes, the output file has all nodes of the simulation """ import pandas as pd voltage = [] current = [] flag = False df_iv = pd.DataFrame() # Extract data from NGSPICE output file with open(path, "r") as output_file: lines = output_file.readlines() for nl, line in enumerate(lines): # print(line) if flag: if line =='\n': current.append(float(lines[nl-1])) elif line.split("\t")[0]!='': voltage.append(float(line.split('\t')[1])) if "Values" in line: flag=True df_iv['V'] = voltage df_iv['I'] = current return voltage, current, df_iv
[docs]def Create_Cell_NetCode(params, a, b, c): """ Build and simulate a mini-module SPICE circuit, then return its IV curve. Parameters ---------- params : list of dict List of dictionaries, each containing single-diode parameters for one cell. file_path : str Directory where the SPICE circuit and output files will be written. V : array-like Voltage points for the DC sweep. V_Step : float, optional Step size for the DC voltage sweep (default is 0.001). file_name : str, optional Suffix for output file names (default is empty). Returns ------- vn : numpy.ndarray Interpolated voltage values from the simulation output. fitted_current : numpy.ndarray Interpolated current values corresponding to vn. """ elementary_diode = f"""X{a} {a} {b} {c} Cell Rs={params['Rs']} Rp={params['Rsh']} I={params['I']} BV={30} N={params['N']} Is={params['Is']} """ return(elementary_diode)
[docs]def MiniMod_Spice(params, file_path, V, V_Step=0.001, file_name ='', spicepath=''): """ Build and simulate a mini-module SPICE circuit, then return its IV curve. Parameters ---------- params : list of dict List of dictionaries, each containing single-diode parameters for one cell. file_path : str Directory where the SPICE circuit and output files will be written. V : array-like Voltage points for the DC sweep. V_Step : float, optional Step size for the DC voltage sweep (default is 0.001). file_name : str, optional Suffix for output file names (default is empty). Returns ------- vn : numpy.ndarray Interpolated voltage values from the simulation output. fitted_current : numpy.ndarray Interpolated current values corresponding to vn. """ import pvlib import numpy as np N_s = len(params) Measurment_offset = {'I': 0, 'Is': 0, 'Rs': 0.0035873181014406622, 'Rsh': 0, 'N': 0} # Define nominal temperature TNOM=25 # Crete Cell netlist for series connections elementary_diode_netlist = '' for n in range(N_s-1): Cell_Params=params[n].copy() Cell_Params['I'] -= Measurment_offset['I'] Cell_Params['Is'] -= Measurment_offset['Is'] Cell_Params['Rs'] -= Measurment_offset['Rs'] Cell_Params['Rsh'] -= Measurment_offset['Rsh'] Cell_Params['N'] -= Measurment_offset['N'] a = n+n+1 b = n+n+2 c = n+n+3 elementary_diode=Create_Cell_NetCode(Cell_Params,a,b,c) elementary_diode_netlist+=elementary_diode if N_s==1: n=0 else: n+=1 Cell_Params=params[n].copy() a = n+n+1 b = n+n+2 # last node has to end at ground c = 0 elementary_diode=Create_Cell_NetCode(Cell_Params,a,b,c) elementary_diode_netlist+=elementary_diode # Create Circuit Content circuit_content = f""" * Import the diode model .include SolarCell.cp * Define the DC sweep V1 1 0 DC 0V {elementary_diode_netlist} .control * set the sweep for the apropriate voltage range in steps of V_Step dc V1 {V[0]} {V[-1]+0.1} {V_Step} set filetype=ascii write {file_path}solar_circuit{file_name}.out write {file_path}solar_circuit{file_name}.txt quit .endc """ with open(f"{file_path}solar_circuit{file_name}.cir", "w") as circuit_file: circuit_file.write(circuit_content) # Simulate and read output file run_ngspice(spicepath , f"{file_path}solar_circuit{file_name}.cir") _, _, df_iv = ngpsice_read_voltage_current_modules(f"{file_path}solar_circuit{file_name}.out"); # use PVLIB to snip any negative values Vp,Ip = pvlib.ivtools.utils.rectify_iv_curve(df_iv['V'], df_iv['I']) vn= np.squeeze(np.linspace(V[0],np.max(V), len(V) )) # linear interpret to ensure the generated IV fits the points provided fitted_current = np.interp(vn, Vp, Ip) return(vn, fitted_current)