Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for several issues with new heat modelling in built environment #1404

Merged
merged 13 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions app/models/gql/runtime/functions/update.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def update_something_by(*value_terms)
scope.update_object = object # for UPDATE_OBJECT()
input_value = input_value_proc.respond_to?(:call) ? input_value_proc.call : input_value_proc
input_value = input_value.first if input_value.is_a?(::Array)
original_value = object[attribute_name]
original_value = object[attribute_name.to_sym]

if original_value.is_a?(Numeric)
original_value = original_value.to_f
Expand All @@ -145,7 +145,11 @@ def update_something_by(*value_terms)

# @private
def update_element_with(object, attribute_name, value)
object.send "#{attribute_name}=", value
if object.is_a?(Hash) && object.key?(attribute_name.to_sym)
object[attribute_name.to_sym] = value
else
object.send("#{attribute_name}=", value)
end
end

# Private: at the moment only takes care of percentages and absolute numbers.
Expand Down
3 changes: 1 addition & 2 deletions app/models/qernel/fever_facade/calculators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ def initialize(ordered_producers, ordered_consumers, context)
@ordered_consumers = ordered_consumers
@ordered_producers = ordered_producers

# first build then refactor
# make sure we can forget what we don't need
setup
end

Expand Down Expand Up @@ -57,6 +55,7 @@ def setup
producer_share -= consumer_share

consumer.build_activity(producer, (consumer_share / consumer_share_in_total))
consumer.inject_share_to_producer(producer, (consumer_share / consumer_share_in_total))
end
end
end
Expand Down
11 changes: 5 additions & 6 deletions app/models/qernel/fever_facade/consumer_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@ class ConsumerAdapter < Adapter
def initialize(node, context)
super
@was = @node.demand
# TODO: at the init we should set all edges shares to 0!!!
# Then set them again -rigth? or is it not neccesary? how does future graph work? they are unset right?
# Set all edges to 0.0 to ensure that all edges set in the setup phase sum to 1.0
# after calculating the consumer producer pairs
@node.input(:useable_heat).edges.each { |e| e.share = 0.0 }
end

# How much has the consumer already been filled with
# How much has the consumer already been filled with (could also be sum of input_edges)
def share_met
@share_met ||= 0.0
# Of kan dat toch vanuit de summed edges zoals we in de vorige commit hadden?
end

# Injects share after to calculation
Expand All @@ -42,8 +42,7 @@ def participant_for(tech_type, share)
end

def inject!
# TODO: this feels very very nasty, but I think we have to! Otherwise the
# graph wil try to solve it himself based on something unknown
# Inject shares again
@node.input(:useable_heat).edges.each { |e| e.share = 0.0 }

calculable_activities.each_value do |calc_activity|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def demand_curve_from_activities
def production_curve_from_activities
Merit::CurveTools.add_curves(
calculable_activities.filter_map do |_, activity|
activity.activities.sum(&:production_curve) unless activity.empty?
unless activity.empty?
Merit::CurveTools.add_curves(activity.activities.map(&:production_curve))
end
end
) || EMPTY_CURVE
end
Expand Down
23 changes: 16 additions & 7 deletions app/models/qernel/fever_facade/group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module FeverFacade
# Fever instance.
class Group
attr_reader :name
attr_reader :calculators

# Public: Creates a new Group.
#
Expand All @@ -17,13 +18,8 @@ class Group
def initialize(name, plugin)
@name = name
@context = Context.new(plugin, plugin.graph)
end

# Public: Sets up calculators where consumers and producers are matched
def calculators
@calculators ||= Qernel::FeverFacade::Calculators.new(
ordered_producers, ordered_consumers, @context
)
setup_calculators
end

# Public: Instructs the calculator to compute a single frame.
Expand Down Expand Up @@ -62,7 +58,20 @@ def ordered_consumers
end

def producer_adapters
adapters.select { |adapter| adapter.config.type == :producer }
adapters.select { |adapter| adapter.is_a?(Qernel::FeverFacade::ProducerAdapter) }
end

def consumer_adapters
adapters.select { |adapter| adapter.is_a?(Qernel::FeverFacade::ConsumerAdapter) }
end

private

# Private: Sets up calculators where consumers and producers are matched
def setup_calculators
@calculators = Qernel::FeverFacade::Calculators.new(
ordered_producers, ordered_consumers, @context
)
end
end
end
Expand Down
5 changes: 4 additions & 1 deletion app/models/qernel/fever_facade/producer_adapter/inject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ module Inject
def inject_demand!
production = producer.output_curve.sum

# TODO: get aggerate curve etc.
inject_aggregator_attributes!(production)

# MWh -> MJ
Expand Down Expand Up @@ -50,6 +49,10 @@ def inject_aggregator_attributes!(production)

