Skip to content
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: 7 additions & 1 deletion app/models/qernel/node_api/capacity_production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ def input_of_bio_resources
input_of_bio_oil +
input_of_biodiesel +
input_of_bio_ethanol +
input_of_biomethanol +
input_of_bionaphtha +
input_of_bio_pyrolysis_oil +
input_of_biogas +
input_of_greengas +
input_of_network_gas +
Expand All @@ -73,7 +76,10 @@ def input_of_bio_fuels
input_of_bio_kerosene +
input_of_bio_oil +
input_of_bio_lng +
input_of_bio_ethanol
input_of_bio_ethanol +
input_of_biomethanol +
input_of_bionaphtha +
input_of_bio_pyrolysis_oil
end
end

Expand Down
2 changes: 1 addition & 1 deletion app/serializers/costs_parameters_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ def costs_building_and_installations
#
# Has subtotal queries
def costs_production
%w[power_plants chp_plants heat_plants dedicated_hydrogen_production biomass other]
%w[power_plants chp_plants heat_plants dedicated_hydrogen_production liquid_fuels biomass other ]
end

# Internal: Categories within costs_storage_and_conversion
Expand Down
104 changes: 104 additions & 0 deletions app/serializers/node_serializer_data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,106 @@ module NodeSerializerData
}
}.freeze

LIQUID_FUELS_GENERIC_ATTRIBUTES_AND_METHODS = {
technical: {
'demand * loss_output_conversion / BILLIONS' => {
label: 'Annual fuel production',
key: :annual_fuel_production,
unit: 'PJ / year'
Comment on lines +626 to +629
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to have is to add the production per output carrier as a collapsable list here.

},
'input_capacity * number_of_units' => {
label: 'Installed input capacity',
key: :total_installed_input_capacity,
unit: 'MW'
},
'1 - loss_output_conversion' => {
label: 'Production efficiency',
key: :production_efficiency,
unit: '%',
formatter: FORMAT_FAC_TO_PERCENT
},
full_load_hours: { label: 'Full load hours', unit: 'hour / year' },
free_co2_factor: { label: 'CCS capture rate', unit: '%', formatter: FORMAT_FAC_TO_PERCENT },
technical_lifetime: { label: 'Technical lifetime', unit: 'years', formatter: ->(n) { n.to_i } }
}
}.freeze

# If the node belongs to the liquid_fuels presentation group then
# add these
LIQUID_FUELS_WITHOUT_CCS_ATTRIBUTES_AND_METHODS = LIQUID_FUELS_GENERIC_ATTRIBUTES_AND_METHODS.merge({
cost: {
'initial_investment_per(:mw_input_capacity) + cost_of_installing_per(:mw_input_capacity) + decommissioning_costs_per(:mw_input_capacity)' => {
label: 'Investment over lifetime per MW input',
key: :total_initial_investment_per_mw_input_capacity,
unit: 'EUR / MW',
formatter: ->(n) { n.to_i }
},
'fixed_operation_and_maintenance_costs_per(:mw_input_capacity)' => {
label: 'Fixed operation and maintenance costs',
key: :fixed_operation_and_maintenance_costs_per_mw_input_capacity,
unit: 'EUR / MW / year',
formatter: ->(n) { n.to_i }
},
'variable_operation_and_maintenance_costs_per(:full_load_hour)' => {
label: 'Variable operation and maintenance costs',
unit: 'EUR / full load hour'
},
:wacc => {
label: 'Weighted average cost of capital',
unit: '%',
formatter: FORMAT_FAC_TO_PERCENT
},
:takes_part_in_ets => {
label: 'Do emissions have to be paid through the ETS?',
unit: 'boolean',
formatter: ->(x) { x == 1 }
}
}
}).freeze

