Improve efficiency: Was it worth it?

That page of their manual is my bible. It’s what I’m using to draw the “nominal” parts of the efficiency graph.

There’s no time like the present…

As you can see, it’s trundling along happily. It’s using 1.6kW though which is more than we’d typically be using. Here’s a recent comparison from a slightly colder day where the average is just under 1kW.

I’ve never seen it running stably like this for a long period of time, so now I’m seeing a thing happen that you all talk about that we should expect. The outdoor temperature went from 7 to 8 Celsius at 10:48. About 3 minutes later the return temperature also went up. That might be because the house is warming after an hour running the pump or it might be correlated with the external change. However the same thing just happened when outdoors went from 8 to 9…

So I think the changing external temperature is going to knock it out of steady-state because it’s getting above the target flow. I’m going to keep watching to see.

Of course if I raise the target flow to match the increased outside temp then the radiators will get even hotter and the house will get hotter and at some point it’s just going to be too much. Of course my efficiency will also fall - although it’s current 381% is pretty impressive. At the moment it’s modulated down to it’s minimum of 26Wh per minute (1.56kW) so we’re stuck with that well known problem of a device that’s rated for cold weather struggling to modulate down when it’s warmer. It’s a bit sad that the target cold temp is -2 and the current temp is 8 and it is struggling to manage comfortably across even that small range.

Here’s the buckets of power usage since 2019-10-01:

So it can go down to 1.08kW but it doesn’t seem to want to.

Clearly “off” is it’s most common state by a long way.

1 Like

As expected we’ve started cycling.

I raised the flow temp to 37 and it’s modulated down so it’s using 1.6kW. The flow-return delta is 3 Celsius, that’s quite common here because the house is warm.

As we can see, the flow is still edging up above the target. I’m nudging the flow up to 39.

Unfortunately the sun is breaking through now and the outside temperature is rising. The heat pump just can’t cope running constantly when it’s this warm. Arguably it shouldn’t be needed, but of course our old house is leaking heat through the walls. So we need some heat, but not as much as we get from the pump running all the time.

Bumping it up does seem to have stopped it cycling, but the house is certainly getting too warm in a number of places now. I’m going to have to bring the experiment to an end.

2 Likes


for your curiosity here my IR of my house it not as define as yours – the temp spread is from -5.0c the background temp and the hottest spot on the window at -2.0. on my worse window ( needs replacing) i but it is at the highest contrast. you notice my house irradiates as much heat as the the trees in the background from the the walls most of my heat loss it through the windows… interior wise I loose the most on the floor and infiltration/ventilation for tight house…

but also wanted to mention if you insulated from the inside- considering how you are heating your house as demand heating - those rooms would warm up faster.

2 Likes

Nice experiment @MyForest its got quite warm today hasnt it! Looks like your basically getting the COP and expected result from the datasheet, great stuff! Yes its a real shame they dont modulate down further… i guess that’s where buffer tanks and your forced longer period stop/start cycles come in…

1 Like

Wow @stephen that’s really good. You must be lovely and snug in there.

Your IR tool does a nice job of overlaying the photo into the thermal image.

Just for lolz…

Clearly it makes no sense to be heating when it’s this warm.

1 Like

The simple integrating controller in my non-modulating Thermia ground/underfloor heatpump works well. The greater the demand, the longer the compressor stays on.

In process control language it’s a cut-down PID controller (Wikipedia). Here’s a real-life example:

  1. Choose a target for the circulating water. Right now the outside temperature is 3 degrees C and the target is 26 degrees.

  2. Calculations

  • The integral is an accumulator which starts at 0 at switch-on.

  • Once a minute, measure the difference between the flow pipe to the underfloor and the set point. (Not the return because that varies less.) This difference is the “error” in process control.

  • If the flow temperature is X degrees below the set point, subtract X from the accumulator.

  • Or if the flow is X degrees above the set point, add X to the accumulator.

  1. Decisions, also once per minute
  • If the accumulator is -60 or below, turn on the compressor.
  • If the accumulator is between -60 and 0, leave the compressor in its current state (on or off).
  • If the accumulator is 0 or greater, turn off the compressor.
  • If the error is greater than 10 (i.e. too hot) turn off the compressor.

The thresholds at 0 and -60 provide hysterisis, and are adjustable.
The relationship between outside temperature and set point is also adjustable - typically a drop of 1 degree in outside temperature should increase the set point by 2 or 3.

Might be some ideas for you heat pump owners there.

2 Likes

Hi @Steve

Your algorithm is now running my house. Here’s how it’s performing.

The outdoor temperature is relatively stable at the moment, so for simplicity I’ll just show the actual set points in Celsius (outdoor temp → set point):