# TODO: should be a method on collection of participants
demand = participants.sum(&:demand)
# puts @node.key
# puts demand
# puts production
# debugger if @node.key == :buildings_space_heater_heatpump_air_water_network_gas
edge = conv.output(:useable_heat).edges.first

edge.share = demand.positive? ? production / demand : 0.0
Expand Down
12 changes: 7 additions & 5 deletions app/models/qernel/fever_facade/summary.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def demand

suppliers =
@group.adapters.select do |adapter|
adapter.installed? && adapter.participant.is_a?(Fever::Consumer)
adapter.installed? && adapter.is_a?(Qernel::FeverFacade::ConsumerAdapter)
end

return [0.0] * 8760 if suppliers.none?
Expand All @@ -34,10 +34,7 @@ def demand
def production
return @production if @production

suppliers =
@group.adapters.reject do |adapter|
adapter.participant.is_a?(Fever::Consumer)
end
suppliers = @group.producer_adapters

return [0.0] * 8760 if suppliers.none?

Expand Down Expand Up @@ -138,6 +135,11 @@ def total_production_curve_for_producer(producer_key)
producer.producer.output_curve
end

# In the summary and export first the consumers and then the producers are shown
def nodes
@group.consumer_adapters.map(&:node) + @group.producer_adapters.map(&:node)
end

private

# Internal: Compares production and demand and creates two new curves
Expand Down
2 changes: 1 addition & 1 deletion app/models/qernel/merit_facade/consumption_loss_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def loss_share
end

edge = edges.first
loss_share = edge.rgt_output.conversion * edge.parent_share
loss_share = edge.rgt_output.conversion

other_shares =
edge.rgt_node.outputs.sum do |input|
Expand Down
6 changes: 5 additions & 1 deletion app/models/qernel/plugins/causality.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class Causality
CURVE_ROTATE = 2160

before :first_calculation, :clone_dataset
before :first_calculation, :early_setup
after :first_calculation, :setup
after :first_calculation, :run
before :recalculation, :inject
Expand Down Expand Up @@ -57,7 +58,6 @@ def run(lifecycle)

# Internal: Sets up Fever and Merit.
def setup
@fever.setup
@merit.setup
@heat_network.setup
@agriculture_heat.setup
Expand All @@ -69,6 +69,10 @@ def setup
@reconciliation.setup_early
end

def early_setup
@fever.setup
end

def inject(lifecycle)
merit_calc = Merit::StepwiseCalculator.new.calculate(@merit.order)

Expand Down
62 changes: 46 additions & 16 deletions app/serializers/fever_csv_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
# Creates CSV rows describing heat demand and supply for one or more fever groups.
class FeverCSVSerializer
attr_reader :filename

def self.time_column(year)
# We don't model leap days: 1970 is a safe choice for accurate times in the CSV.
base_date = Time.utc(1970, 1, 1)

['Time'] +
Array.new(8760) do |i|
(base_date + i.hours).strftime("#{year}-%m-%d %R")
end
end

def initialize(graph, groups, filename)
@graph = graph
@groups = groups.map(&:to_sym)
Expand All @@ -16,32 +27,51 @@ def to_csv_rows
'enabled for this scenario']]
end

[headers, *data]
data
end

private

def headers
def columns_for(node, summary)
[
'Production (MW)',
'Demand (MW)',
'Buffering and time-shifting (MW)',
'Deficit (MW)'
demand_column_for(node, summary),
production_column_for(node, summary)
]
end

def data
curve(:production).zip(
curve(:demand),
curve(:surplus),
curve(:deficit)
)
def safe_curve(curve)
curve.empty? ? [0.0] * 8760 : curve
end

def demand_column_for(node, summary)
case node.fever.type
when :consumer
["#{node.key}.input.demand (MW)"] +
safe_curve(summary.total_demand_curve_for_consumer(node.key))
when :producer
["#{node.key}.output.demand (MW)"] +
safe_curve(summary.total_demand_curve_for_producer(node.key))
end
end

def production_column_for(node, summary)
case node.fever.type
when :consumer
["#{node.key}.input.production (MW)"] +
safe_curve(summary.total_production_curve_for_consumer(node.key))
when :producer
["#{node.key}.output.production (MW)"] +
safe_curve(summary.total_production_curve_for_producer(node.key))
end
end

def curve(type)
Merit::CurveTools.add_curves(
@groups.map { |group| summary(group).public_send(type) }
)
def data
(
[self.class.time_column(@graph.year)] + @groups.flat_map do |group|
summary = summary(group)
summary.nodes.flat_map { |node| columns_for(node, summary) }
end
).transpose
end

def summary(group)
Expand Down