# If the node belongs to the liquid_fuels_ccs presentation group then
# add these
LIQUID_FUELS_CCS_ATTRIBUTES_AND_METHODS = LIQUID_FUELS_GENERIC_ATTRIBUTES_AND_METHODS.merge({
cost: {
'initial_investment_per(:mw_input_capacity) + cost_of_installing_per(:mw_input_capacity) + decommissioning_costs_per(:mw_input_capacity)' => {
label: 'Investment over lifetime per MW input',
key: :total_initial_investment_per_mw_input_capacity,
unit: 'EUR / MW',
formatter: ->(n) { n.to_i }
},
'ccs_investment_per(:mw_input_capacity)' => {
label: 'Additional initial investment for CCS',
key: :ccs_investment_per_mw_input_capacity,
unit: 'EUR / MW',
formatter: ->(n) { n.to_i }
},
'fixed_operation_and_maintenance_costs_per(:mw_input_capacity)' => {
label: 'Fixed operation and maintenance costs',
key: :fixed_operation_and_maintenance_costs_per_mw_input_capacity,
unit: 'EUR / MW / year',
formatter: ->(n) { n.to_i }
},
'variable_operation_and_maintenance_costs_per(:full_load_hour)' => {
label: 'Variable operation and maintenance costs',
unit: 'EUR / full load hour'
},
:variable_operation_and_maintenance_costs_for_ccs_per_full_load_hour => {
label: 'Additional variable operation and maintenance costs for CCS',
unit: 'EUR / full load hour'
},
:wacc => {
label: 'Weighted average cost of capital',
unit: '%',
formatter: FORMAT_FAC_TO_PERCENT
},
:takes_part_in_ets => {
label: 'Do emissions have to be paid through the ETS?',
unit: 'boolean',
formatter: ->(x) { x == 1 }
}
}
}).freeze