{“3”: 26, “4”: 28, “5”: 30, “6”: 32, “7”: 34, “8”: 36, “9”: 38}

I’m using the same constants in my implementation as you list above.

It got off to a rocky start because I was writing the code as it started running. It also caused a defrost which made the accumulator plummet.

You can see it’s starting to get more gentle now. The house is sensibly warm.

At about 17:15 the accumulator is rising, but the efficiency has dropped. The gold line is the nominal efficiency. At that point my control algorithm would have turned the heat pump off and let it all cool down.

I’ll add more updates if anything interesting happens.

As you can see from the energy graph at the top, this is a modulating heat pump and it had modulated down as far as it could.

1 Like

Bother, it’s gone out of control.

It’s 9 Celsius outside so the setpoint is 38.

However, the heat pump I have includes a “target flow temp” which I’ve constrained to 34 when it’s 9 Celsius outside.

So now the flow isn’t getting to the set point and the accumulator is diving down.

Lesson: The set point must always be less than the max flow temp.

I’ll intervene.

That’s fast coding, David! It helps that you knew your system well beforehand.

Suggest to experiment with different values for the -60 threshold, to tune it to the thermal inertia of your system. No doubt you already worked out that a smaller negative number means too much cycling, while a bigger number means more temperature overshoot/undershoot.

Thanks for the kind words @Steve. I’m sorta cheating because I’ve set myself up for experimentation with this Emoncms app and the way “actions” can contribute to my control algorithm.

My system isn’t blending well with the algorithm because it has a “target flow temp” and the accumulator algorithm is expecting the flow to just keep rising so would trigger the “diff>10” rule.

Within the gentle bouncing behaviour, I’m seeing it behave pretty reasonably. As you’ll see in the graph below, I obviously intervened around 19:30.

Here’s the whole experiment:

So there’s a few interesting things going on.

The defrost at 15:00 was handled pretty reasonably.

The shutdown at 23:07 was because the flow was set to a low temp. It’s a common problem when it’s warm (say 10 Celsius outside) because the flow required to dump all the heat would make everyone too warm. The accumulator had a gentle pause and then resumed. My control algorithm would have shut it down (for about the next hour or more). Notably the efficiency dropped off during that lull. However, the CoP of 3.81 during that time is pretty nice. As before, the gold line on the efficiency graph is what the vendor puts in their manual.

This couple of hours in the graph below is interesting because it seems to be behaving really nicely. I think that’s how we all imagine it behaving.

I had a similar couple of hours yesterday (in the graph below) and now I’m torn. We can see the accumulator was more efficient in CoP terms, but it used 2kWh rather than the 1.2kWh I used yesterday. I’m always striving for better efficiency, but I’m finding the long cycles that give good CoPs are actually consuming more energy so I’m experimenting with turning it off a bit more.

I left it unattended this evening which is a bit unfair. As you say, the thresholds need tweaking to make it run more smoothly. We can see how the “target” flow temp kept falling as the outdoor temp got warmer (yes, in the dark!)

I thought I’d share this next picture as an example of where tweaking is required because we can see around midnight it starts to cycle because the max flow temp is down at 33 Celsius. In that scenario the “set point” I coded was 31 so there’s not much change per cycle and we can see it ran for over an hour and was fairly unhappy. In that last run the CoP was 2.6.

Eventually it got to 11 Celsius outside and I hadn’t coded flow temps / set points that far so it stumbled and stopped doing anything. The heat loss from our house at that point is (finally) very low so arguably it could just shut itself off there. Unfortunately I know the others were all sat under blankets at that point to keep warm :slight_smile:

Here’s another example of it behaving really well. It would make a good pairing with a buffer thank like @FredM67 has:

Part of what I’m doing with the experiments is playing old data through new algorithms. I can pick any point since the heat pump was installed and ask a new algorithm to tell me what it would do at that point in time. Unfortunately, without being able to actually act back then, it can’t change the state to implement it’s behaviour to cause a long-run simulation. I haven’t figured out how to do that yet. Of course I could simulate the device which John and Trystan did, but that would be the theoretical behaviour, not what would happen in my real setup. So at the moment I’m not sure how to do robust evaluations of novel control algorithms. One way is to just let them run, but that only gives you a small data set on a limited set of scenarios as we saw with our accumulator run today.

The other thing that’s important to me (and not everyone of course) is that I’m looking for the system to be robust. When there’s a squally shower that drops the temp by 5 degrees I want it to handle that. When there’s a power cut I want it to jump back into action and do the right thing as soon as it can. This is hard to balance with squeezing the ultimate performance from the system. I can only imagine this is going to get worse when I try to integrate with solar, an agile tarif and a car / battery using the demand shaper.

