Source code for sluyspy.solar_panels

# -*- coding: utf-8 -*-
# SPDX-License-Identifier: EUPL-1.2
#  
#  Copyright (c) 2022-2024  Marc van der Sluys - Nikhef/Utrecht University - marc.vandersluys.nl
#   
#  This file is part of the sluyspy Python package:
#  Marc van der Sluys' personal Python modules.
#  See: https://github.com/MarcvdSluys/sluyspy
#   
#  This is free software: you can redistribute it and/or modify it under the terms of the European Union
#  Public Licence 1.2 (EUPL 1.2).  This software is distributed in the hope that it will be useful, but
#  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
#  PURPOSE.  See the EU Public Licence for more details.  You should have received a copy of the European
#  Union Public Licence along with this code.  If not, see <https://www.eupl.eu/1.2/en/>.


"""Solar-panel functions for the sluyspy package"""


import pandas as _pd
import pytz as _tz
import solarenergy as _se
from sluyspy import env as _env

# My computing environment:
_env = _env.environment()

# My solar-panel specs:
specs = _se.read_solar_panel_specs()


[docs] def read_detailed_log(file_name='Current/detailed-log.csv', last_only=None, header=None, sun_position=False, rem_cols=True, no_p0rows=False, no_elec=False, no_cond=True): """Read solar-panel detailed-log.csv and select the useful data. Parameters: file_name (str): Name of the input file (optional; default=None -> detailed-log.csv). header (int): Number of lines to consider as header and skip (optional; default: 0). last_only (int): Number of last lines to read. This tails the last lines into a separate file and reads that. sun_position (bool): Add (compute) Sun position to the data (slow! - optional; default: False). rem_cols (bool): Remove default columns (constants, duplicates, uninteresting). no_p0rows (bool): Remove rows without power (P=0; Relay=closed; Cond!=OK). no_elec (bool): Remove electricity details (some P,V,I, f). no_cond (bool): Remove condition/status columns (Cond, Relay, Tinv). Returns: (pandas.DataFrame): DataFrame containing solar-panel data. """ col_names = ['date', 'time', 'SN1', 'Type', 'SN2', 'Pdc', 'x0', 'Idc', 'x1', 'Vdc', 'x2', 'Pac', 'x3', 'x4', 'Iac', 'x5', 'x6', 'Vac', 'x7', 'x8', 'Pdc2', 'Pac2', 'x9', 'Eday', 'Etot', 'Freq', 't_oper', 't_feedin', 'Btooth', 'Cond', 'Relay', 'Tinv'] # Issue: detailed-log.csv becomes very long -> reading is slow. Tail the last 3000 lines/minutes (2+ # days) to a shorter file and read that (currently saves a factor 17 on CPU time (2022-11-12)): if last_only is not None: from pathlib import Path import sluyspy.system as ssys tmpfile = ssys.temp_file_name(_env.sp_dir+'Current', '.detailed-log-last', 'csv') ssys.tail_file(_env.sp_dir+file_name, tmpfile, last_only) # Copy last last_only lines to tmpfile df = _pd.read_csv(tmpfile, header=header, sep=r'\s*,\s*', engine='python', names=col_names) Path.unlink(Path(tmpfile)) # Remove temporary file else: df = _pd.read_csv(_env.sp_dir+file_name, header=header, sep=r'\s*,\s*', engine='python', names=col_names) # Remove unwanted columns: if rem_cols: del df['SN1'], df['SN2'], df['Type'] # Not interesting - constant del df['Pdc2'], df['Pac2'] # Not interesting - duplicates del df['x0'], df['x1'], df['x2'], df['x3'], df['x4'], df['x5'], df['x6'], df['x7'], df['x8'], df['x9'] # x_i are always 0 del df['Eday'], df['t_oper'], df['t_feedin'], df['Btooth'] # Not interested in now # , df['Etot'] # Remove rows witout power (P=0; Relay=closed; Cond!=OK): if no_p0rows: df = df[df['Relay'] == 'Closed'] # Only keep data when the relay is closed (i.e., providing a current/energy) df = df[df['Cond'] == 'Ok'] # Only keep data when the condition is 'OK' df = df[df['Pdc'] > 0] # Only keep data if there is power df = df[df['Pac'] > 0] # Only keep data if there is power # df = df[df['Pac'] < 2990] # Only keep data for P < 3kW, since 3kW may indicate >3kW # df = df[df['Tinv'] > 0] # Only keep data if we have a temperature (of the inverter!) # No longer needed: del df['Relay'], df['Cond'], df['Tinv'] elif no_cond: del df['Cond'], df['Relay'], df['Tinv'] # Slightly more accurate: ~3-4 significant digits -> 4-5: df['Pdc'] = df['Idc'] * df['Vdc'] df['Pac'] = df['Iac'] * df['Vac'] if no_elec: del df['Pdc'], df['Idc'], df['Vdc'], df['Iac'], df['Vac'], df['Freq'] # Combine date and time columns to a timezone-aware datetime: df['date'] = _pd.DatetimeIndex(_pd.to_datetime(df['date']+' '+df['time'])) df = df.rename(columns={'date': 'dtm'}) # Rename column date -> dtm # df['dtm'] = df['dtm'].dt.round('min') mytz = _tz.timezone(specs.tz) df.dtm = df.dtm.dt.tz_localize(mytz) # Timezone naive -> timezone aware. Indicate that the existing times are in mytz, without conversion. del df['time'] # Remove column 'time' # print(df['dtm']) # Compute Sun position (uses SolTrack behind the scenes): if sun_position: df['sunAz'],df['sunAlt'],df['sunDist'] = \ _se.sun_position_from_datetime(specs.geo_lon,specs.geo_lat, df['dtm']) return df
[docs] def write_day_file(df, subdir='', prefix=specs.name, ext='csv', verbosity=0): """Write one day of (e.g.) minutely solar-panel data to daily file named 'subdir/prefix-yyyymmdd.ext'. Parameters: df (pandas.DataFrame): Pandas DataFrame with columns datetime, Pac and Etot. subdir (str): Subdirectory to save file in, with trailing slash. prefix (str): File name prefix (defaults to name of the plant, followed by a hyphen). ext (str): File extension to use. verbosity (int): Verbosity (0-2; defaults to 0). """ if len(df) < 1: if verbosity > 0: print('No data available, no file created.') return # Want separate date and time columns: df['date'] = df.dtm.dt.date df['time'] = df.dtm.dt.time date = df.loc[:, 'dtm'].dt.date.iloc[0].strftime('%Y%m%d') # .loc[0, ''] doesn't work, since row 0 does not exist out_file_name = _env.sp_dir+subdir+prefix+'-'+date+'.'+ext if verbosity > 0: print('Saving data to '+out_file_name) # Open file and write header: out_file = open(out_file_name, 'w') out_file.write('sep=;\n') out_file.write('Version CSV1|Tool SBFspot3.3.1 (Linux)|Linebreaks CR/LF|Delimiter semicolon|Decimalpoint dot|Precision 3\n') out_file.write('\n') out_file.write(';SN: '+specs.inv_sn+';SN: '+specs.inv_sn+'\n') out_file.write(';'+specs.inv_model+';'+specs.inv_model+'\n') out_file.write(';'+specs.inv_sn+';'+specs.inv_sn+'\n') out_file.write(';Total yield;Power\n') out_file.write(';Counter;Analog\n') out_file.write('yyyy-MM-dd;HH:mm:ss;kWh;kW\n') for row in df.iterrows(): ser = row[1] if verbosity > 1: print('%s;%s;%0.3f;%5.3f' % (ser.date, ser.time, ser.Etot, ser.Pac)) out_file.write('%s;%s;%0.3f;%5.3f\n' % (ser.date, ser.time, ser.Etot, ser.Pac/1e3)) # Pac: W -> kW out_file.close() return