Skip to content
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ gem 'ruby-progressbar'

# own gems
gem 'quintel_merit', ref: '54d2be1', github: 'quintel/merit'
gem 'atlas', ref: 'd8b096b', github: 'quintel/atlas'
gem 'atlas', ref: '34f6d2b', github: 'quintel/atlas'
gem 'fever', ref: '2a91194', github: 'quintel/fever'
gem 'refinery', ref: 'c39c9b1', github: 'quintel/refinery'
gem 'rubel', ref: 'e36554a', github: 'quintel/rubel'
Expand Down
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
GIT
remote: https://github.com/quintel/atlas.git
revision: d8b096b4ee9d94e81ff3e8e5bbbd753e72e0e32a
ref: d8b096b
revision: 34f6d2b877b91edf636f449eb1396f6329d63614
ref: 34f6d2b
specs:
atlas (1.0.0)
activemodel (>= 7)
Expand Down
2 changes: 2 additions & 0 deletions app/models/qernel/merit_facade/flex_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ def self.factory(node, context)
PowerToHeatAdapter
when :power_to_heat
HouseholdPowerToHeatAdapter
when :temperature_based_p2h
TemperatureBasedPowerToHeatAdapter
when :curtailment
CurtailmentAdapter
when :heat_storage
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

module Qernel
module MeritFacade
# Implements behaviour specific to the power2heat (pumps & boilers) that
# are dependent on outside temperature.
#
# The participant will be fully unavailable when the outside temperature
# is above the temperature_cutoff point (in degrees Celsius). And
# will have an availability of 1.0, when below this point.
#
# Users can also set their own availabilty curves on the node to overwrite
# this behaviour.
class TemperatureBasedPowerToHeatAdapter < FlexAdapter
def producer_attributes
attrs = super

attrs[:availability] = Merit::Curve.new(
@context.curves.rotate(availability_curve)
)

attrs
end

def inject!
super

target_api.availability = availability_curve.sum / availability_curve.length
end

private

def producer_class
Merit::Flex::VariableConsumer
end

# Internal: the availability curve can be directly set by the user,
# or it can be calculated based on the outside temperature and
# the temperature cutoff
def availability_curve
if availability_curve_from_node?
availability_curve_from_node
else
availability_curve_based_on_temperature
end
end

# Internal: the p2h pump should be fully unavailable when temperature is
# above the cutoff point, and fully available when it's below.
def availability_curve_based_on_temperature
temperature_curve.map do |temp|
temp > @context.node_config(node).temperature_cutoff ? 0.0 : 1.0
end
end

# Internal: The curve of air temperatures in the region.
def temperature_curve
@context.curves.derotate(
@context.curves.curve('weather/air_temperature', @node)
)
end

def availability_curve_from_node?
availability_curve_from_node&.any?
end

# Internal: availibity curve set on the node (user uploaded)
def availability_curve_from_node
source_api.availability_curve
end
end
end
end
14 changes: 14 additions & 0 deletions db/migrate/20251027143649_temperature_setpoint_p2h.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'etengine/scenario_migration'

class TemperatureSetpointP2h < ActiveRecord::Migration[7.1]
include ETEngine::ScenarioMigration

def up
migrate_scenarios do |scenario|
if scenario.user_values['capacity_of_energy_heat_flexibility_p2h_boiler_electricity']&.positive? ||
scenario.user_values['capacity_of_energy_heat_flexibility_p2h_heatpump_electricity']&.positive?
scenario.user_values['temperature_cutoff_of_energy_flexibility_p2h'] = 30.0
end
end
end
end
2 changes: 1 addition & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2025_10_08_073517) do
ActiveRecord::Schema[7.1].define(version: 2025_10_27_143649) do
create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.string "name", limit: 191, null: false
t.string "record_type", limit: 191, null: false
Expand Down