Running the experiment has given me some interesting ideas to try out.

Firstly I think I should see if I can go for lower flow temperatures and potentially longer runs. That’s a similar outcome from when I experimented using Trystan’s algorithm.

Secondly, I’m pondering how the sporadic hot water demand will affect this. I’ve been “resetting” the flow by pushing the hot water round the radiators and I think that would work with the accumulator, you’d just need to pause it when heating hot water.

Is your system doing space heating and hot water @Steve?

I should also add a big “thank you” for sharing your algorithm so concisely. It made re-implementing it trivial.

It’s nice that Emoncms made it easy for me to add something like the accumulator value to the graphs. That would have been a chore in the vendor app / site - it would have inevitably ended up with spreadsheets and lots of copy-n-pasting which makes me sad.

Just for posterity, there’s the implementation.

I tried to mimic your English description as much as possible so it would tally when people were comparing it.

I could go on endlessly about the things I normally do differently but that doesn’t matter. I should mention that this does run in a Docker container that fires up each minute so it uses a file to store the accumulator state.

Oh, the other thing that’s probably worth pointing out is that I turn off the pumps when we get to the “off” states so that might be different to how other people have it.

(posting as an image else it’ll be a huge wall of text in the thread - actually it turned out OK, see the next post)

import sys
import datetime
import logging
import os

from typing import Generator
from .action import Action

from .temperature_thresholds import TemperatureThresholds
from .target_water_temperature import TargetWaterTemperature

from .device_infos import DeviceInfo, DeviceInfos

# --------------------------------------------------------------------------------
class Accumulator:
    @staticmethod
    def accumulate(calculationMoment: datetime.datetime, deviceInfos: DeviceInfos) -> Generator[Action, None, None]:

        currentAccumulator = Accumulator.readAccumulator()

        deviceInfo = deviceInfos[-1]

        setPoint = Accumulator.getSetPoint(deviceInfo)

        currentHeatingFlow = deviceInfo["FlowTemperature"]

        difference = abs(setPoint - currentHeatingFlow)

        if setPoint > currentHeatingFlow:
            newAccumulator = currentAccumulator - difference
        else:
            newAccumulator = currentAccumulator + difference

        if newAccumulator != currentAccumulator:
            Accumulator.writeAccumulator(newAccumulator)

        logging.debug(
            "The flow is "
            + str(currentHeatingFlow)
            + " and the set point is "
            + str(setPoint)
            + ". The accumulator is was "
            + str(currentAccumulator)
            + " and is now "
            + str(newAccumulator)
        )

        if newAccumulator <= -60:
            targetTemperature = Accumulator.getTargetFlowTemp(deviceInfo)
            if targetTemperature != deviceInfo["TargetHCTemperatureZone1"]:
                yield Action(
                    "SetHeatFlowTemperatureZone1",
                    targetTemperature,
                    "Setting target flow temperature to " + str(targetTemperature) + " °C. The flow is currently " + str(deviceInfo["FlowTemperature"]) + " °C",
                )
            if not deviceInfo["Power"]:
                logging.info("Turning on because accumulator is now " + str(newAccumulator))
                yield Action(
                    "Power", "true", "Accumulator is " + str(newAccumulator),
                )

        if newAccumulator >= 0:
            if deviceInfo["Power"]:
                logging.info("Turning off because accumulator is now " + str(newAccumulator))
                yield Action(
                    "Power", "false", "Accumulator is " + str(newAccumulator),
                )

        if difference > 10 and currentHeatingFlow > setPoint:
            if deviceInfo["Power"]:
                logging.info("Turning off because difference is " + str(difference))
                yield Action(
                    "Power", "false", "Flow temp difference is " + str(difference),
                )

    @staticmethod
    def getSetPoint(deviceInfo) -> int:
        outdoors = float(deviceInfo["OutdoorTemperature"])
        outdoors = int(outdoors + 0.5)

        return {"3": 26, "4": 26, "5": 27, "6": 28, "7": 29, "8": 29, "9": 30, "10": 31,}[str(outdoors)]

    @staticmethod
    def getTargetFlowTemp(deviceInfo) -> int:
        outdoors = float(deviceInfo["OutdoorTemperature"])
        outdoors = int(outdoors + 0.5)

        return {"3": 40, "4": 39, "5": 38, "6": 37, "7": 36, "8": 35, "9": 34, "10": 33,}[str(outdoors)]

    @staticmethod
    def accumulatorFilePath() -> str:
        return "../accumulator.txt"

    @staticmethod
    def writeAccumulator(newValue: float) -> None:
        logging.debug("Setting accumulator to " + str(newValue))
        with open(Accumulator.accumulatorFilePath(), "a+t",) as accumulator:
            accumulator.write(datetime.datetime.utcnow().isoformat() + "\t" + str(newValue) + "\n")

    @staticmethod
    def readAccumulator() -> float:
        if os.path.exists(Accumulator.accumulatorFilePath()):
            with open(Accumulator.accumulatorFilePath(), "r+t") as accumulator:
                return float(accumulator.readlines()[-1].strip().split("\t")[-1])
        return 0