CO2_GENERIC_ATTRIBUTES_AND_METHODS = {
cost: {
'total_initial_investment_per(:plant)' => {
Expand Down Expand Up @@ -818,6 +918,10 @@ def attributes_and_methods_to_show
P2P_ATTRIBUTES_AND_METHODS
when :v2g
V2G_ATTRIBUTES_AND_METHODS
when :liquid_fuels
LIQUID_FUELS_WITHOUT_CCS_ATTRIBUTES_AND_METHODS
when :liquid_fuels_ccs
LIQUID_FUELS_CCS_ATTRIBUTES_AND_METHODS
when :biomass
BIOMASS_ATTRIBUTES_AND_METHODS
when :steel
Expand Down
109 changes: 109 additions & 0 deletions db/migrate/20251008073517_chemicals_and_synthetic_products.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
require 'etengine/scenario_migration'

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

FOSSIL_KEY = 'industry_useful_demand_for_chemical_refineries'
FOSSIL_KEYS = %w[ energy_export_oil_products
energy_distribution_diesel
energy_distribution_gasoline
energy_distribution_heavy_fuel_oil
energy_distribution_kerosene
energy_distribution_lpg
energy_distribution_naphtha
industry_locally_available_refinery_gas_for_chemical].freeze

FOSSIL_KEYS_MAPPING = {
'energy_export_oil_products' => 'industry_refinery_transformation_crude_oil_to_energy_export_oil_products_crude_oil_demand',
'energy_distribution_diesel' => 'industry_refinery_transformation_crude_oil_to_energy_distribution_diesel_diesel_demand',
'energy_distribution_gasoline' => 'industry_refinery_transformation_crude_oil_to_energy_distribution_gasoline_gasoline_demand',
'energy_distribution_heavy_fuel_oil' => 'industry_refinery_transformation_crude_oil_to_energy_distribution_heavy_fuel_oil_heavy_fuel_oil_demand',
'energy_distribution_kerosene' => 'industry_refinery_transformation_crude_oil_to_energy_distribution_kerosene_kerosene_demand',
'energy_distribution_lpg' => 'industry_refinery_transformation_crude_oil_to_energy_distribution_lpg_lpg_demand',
'energy_distribution_naphtha' => 'industry_refinery_transformation_crude_oil_to_energy_distribution_naphtha_naphtha_demand',
'industry_locally_available_refinery_gas_for_chemical' => 'industry_refinery_transformation_crude_oil_to_industry_locally_available_refinery_gas_for_chemical_refinery_gas_demand'
}.freeze

KEROSENE_OLD_KEY = 'output_of_energy_production_synthetic_kerosene_must_run'
KEROSENE_NEW_KEY = 'output_of_energy_production_fischer_tropsch'
KEROSENE_OUTPUT_CONVERSION_CORRECTION = 1.5

WP_KEY = 'external_coupling_industry_chemical_other_non_energetic_wood_pellets_share'
BION_KEY = 'external_coupling_industry_chemical_other_non_energetic_bionaphtha_share'

RENAME = {
'capacity_of_energy_production_synthetic_kerosene_dispatchable' => ['capacity_of_energy_production_fischer_tropsch_synthetic_dispatchable'],
'investment_costs_co2_utilisation' => ['investment_costs_fischer_tropsch', 'investment_costs_methanol_synthesis'],
'om_costs_co2_utilisation' => ['om_costs_methanol_synthesis', 'om_costs_fischer_tropsch'],
'transport_plane_using_kerosene_share' => ['transport_plane_using_kerosene_mix_share'],
'external_coupling_energy_chemical_other_transformation_external_coupling_node_bio_oil_output_share' => ['external_coupling_energy_chemical_other_transformation_external_coupling_node_bionaphtha_output_share'],
'external_coupling_energy_chemical_refineries_transformation_external_coupling_node_bio_oil_output_share' => ['external_coupling_energy_chemical_refineries_transformation_external_coupling_node_bionaphtha_output_share'],
'external_coupling_energy_chemical_fertilizers_transformation_external_coupling_node_bio_oil_output_share' => ['external_coupling_energy_chemical_fertilizers_transformation_external_coupling_node_bionaphtha_output_share'],
'output_of_energy_production_synthetic_methanol' => ['output_of_energy_production_methanol_synthesis']
}.freeze

REMOVE = %w[external_coupling_energy_production_synthetic_methanol_demand
external_coupling_energy_production_synthetic_kerosene_demand
capacity_of_energy_production_synthetic_kerosene_dispatchable
investment_costs_co2_utilisation
om_costs_co2_utilisation
transport_plane_using_kerosene_share
external_coupling_energy_chemical_other_transformation_external_coupling_node_bio_oil_output_share
external_coupling_energy_chemical_refineries_transformation_external_coupling_node_bio_oil_output_share
external_coupling_energy_chemical_fertilizers_transformation_external_coupling_node_bio_oil_output_share
output_of_energy_production_synthetic_methanol]

def up
@defaults = JSON.load(File.read(
Rails.root.join("db/migrate/#{File.basename(__FILE__, '.rb')}/dataset_values.json")
))

migrate_scenarios do |scenario|
migrate_fossil_refinery(scenario)
migrate_synthetic_kerosene(scenario)
migrate_ext_coupling_bionaphtha(scenario)
rename_inputs(scenario)
remove_inputs(scenario)
end
end

private

def migrate_fossil_refinery(scenario)
return unless scenario.user_values.key?(FOSSIL_KEY)
dataset_refinery_size = 0
FOSSIL_KEYS.each do |key|
mapped_key = FOSSIL_KEYS_MAPPING[key]
dataset_refinery_size += @defaults[scenario.area_code][mapped_key]
end
scenario.user_values[FOSSIL_KEY] = (scenario.user_values[FOSSIL_KEY] / 100) * (dataset_refinery_size / 1000)
end

def migrate_synthetic_kerosene(scenario)
return unless scenario.user_values.key?(KEROSENE_OLD_KEY)
scenario.user_values[KEROSENE_NEW_KEY] = scenario.user_values.delete(KEROSENE_OLD_KEY) * KEROSENE_OUTPUT_CONVERSION_CORRECTION
end

def migrate_ext_coupling_bionaphtha(scenario)
return unless scenario.user_values.key?(WP_KEY)
wood_pellets = scenario.user_values[WP_KEY]
scenario.user_values[BION_KEY] = wood_pellets
scenario.user_values[WP_KEY] = 0.0
end

def rename_inputs(scenario)
RENAME.each do |old_key, new_keys|
next unless scenario.user_values.key?(old_key)
Array(new_keys).each do |new_key|
scenario.user_values[new_key] = scenario.user_values[old_key]
end
end
end

def remove_inputs(scenario)
REMOVE.each do |key|
next unless scenario.user_values.key?(key)
scenario.user_values.delete(key)
end
end
end

Large diffs are not rendered by default.

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_09_26_121929) do
ActiveRecord::Schema[7.1].define(version: 2025_10_08_073517) 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