I thought I would put this in a new topic since my last post wandered of a bit.
So I have developed a bit of python that aims to predict the heat output of an ecodan heat pump depending on flow temp , input power and ambient temp. So on the face of it it does the same job as the simulated carnot equation on the heatpump app. The reason for doing it is that there seems to be some variation in COP for a given flow and ambient - depending on presumably the speed of the compressor - I am not sure to what extent if any the carnot equation would take this into account - I assume the variation is due to losses in the compressor being more pronounced at lower throughout - but Im no fridge man so dont really know.
Any way python code to follow once I work out how to include it.
import json
import time
import numpy as np
#Ecodan output predictor - this program will predict expected output from a 14kw Mitsubishi Ecodan Heat pump based on the manufacturers data and for a given flow temp
# ambient temp and input power
def find_output_power(input_power,cop_array,output_array):
# this routine calculates the output of the heatpup for a given input power to the heatpump - based on the given COP's and outputs from the ecodan data sheet
# the inputs to the routine as follows:
# input power - is the power being delivered to the heatpump
# cop_array - is an array passed to the routin which contains the COP's for a given flow temp and ambient temp and for a range of output powers as defined in the ecodan data book
# output_array - is an array containing the corresponding outputs from the ecodan databook
# The routine uses linear interpolation to find the output power for a given inout power by interpolating between the points in the arrays.
# print(cop_array)
# print(output_array)
output_power = 0
#determine which range we are operating in - the data sheet gives a range of four outputs for the heatpump as well as corresponding COP - from this we can calculate
# the corresponding input power = output power/cop - and find what range we are currently in from the current iput power
if input_power > (output_array[3]/cop_array[3]) : indexh = 3
if input_power <= (output_array[3]/cop_array[3]) : indexh = 3
if input_power <=(output_array[2]/cop_array[2]) : indexh = 2
if input_power <= (output_array[1]/cop_array[1]) : indexh = 1
indexl = indexh-1
# find the high and low boundaries of the operating region/
inpowerl = output_array[indexl]/cop_array[indexl]
inpowerh = output_array[indexh]/cop_array[indexh]
outpowerl = output_array[indexl]
outpowerh = output_array[indexh]
# print(inpowerl)
# print(inpowerh)
# print(outpowerl)
# print(outpowerh)
# calculate the slope of the line in the operating region
slope = (outpowerh-outpowerl)/(inpowerh - inpowerl)
# print (slope)
# calculate the output power
output_power = (slope * (input_power - inpowerl)) + outpowerl
# print(output_power)
return (output_power)
def bilinear_interpolation(x,y,x_,y_,val):
# this routine does bilinear interpolation between two points in a 2 d plane - got it off the web
a = 1 /((x_[1] - x_[0]) * (y_[1] - y_[0]))
xx = np.array([[x_[1]-x],[x-x_[0]]],dtype='float32')
f = np.array(val).reshape(2,2)
yy = np.array([[y_[1]-y],[y-y_[0]]],dtype='float32')
b = np.matmul(f,yy)
return a * np.matmul(xx.T, b)
##main part of the program
# run a loop to evaluate every 20 seconds
while True :
#get the heatpump input power from emoncms
header = {'content-type':'application/json'}
url = "http://192.168.0.100/input/get/emonpi/power2&apikey=00493cf9xxxxxxxxxxx"
response = requests.get(url,headers = header)
str_response = response.content.decode("utf-8")
#print(str_response)
#print(type(str_response))
response_dict = response.json()
#print(type(response_dict))
#print(response_dict)
# set the power used for pumps etc - i.e this needs to be subtracted from power input to heat pump
pumping_power = 190
input_power = ( response_dict["value"] - pumping_power)/1000
# print(input_power)
#get heatpump flow temp from emoncms
url = "http://192.168.0.100/input/get/emontxshield/t1&apikey=00493cxxxxxxxxxxxxx"
response = requests.get(url,headers = header)
str_response = response.content.decode("utf-8")
#print(str_response)
#print(type(str_response))
response_dict = response.json()
#print(type(response_dict))
#print(response_dict)
#print(response_dict["value"]/1000)
flow_temp = response_dict["value"]
# print(flow_temp)
#get outside air temp from emoncms
url = "http://192.168.0.100/input/get/emontxshield/t5&apikey=00493cf9xxxxxxxxxxxxxxxxx"
response = requests.get(url,headers = header)
str_response = response.content.decode("utf-8")
#print(str_response)
#print(type(str_response))
response_dict = response.json()
#print(type(response_dict))
#print(response_dict)
#print(response_dict["value"]/1000)
ambient_temp = response_dict["value"]
# print(ambient_temp)
# following sets up all the data arrays for different combinations of flow and ambient temperature - based on the values from the ecodan databook
# if you have a different heatpump you will have to edit all of these to suit your model
# for each point on the flow x ambient table there is an array for output and cop at 5 different invertor power levels
f25am25_output = np.array([5.5,8,10,10])
f25am25_cop = np.array([1.75,1.75,1.65,1.65])
f25am20_output = np.array([6.7,8.4,10.5,12])
f25am20_cop = np.array([2.05,2.15,2,1.75])
f25am15_output = np.array([7.8,8.8,11,14])
f25am15_cop = np.array([2.35,2.5,2.3,1.85])
f25am10_output = np.array([5.9,11.2,14,14.4])
f25am10_cop = np.array([2.3,2.7,2.5,2.25])
f25am7_output = np.array([4,11.2,14,15.9])
f25am7_cop = np.array([2.25,3,2.8,2.5])
f25a2_output = np.array([5.1,11.2,14,16.3])
f25a2_cop = np.array([3.65,3.4,3.15,3])
f25a7_output = np.array([4.2,11.2,14,16.6])
f25a7_cop = np.array([4.45,4.8,4.45,4.25])
f25a12_output = np.array([6.2,11.2,14,16.8])
f25a12_cop = np.array([4.9,5.55,5.15,4.9])
f25a15_output = np.array([6.4,11.2,14,18.2])
f25a15_cop = np.array([5.1,5.65,5.25,5])
f25a20_output = np.array([6.7,11.2,14,20.8])
f25a20_cop = np.array([8,7.35,6.85,6.5])
f35am25_output = np.array([5.5,8,10,10])
f35am25_cop = np.array([1.75,1.75,1.65,1.65])
f35am20_output = np.array([6.7,8.4,10.5,12])
f35am20_cop = np.array([2.05,2.15,2,1.75])
f35am15_output = np.array([7.8,8.8,11,14])
f35am15_cop = np.array([2.35,2.5,2.3,1.85])
f35am10_output = np.array([5.9,11.2,14,14.4])
f35am10_cop = np.array([2.3,2.7,2.5,2.25])
f35am7_output = np.array([4,11.2,14,15.9])
f35am7_cop = np.array([2.25,3,2.8,2.5])
f35a2_output = np.array([5.1,11.2,14,16.3])
f35a2_cop = np.array([3.65,3.4,3.15,3])
f35a7_output = np.array([4.2,11.2,14,16.6])
f35a7_cop = np.array([4.45,4.8,4.45,4.25])
f35a12_output = np.array([6.2,11.2,14,16.8])
f35a12_cop = np.array([4.9,5.55,5.15,4.9])
f35a15_output = np.array([6.4,11.2,14,18.2])
f35a15_cop = np.array([5.1,5.65,5.25,5])
f35a20_output = np.array([6.7,11.2,14,20.8])
f35a20_cop = np.array([8,7.35,6.85,6.5])
f40am25_output = np.array([5,7.7,9.7,9.7])
f40am25_cop = np.array([1.5,1.65,1.5,1.5])
f40am20_output = np.array([6.1,8.1,10.2,11.6])
f40am20_cop = np.array([1.75,2,1.85,1.6])
f40am15_output = np.array([7.2,8.8,11,13.5])
f40am15_cop = np.array([2,2.3,2.1,1.7])
f40am10_output = np.array([5.6,11.2,14,14.2])
f40am10_cop = np.array([2.05,2.55,2.2,2.1])
f40am7_output = np.array([4,11.2,14,15.7])
f40am7_cop = np.array([2.05,2.75,2.55,2.3])
f40a2_output = np.array([4.7,11.2,14,16])
f40a2_cop = np.array([3.1,3.15,2.9,2.75])
f40a7_output = np.array([3.7,11.2,14,16.4])
f40a7_cop = np.array([3.5,4.35,4,3.8])
f40a12_output = np.array([5.8,11.2,14,16.6])
f40a12_cop = np.array([4.65,5.25,4.8,4.55])
f40a15_output = np.array([5.9,11.2,14,17.9])
f40a15_cop = np.array([5.05,5.45,5,4.75])
f40a20_output = np.array([6.4,11.2,14,20.5])
f40a20_cop = np.array([6.65,6.6,6.05,5.75])
f45am25_output = np.array([4.6,7.5,9.3,9.3])
f45am25_cop = np.array([1.2,1.5,1.35,1.35])
f45am20_output = np.array([5.5,7.8,9.8,11.2])
f45am20_cop = np.array([1.4,1.85,1.65,1.45])
f45am15_output = np.array([6.5,8.8,11,13.1])
f45am15_cop = np.array([1.6,2.1,1.9,1.55])
f45am10_output = np.array([5.3,11.2,14,14])
f45am10_cop = np.array([1.75,2.35,1.9,1.9])
f45am7_output = np.array([4,11.2,14,15.5])
f45am7_cop = np.array([1.85,2.5,2.3,2.1])
f45a2_output = np.array([4.2,11.2,14,15.8])
f45a2_cop = np.array([2.5,2.9,2.65,2.5])
f45a7_output = np.array([3.2,11.2,14,16.1])
f45a7_cop = np.array([2.55,3.85,3.5,3.3])
f45a12_output = np.array([5.4,11.2,14,16.4])
f45a12_cop = np.array([4.35,4.9,4.45,4.2])
f45a15_output = np.array([5.4,11.2,14,17.7])
f45a15_cop = np.array([4.95,5.25,4.75,4.5])
f45a20_output = np.array([6,11.2,14,20.2])
f45a20_cop = np.array([5.3,5.8,5.25,4.95])
f50am25_output = np.array([3.6,11.2,14,14.7])
f50am25_cop = np.array([2.4,4.87,6.51,7.17])
f50am20_output = np.array([0,0,0,0])
f50am20_cop = np.array([1,1,1,1])
f50am20_output = np.array([0,0,0,0])
f50am20_cop = np.array([1,1,1,1])
f50am15_output = np.array([5.7,8.8,11,12.6])
f50am15_cop = np.array([1.3,1.9,1.75,1.45])
f50am10_output = np.array([4.6,11.2,14,14])
f50am10_cop = np.array([1.45,2.1,1.85,1.85])
f50am7_output = np.array([3.6,11.2,14,14.7])
f50am7_cop = np.array([1.5,2.3,2.15,2.05])
f50a2_output = np.array([3.7,11.2,14,15.1])
f50a2_cop = np.array([2.05,2.75,2.55,2.45])
f50a7_output = np.array([2.7,11.2,14,15.4])
f50a7_cop = np.array([2,3.4,3.15,2.9])
f50a12_output = np.array([5.2,11.2,14,15.6])
f50a12_cop = np.array([3.7,4.3,4,3.7])
f50a15_output = np.array([5.3,11.2,14,16.9])
f50a15_cop = np.array([4.35,4.65,4.3,4])
f50a20_output = np.array([5.6,11.2,14,19.3])
f50a20_cop = np.array([4.4,5.1,4.75,4.4])
f55am25_output = np.array([3.1,11.2,14,14.1])
f55am25_cop = np.array([0,0,0,0])
f55am20_output = np.array([3.1,11.2,14,14.1])
f55am20_cop = np.array([0,0,0,0])
f55am15_output = np.array([4.8,8.8,11,12.2])
f55am15_cop = np.array([1,1.75,1.6,1.3])
f55am10_output = np.array([4,11.2,14,14])
f55am10_cop = np.array([1.1,1.85,1.75,1.75])
f55am7_output = np.array([3.1,11.2,14,14])
f55am7_cop = np.array([1.15,2.1,1.95,1.95])
f55a2_output = np.array([3.2,11.2,14,14.3])
f55a2_cop = np.array([1.55,2.55,2.4,2.35])
f55a7_output = np.array([2.2,11.2,14,14.6])
f55a7_cop = np.array([1.45,2.9,2.75,2.5])
f55a12_output = np.array([5,11.2,14,14.8])
f55a12_cop = np.array([3.05,3.7,3.5,3.2])
f55a15_output = np.array([5.2,11.2,14,16])
f55a15_cop = np.array([3.7,4.05,3.85,3.5])
f55a20_output = np.array([5.1,11.2,14,18.3])
f55a20_cop = np.array([3.45,4.4,4.2,3.8])
f60am25_output = np.array([3.2,11.2,14,14.3])
f60am25_cop = np.array([2.06,4.39,5.83,6.09])
f60am20_output = np.array([3.2,11.2,14,14.3])
f60am20_cop = np.array([2.06,4.39,5.83,6.09])
f60am15_output = np.array([3.2,11.2,14,14.3])
f60am15_cop = np.array([2.06,4.39,5.83,6.09])
f60am10_output = np.array([3.2,11.2,14,14.3])
f60am10_cop = np.array([2.06,4.39,5.83,6.09])
f60am7_output = np.array([3.2,11.2,14,14.3])
f60am7_cop = np.array([2.06,4.39,5.83,6.09])
f60a2_output = np.array([3.2,11.2,14,14.3])
f60a2_cop = np.array([2.06,4.39,5.83,6.09])
f60a7_output = np.array([3.2,11.2,14,14.3])
f60a7_cop = np.array([2.06,4.39,5.83,6.09])
f60a12_output = np.array([3.2,11.2,14,14.3])
f60a12_cop = np.array([2.06,4.39,5.83,6.09])
f60a15_output = np.array([3.2,11.2,14,14.3])
f60a15_cop = np.array([2.06,4.39,5.83,6.09])
f60a20_output = np.array([3.2,11.2,14,14.3])
f60a20_cop = np.array([2.06,4.39,5.83,6.09])
#flow_temp = 38.4 # for testing
#ambient_temp = 7.5 # for testing
#input_power = 2.217 # for testing
# following code generates pointers to the 2d table of arrays based on actual ambient and flow temps
ambient_index_low = 0
ambient_index_high = 0
flow_index_low = 0
flow_index_high = 0
if flow_temp > 55 :
flow_index_low = 5
flow_index_high = 6
flow_low = 55
flow_high = 60
if flow_temp <= 55 :
flow_index_low = 4
flow_index_high = 5
flow_low = 50
flow_high = 55
if flow_temp <= 50 :
flow_index_low = 3
flow_index_high = 4
flow_low = 45
flow_high = 50
if flow_temp <= 45 :
flow_index_low = 2
flow_index_high = 3
flow_low = 40
flow_high = 45
if flow_temp <= 40 :
flow_index_low = 1
flow_index_high = 2
flow_low = 35
flow_high = 40
if flow_temp <= 35 :
flow_index_low = 0
flow_index_high = 1
flow_low = 25
flow_high = 35
if ambient_temp > 15 :
ambient_index_low = 8
ambient_index_high = 9
ambient_low = 15
ambient_high = 20
if ambient_temp <= 15 :
ambient_index_low = 7
ambient_index_high = 8
ambient_low = 12
ambient_high = 15
if ambient_temp <= 12 :
ambient_index_low = 6
ambient_index_high = 7
ambient_low = 7
ambient_high = 12
if ambient_temp <= 7 :
ambient_index_low = 5
ambient_index_high = 6
ambient_low = 2
ambient_high = 7
if ambient_temp <= 2 :
ambient_index_low = 4
ambient_index_high = 5
ambient_low = -7
ambient_high = 2
if ambient_temp <= -7 :
ambient_index_low = 3
ambient_index_high = 4
ambient_low = -10
ambient_high = -7
if ambient_temp <= -10 :
ambient_index_low = 2
ambient_index_high = 3
ambient_low = -15
ambient_high = -10
if ambient_temp <= -15 :
ambient_index_low = 1
ambient_index_high = 2
ambient_low = -20
ambient_high = -15
if ambient_temp <= -20 :
ambient_index_low = 0
ambient_index_high = 1
ambient_low = -25
ambient_high = -20
# print(flow_index_low)
# print(flow_index_high)
# print(ambient_index_low)
# print(ambient_index_high)
# print("")
# print(flow_low , flow_high)
# print(ambient_low , ambient_high)
# variables to store the results of the four points that bound the current ambient and flow temp
output_power_A = 0
output_power_B = 0
output_power_C = 0
output_power_D = 0
# build the full 2D array containing all the 1d arrays for differnet flow and ambient temps
datatable_input_power = np.array([[f25am25_cop,f35am25_cop,f40am25_cop,f45am25_cop,f50am25_cop,f55am25_cop,f60am25_cop],[f25am20_cop,f35am20_cop,f40am20_cop,f45am20_cop,f50am20_cop,f55am20_cop,f60am20_cop],[f25am15_cop,f35am15_cop,f40am15_cop,f45am15_cop,f50am15_cop,f55am15_cop,f60am7_cop],[f25am10_cop,f35am10_cop,f40am10_cop,f45am10_cop,f50am10_cop,f55am10_cop,f60am10_cop],[f25am7_cop,f35am7_cop,f40am7_cop,f45am7_cop,f50am7_cop,f55am7_cop,f60am7_cop],[f25a2_cop,f35a2_cop,f40a2_cop,f45a2_cop,f50a2_cop,f55a2_cop,f60a2_cop],[f25a7_cop,f35a7_cop,f40a7_cop,f45a7_cop,f50a7_cop,f55a7_cop,f60a7_cop],[f25a12_cop,f35a12_cop,f40a12_cop,f45a12_cop,f50a12_cop,f55a12_cop,f60a12_cop],[f25a15_cop,f35a15_cop,f40a15_cop,f45a15_cop,f50a15_cop,f55a15_cop,f60a15_cop],[f25a20_cop,f35a20_cop,f40a20_cop,f45a20_cop,f50a20_cop,f55a20_cop,f60a20_cop]])
datatable_output_power = np.array([[f25am25_output,f35am25_output,f40am25_output,f45am25_output,f50am25_output,f55am25_output,f60am25_output],[f25am20_output,f35am20_output,f40am20_output,f45am20_output,f50am20_output,f55am20_output,f60am20_output],[f25am15_output,f35am15_output,f40am15_output,f45am15_output,f50am15_output,f55am15_output,f60am7_output],[f25am10_output,f35am10_output,f40am10_output,f45am10_output,f50am10_output,f55am10_output,f60am10_output],[f25am7_output,f35am7_output,f40am7_output,f45am7_output,f50am7_output,f55am7_output,f60am7_output],[f25a2_output,f35a2_output,f40a2_output,f45a2_output,f50a2_output,f55a2_output,f60a2_output],[f25a7_output,f35a7_output,f40a7_output,f45a7_output,f50a7_output,f55a7_output,f60a7_output],[f25a12_output,f35a12_output,f40a12_output,f45a12_output,f50a12_output,f55a12_output,f60a12_output],[f25a15_output,f35a15_output,f40a15_output,f45a15_output,f50a15_output,f55a15_output,f60a15_output],[f25a20_output,f35a20_output,f40a20_output,f45a20_output,f50a20_output,f55a20_output,f60a20_output]])
# find the four output powers that form a bounding box around the actual current ambient and flow temp operating condition
output_power_A = (find_output_power(input_power,datatable_input_power[ambient_index_low,flow_index_low],datatable_output_power[ambient_index_low,flow_index_low]))
output_power_B = (find_output_power(input_power,datatable_input_power[ambient_index_low,flow_index_high],datatable_output_power[ambient_index_low,flow_index_high]))
output_power_C = (find_output_power(input_power,datatable_input_power[ambient_index_high,flow_index_low],datatable_output_power[ambient_index_high,flow_index_low]))
output_power_D = (find_output_power(input_power,datatable_input_power[ambient_index_high,flow_index_high],datatable_output_power[ambient_index_high,flow_index_high]))
# print(output_power_A)
# print(output_power_B)
# print(output_power_C)
# print(output_power_D)
# Do bilinear interpolation between the 4 bounding points to find the predicted output power
output = bilinear_interpolation(x=ambient_temp,
y=flow_temp,
x_=[ambient_low,ambient_high],
y_=[flow_low,flow_high],
val=[output_power_A,output_power_B,output_power_C,output_power_D])
# print("")
# print(output[0,0])
# print(output[0,0]/input_power)
# send the result back to emoncms using an HTTP post
output_heat = output[0,0]
if output_heat <= 0 :
output_heat = 0
output_heat_str = str(output_heat)
print(output_heat_str)
jsonheat = "heat :"+output_heat_str
# print(jsonheat)
url=" "
url="http://192.168.0.100/emoncms/input/post?node=emontx&json={value1:100,output_heat:%s}&apikey=00493cxxxxxxxxxxxxxxxxxxxxx"%output_heat_str
header = {'content-type':'application/json'}
# print(url)
response =requests.get(url,headers = header)
str_response = response.content.decode("utf-8")
print(str_response)
# 20 second loop
time.sleep(20)