1 Like

Yes my Thermia system does hot water too, although not often – 2 or 3 times a day for about 10 minutes – since we have low usage.

During that time the algorithm loses sight of the flow temperature so the designers used a “carry on as before” strategy: each minute they re-use the last error reading from the minute before the start of the HW cycle. Usually this means the accumulator drifts more negative, giving the desired “catch up” effect after the HW is done.

1 Like

Thanks for showing the code. Is the mapping array in getSetPoint the right way round? Would expect the set point to fall as the outdoor temp rises.

You’re quite right.

I was too focussed on the set point relationship to the flow temp, but it’s actually a proxy for the heat demand figure for the house.

What you are looking for is for the set point to cause the accumulator to drop more often when it’s cold outside so you need a higher set point when it’s cold to give a larger difference and more action.

I’m now using this:

28 - (2 * outdoors / 3)

which hits the 26C at 3C outside point.

-10 34.7
-9 34.0
-8 33.3
-7 32.7
-6 32.0
-5 31.3
-4 30.7
-3 30.0
-2 29.3
-1 28.7
0 28.0
1 27.3
2 26.7
3 26.0
4 25.3
5 24.7
6 24.0
7 23.3
8 22.7
9 22.0
10 21.3
11 20.7
12 20.0
13 19.3
14 18.7
15 18.0
16 17.3
17 16.7
18 16.0
19 15.3

Unfortunately it’s racing away positive because the flow is 27 at the moment, it’s 8 outside => set point of 22.7 so the “difference” is +4. The system is off and the flow will fall to 22.7 in about 2 hours and then the accumulator will start to turn negative. However, it’ll then take 4-6 hours to pull it back down because it’ll be at something like +400.

I would expect the system to come back on within an hour or the residents are going to start shouting at me.

To do that I need it to go from 0 to -60 in an hour so I need the set point to be a smidge higher than the flow. If I make the set point at 8 C to be 28 that would do the job. So I’ll adjust the curve to be:

setPoint = 34 - (2 * outdoors / 3)

which gives these set points:

-10 40.7
-9 40.0
-8 39.3
-7 38.7
-6 38.0
-5 37.3
-4 36.7
-3 36.0
-2 35.3
-1 34.7
0 34.0
1 33.3
2 32.7
3 32.0
4 31.3
5 30.7
6 30.0
7 29.3
8 28.7
9 28.0
10 27.3
11 26.7
12 26.0
13 25.3
14 24.7
15 24.0
16 23.3
17 22.7
18 22.0
19 21.3

On my first cycle it now says:

The flow is 25.5 and the set point is 28.7. The accumulator was 0 and is now -2

So we’re in the right sort of behaviour.

Interesting. I was going to just put a pause on the accumulator at that point. I’ll ponder the consequences.

I’m getting a “catch up” at the end of my hot water run because I take the really hot water in the pipes and pump it round the radiators. It’s only a couple of minutes, but it seems to be working (well, so far in the Winter anyway!)

With your algorithm it gives you a good behaviour even if the hot water runs for a longer time for some reason then the accumulator will keep driving down and trigger the space heating soon after. By using the “difference” from before the hot water came on they are essentially extrapolating the behaviour over the time of the hot water production so it doesn’t get confused by the exciting flow temp. It’s a good strategy which is working nicely because the changes that would disrupt the extrapolation, such as outdoor temp changes, won’t be very large over a 10 or even 30 minute period.

Good, now your mapping is heading in the right direction.

You are right that there are two parameters: firstly the slope, currently 0.67 flow degrees per outside degree. Depends on the characteristics of the system and the house.

Secondly the offset, currently 34. Altering this is the same as turning a roomstat up or down.

Oh dear, still no joy. There were comments from the residents. The sun went away and storm Jorge arrived so everyone felt cold. I had to intervene at 16:35 to bring it back to life.

Clearly I’m going to need to make it oscillate more aggressively.

I think I’ll have to try some virtual experiments before I do any more practical ones that upset people :slight_smile:

It might be a while before I get back to trying that I’m afraid.

Amusingly I did turn it on manually using the vendor application but the accumulator algorithm turned it back off again on the next cycle. It’s a good example of the need to allow multiple agents to control things for comfort.

I tried is again a couple of days ago @TrystanLea - still no dice. The Ecodan started cycling.

As you say, it also got too warm!