diff --git a/README.md b/README.md
index 1d68d28..0bbac76 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# consumption_analysis [![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-sa/4.0/) [![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FFUEL4EP%2Fconsumption_analysis&count_bg=%2379C83D&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=false)](https://hits.seeyoufarm.com)
-Python statistic software for consumption analysis of electrical power, fresh water, oil, gas, pellets, and heat pump energy
+Python statistic software for consumption analysis of electrical power, fresh water, oil, gas, pellets, heat pump energy, and firewood
# Installation for [(K)ubuntu] LINUX / Raspian OS:
@@ -26,9 +26,11 @@ Optional: Put example data bases into the working directory
* gas_energy_consumption.caf
* pellets_energy_consumption.caf
* heat_pump_energy_consumption.caf
+* firewood_mass_consumption.caf
**Required python version**
- 3.x
+- python2 is **NOT** supported anymore
**Required python modules:**
@@ -63,6 +65,14 @@ Optional: Put example data bases into the working directory
* moving averages over one year will be calculated after at least one year of data collection
* therefore **for the first year** of data entries, no moving averages will be calculated nor displayed
+# Hints for consumption inputs
+
+- The readings of a consumption meter should be inputted into the 'consal' software immediately after reading. The concerning time stamp of the data base is taken based on the system time of your computer at the time of the input.
+- The readings can be done at any time. They can be non-equidistant in time.
+- The time span between two readings can vary arbitrarily, e.g. after 1 hour, then after 3.2 days, then after 1.34 weeks, then after 5.6 hours, ..
+- For creating the graphical views, the data base vectors are resampled equidistantly in time, in the current software version every 3 hours.
+- In case of a newly created data base, the next data base input should be done after the resampling time at the earliest, i.e. after 3 hours.
+
# Usage:
@@ -115,7 +125,22 @@ Options:
--hp analyze heat pump consumption
--whp=FILE file storing data base for heat pump consumption analysis
-
+ -f analyze firewood mass consumption of a stove (note: mass
+ of each oven charge needs to be inputted)
+
+ --ff=FILE file storing data base for firewood mass consumption
+ analysis
+
+
+# Input modes
+
+1. Input of meter readings
+ - For water, electricity, gas, oil, heat pump energy, pellets energy
+ - The inputted consumption values are already summed-up by the meter
+2. Input of non-meter readings, e.g. firewood charge of a stove
+ - For firewood charge of a stove
+ - The inputted consumption values are not summed-up by a meter, but **each** furnance charge is weigthed by a scale. The summing-up is done by the 'consal' software instead.
+ - **IMPORTANT**: For summing-up the firewood mass consumption correctly, **each furnance charge needs to be weigthed and the firewood weight needs to be inputted with 'consal -f -i', see also below.**
# Examples on usage:
@@ -153,60 +178,74 @@ Options:
consal --hp
+
+7. Run analysis on provided data base for firewood mass consumption:
+
+
+ consal -f
-7. Add a new consumption value to an existing data base and then run an analysis of electrical power consumption:
+8. Add a new consumption value to an existing data base and then run an analysis of electrical power consumption:
consal -i -e
-8. Add a new consumption value to an existing data base and then run an analysis of water consumption:
+9. Add a new consumption value to an existing data base and then run an analysis of water consumption:
consal -i -w
-9. Add a new consumption value to an existing data base and then run an analysis of heating oil consumption:
+10. Add a new consumption value to an existing data base and then run an analysis of heating oil consumption:
consal -i -o
-10. Add a new consumption value to an existing data base and then run an analysis of gas consumption:
+11. Add a new consumption value to an existing data base and then run an analysis of gas consumption:
consal -i -g
-11. Add a new consumption value to an existing data base and then run an analysis of pellets consumption:
+12. Add a new consumption value to an existing data base and then run an analysis of pellets consumption:
consal -i -p
-12. Add a new consumption value to an existing data base and then run an analysis of heat pump energy consumption:
+13. Add a new consumption value to an existing data base and then run an analysis of heat pump energy consumption:
consal -i \--hp
+
+14. Add the weight of a new furnance charge to an existing data base and then run an analysis of firewood mass consumption:
+
+
+ consal -i -f
-13. Create a new data base for electrical power consumption:
+15. Create a new data base for electrical power consumption:
consal -i -n -e
-14. Create a new data base for water consumption:
+16. Create a new data base for water consumption:
consal -i -n -w
-15. Create a new data base for heating oil consumption:
+17. Create a new data base for heating oil consumption:
consal -i -n -o
-16. Create a new data base for gas consumption:
+18. Create a new data base for gas consumption:
consal -i -n -g
-17. Create a new data base for pellets consumption:
+19. Create a new data base for pellets consumption:
consal -i -n -p
-18. Create a new data base for heat pump energy consumption:
+20. Create a new data base for heat pump energy consumption:
consal -i -n \--hp
+
+21. Create a new data base for firewood mass consumption:
+
+ consal -i -n -f
@@ -218,6 +257,8 @@ Options:
- 13th June 2021: Migration to python3
- 28th October 2022: Fixes due to library updates, update of images
- 21st November 2022: Added analysis options for gas, pellets, and heat pump energy
+- 15th December 2022: Added analysis option for firewood mass consumption
+ Decreased resampling time to 3 hours
diff --git a/__pycache__/graphics.cpython-38.pyc b/__pycache__/graphics.cpython-38.pyc
deleted file mode 100644
index 5ea9381..0000000
Binary files a/__pycache__/graphics.cpython-38.pyc and /dev/null differ
diff --git a/__pycache__/io_module.cpython-38.pyc b/__pycache__/io_module.cpython-38.pyc
deleted file mode 100644
index 8f6e0b2..0000000
Binary files a/__pycache__/io_module.cpython-38.pyc and /dev/null differ
diff --git a/__pycache__/messaging.cpython-38.pyc b/__pycache__/messaging.cpython-38.pyc
deleted file mode 100644
index 62c6280..0000000
Binary files a/__pycache__/messaging.cpython-38.pyc and /dev/null differ
diff --git a/consal.py b/consal.py
old mode 100755
new mode 100644
index 6db88db..c6d6791
--- a/consal.py
+++ b/consal.py
@@ -1,14 +1,14 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
-desc="""consal.py is doing a statistical analysis of electrical power, water, oil, gas, pellets, and heat pump consumptions"""
+desc="""consal.py is doing a statistical analysis of electrical power, water, oil, gas, pellets, heat pump, and firewood consumptions"""
-# $Rev: 79 $:
+# $Rev: 81 $:
# $Author: ewald $:
-# $Date: 2022-12-03 09:45:53 +0100 (Sa, 03. Dez 2022) $:
-# $Id: consal.py 79 2022-12-03 08:45:53Z ewald $
+# $Date: 2022-12-15 13:06:52 +0100 (Do, 15. Dez 2022) $:
+# $Id: consal.py 81 2022-12-15 12:06:52Z ewald $
-__my_version__ = "$Revision: 79 $"
+__my_version__ = "$Revision: 81 $"
ELECTRICAL_POWER_CONSUMPTION_FILE="electrical_power_consumption.caf"
WATER_CONSUMPTION_FILE="water_consumption.caf"
@@ -16,15 +16,17 @@
GAS_CONSUMPTION_FILE ="gas_energy_consumption.caf"
PELLETS_CONSUMPTION_FILE ="pellets_energy_consumption.caf"
HEAT_PUMP_CONSUMPTION_FILE ="heat_pump_energy_consumption.caf"
+FIREWOOD_CONSUMPTION_FILE ="firewood_mass_consumption.caf"
TIME_COL=0
VALUE_COL= 1
STRICTLY_INCREASING=1
NOT_STRICTLY_INCREASING=0
MOVING_AVERAGE_DAYS=365
-RESAMPLE_TIME_STEP=0.25
+RESAMPLE_TIME_STEP=0.125 # resample every 3 hours
EPSILON=1e-6
ALPHA=1e-2
MAX_MEASUREMENT_VARIATION=4
+MAX_FIREWOOD_CHARGE=15.0 # adapt this parameter to the maximum allowed charge of your stove
import sys
import optparse
@@ -62,6 +64,7 @@ def __init__(self):
self.write_table_name=''
self.table=[]
self.np=0
+ self.increment_flag=False
#list
self.tl=[]
self.cl=[]
@@ -86,6 +89,14 @@ def __init__(self):
#status flag
self.status=True
+ def set_increment_flag(self, name):
+ # set the increment_flag for firewood mass consumption, i.e. each charge of an oven shall be weighted
+ if ( name == "firewood mass consumption" ):
+ self.increment_flag = True
+ #print ("firewood increment_flag is set")
+ else:
+ self.increment_flag = False
+
def set_name(self, name):
self.name = name
@@ -165,29 +176,59 @@ def check_strictly_increasing_new_row(self, vec):
def check_measurement_input(self, vec):
if self.status != False:
- last_row=self.table.last_row()
- first_row=self.table.first_row()
- #bisheriger Durchschnittsverbrauch pro Tag
- slope=(last_row[1]-first_row[1])/(last_row[0]-first_row[0])
- for j, el in enumerate(vec):
- #print j, el
- if isinstance(el, float) and isinstance(last_row[j], float) and self.table.strictly_increasing_check_mask[j]:
- if last_row[j] >= el:
- if j == 0:
- print ("\nWrong input: Actual timestamp \'%.3f\' is smaller than latest table entry \'%.3f\'\n" % (el, last_row[j]))
- if j == 1:
- print ("\nWrong input: Actual measurement \'%.3f\' is smaller than latest table entry \'%.3f\'\n" % (el, last_row[j]))
- return False
- else:
- if j==1:
- actual_slope=(el-last_row[j])/(vec[0]-last_row[0])
- print ("\nActual \'%s\' per day: \'%.3f\'\n" % (self.name, actual_slope))
- if actual_slope > MAX_MEASUREMENT_VARIATION*slope:
- print ("\nWrong input: Actual measurement slope \'%.3f\' is more than \'x%s\' bigger than average slope \'%.3f\'\n" % (actual_slope, MAX_MEASUREMENT_VARIATION, slope))
+ if self.increment_flag == False:
+ # check input from a meter
+ last_row=self.table.last_row()
+ first_row=self.table.first_row()
+ #bisheriger Durchschnittsverbrauch pro Tag
+ slope=(last_row[1]-first_row[1])/(last_row[0]-first_row[0])
+ for j, el in enumerate(vec):
+ #print ( j, el )
+ if isinstance(el, float) and isinstance(last_row[j], float) and self.table.strictly_increasing_check_mask[j]:
+ if last_row[j] >= el:
+ if j == 0:
+ print ("\nWrong input: Actual timestamp \'%.3f\' is smaller than latest table entry \'%.3f\'\n" % (el, last_row[j]))
+ if j == 1:
+ print ("\nWrong input: Actual measurement \'%.3f\' is smaller than latest table entry \'%.3f\'\n" % (el, last_row[j]))
+ return False
+ else:
+ if j==1:
+ actual_slope=(el-last_row[j])/(vec[0]-last_row[0])
+ print ("\nActual \'%s\' per day: \'%.3f\'\n" % (self.name, actual_slope))
+ if actual_slope > MAX_MEASUREMENT_VARIATION*slope:
+ print ("\nWrong input: Actual measurement slope \'%.3f\' is more than \'x%s\' bigger than average slope \'%.3f\'\n" % (actual_slope, MAX_MEASUREMENT_VARIATION, slope))
+ return False
+ else:
+ # check firewood charge input
+ last_row=self.table.last_row()
+ for j, el in enumerate(vec):
+ #print ( j, el )
+ if isinstance(el, float) and isinstance(last_row[j], float):
+ if last_row[j] >= el:
+ if j == 0:
+ print ("\nWrong input: Actual timestamp \'%.3f\' is smaller than latest table entry \'%.3f\'\n" % (el, last_row[j]))
+ return False
+ if el <= 0:
+ if j == 1:
+ print ("\nWrong input: Actual charge \'%.3f\' must be positive" % el)
return False
+ else:
+ if j==1:
+ if (el > MAX_FIREWOOD_CHARGE ):
+ print ("\nWrong input: Actual charge \'%.3f\' is exceeding the maximum charge of \'%.1f\' of the stove (see define MAX_FIREWOOD_CHARGE)" % (el, MAX_FIREWOOD_CHARGE) )
+ return False
return True
else:
return False
+
+ def accumulate_charge_in_increment_mode(self, input_value):
+ if self.status != False:
+ if self.increment_flag == True:
+ if not self.newDB_flag:
+ last_row=self.table.last_row()
+ #print ( last_row )
+ input_value = input_value + last_row[1]
+ return input_value
def add_row(self, row):
if self.status != False:
@@ -311,20 +352,24 @@ def input_measurement(self, consistency_check_on):
self.status=False
if self.status != False:
# determine local time for the local timezone
- t1 = datetime.datetime(1970, 1, 1, 0, 0, 0) # 1970-01-01 00:0:0
+ t1 = datetime.datetime(1970, 1, 1, 0, 0, 0) # 1970-01-01 00:0:0
tl = datetime.datetime.now()
tnow=(time.mktime(tl.timetuple())-time.mktime(t1.timetuple()))/24/3600
done = False
while not done:
fp=input_float('Please input actual measurement of \'%s\'\n\n' % self.name)
if self.newDB_flag:
- done = True
+ done = True
else:
done = self.check_measurement_input([tnow, fp])
+ # accumulate firewood charge of stove to last firewood consumption value, i.e. increment_flag == True
+ #print ( fp )
+ fp = self.accumulate_charge_in_increment_mode(fp)
+ #print (fp )
if self.newDB_flag:
- tlminus1 = tl - datetime.timedelta(seconds=1)
- tnowminus1=(time.mktime(tlminus1.timetuple())-time.mktime(t1.timetuple()))/24/3600
- self.add_row( [tnowminus1, fp-ALPHA])
+ tlminus60 = tl - datetime.timedelta(seconds=60) #add an artificial table entry for the first entry, timesamp is 60 seconds earlier
+ tnowminus60=(time.mktime(tlminus60.timetuple())-time.mktime(t1.timetuple()))/24/3600
+ self.add_row( [tnowminus60, fp-ALPHA])
self.add_row( [tnow, fp])
else:
self.add_row( [tnow, fp])
@@ -334,6 +379,7 @@ def input_measurement(self, consistency_check_on):
def consumption_analysis(self, name, working_dir, input_file, ylabel,
consistency_check_on, input_flag, newDB_flag):
self.set_name(name)
+ self.set_increment_flag(name)
self.set_working_dir(working_dir)
self.set_table_name(input_file)
self.set_newDB_flag(newDB_flag)
@@ -597,6 +643,13 @@ def main():
parser.add_option("--whp", type="string",
help="file storing data base for heat pump energy consumption analysis",
metavar="FILE",dest="file_heat_pump")
+
+ parser.add_option("-f", help="analyze firewood mass consumption of a stove (note: mass of each oven charge needs to be inputted)", dest="firewood",
+ action='store_true')
+
+ parser.add_option("--ff", type="string",
+ help="file storing data base for firewood mass consumption analysis",
+ metavar="FILE",dest="file_firewood")
parser.set_defaults(verbose=0, no_consistency_check=False, no_greater_than_check=False, newDB=False,
wdir=WORKING_DIR, epower=False,
@@ -605,7 +658,8 @@ def main():
file_water=WATER_CONSUMPTION_FILE, gas=False,
file_gas=GAS_CONSUMPTION_FILE, pellets=False,
file_pellets=PELLETS_CONSUMPTION_FILE, heat_pump=False,
- file_heat_pump=HEAT_PUMP_CONSUMPTION_FILE, version=False, input_flag=False)
+ file_heat_pump=HEAT_PUMP_CONSUMPTION_FILE, firewood=False,
+ file_firewood=FIREWOOD_CONSUMPTION_FILE, version=False, input_flag=False)
(options, args) = parser.parse_args()
@@ -679,7 +733,7 @@ def main():
options.wdir, options.file_pellets, 'pellets energy [kWh]',
not(options.no_consistency_check), options.input_flag, options.newDB)
- #analyze heat pump energy consumption
+ #analyze heat pump energy consumption
if options.heat_pump:
stdMsg("\n\nStarting analysis of heat pump energy consumption ..\n")
#check if data base file is existing and readable
@@ -690,6 +744,18 @@ def main():
heat_pump.consumption_analysis('heat pump energy consumption',
options.wdir, options.file_heat_pump, 'heat pump energy [kWh]',
not(options.no_consistency_check), options.input_flag, options.newDB)
+
+ #analyze firewood mass consumption
+ if options.firewood:
+ stdMsg("\n\nStarting analysis of firewood mass consumption of a stove (note: mass of each oven charge needs to be inputted) ..\n")
+ #check if data base file is existing and readable
+ check_database_file(options.wdir, options.file_firewood, options.newDB)
+ #initialize data analysis
+ firewood=consumption()
+ #run analysis
+ firewood.consumption_analysis('firewood mass consumption',
+ options.wdir, options.file_firewood, 'firewood mass [kg]',
+ not(options.no_consistency_check), options.input_flag, options.newDB)
if not options.epower and not options.oil and not options.water and not options.gas and not options.pellets and not options.heat_pump:
diff --git a/electrical_power_consumption.caf b/electrical_power_consumption.caf
index c01a353..2f20874 100644
--- a/electrical_power_consumption.caf
+++ b/electrical_power_consumption.caf
@@ -600,3 +600,7 @@
19264.809525462966 109450.3
19282.4340625 109679.2
19291.40403935185 109790.4
+19310.339016203703 110026.3
+19323.384386574075 110198.0
+19326.363078703704 110230.7
+19340.520914351855 110397.0
diff --git a/firewood_mass_consumption.caf b/firewood_mass_consumption.caf
new file mode 100644
index 0000000..99d413b
--- /dev/null
+++ b/firewood_mass_consumption.caf
@@ -0,0 +1,2 @@
+19341.415324074074 13.59
+19341.41601851852 13.6
diff --git a/pellets_energy_consumption.caf b/pellets_energy_consumption.caf
index 1b805de..ec77d3a 100644
--- a/pellets_energy_consumption.caf
+++ b/pellets_energy_consumption.caf
@@ -5,3 +5,8 @@
19326.364085648147 318.9
19330.390555555554 649.2
19331.350011574075 726.5
+19332.388796296298 827.4
+19333.363240740742 905.5
+19334.39611111111 993.0
+19340.603946759256 1656.5
+19341.356666666667 1748.5
diff --git a/water_consumption.caf b/water_consumption.caf
index 2697d92..7cdf90b 100644
--- a/water_consumption.caf
+++ b/water_consumption.caf
@@ -587,3 +587,6 @@
19264.810567129633 59.7
19282.434710648147 66.653
19291.404537037037 70.091
+19310.339513888888 77.835
+19323.38491898148 83.99
+19326.363645833335 84.95