Skip to content

Commit

Permalink
- option to use ecodan lookup table for cop calculation
Browse files Browse the repository at this point in the history
- dynamic room temperature solver
  • Loading branch information
TrystanLea committed Sep 29, 2021
1 parent ae32edf commit 83bd24e
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 29 deletions.
107 changes: 107 additions & 0 deletions heatlossjs/ecodan.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// Work out Mitsubushi Ecodan COP using look up table approach
function get_ecodan_cop(outlet,outside,load)
{
var ecodan = {
"min": {
"data": [
//-15, -10, -7, 2, 7, 12, 15, 20
[null, 3.64, 3.52, 4.16, 5.69, 6.59, 7.06, 7.78], // 25
[2.66, 3.01, 2.99, 3.59, 4.64, 5.26, 5.64, 6.26], // 35
[2.38, 2.68, 2.67, 3.23, 4.03, 4.49, 4.78, 5.25], // 40
[2.10, 2.34, 2.35, 2.86, 3.41, 3.73, 3.91, 4.23], // 45
[null, 2.10, 2.12, 2.54, 3.07, 3.32, 3.46, 3.71], // 50
[null, 1.86, 1.89, 2.21, 2.73, 2.91, 3.01, 3.19], // 55
[null, null, null, null, null, null, null, null] // 60
]
},
"mid": {
"data": [
//-15, -10, -7, 2, 7, 12, 15, 20
[null, 3.64, 3.85, 4.90, 5.89, 6.58, 7.08, 7.98], // 25
[2.66, 3.01, 3.25, 3.54, 4.63, 5.35, 5.79, 6.54], // 35
[2.38, 2.68, 2.87, 3.35, 4.18, 4.66, 4.97, 5.48], // 40
[2.10, 2.34, 2.50, 3.15, 3.73, 3.98, 4.15, 4.43], // 45
[null, 2.10, 2.25, 2.78, 3.23, 3.43, 3.56, 3.78], // 50
[null, 1.86, 2.00, 2.41, 2.74, 2.88, 2.98, 3.14], // 55
[null, null, null, 2.05, 2.56, 2.59, 2.62, 2.68] // 60
]
},
"max": {
"data": [
//-15, -10, -7, 2, 7, 12, 15, 20
[null, 3.30, 3.60, 4.20, 5.48, 6.20, 6.65, 7.41], // 25
[2.44, 2.78, 3.00, 3.50, 4.50, 4.98, 5.28, 5.79], // 35
[2.22, 2.51, 2.70, 3.15, 4.01, 4.37, 4.59, 4.98], // 40
[2.00, 2.25, 2.40, 2.80, 3.52, 3.75, 3.91, 4.16], // 45
[null, 2.05, 2.16, 2.47, 3.10, 3.27, 3.38, 3.57], // 50
[null, 1.85, 1.92, 2.13, 2.68, 2.78, 2.85, 2.97], // 55
[null, null, null, 1.80, 2.26, 2.30, 2.33, 2.38] // 60
]
}
}

var load_index = "min";
if (load>=0.6) load_index = "mid";
if (load>=0.8) load_index = "max";

// 1. Find outlet index
var outlet_index = false;
var rows = [25,35,40,45,50,55,60];
for (var z=0; z<rows.length; z++) {
if (rows[z]>outlet) {
outlet_index = z-1;
break
}
else if (rows[z]==outlet) {
outlet_index = z;
break
}
}
// Limit outlet_index by available data
if (outlet_index<0) outlet_index = 0;
if (outlet_index===false || outlet_index == rows.length-1) {
outlet_index = rows.length - 2;
}

// 2. Find outside index
var outside_index = false;
var cols = [-15,-10,-7,2,7,12,15,20];
for (var z=0; z<rows.length; z++) {
if (cols[z]>outside) {
outside_index = z-1;
break
}
else if (cols[z]==outside) {
outside_index = z;
break
}
}
// Limit outlet_index by available data
if (outside_index<0) outside_index = 0;
if (outside_index===false || outside_index == cols.length-1) {
outside_index = cols.length - 2;
}



// Work out linear interpolation part 1
var a = ecodan[load_index].data[outlet_index][outside_index]
var b = ecodan[load_index].data[outlet_index][outside_index+1]
var c1 = a + (b-a) * (outside - cols[outside_index]) / (cols[outside_index+1] - cols[outside_index]);
if (a==null || b==null) c1 = null;

// Work out linear interpolation part 2
var a = ecodan[load_index].data[outlet_index+1][outside_index]
var b = ecodan[load_index].data[outlet_index+1][outside_index+1]
var c2 = a + (b-a) * (outside - cols[outside_index]) / (cols[outside_index+1] - cols[outside_index]);
if (a==null || b==null) c2 = null;

// Work out linear interpolation part 3
var cop = c1 + (c2-c1) * (outlet - rows[outlet_index]) / (rows[outlet_index+1] - rows[outlet_index]);
if (c1==null || c2==null) cop = null;

return cop;
}



