From 7c52deca7e2d383aab666822e6eacb9d8e16962b Mon Sep 17 00:00:00 2001 From: louispt1 Date: Fri, 30 Aug 2024 14:10:11 +0200 Subject: [PATCH] New adapter types must run import/export for must-run-interconnectors --- .../api/v3/scenarios_controller.rb | 27 +++++++++--------- app/models/curve_handler/config.rb | 4 +-- .../qernel/merit_facade/always_on_adapter.rb | 2 +- .../qernel/merit_facade/consumer_adapter.rb | 2 ++ .../merit_facade/export_consumer_adapter.rb | 28 +++++++++++++++++++ .../merit_facade/import_always_on_adapter.rb | 20 +++++++++++++ .../qernel/merit_facade/producer_adapter.rb | 2 ++ app/models/scenario/user_updates.rb | 4 +-- app/views/inspect/scenarios/_form.html.haml | 2 +- 9 files changed, 71 insertions(+), 20 deletions(-) create mode 100644 app/models/qernel/merit_facade/export_consumer_adapter.rb create mode 100644 app/models/qernel/merit_facade/import_always_on_adapter.rb diff --git a/app/controllers/api/v3/scenarios_controller.rb b/app/controllers/api/v3/scenarios_controller.rb index 817db7add..39ff1aa28 100644 --- a/app/controllers/api/v3/scenarios_controller.rb +++ b/app/controllers/api/v3/scenarios_controller.rb @@ -103,7 +103,7 @@ def dashboard end if serializer.errors.any? - render json: { errors: serializer.errors }, status: 422 + render json: { errors: serializer.errors }, status: :unprocessable_entity else render json: serializer end @@ -188,7 +188,7 @@ def create render json: ScenarioSerializer.new(self, @scenario) rescue ActiveRecord::RecordInvalid - render json: { errors: @scenario.errors }, status: 422 + render json: { errors: @scenario.errors }, status: :unprocessable_entity end # POST /api/v3/scenarios/interpolate @@ -267,7 +267,7 @@ def update end if serializer.errors.any? - render json: { errors: serializer.errors }, status: 422 + render json: { errors: serializer.errors }, status: :unprocessable_entity else render json: serializer end @@ -288,7 +288,7 @@ def destroy def merge authorize!(:create, Scenario) - merge_params = params.permit(scenarios: [:scenario_id, :weight]) + merge_params = params.permit(scenarios: %i[scenario_id weight]) merge_params[:scenarios].each do |scenario| authorize!(:read, Scenario.find(scenario[:scenario_id])) @@ -301,10 +301,10 @@ def merge # redirect_to api_v3_scenario_url(scenario) render json: ScenarioSerializer.new(self, scenario) else - render json: { errors: merger.errors }, status: 422 + render json: { errors: merger.errors }, status: :unprocessable_entity end - rescue ScenarioMerger::Error => ex - render json: { errors: { base: [ex.message] } }, status: 400 + rescue ScenarioMerger::Error => e + render json: { errors: { base: [e.message] } }, status: :bad_request end # POST /api/v3/scenarios/:id/couple @@ -332,7 +332,6 @@ def uncouple # def couple coupling_parameters[:groups].each { |coupling| @scenario.activate_coupling(coupling) } - if @scenario.save render json: ScenarioSerializer.new(self, @scenario) else @@ -423,12 +422,12 @@ def filtered_metadata(scenario) # # Returns a hash. def scaler_attributes - if params[:scenario] && params[:scenario][:scale] - params[:scenario].require(:scale).permit( - :area_attribute, :value, - :has_agriculture, :has_energy, :has_industry - ) - end + return unless params[:scenario] && params[:scenario][:scale] + + params[:scenario].require(:scale).permit( + :area_attribute, :value, + :has_agriculture, :has_energy, :has_industry + ) end # Internal: Parameters for coupling and uncoupling diff --git a/app/models/curve_handler/config.rb b/app/models/curve_handler/config.rb index e99f2928f..dd1883e2d 100644 --- a/app/models/curve_handler/config.rb +++ b/app/models/curve_handler/config.rb @@ -87,7 +87,7 @@ def initialize( @key = key @processor_key = processor_key.to_sym @reducer_key = reducer_key&.to_sym - @input_keys = reducer_key && input_keys || [] + @input_keys = (reducer_key && input_keys) || [] @display_group = display_group&.to_sym @internal = !!internal @disables = disables @@ -152,7 +152,7 @@ def reducer # Public: Returns whether the processor should set any input values for the scenario. def sets_inputs? - @reducer_key && @input_keys.any? || false + (@reducer_key && @input_keys.any?) || false end # Public: Returns if the curve is for internal use and experienced users only. diff --git a/app/models/qernel/merit_facade/always_on_adapter.rb b/app/models/qernel/merit_facade/always_on_adapter.rb index c76ce6bd1..74f65287f 100644 --- a/app/models/qernel/merit_facade/always_on_adapter.rb +++ b/app/models/qernel/merit_facade/always_on_adapter.rb @@ -25,7 +25,7 @@ def producer_class case @config.subtype when :volatile then Merit::VolatileProducer - when :must_run then Merit::MustRunProducer + when :must_run, :import_must_run then Merit::MustRunProducer else raise "Unknown producer class for node '#{@node.key}'" end diff --git a/app/models/qernel/merit_facade/consumer_adapter.rb b/app/models/qernel/merit_facade/consumer_adapter.rb index 4555c91ef..e6637e217 100644 --- a/app/models/qernel/merit_facade/consumer_adapter.rb +++ b/app/models/qernel/merit_facade/consumer_adapter.rb @@ -8,6 +8,8 @@ def self.factory(node, context) case context.node_config(node).subtype when :pseudo PseudoConsumerAdapter + when :export_must_run + ExportConsumerAdapter when :consumption_loss ConsumptionLossAdapter when :electricity_loss diff --git a/app/models/qernel/merit_facade/export_consumer_adapter.rb b/app/models/qernel/merit_facade/export_consumer_adapter.rb new file mode 100644 index 000000000..4d5ec0f1b --- /dev/null +++ b/app/models/qernel/merit_facade/export_consumer_adapter.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module Qernel + module MeritFacade + # This class adapts an export consumer to act as must run. It uses the same methods for flexible edges as the export adapter. + class ExportConsumerAdapter < ConsumerAdapter + def inject! + input_edge = target_api.node.input(@context.carrier).edges.first + demand = participant.production(:mj) + + target_api.demand = demand + + if input_edge.edge_type == :inversed_flexible + # We need to override the calculation of an inversed flexible edge. + # and set the demand explicitly. + input_edge.dataset_set(:value, demand) + input_edge.dataset_set(:calculated, true) + end + + super + end + + def input_of_carrier + source_api.full_load_hours * source_api.input_capacity * MJ_TO_MHW + end + end + end +end diff --git a/app/models/qernel/merit_facade/import_always_on_adapter.rb b/app/models/qernel/merit_facade/import_always_on_adapter.rb new file mode 100644 index 000000000..1e68ccc2d --- /dev/null +++ b/app/models/qernel/merit_facade/import_always_on_adapter.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Qernel + module MeritFacade + # This class adapts an import producer to act as must run. It uses the same methods for flexible edges as the import adapter. + class ImportAlwaysOnAdapter < AlwaysOnAdapter + def inject! + super + elec_edge = target_api.node.output(:electricity).edges.first + + return unless elec_edge.edge_type == :flexible + + # We need to override the calculation of the flexible edge and set the + # demand explicitly. + elec_edge.dataset_set(:value, target_api.demand) + elec_edge.dataset_set(:calculated, true) + end + end + end +end diff --git a/app/models/qernel/merit_facade/producer_adapter.rb b/app/models/qernel/merit_facade/producer_adapter.rb index c41ce76a0..a5c3a6734 100644 --- a/app/models/qernel/merit_facade/producer_adapter.rb +++ b/app/models/qernel/merit_facade/producer_adapter.rb @@ -17,6 +17,8 @@ def self.factory(node, context) ElectrolyserAdapter when :import ImportAdapter + when :import_must_run + ImportAlwaysOnAdapter when :backup BackupAdapter when :always_on_battery_park diff --git a/app/models/scenario/user_updates.rb b/app/models/scenario/user_updates.rb index 297520fa3..6021341fc 100644 --- a/app/models/scenario/user_updates.rb +++ b/app/models/scenario/user_updates.rb @@ -22,10 +22,10 @@ def inputs # def update_inputs_for_api(params) params.each_pair do |input_id, value| - if input = Input.get(input_id) + if (input = Input.get(input_id)) if value == 'reset' delete_from_user_values(input.key) - elsif typed_value = value.to_f + elsif (typed_value = value.to_f) update_input(input, typed_value) end else diff --git a/app/views/inspect/scenarios/_form.html.haml b/app/views/inspect/scenarios/_form.html.haml index b91aa9ccc..436f3c382 100644 --- a/app/views/inspect/scenarios/_form.html.haml +++ b/app/views/inspect/scenarios/_form.html.haml @@ -21,7 +21,7 @@ The scenario will be migrated when the model is updated, regardless of how long hash passed since it was last updated by a user. - = f.input :active_couplings, label: 'Active couplings', as: :text, input_html: { value: @scenario.active_couplings.join("\n") } + = f.input :active_couplings, label: 'Active couplings', as: :text, input_html: { value: @scenario.active_couplings.join("\n"), data: { codemirror: 'yaml' } } %dl.hint{ style: 'margin: 0 0 10px' } %dt Currently inactive: %dd