diff --git a/Gemfile b/Gemfile index 300078bf8..2b26aacee 100644 --- a/Gemfile +++ b/Gemfile @@ -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' diff --git a/Gemfile.lock b/Gemfile.lock index 57e193fd3..cbb3c221f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -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) diff --git a/app/models/qernel/merit_facade/flex_adapter.rb b/app/models/qernel/merit_facade/flex_adapter.rb index cb45d5462..af7ea4856 100644 --- a/app/models/qernel/merit_facade/flex_adapter.rb +++ b/app/models/qernel/merit_facade/flex_adapter.rb @@ -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 diff --git a/app/models/qernel/merit_facade/temperature_based_power_to_heat_adapter.rb b/app/models/qernel/merit_facade/temperature_based_power_to_heat_adapter.rb new file mode 100644 index 000000000..bc3192d90 --- /dev/null +++ b/app/models/qernel/merit_facade/temperature_based_power_to_heat_adapter.rb @@ -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 diff --git a/db/migrate/20251027143649_temperature_setpoint_p2h.rb b/db/migrate/20251027143649_temperature_setpoint_p2h.rb new file mode 100644 index 000000000..2a92ae92f --- /dev/null +++ b/db/migrate/20251027143649_temperature_setpoint_p2h.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index dd696ff0c..345c95681 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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