54 changes: 53 additions & 1 deletion heatlossjs/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,25 @@ $.ajax({
}
});

var dynamic_simulation_interval = false;
config.solver_running = false;

var app = new Vue({
el: '#heatloss',
data: config,
methods: {
update: function() {
calculate();

if (config.solver_running) {
clearInterval(dynamic_simulation_interval);
dynamic_simulation_interval = setInterval(dynamic,100);
}
},
focus: function() {
if (config.solver_running) {
clearInterval(dynamic_simulation_interval);
}
},
add_element_type: function() {
var last = false;
Expand Down Expand Up @@ -143,6 +156,14 @@ var app = new Vue({
open_in_sapjs: function() {
localStorage.setItem("heatlossjs",JSON.stringify(config));
window.location = "/sapjs?load=heatlossjs"
},
start_solver: function() {
dynamic_simulation_interval = setInterval(dynamic,100);
config.solver_running = true;
},
stop_solver: function() {
clearInterval(dynamic_simulation_interval);
config.solver_running = false;
}
},

Expand All @@ -151,6 +172,7 @@ var app = new Vue({
if (isNaN(val)) {
return val;
} else {
if (val==null) return 0;
return val.toFixed(dp)
}
},
Expand All @@ -169,6 +191,11 @@ function calculate() {
total_heat_output: 0
};

config.JK = 50;

config.heating_MWT *=1;
if (config.heating_MWT==null || config.heating_MWT=='') config.heating_MWT = 0;

for (var z in config.rooms) {
var room = config.rooms[z]

Expand Down Expand Up @@ -307,14 +334,39 @@ function calculate() {
config.house.heatloss += room.heat
config.house.kwh += room.kwh
config.house.total_heat_output += room.total_heat_output

if (room.energy==undefined) room.energy = room.temperature * config.JK;
room.energy += ( -room.heat + room.total_heat_output) * 0.01
}

// Heat pump model
var heatpump_deltaT = config.house.total_heat_output / ((config.heatpump_flow_rate / 60)*4150)
config.heatpump_flow_temperature = config.heating_MWT + 0.5*heatpump_deltaT;
config.heatpump_COP = 0.49 * (config.heatpump_flow_temperature+4+273) / ((config.heatpump_flow_temperature+4+273)-(config.T.external-6+273));

if (config.cop_calculation_method==undefined) {
config.cop_calculation_method = "ecodan";
}

if (config.cop_calculation_method=="carnot") {
config.heatpump_COP = 0.49 * (config.heatpump_flow_temperature+4+273) / ((config.heatpump_flow_temperature+4+273)-(config.T.external-6+273));
} else {
if (config.heatpump_capacity==undefined) config.heatpump_capacity = 5.0;
config.heatpump_COP = get_ecodan_cop(config.heatpump_flow_temperature,config.T.external,(config.house.total_heat_output*0.001)/config.heatpump_capacity);
}
config.heatpump_elec = config.house.total_heat_output / config.heatpump_COP
}

// dynamic_simulation_interval = setInterval(dynamic,100);

function dynamic() {
calculate()
for (var z in config.rooms) {
config.rooms[z].temperature = config.rooms[z].energy / config.JK;
}
}



function open_file(e) {
console.log(e)
var file = e.target.files[0];
Expand Down
85 changes: 58 additions & 27 deletions heatlossjs/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<tr><th>Element Name</th><th>U-value</th></tr>
<tr v-for="(element,element_name) in element_type">
<td><input type="text" v-bind:value="element_name" @change="change_element_type_name($event,element_name)" /></td>
<td style="width: 120px;"><input style="width:60%;" type="text" v-model.number="element.uvalue" @change="update" /><span class="unit">W/K.m2</span></td>
<td style="width: 120px;"><input style="width:60%;" type="text" v-model.number="element.uvalue" @change="update" @focus="focus" /><span class="unit">W/K.m2</span></td>
</tr>
<tr>
<td>
Expand All @@ -38,7 +38,7 @@
<tr><th>Boundary</th><th>Temperature</th></tr>
<tr v-for="(value,boundaryName) in T">
<td>{{boundaryName}}</td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="T[boundaryName]" @change="update" /><span class="unit">C</span></td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="T[boundaryName]" @change="update" @focus="focus" /><span class="unit">C</span></td>
</tr>
</table>
</div>
Expand All @@ -47,32 +47,63 @@
<tr><th>Other</th><th>Value</th></tr>
<tr>
<td>Radiator Mean Water Temperature (MWT)</td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="heating_MWT" @change="update" /><span class="unit">C</span></td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="heating_MWT" @change="update" @focus="focus" /><span class="unit">C</span></td>
</tr>
<tr>
<td>Heating demand degree days</td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="degreedays" @change="update" /><span class="unit"></span></td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="degreedays" @change="update" @focus="focus" /><span class="unit"></span></td>
</tr>
</table>
</div>
<div style="width: 50%; float: right;">
<table>
<tr><th>Heat pump</th><th>Value</th></tr>
<tr>
<td>Solve room temperatures</td>
<td>
<button v-if="!solver_running" @click="start_solver">Start</button>
<button v-else @click="stop_solver">Stop</button>
</td>
</tr>
</table>
</div>
<div style="clear: both;"></div>
</div>
</div>

<div class="section">
<div class="section-heading"><b>Heat pump</b></div>
<div class="room-elements">
<table>
<tr>
<td>Flow rate (L/min)</td>
<td style="width: 120px;"><input style="width: 60%;" type="text" v-model.number="heatpump_flow_rate" @change="update" /><span class="unit">L/min</span></td>
<td style="width:250px"><input style="width: 60px" type="text" v-model.number="heatpump_flow_rate" @change="update" @focus="focus" /><span class="unit">L/min</span></td>
</tr>
<tr>
<td>Flow temperature</td>
<td>{{ heatpump_flow_temperature | toFixed(1) }}<span class="unit">C</span></td>
<td>{{ heatpump_flow_temperature | toFixed(1) }} <span class="unit">C</span></td>
</tr>
<tr>
<td>COP Calculation</td>
<td>
<select v-model="cop_calculation_method" @change="update" @focus="focus">
<option value="carnot">Carnot COP equation</option>
<option value="ecodan">EcoDan lookup table interpolation</option>
</select>
</td>
</tr>
<tr v-if="cop_calculation_method=='ecodan'">
<td>Heat pump capacity (kW heat)</td>
<td><input style="width: 60px" type="text" v-model.number="heatpump_capacity" @change="update" @focus="focus" /><span class="unit">kW</span></td>
</tr>
<tr>
<td>COP</td>
<td>{{ heatpump_COP | toFixed(1) }}</td>
<td>{{ heatpump_COP | toFixed(2) }}</td>
</tr>
</table>
</div>
<div style="clear: both;"></div>
<tr>
<td>Electric input</td>
<td>{{ heatpump_elec | toFixed(0) }} <span class="unit">W</span></td>
</tr>
</table>
</div>
</div>

Expand All @@ -87,22 +118,22 @@
<td>Name:</td>
<td><input type="text" v-bind:value="roomName" @change="change_room_name($event,roomName)" /></td>
<td>Set point:</td>
<td><input type="text" v-model.number="room.temperature" @change="update" style="width:65px" /></td>
<td><input type="text" v-model.number="room.temperature" @change="update" @focus="focus" style="width:65px" /></td>
<td>Air changes per hour:</td>
<td><input type="text" v-model.number="room.air_change_an_hour" @change="update" style="width:65px" /></td>
<td><input type="text" v-model.number="room.air_change_an_hour" @change="update" @focus="focus" style="width:65px" /></td>
</tr>
</table>
<table style="margin-bottom: 5px;">
<tr>
<td>Room</td>
<td style="text-align: right;">Width:</td>
<td style="width: 65px;"><input type="text" v-model.number="room.width" @change="update" /><span class="unit">m</span></td>
<td style="width: 65px;"><input type="text" v-model.number="room.width" @change="update" @focus="focus" /><span class="unit">m</span></td>
<td style="text-align: right;">Length:</td>
<td style="width: 65px;"><input type="text" v-model.number="room.length" @change="update" /><span class="unit">m</span></td>
<td style="width: 65px;"><input type="text" v-model.number="room.length" @change="update" @focus="focus" /><span class="unit">m</span></td>
<td style="text-align: right;">Height:</td>
<td style="width: 65px;"><input type="text" v-model.number="room.height" @change="update" /><span class="unit">m</span></td>
<td style="width: 65px;"><input type="text" v-model.number="room.height" @change="update" @focus="focus" /><span class="unit">m</span></td>
<td style="text-align: right;">Area:</td>
<td style="width: 65px;"><input type="text" v-model.number="room.area" @change="update" /><span class="unit">m2</span></td>
<td style="width: 65px;"><input type="text" v-model.number="room.area" @change="update" @focus="focus" /><span class="unit">m2</span></td>
<td style="text-align: right;">Volume:</td>
<td><div class="value">{{ room.volume | toFixed(1) }}</div><span class="unit">m3</span></td>
<td style="text-align: right;"><b>Heat loss:</b></td>
Expand All @@ -128,25 +159,25 @@
<tr v-for="(element,elementIndex) in room.elements">
<td>{{ elementIndex }}</td>
<td>
<select v-model="element.type" @change="update">
<select v-model="element.type" @change="update" @focus="focus">
<option v-for="(type,element_name) in element_type">{{ element_name }}</option>
</select>
</td>
<td>
<select v-model="element.boundary" @change="update">
<select v-model="element.boundary" @change="update" @focus="focus">
<option v-for="(value,key) in T" v-bind:value="key">{{ key | toUpperCase }}</option>
<option v-for="(value,key) in rooms" v-bind:value="key">{{ key | toUpperCase }}</option>
</select>
</td>
<td>
<select v-model="element.subtractfrom" @change="update">
<select v-model="element.subtractfrom" @change="update" @focus="focus">
<option></option>
<option v-for="(value,key) in room.elements">{{ key }}</option>
</select>
</td>
<td><input type="text" v-model="element.orientation" @change="update" /></td>
<td><input type="text" v-model.number="element.width" @change="update" /><span class="unit">m</span></td>
<td><input type="text" v-model.number="element.height" @change="update" /><span class="unit">m</span></td>
<td><input type="text" v-model="element.orientation" @change="update" @focus="focus"/></td>
<td><input type="text" v-model.number="element.width" @change="update" @focus="focus"/><span class="unit">m</span></td>
<td><input type="text" v-model.number="element.height" @change="update" @focus="focus"/><span class="unit">m</span></td>
<td><input type="text" v-bind:value="element.A | toFixed(1)" /><span class="unit">m2</span></td>
<td>
<div class="value">{{ element.uvalue }}</div>
Expand Down Expand Up @@ -180,11 +211,11 @@
<table class="room-radiators-table">
<tr v-for="(radiator,radiatorIndex) in room.radiators">
<td>
<input type="text" v-model="radiator.name" @change="update" style="width:400px">
<input type="text" v-model="radiator.name" @change="update" @focus="focus" style="width:400px">
</td>
<td>
<div v-if="radiator.model=='pow'">
<input type="text" v-model.number="radiator.heat50k" @change="update"style="width:50px">
<input type="text" v-model.number="radiator.heat50k" @change="update" @focus="focus" style="width:50px">
<span class="unit">W @ 50k</span>
</div>
<div v-else>
Expand All @@ -210,12 +241,12 @@
</td>
<td>
Area:
<input type="text" v-model.number="ufh.area" @change="update" style="width:50px">
<input type="text" v-model.number="ufh.area" @change="update" @focus="focus" style="width:50px">
<span class="unit">m2</span>
</td>
<td>
Floor covering:
<input type="text" v-model.number="ufh.flooring" @change="update" style="width:50px">
<input type="text" v-model.number="ufh.flooring" @change="update" @focus="focus" style="width:50px">
<span class="unit">W/m2.K</span>
</td>
<td style="width: 120px;">
Expand Down
3 changes: 2 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ <h2>heatloss.js example</h2>
</div>
</div>
</body>
<script type="text/javascript" src="heatlossjs/model.js?v=3"></script>
<script type="text/javascript" src="heatlossjs/ecodan.js?v=3"></script>
<script type="text/javascript" src="heatlossjs/model.js?v=4"></script>

0 comments on commit 83bd24e

Please sign in to comment.