Source code for opentisim.hydrogen_system

# package(s) for data handling
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib

# opentisim package
from opentisim.hydrogen_objects import *
from opentisim import hydrogen_defaults
from opentisim import core

[docs]class System: """This class implements the 'complete supply chain' concept (Van Koningsveld et al, 2020) for hydrogen terminals. The module allows variation of the commodity type, the storage type and the h2retrieval type. Terminal development is governed by three triggers: the allowable berth occupancy, the allowable dwell time and an h2retrieval trigger.""" def __init__(self, startyear=2019, lifecycle=20, operational_hours=5840, debug=False, elements=[], commodity_type_defaults=hydrogen_defaults.commodity_ammonia_data, storage_type_defaults=hydrogen_defaults.storage_nh3_data, h2retrieval_type_defaults=hydrogen_defaults.h2retrieval_nh3_data, allowable_berth_occupancy=0.5, allowable_dwelltime=14 / 365, h2retrieval_trigger=1): # time inputs self.startyear = startyear self.lifecycle = lifecycle self.operational_hours = operational_hours # provide intermediate outputs via print statements if debug = True self.debug = debug # collection of all terminal objects self.elements = elements # default values to use in selecting which commodity is imported self.commodity_type_defaults = commodity_type_defaults self.storage_type_defaults = storage_type_defaults self.h2retrieval_type_defaults = h2retrieval_type_defaults # triggers for the various elements (berth, storage and h2retrieval) self.allowable_waiting_service_time_ratio_berth = allowable_waiting_service_time_ratio_berth self.allowable_berth_occupancy = allowable_berth_occupancy self.allowable_dwelltime = allowable_dwelltime self.h2retrieval_trigger = h2retrieval_trigger # storage variables for revenue self.revenues = [] # *** Overall terminal investment strategy for terminal class.
[docs] def simulate(self): """The 'simulate' method implements the terminal investment strategy for this terminal class. This method automatically generates investment decisions, parametrically derived from overall demand trends and a number of investment triggers. Generic approaches based on: - Van Koningsveld, M. (Ed.), Verheij, H., Taneja, P. and De Vriend, H.J. (2020). Ports and Waterways. Navigating the changing world. TU Delft, Delft, The Netherlands. - Van Koningsveld, M. and J. P. M. Mulder. 2004. Sustainable Coastal Policy Developments in the Netherlands. A Systematic Approach Revealed. Journal of Coastal Research 20(2), pp. 375-385 Specific application based on: - Ijzermans, W., 2019. Terminal design optimization. Adaptive agribulk terminal planning in light of an uncertain future. Master's thesis. Delft University of Technology, Netherlands. URL: http://resolver.tudelft.nl/uuid:7ad9be30-7d0a-4ece-a7dc-eb861ae5df24. The simulate method applies frame of reference style decisions while stepping through each year of the terminal lifecycle and check if investment is needed (in light of strategic objective, operational objective, QSC, decision recipe, intervention method): 1. for each year estimate the anticipated vessel arrivals based on the expected demand 2. for each year evaluate which investment are needed given the strategic and operational objectives 3. for each year calculate the energy costs (requires insight in realized demands) 4. for each year calculate the demurrage costs (requires insight in realized demands) 5. for each year calculate terminal revenues (requires insight in realized demands) 6. for each year calculate the throughput (requires insight in realized demands) 6. for each year calculate terminal throughputequires insight in realized demands) 7. collect all cash flows (capex, opex, revenues) 8. calculate PV's and aggregate to NPV """ for year in range(self.startyear, self.startyear + self.lifecycle): """ The simulate method is designed according to the following overall objectives for the terminal: - strategic objective: To maintain a profitable enterprise (NPV > 0) over the terminal lifecycle - operational objective: Annually invest in infrastructure upgrades when performance criteria are triggered """ if self.debug: print('') print('### Simulate year: {} ############################'.format(year)) # 1. for each year estimate the anticipated vessel arrivals based on the expected demand smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, total_calls, total_vol, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned, total_calls_planned, total_vol_planned = self.calculate_vessel_calls(year) if self.debug: print('--- Cargo volume and vessel calls for {} ---------'.format(year)) print(' Total cargo volume: {}'.format(total_vol)) print(' Total vessel calls: {}'.format(total_calls)) print(' Small Hydrogen calls: {}'.format(smallhydrogen_calls)) print(' Large Hydrogen calls: {}'.format(largehydrogen_calls)) print(' Small ammonia calls: {}'.format(smallammonia_calls)) print(' Large ammonia calls: {}'.format(largeammonia_calls)) print(' Handysize calls: {}'.format(handysize_calls)) print(' Panamax calls: {}'.format(panamax_calls)) print(' VLCC calls: {}'.format(vlcc_calls)) print('----------------------------------------------------') # 2. for each year evaluate which investment are needed given the strategic and operational objectives self.berth_invest(year) if self.debug: print('') print('$$$ Check pipeline jetty ---------------------------') self.pipeline_jetty_invest(year) if self.debug: print('') print('$$$ Check storage ----------------------------------') self.storage_invest(year, self.storage_type_defaults) if self.debug: print('') print('$$$ Check H2 retrieval plants ----------------------') self.h2retrieval_invest(year, self.h2retrieval_type_defaults) if self.debug: print('') print('$$$ Check pipeline hinterland ----------------------') self.pipeline_hinter_invest(year) # 3. for each year calculate the energy costs (requires insight in realized demands) for year in range(self.startyear, self.startyear + self.lifecycle): self.calculate_energy_cost(year) # 4. for each year calculate the demurrage costs (requires insight in realized demands) self.demurrage = [] for year in range(self.startyear, self.startyear + self.lifecycle): self.calculate_demurrage_cost(year) # 5. for each year calculate terminal revenues self.revenues = [] for year in range(self.startyear, self.startyear + self.lifecycle): self.calculate_revenue(year, self.commodity_type_defaults) # 6. for each year calculate the throughput (requires insight in realized demands) self.throughputonline = [] for year in range(self.startyear, self.startyear + self.lifecycle): self.throughput_elements(year) # # 7. collect all cash flows (capex, opex, revenues) # cash_flows, cash_flows_WACC_nominal = self.add_cashflow_elements() # 8. calculate PV's and aggregate to NPV core.NPV(self, Labour(**hydrogen_defaults.labour_data))
# *** Individual investment methods for terminal elements
[docs] def berth_invest(self, year): """ Given the overall objectives of the terminal Decision recipe Berth: QSC: berth_occupancy Problem evaluation: there is a problem if the berth_occupancy > allowable_berth_occupancy - allowable_berth_occupancy = .50 # 50% - a berth needs: - a jetty - berth occupancy depends on: - total_calls and total_vol - total_service_capacity as delivered by the vessels Investment decisions: invest enough to make the berth_occupancy < allowable_berth_occupancy - adding jettys decreases berth_occupancy_rate """ # report on the status of all berth elements if self.debug: print('') print('--- Status terminal @ start of year ----------------') core.report_element(self, Berth, year) core.report_element(self, Jetty, year) core.report_element(self, Pipeline_Jetty, year) core.report_element(self, Storage, year) core.report_element(self, H2retrieval, year) core.report_element(self, Pipeline_Hinter, year) # calculate berth occupancy smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, total_calls, total_vol, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned, total_calls_planned, total_vol_planned = self.calculate_vessel_calls(year) berth_occupancy_planned, berth_occupancy_online, unloading_occupancy_planned, unloading_occupancy_online = self.calculate_berth_occupancy( year, smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned) berths = len(core.find_elements(self, Berth)) if berths != 0: waiting_factor = \ core.occupancy_to_waitingfactor(occupancy=berth_occupancy_online, nr_of_servers_chk=berths, poly_order=6) waiting_time_hours = waiting_factor * unloading_occupancy_online * self.operational_hours / total_calls waiting_time_occupancy = waiting_time_hours * total_calls / self.operational_hours else: waiting_factor = np.inf waiting_time_hours = np.inf waiting_time_occupancy = np.inf # factor, waiting_time_occupancy = self.waiting_time(year) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements(year) if self.debug: print(' Berth occupancy online (@ start of year): {:.2f} (trigger level: {:.2f})'.format(berth_occupancy_online, self.allowable_berth_occupancy)) print(' Berth occupancy planned (@ start of year): {:.2f} (trigger level: {:.2f})'.format(berth_occupancy_planned, self.allowable_berth_occupancy)) print(' Unloading occupancy online (@ start of year): {:.2f}'.format(unloading_occupancy_online)) print(' Unloading occupancy planned (@ start of year): {:.2f}'.format(unloading_occupancy_planned)) print(' waiting time occupancy (@ start of year): {:.2f}'.format(waiting_time_occupancy)) print(' waiting time factor (@ start of year): {:.2f}'.format(waiting_factor)) print(' throughput online {:.2f}'.format(throughput_online)) print(' throughput planned {:.2f}'.format(throughput_planned)) print('') print('--- Start investment analysis ----------------------') print('') print('$$$ Check berth elements ---------------------------') core.report_element(self, Berth, year) core.report_element(self, Jetty, year) core.report_element(self, Pipeline_Jetty, year) while berth_occupancy_planned > self.allowable_berth_occupancy: # while planned berth occupancy is too large add a berth when no crane slots are available if self.debug: print(' *** add Berth to elements') berth = Berth(**hydrogen_defaults.berth_data) berth.year_online = year + berth.delivery_time self.elements.append(berth) berth_occupancy_planned, berth_occupancy_online, unloading_occupancy_planned, unloading_occupancy_online = \ self.calculate_berth_occupancy(year, smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned) if self.debug: print(' Berth occupancy planned (after adding berth): {:.2f}'.format(berth_occupancy_planned)) # print(' Berth occupancy online (after adding berth): {}'.format(berth_occupancy_online)) # while planned berth occupancy is too large add a berth if a jetty is needed berths = len(core.find_elements(self, Berth)) jettys = len(core.find_elements(self, Jetty)) if berths > jettys: length_max = max(hydrogen_defaults.vlcc_data["LOA"], hydrogen_defaults.handysize_data["LOA"], hydrogen_defaults.panamax_data["LOA"], hydrogen_defaults.smallhydrogen_data["LOA"], hydrogen_defaults.largehydrogen_data["LOA"], hydrogen_defaults.smallammonia_data["LOA"], hydrogen_defaults.largeammonia_data["LOA"] ) # maximum of all vessels length_min = min(hydrogen_defaults.vlcc_data["LOA"], hydrogen_defaults.handysize_data["LOA"], hydrogen_defaults.panamax_data["LOA"], hydrogen_defaults.smallhydrogen_data["LOA"], hydrogen_defaults.largehydrogen_data["LOA"], hydrogen_defaults.smallammonia_data["LOA"], hydrogen_defaults.largeammonia_data["LOA"]) # maximum of all vessels if length_max-length_min > 100: nrofdolphins=8 else: nrofdolphins=6 # - depth jetty = Jetty(**hydrogen_defaults.jetty_data) self.jetty_invest(year, nrofdolphins) berth_occupancy_planned, berth_occupancy_online, unloading_occupancy_planned, unloading_occupancy_online = self.calculate_berth_occupancy( year, smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned) if self.debug: print(' Berth occupancy planned (after adding jetty): {}'.format(berth_occupancy_planned))
# print(' Berth occupancy online (after adding jetty): {}'.format(berth_occupancy_online))
[docs] def jetty_invest(self, year, nrofdolphins): """ *** Decision recipe jetty: *** QSC: jetty_per_berth problem evaluation: there is a problem if the jetty_per_berth < 1 investment decisions: invest enough to make the jetty_per_berth = 1 - adding jetty will increase jetty_per_berth - jetty_wall.length must be long enough to accommodate largest expected vessel - jetty_wall.depth must be deep enough to accommodate largest expected vessel - jetty_wall.freeboard must be high enough to accommodate largest expected vessel """ if self.debug: print(' *** add jetty to elements') # add a Jetty element jetty = Jetty(**hydrogen_defaults.jetty_data) # - capex unit_rate = int((nrofdolphins * jetty.mooring_dolphins) + (jetty.Gijt_constant_jetty * jetty.jettywidth * jetty.jettylength) + (jetty.Catwalk_rate* jetty.catwalklength * jetty.catwalkwidth)) mobilisation = int(max((unit_rate * jetty.mobilisation_perc), jetty.mobilisation_min)) jetty.capex = int(unit_rate + mobilisation) # - opex jetty.insurance = unit_rate * jetty.insurance_perc jetty.maintenance = unit_rate * jetty.maintenance_perc jetty.year_online = year + jetty.delivery_time # residual jetty.assetvalue = (unit_rate) * (1 - ((self.lifecycle + self.startyear - jetty.year_online) / jetty.lifespan)) jetty.residual = max(jetty.assetvalue, 0) # add cash flow information to jetty object in a dataframe jetty = core.add_cashflow_data_to_element(self, jetty) self.elements.append(jetty)
[docs] def pipeline_jetty_invest(self, year): """current strategy is to add pipeline as soon as a service trigger is achieved - find out how much service capacity is online - find out how much service capacity is planned - find out how much service capacity is needed - add service capacity until service_trigger is no longer exceeded """ # find the total service rate service_capacity = 0 service_capacity_online = 0 list_of_elements = core.find_elements(self, Pipeline_Jetty) if list_of_elements != []: for element in list_of_elements: service_capacity += element.capacity if year >= element.year_online: service_capacity_online += element.capacity # find the year online, years_online = [] for element in core.find_elements(self, Jetty): years_online.append(element.year_online) # # check if total planned capacity is smaller than target capacity, if so add a pipeline pipelines = len(core.find_elements(self, Pipeline_Jetty)) jettys = len(core.find_elements(self, Jetty)) if jettys > pipelines: if self.debug: print(' *** add jetty pipeline to elements') pipeline_jetty = Pipeline_Jetty(**hydrogen_defaults.jetty_pipeline_data) # - capex unit_rate = pipeline_jetty.unit_rate_factor * pipeline_jetty.length mobilisation = pipeline_jetty.mobilisation pipeline_jetty.capex = int(unit_rate + mobilisation) # - opex pipeline_jetty.insurance = unit_rate * pipeline_jetty.insurance_perc pipeline_jetty.maintenance = unit_rate * pipeline_jetty.maintenance_perc # labour labour = Labour(**hydrogen_defaults.labour_data) pipeline_jetty.shift = (pipeline_jetty.crew * self.operational_hours) / (labour.shift_length * labour.annual_shifts) pipeline_jetty.labour = pipeline_jetty.shift * labour.operational_salary # # find the total service rate, service_rate = 0 years_online = [] for element in core.find_elements(self, Jetty): service_rate += hydrogen_defaults.largehydrogen_data["pump_capacity"] years_online.append(element.year_online) # there should always be a new jetty in the planning new_jetty_years = [x for x in years_online if x >= year] # find the maximum online year of pipeline_jetty or make it [] if core.find_elements(self, Pipeline_Jetty) != []: max_pipeline_years = max([x.year_online for x in core.find_elements(self, Pipeline_Jetty)]) else: max_pipeline_years = [] # decide what online year to use if max_pipeline_years == []: pipeline_jetty.year_online = min(new_jetty_years) elif max_pipeline_years < min(new_jetty_years): pipeline_jetty.year_online = min(new_jetty_years) elif max_pipeline_years == min(new_jetty_years): pipeline_jetty.year_online = max(new_jetty_years) elif max_pipeline_years > min(new_jetty_years): pipeline_jetty.year_online = max(new_jetty_years) # pipeline_jetty.year_online = year # residual pipeline_jetty.assetvalue = unit_rate * (1 - (self.lifecycle + self.startyear - pipeline_jetty.year_online) / pipeline_jetty.lifespan) pipeline_jetty.residual = max(pipeline_jetty.assetvalue, 0) # add cash flow information to pipeline_jetty object in a dataframe pipeline_jetty = core.add_cashflow_data_to_element(self, pipeline_jetty) self.elements.append(pipeline_jetty)
[docs] def storage_invest(self, year, hydrogen_defaults_storage_data): """current strategy is to add storage as long as target storage is not yet achieved - find out how much storage is online - find out how much storage is planned - find out how much storage is needed - add storage until target is reached """ # from all storage objects sum online capacity storage_capacity = 0 storage_capacity_online = 0 list_of_elements = core.find_elements(self, Storage) if list_of_elements != []: for element in list_of_elements: if element.type == hydrogen_defaults_storage_data['type']: storage_capacity += element.capacity if year >= element.year_online: storage_capacity_online += element.capacity if self.debug: print(' a total of {} ton of {} storage capacity is online; {} ton total planned'.format( storage_capacity_online, hydrogen_defaults_storage_data['type'], storage_capacity)) # max_vessel_call_size = max([x.call_size for x in core.find_elements(self, Vessel)]) max_vessel_call_size = hydrogen_defaults.largeammonia_data["call_size"] Demand = [] for commodity in core.find_elements(self, Commodity): try: Demand = commodity.scenario_data.loc[commodity.scenario_data['year'] == year]['volume'].item() except: pass storage_capacity_dwelltime_demand = (Demand * self.allowable_dwelltime) * 1.1 # IJzerman p.26 # find the total throughput throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements(year) storage_capacity_dwelltime_throughput = (throughput_planned_storage * self.allowable_dwelltime) * 1.1 # IJzerman p.26 # or # check if sufficient storage capacity is available while storage_capacity < max_vessel_call_size or (storage_capacity < storage_capacity_dwelltime_demand and storage_capacity < storage_capacity_dwelltime_throughput): if self.debug: print(' *** add storage to elements') # add storage object storage = Storage(**hydrogen_defaults_storage_data) # - capex storage.capex = storage.unit_rate + storage.mobilisation_min # - opex storage.insurance = storage.unit_rate * storage.insurance_perc storage.maintenance = storage.unit_rate * storage.maintenance_perc # labour**hydrogen_defaults labour = Labour(**hydrogen_defaults.labour_data) storage.shift = ((storage.crew_for5 * self.operational_hours) / (labour.shift_length * labour.annual_shifts)) storage.labour = storage.shift * labour.operational_salary if year == self.startyear: storage.year_online = year + storage.delivery_time +1 else: storage.year_online = year + storage.delivery_time # #reinvestment # if year == storage.year_online + storage.lifespan: # residual storage.assetvalue = storage.unit_rate * (1 - ((self.lifecycle + self.startyear - storage.year_online) / storage.lifespan)) storage.residual = max(storage.assetvalue, 0) # add cash flow information to storage object in a dataframe storage = core.add_cashflow_data_to_element(self, storage) self.elements.append(storage) storage_capacity += storage.capacity if self.debug: print(' a total of {} ton of {} storage capacity is online; {} ton total planned'.format( storage_capacity_online, hydrogen_defaults_storage_data['type'], storage_capacity))
[docs] def h2retrieval_invest(self, year, hydrogen_defaults_h2retrieval_data): """current strategy is to add h2 retrieval as long as target h2 retrieval is not yet achieved - find out how much h2 retrieval is online - find out how much h2 retrieval is planned - find out how much h2 retrieval is needed - add h2 retrieval until target is reached """ plant_occupancy_planned, plant_occupancy_online, h2retrieval_capacity_planned, h2retrieval_capacity_online = self.calculate_h2retrieval_occupancy(year, hydrogen_defaults_h2retrieval_data) if self.debug: print(' Plant occupancy planned (@ start of year): {:.2f}'.format(plant_occupancy_planned)) print(' Plant occupancy online (@ start of year): {:.2f}'.format(plant_occupancy_online)) # check if sufficient h2retrieval capacity is available while plant_occupancy_planned > self.h2retrieval_trigger: if self.debug: print(' *** add h2retrieval to elements') # add h2retrieval object h2retrieval = H2retrieval(**hydrogen_defaults_h2retrieval_data) # - capex h2retrieval.capex = h2retrieval.unit_rate + h2retrieval.mobilisation_min # - opex h2retrieval.insurance = h2retrieval.unit_rate * h2retrieval.insurance_perc h2retrieval.maintenance = h2retrieval.unit_rate * h2retrieval.maintenance_perc # labour**hydrogen_defaults labour = Labour(**hydrogen_defaults.labour_data) h2retrieval.shift = ((h2retrieval.crew_for5 * self.operational_hours) / (labour.shift_length * labour.annual_shifts)) h2retrieval.labour = h2retrieval.shift * labour.operational_salary jetty = Jetty(**hydrogen_defaults.jetty_data) if year == self.startyear + jetty.delivery_time: h2retrieval.year_online = year else: h2retrieval.year_online = year + h2retrieval.delivery_time # residual h2retrieval.assetvalue = h2retrieval.unit_rate * ( 1 - (self.lifecycle + self.startyear - h2retrieval.year_online) / h2retrieval.lifespan) h2retrieval.residual = max(h2retrieval.assetvalue, 0) # add cash flow information to h2retrieval object in a dataframe h2retrieval = core.add_cashflow_data_to_element(self, h2retrieval) self.elements.append(h2retrieval) plant_occupancy_planned, plant_occupancy_online, h2retrieval_capacity_planned, h2retrieval_capacity_online = self.calculate_h2retrieval_occupancy(year, hydrogen_defaults_h2retrieval_data) if self.debug: print( ' a total of {} ton of h2retrieval capacity is online; {} ton total planned'.format( h2retrieval_capacity_online, h2retrieval_capacity_planned))
[docs] def pipeline_hinter_invest(self, year): """current strategy is to add pipeline as soon as a service trigger is achieved - find out how much service capacity is online - find out how much service capacity is planned - find out how much service capacity is needed - add service capacity until service_trigger is no longer exceeded """ # find the total service rate service_capacity = 0 service_capacity_online_hinter = 0 list_of_elements_pipeline = core.find_elements(self, Pipeline_Hinter) if list_of_elements_pipeline != []: for element in list_of_elements_pipeline: service_capacity += element.capacity if year >= element.year_online: service_capacity_online_hinter += element.capacity # find the total service rate, service_rate = 0 years_online = [] for element in (core.find_elements(self, H2retrieval)): service_rate += element.capacity years_online.append(element.year_online) # check if total planned length is smaller than target length, if so add a pipeline while service_rate > service_capacity: if self.debug: print(' *** add Hinter Pipeline to elements') pipeline_hinter = Pipeline_Hinter(**hydrogen_defaults.hinterland_pipeline_data) # - capex capacity = pipeline_hinter.capacity unit_rate = pipeline_hinter.unit_rate_factor * pipeline_hinter.length mobilisation = pipeline_hinter.mobilisation pipeline_hinter.capex = int(unit_rate + mobilisation) # - opex pipeline_hinter.insurance = unit_rate * pipeline_hinter.insurance_perc pipeline_hinter.maintenance = unit_rate * pipeline_hinter.maintenance_perc # - labour labour = Labour(**hydrogen_defaults.labour_data) pipeline_hinter.shift = ( (pipeline_hinter.crew * self.operational_hours) / (labour.shift_length * labour.annual_shifts)) pipeline_hinter.labour = pipeline_hinter.shift * labour.operational_salary if year == self.startyear: pipeline_hinter.year_online = year + pipeline_hinter.delivery_time + 1 else: pipeline_hinter.year_online = year + pipeline_hinter.delivery_time # residual pipeline_hinter.assetvalue = unit_rate * ( 1 - (self.lifecycle + self.startyear - pipeline_hinter.year_online) / pipeline_hinter.lifespan) pipeline_hinter.residual = max(pipeline_hinter.assetvalue, 0) # add cash flow information to pipeline_hinter object in a dataframe pipeline_hinter = core.add_cashflow_data_to_element(self, pipeline_hinter) self.elements.append(pipeline_hinter) service_capacity += pipeline_hinter.capacity if self.debug: print( ' a total of {} ton of pipeline hinterland service capacity is online; {} ton total planned'.format( service_capacity_online_hinter, service_capacity))
# *** Energy costs, demurrage costs and revenue calculation methods
[docs] def calculate_energy_cost(self, year): """ The energy cost of all different element are calculated. 1. At first find the consumption, capacity and working hours per element 2. Find the total energy price to multiply the consumption with the energy price """ energy = Energy(**hydrogen_defaults.energy_data) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) # calculate pipeline jetty energy list_of_elements_Pipelinejetty = core.find_elements(self, Pipeline_Jetty) pipelinesj=0 for element in list_of_elements_Pipelinejetty: if year >= element.year_online: pipelinesj += 1 consumption = throughput_online/pipelinesj * element.consumption_coefficient if consumption * energy.price != np.inf: element.df.loc[element.df['year'] == year, 'energy'] = consumption * energy.price else: element.df.loc[element.df['year'] == year, 'energy'] = 0 # calculate storage energy list_of_elements_Storage = core.find_elements(self, Storage) max_vessel_call_size = hydrogen_defaults.largeammonia_data["call_size"] throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) storage_capacity_dwelltime_throughput = (throughput_online * self.allowable_dwelltime) * 1.1 for element in list_of_elements_Storage: if year >= element.year_online: consumption = element.consumption hours = self.operational_hours capacity = max(max_vessel_call_size, storage_capacity_dwelltime_throughput) if consumption * capacity * hours * energy.price != np.inf: element.df.loc[element.df['year'] == year, 'energy'] = consumption * capacity * energy.price else: element.df.loc[element.df['year'] == year, 'energy'] = 0 # calculate H2 retrieval energy list_of_elements_H2retrieval = core.find_elements(self, H2retrieval) # find the total throughput, # throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements(year) hydrogen_defaults_h2retrieval_data = self.h2retrieval_type_defaults plant_occupancy_planned, plant_occupancy_online, h2retrieval_capacity_planned, h2retrieval_capacity_online = self.calculate_h2retrieval_occupancy(year, hydrogen_defaults_h2retrieval_data) for element in list_of_elements_H2retrieval: if year >= element.year_online: consumption = element.consumption capacity = element.capacity * self.operational_hours if consumption * throughput_online * energy.price != np.inf: element.df.loc[element.df['year'] == year, 'energy'] = consumption * plant_occupancy_online * capacity * energy.price else: element.df.loc[element.df['year'] == year, 'energy'] = 0 # calculate hinterland pipeline energy list_of_elements_hinter = core.find_elements(self, Pipeline_Hinter) plant_occupancy_planned, plant_occupancy_online, h2retrieval_capacity_planned, h2retrieval_capacity_online = self.calculate_h2retrieval_occupancy(year, hydrogen_defaults_h2retrieval_data) pipelines = 0 for element in list_of_elements_hinter: if year >= element.year_online: pipelines += 1 consumption = element.consumption_coefficient if consumption * energy.price != np.inf: element.df.loc[element.df['year'] == year, 'energy'] = consumption * throughput_online/pipelines * energy.price else: element.df.loc[element.df['year'] == year, 'energy'] = 0
[docs] def calculate_demurrage_cost(self, year): """Find the demurrage cost per type of vessel and sum all demurrage cost""" smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, total_calls, total_vol, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned, total_calls_planned, total_vol_planned = self.calculate_vessel_calls(year) berth_occupancy_planned, berth_occupancy_online, unloading_occupancy_planned, unloading_occupancy_online = self.calculate_berth_occupancy(year, smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned,handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned) berths = len(core.find_elements(self, Berth)) waiting_factor = \ core.occupancy_to_waitingfactor(occupancy=berth_occupancy_online, nr_of_servers_chk=berths, poly_order=6) waiting_time_hours = waiting_factor * unloading_occupancy_online * self.operational_hours / total_calls waiting_time_occupancy = waiting_time_hours * total_calls / self.operational_hours # Find the demurrage cost per type of vessel # if service_rate != 0: smallhydrogen = Vessel(**hydrogen_defaults.smallhydrogen_data) service_time_smallhydrogen = smallhydrogen.call_size / smallhydrogen.pump_capacity waiting_time_hours_smallhydrogen = waiting_factor * service_time_smallhydrogen penalty_time_smallhydrogen = max(0, waiting_time_hours_smallhydrogen - smallhydrogen.all_turn_time) demurrage_time_smallhydrogen = penalty_time_smallhydrogen * smallhydrogen_calls demurrage_cost_smallhydrogen = demurrage_time_smallhydrogen * smallhydrogen.demurrage_rate largehydrogen = Vessel(**hydrogen_defaults.largehydrogen_data) service_time_largehydrogen = largehydrogen.call_size / largehydrogen.pump_capacity waiting_time_hours_largehydrogen = waiting_factor * service_time_largehydrogen penalty_time_largehydrogen = max(0, waiting_time_hours_largehydrogen - largehydrogen.all_turn_time) demurrage_time_largehydrogen = penalty_time_largehydrogen * largehydrogen_calls demurrage_cost_largehydrogen = demurrage_time_largehydrogen * largehydrogen.demurrage_rate smallammonia = Vessel(**hydrogen_defaults.smallammonia_data) service_time_smallammonia = smallammonia.call_size / smallammonia.pump_capacity waiting_time_hours_smallammonia = waiting_factor * service_time_smallammonia penalty_time_smallammonia = max(0, waiting_time_hours_smallammonia - smallammonia.all_turn_time) demurrage_time_smallammonia = penalty_time_smallammonia * smallammonia_calls demurrage_cost_smallammonia = demurrage_time_smallammonia * smallammonia.demurrage_rate largeammonia = Vessel(**hydrogen_defaults.largeammonia_data) service_time_largeammonia = largeammonia.call_size / largeammonia.pump_capacity waiting_time_hours_largeammonia = waiting_factor * service_time_largeammonia penalty_time_largeammonia = max(0, waiting_time_hours_largeammonia - largeammonia.all_turn_time) demurrage_time_largeammonia = penalty_time_largeammonia * largeammonia_calls demurrage_cost_largeammonia = demurrage_time_largeammonia * largeammonia.demurrage_rate handysize = Vessel(**hydrogen_defaults.handysize_data) service_time_handysize = handysize.call_size / handysize.pump_capacity waiting_time_hours_handysize = waiting_factor * service_time_handysize penalty_time_handysize = max(0, waiting_time_hours_handysize - handysize.all_turn_time) demurrage_time_handysize = penalty_time_handysize * handysize_calls demurrage_cost_handysize = demurrage_time_handysize * handysize.demurrage_rate panamax = Vessel(**hydrogen_defaults.panamax_data) service_time_panamax = panamax.call_size / panamax.pump_capacity waiting_time_hours_panamax = waiting_factor * service_time_panamax penalty_time_panamax = max(0, waiting_time_hours_panamax - panamax.all_turn_time) demurrage_time_panamax = penalty_time_panamax * panamax_calls demurrage_cost_panamax = demurrage_time_panamax * panamax.demurrage_rate vlcc = Vessel(**hydrogen_defaults.vlcc_data) service_time_vlcc = vlcc.call_size / vlcc.pump_capacity waiting_time_hours_vlcc = waiting_factor * service_time_vlcc penalty_time_vlcc= max(0, waiting_time_hours_vlcc - vlcc.all_turn_time) demurrage_time_vlcc = penalty_time_vlcc * vlcc_calls demurrage_cost_vlcc = demurrage_time_vlcc * vlcc.demurrage_rate total_demurrage_cost = demurrage_cost_smallhydrogen + demurrage_cost_largehydrogen + demurrage_cost_smallammonia + demurrage_cost_largeammonia + demurrage_cost_handysize + demurrage_cost_panamax + demurrage_cost_vlcc self.demurrage.append(total_demurrage_cost)
[docs] def calculate_revenue(self, year, hydrogen_defaults_commodity_data): """ 1. calculate the value of the total throughput in year (throughput * handling fee) """ # gather the fee from the selected commodity commodity = Commodity(**hydrogen_defaults_commodity_data) fee = commodity.handling_fee throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements(year) if self.debug: print(' Revenues: {}'.format(int(throughput_online * fee))) try: self.revenues.append(throughput_online * fee) except: pass
# *** General functions
[docs] def calculate_vessel_calls(self, year=2019): """Calculate volumes to be transported and the number of vessel calls (both per vessel type and in total) """ # intialize values to be returned smallhydrogen_vol = 0 largehydrogen_vol = 0 smallammonia_vol = 0 largeammonia_vol = 0 handysize_vol = 0 panamax_vol = 0 vlcc_vol = 0 total_vol = 0 smallhydrogen_vol_planned = 0 largehydrogen_vol_planned = 0 smallammonia_vol_planned = 0 largeammonia_vol_planned = 0 handysize_vol_planned = 0 panamax_vol_planned = 0 vlcc_vol_planned = 0 total_vol_planned = 0 # gather volumes from each commodity scenario and calculate how much is transported with which vessel throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) commodities = core.find_elements(self, Commodity) for commodity in commodities: try: volume = commodity.scenario_data.loc[commodity.scenario_data['year'] == year]['volume'].item() smallhydrogen_vol += volume/volume * throughput_online * commodity.smallhydrogen_perc / 100 largehydrogen_vol += volume/volume * throughput_online * commodity.largehydrogen_perc / 100 smallammonia_vol += volume/volume * throughput_online * commodity.smallammonia_perc / 100 largeammonia_vol += volume/volume * throughput_online * commodity.largeammonia_perc / 100 handysize_vol += volume/volume * throughput_online * commodity.handysize_perc / 100 panamax_vol += volume/volume * throughput_online * commodity.panamax_perc / 100 vlcc_vol += volume/volume * throughput_online * commodity.vlcc_perc / 100 total_vol += volume/volume * throughput_online except: pass # gather vessels and calculate the number of calls each vessel type needs to make vessels = core.find_elements(self, Vessel) for vessel in vessels: if vessel.type == 'Smallhydrogen': smallhydrogen_calls = int(np.ceil(smallhydrogen_vol / vessel.call_size)) elif vessel.type == 'Largehydrogen': largehydrogen_calls = int(np.ceil(largehydrogen_vol / vessel.call_size)) elif vessel.type == 'Smallammonia': smallammonia_calls = int(np.ceil(smallammonia_vol / vessel.call_size)) elif vessel.type == 'Largeammonia': largeammonia_calls = int(np.ceil(largeammonia_vol / vessel.call_size)) elif vessel.type == 'Handysize': handysize_calls = int(np.ceil(handysize_vol / vessel.call_size)) elif vessel.type == 'Panamax': panamax_calls = int(np.ceil(panamax_vol / vessel.call_size)) elif vessel.type == 'VLCC': vlcc_calls = int(np.ceil(vlcc_vol / vessel.call_size)) total_calls = np.sum( [smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls]) for commodity in commodities: try: volume = commodity.scenario_data.loc[commodity.scenario_data['year'] == year]['volume'].item() smallhydrogen_vol_planned += volume * commodity.smallhydrogen_perc / 100 largehydrogen_vol_planned += volume * commodity.largehydrogen_perc / 100 smallammonia_vol_planned += volume * commodity.smallammonia_perc / 100 largeammonia_vol_planned += volume * commodity.largeammonia_perc / 100 handysize_vol_planned += volume * commodity.handysize_perc / 100 panamax_vol_planned += volume * commodity.panamax_perc / 100 vlcc_vol_planned += volume * commodity.vlcc_perc / 100 total_vol_planned += volume except: pass for vessel in vessels: if vessel.type == 'Smallhydrogen': smallhydrogen_calls_planned = int(np.ceil(smallhydrogen_vol_planned / vessel.call_size)) elif vessel.type == 'Largehydrogen': largehydrogen_calls_planned = int(np.ceil(largehydrogen_vol_planned / vessel.call_size)) elif vessel.type == 'Smallammonia': smallammonia_calls_planned = int(np.ceil(smallammonia_vol_planned / vessel.call_size)) elif vessel.type == 'Largeammonia': largeammonia_calls_planned = int(np.ceil(largeammonia_vol_planned / vessel.call_size)) elif vessel.type == 'Handysize': handysize_calls_planned = int(np.ceil(handysize_vol_planned / vessel.call_size)) elif vessel.type == 'Panamax': panamax_calls_planned = int(np.ceil(panamax_vol_planned / vessel.call_size)) elif vessel.type == 'VLCC': vlcc_calls_planned = int(np.ceil(vlcc_vol_planned / vessel.call_size)) total_calls_planned = np.sum( [smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned]) return smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, \ largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, total_calls, total_vol, \ smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, \ largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned, \ total_calls_planned, total_vol_planned
[docs] def calculate_berth_occupancy(self, year, smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned): """- Find all cranes and sum their effective_capacity to get service_capacity - Divide callsize_per_vessel by service_capacity and add mooring time to get total time at berth - Occupancy is total_time_at_berth divided by operational hours """ # list all vessel objects in system list_of_elements = core.find_elements(self, Jetty) # find the total service rate and determine the time at berth (in hours, per vessel type and in total) nr_of_jetty_planned = 0 nr_of_jetty_online = 0 if list_of_elements != []: for element in list_of_elements: nr_of_jetty_planned += 1 if year >= element.year_online: nr_of_jetty_online += 1 # estimate berth occupancy time_at_berth_smallhydrogen_planned = smallhydrogen_calls_planned * ( (hydrogen_defaults.smallhydrogen_data["call_size"] / hydrogen_defaults.smallhydrogen_data["pump_capacity"]) + hydrogen_defaults.smallhydrogen_data["mooring_time"]) time_at_berth_largehydrogen_planned = largehydrogen_calls_planned * ( (hydrogen_defaults.largehydrogen_data["call_size"] / hydrogen_defaults.largehydrogen_data["pump_capacity"]) + hydrogen_defaults.largehydrogen_data["mooring_time"]) time_at_berth_smallammonia_planned = smallammonia_calls_planned * ( (hydrogen_defaults.smallammonia_data["call_size"] / hydrogen_defaults.smallammonia_data["pump_capacity"]) + hydrogen_defaults.smallammonia_data["mooring_time"]) time_at_berth_largeammonia_planned = largeammonia_calls_planned * ( (hydrogen_defaults.largeammonia_data["call_size"] / hydrogen_defaults.largeammonia_data["pump_capacity"]) + hydrogen_defaults.largeammonia_data["mooring_time"]) time_at_berth_handysize_planned = handysize_calls_planned * ( (hydrogen_defaults.handysize_data["call_size"] / hydrogen_defaults.handysize_data["pump_capacity"]) + hydrogen_defaults.handysize_data["mooring_time"]) time_at_berth_panamax_planned = panamax_calls_planned * ( (hydrogen_defaults.panamax_data["call_size"] / hydrogen_defaults.panamax_data["pump_capacity"]) + hydrogen_defaults.panamax_data["mooring_time"]) time_at_berth_vlcc_planned = vlcc_calls_planned * ( (hydrogen_defaults.vlcc_data["call_size"] / hydrogen_defaults.vlcc_data["pump_capacity"] ) + hydrogen_defaults.vlcc_data["mooring_time"]) total_time_at_berth_planned = np.sum( [time_at_berth_smallhydrogen_planned, time_at_berth_largehydrogen_planned, time_at_berth_smallammonia_planned, time_at_berth_largeammonia_planned, time_at_berth_handysize_planned, time_at_berth_panamax_planned, time_at_berth_vlcc_planned]) # berth_occupancy is the total time at berth divided by the operational hours berth_occupancy_planned = total_time_at_berth_planned / (self.operational_hours * nr_of_jetty_planned) # estimate crane occupancy time_at_unloading_smallhydrogen_planned = smallhydrogen_calls * (hydrogen_defaults.smallhydrogen_data["call_size"] / hydrogen_defaults.smallhydrogen_data["pump_capacity"]) time_at_unloading_largehydrogen_planned = largehydrogen_calls * (hydrogen_defaults.largehydrogen_data["call_size"] / hydrogen_defaults.largehydrogen_data["pump_capacity"]) time_at_unloading_smallammonia_planned = smallammonia_calls * (hydrogen_defaults.smallammonia_data["call_size"] / hydrogen_defaults.smallammonia_data["pump_capacity"]) time_at_unloading_largeammonia_planned = largeammonia_calls * (hydrogen_defaults.largeammonia_data["call_size"] / hydrogen_defaults.handysize_data["pump_capacity"]) time_at_unloading_handysize_planned = handysize_calls * (hydrogen_defaults.handysize_data["call_size"] / hydrogen_defaults.largeammonia_data["pump_capacity"]) time_at_unloading_panamax_planned = panamax_calls * (hydrogen_defaults.panamax_data["call_size"] / hydrogen_defaults.panamax_data["pump_capacity"]) time_at_unloading_vlcc_planned = vlcc_calls * (hydrogen_defaults.vlcc_data["call_size"] / hydrogen_defaults.vlcc_data["pump_capacity"]) total_time_at_unloading_planned = np.sum( [time_at_unloading_smallhydrogen_planned, time_at_unloading_largehydrogen_planned, time_at_unloading_smallammonia_planned, time_at_unloading_largeammonia_planned, time_at_unloading_handysize_planned, time_at_unloading_panamax_planned, time_at_unloading_vlcc_planned]) # berth_occupancy is the total time at berth divided by the operational hours unloading_occupancy_planned = total_time_at_unloading_planned / (self.operational_hours * nr_of_jetty_planned) if nr_of_jetty_online != 0: time_at_berth_smallhydrogen_online = smallhydrogen_calls * ( (hydrogen_defaults.smallhydrogen_data["call_size"] / hydrogen_defaults.smallhydrogen_data["pump_capacity"]) + hydrogen_defaults.smallhydrogen_data["mooring_time"]) time_at_berth_largehydrogen_online = largehydrogen_calls * ( (hydrogen_defaults.largehydrogen_data["call_size"] / hydrogen_defaults.largehydrogen_data["pump_capacity"]) + hydrogen_defaults.largehydrogen_data["mooring_time"]) time_at_berth_smallammonia_online = smallammonia_calls * ( (hydrogen_defaults.smallammonia_data["call_size"] / hydrogen_defaults.smallammonia_data["pump_capacity"]) + hydrogen_defaults.smallammonia_data["mooring_time"]) time_at_berth_largeammonia_online = largeammonia_calls * ( (hydrogen_defaults.largeammonia_data["call_size"] / hydrogen_defaults.largeammonia_data["pump_capacity"]) + hydrogen_defaults.largeammonia_data["mooring_time"]) time_at_berth_handysize_online = handysize_calls * ( (hydrogen_defaults.handysize_data["call_size"] / hydrogen_defaults.handysize_data["pump_capacity"]) + hydrogen_defaults.handysize_data["mooring_time"]) time_at_berth_panamax_online = panamax_calls * ( (hydrogen_defaults.panamax_data["call_size"] / hydrogen_defaults.panamax_data["pump_capacity"]) + hydrogen_defaults.panamax_data["mooring_time"]) time_at_berth_vlcc_online = vlcc_calls * ( (hydrogen_defaults.vlcc_data["call_size"] / hydrogen_defaults.vlcc_data["pump_capacity"]) + hydrogen_defaults.vlcc_data["mooring_time"]) total_time_at_berth_online = np.sum( [time_at_berth_smallhydrogen_online,time_at_berth_largehydrogen_online, time_at_berth_smallammonia_online, time_at_berth_largeammonia_online,time_at_berth_handysize_online, time_at_berth_panamax_online, time_at_berth_vlcc_online]) # berth_occupancy is the total time at berth devided by the operational hours berth_occupancy_online = min([total_time_at_berth_online / (self.operational_hours * nr_of_jetty_online), 1]) time_at_unloading_smallhydrogen_online = smallhydrogen_calls * (hydrogen_defaults.smallhydrogen_data["call_size"] / hydrogen_defaults.smallhydrogen_data["pump_capacity"]) time_at_unloading_largehydrogen_online = largehydrogen_calls * (hydrogen_defaults.largehydrogen_data["call_size"] / hydrogen_defaults.largehydrogen_data["pump_capacity"]) time_at_unloading_smallammonia_online = smallammonia_calls * (hydrogen_defaults.smallammonia_data["call_size"] / hydrogen_defaults.smallammonia_data["pump_capacity"]) time_at_unloading_largeammonia_online = largeammonia_calls * (hydrogen_defaults.largeammonia_data["call_size"] / hydrogen_defaults.largeammonia_data["pump_capacity"]) time_at_unloading_handysize_online = handysize_calls * (hydrogen_defaults.handysize_data["call_size"] / hydrogen_defaults.handysize_data["pump_capacity"]) time_at_unloading_panamax_online = panamax_calls * (hydrogen_defaults.panamax_data["call_size"] / hydrogen_defaults.panamax_data["pump_capacity"]) time_at_unloading_vlcc_online = vlcc_calls * (hydrogen_defaults.vlcc_data["call_size"] / hydrogen_defaults.vlcc_data["pump_capacity"]) total_time_at_unloading_online = np.sum( [time_at_unloading_smallhydrogen_online, time_at_unloading_largehydrogen_online, time_at_unloading_smallammonia_online, time_at_unloading_largeammonia_online, time_at_unloading_handysize_online, time_at_unloading_panamax_online, time_at_unloading_vlcc_online]) # berth_occupancy is the total time at berth devided by the operational hours unloading_occupancy_online = min([total_time_at_unloading_online / (self.operational_hours * nr_of_jetty_online), 1]) else: berth_occupancy_online = float("inf") unloading_occupancy_online = float("inf") else: # if there are no cranes the berth occupancy is 'infinite' so a berth is certainly needed berth_occupancy_planned = float("inf") berth_occupancy_online = float("inf") unloading_occupancy_planned = float("inf") unloading_occupancy_online = float("inf") return berth_occupancy_planned, berth_occupancy_online, unloading_occupancy_planned, unloading_occupancy_online
[docs] def calculate_h2retrieval_occupancy(self, year, hydrogen_defaults_h2retrieval_data): """ - Divide the throughput by the service rate to get the total hours in a year - Occupancy is total_time_at_h2retrieval divided by operational hours """ # Find throughput throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh= self.throughput_elements(year) Demand = [] for commodity in core.find_elements(self, Commodity): try: Demand = commodity.scenario_data.loc[commodity.scenario_data['year'] == year]['volume'].item() except: pass # find the total service rate and determine the time at h2retrieval h2retrieval_capacity_planned = 0 h2retrieval_capacity_online = 0 h2retrieval = H2retrieval(**hydrogen_defaults_h2retrieval_data) capacity = h2retrieval.capacity yearly_capacity = capacity * self.operational_hours list_of_elements = core.find_elements(self, H2retrieval) if list_of_elements != []: for element in list_of_elements: h2retrieval_capacity_planned += yearly_capacity if year >= element.year_online: h2retrieval_capacity_online += yearly_capacity # h2retrieval_occupancy is the total time at h2retrieval divided by the operational hours plant_occupancy_planned = Demand / h2retrieval_capacity_planned if h2retrieval_capacity_online != 0: time_at_plant_online = throughput_online / h2retrieval_capacity_online# element.capacity # h2retrieval occupancy is the total time at h2retrieval divided by the operational hours plant_occupancy_online = min([time_at_plant_online, 1]) else: plant_occupancy_online = float("inf") else: # if there are no cranes the berth occupancy is 'infinite' so a berth is certainly needed plant_occupancy_planned = float("inf") plant_occupancy_online = float("inf") return plant_occupancy_planned, plant_occupancy_online, h2retrieval_capacity_planned, h2retrieval_capacity_online
[docs] def throughput_elements(self, year): """ - Find which elements are important and needs to be included - Find from each element the online capacity - Find where the lowest value is present, in the capacity or in the demand """ #Find jetty capacity Jetty_cap_planned = 0 Jetty_cap = 0 for element in core.find_elements(self, Jetty): Jetty_cap_planned += ((hydrogen_defaults.smallhydrogen_data["pump_capacity"] + hydrogen_defaults.largehydrogen_data["pump_capacity"] + hydrogen_defaults.smallammonia_data["pump_capacity"] + hydrogen_defaults.largeammonia_data["pump_capacity"] + hydrogen_defaults.handysize_data["pump_capacity"] + hydrogen_defaults.panamax_data["pump_capacity"] + hydrogen_defaults.vlcc_data["pump_capacity"])/7 * self.operational_hours) if year >= element.year_online: Jetty_cap += ((hydrogen_defaults.smallhydrogen_data["pump_capacity"] + hydrogen_defaults.largehydrogen_data["pump_capacity"] + hydrogen_defaults.smallammonia_data["pump_capacity"] + hydrogen_defaults.largeammonia_data["pump_capacity"] + hydrogen_defaults.handysize_data["pump_capacity"] + hydrogen_defaults.panamax_data["pump_capacity"] + hydrogen_defaults.vlcc_data["pump_capacity"])/7 * self.operational_hours) # Find pipeline jetty capacity pipelineJ_capacity_planned = 0 pipelineJ_capacity_online = 0 list_of_elements = core.find_elements(self, Pipeline_Jetty) if list_of_elements != []: for element in list_of_elements: pipelineJ_capacity_planned += element.capacity * self.operational_hours if year >= element.year_online: pipelineJ_capacity_online += element.capacity * self.operational_hours # Find storage capacity storage_capacity_planned = 0 storage_capacity_online = 0 list_of_elements = core.find_elements(self, Storage) if list_of_elements != []: for element in list_of_elements: storage_capacity_planned += element.capacity if year >= element.year_online: storage_capacity_online += element.capacity storage_cap_planned = storage_capacity_planned / self.allowable_dwelltime / 1.1 storage_cap_online = storage_capacity_online / self.allowable_dwelltime/ 1.1 #Find H2retrieval capacity h2retrieval_capacity_planned = 0 h2retrieval_capacity_online = 0 list_of_elements = core.find_elements(self, H2retrieval) if list_of_elements != []: for element in list_of_elements: h2retrieval_capacity_planned += element.capacity * self.operational_hours if year >= element.year_online: h2retrieval_capacity_online += element.capacity * self.operational_hours # Find pipeline hinter capacity pipelineh_capacity_planned = 0 pipelineh_capacity_online = 0 list_of_elements = core.find_elements(self, Pipeline_Hinter) if list_of_elements != []: for element in list_of_elements: pipelineh_capacity_planned += element.capacity * self.operational_hours if year >= element.year_online: pipelineh_capacity_online += element.capacity * self.operational_hours #Find demand Demand = [] for commodity in core.find_elements(self, Commodity): try: Demand = commodity.scenario_data.loc[commodity.scenario_data['year'] == year]['volume'].item() except: pass # Find the possible and online throuhgput including all elements throughput_planned = min(Jetty_cap_planned, pipelineJ_capacity_planned, storage_cap_planned, h2retrieval_capacity_planned, pipelineh_capacity_planned, Demand) throughput_online = min(h2retrieval_capacity_online, Jetty_cap, pipelineJ_capacity_online, pipelineh_capacity_online, storage_cap_online, Demand) # Find from all elements the possible throughput if they were not there throughput_planned_jetty = min(pipelineJ_capacity_planned, storage_cap_planned, h2retrieval_capacity_planned, pipelineh_capacity_planned, Demand) throughput_planned_pipej = min(Jetty_cap_planned, storage_cap_planned, h2retrieval_capacity_planned, pipelineh_capacity_planned, Demand) throughput_planned_storage = min(Jetty_cap_planned, pipelineJ_capacity_planned, h2retrieval_capacity_planned, pipelineh_capacity_planned, Demand) throughput_planned_h2retrieval = min(Jetty_cap_planned, pipelineJ_capacity_planned, storage_cap_planned, pipelineh_capacity_planned, Demand) throughput_planned_pipeh = min(Jetty_cap_planned, pipelineJ_capacity_planned, storage_cap_planned, h2retrieval_capacity_planned, Demand) return throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh self.throughput.append(throughput_online)
[docs] def check_throughput_available(self, year): list_of_elements = core.find_elements(self, Storage) capacity = 0 for element in list_of_elements: capacity += element.capacity throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements(year) storage_capacity_dwelltime_throughput = (throughput_planned_storage * self.allowable_dwelltime) * 1.1 # IJzerman p.26 # when there are more slots than installed cranes ... if capacity < storage_capacity_dwelltime_throughput: return True else: return False
# *** Plotting functions
[docs] def terminal_elements_plot(self, width=0.1, alpha=0.6, fontsize=20): """Gather data from Terminal and plot which elements come online when""" # collect elements to add to plot years = [] berths = [] jettys = [] pipelines_jetty = [] storages = [] h2retrievals = [] pipelines_hinterland = [] throughputs_online = [] matplotlib.rcParams.update({'font.size': 18}) for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) berths.append(0) jettys.append(0) pipelines_jetty.append(0) storages.append(0) h2retrievals.append(0) pipelines_hinterland.append(0) throughputs_online.append(0) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: berths[-1] += 1 if isinstance(element, Jetty): if year >= element.year_online: jettys[-1] += 1 if isinstance(element, Pipeline_Jetty): if year >= element.year_online: pipelines_jetty[-1] += 1 if isinstance(element, Storage): if year >= element.year_online: storages[-1] += 1 if isinstance(element, H2retrieval): if year >= element.year_online: h2retrievals[-1] += 1 if isinstance(element, Pipeline_Hinter): if year >= element.year_online: pipelines_hinterland[-1] += 1 # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x + 0 * width for x in years], berths, width=width, alpha=alpha, label="Berths", color='#aec7e8', edgecolor='darkgrey') ax1.bar([x + 1 * width for x in years], jettys, width=width, alpha=alpha, label="Jettys", color='#c7c7c7', edgecolor='darkgrey') ax1.bar([x + 2 * width for x in years], pipelines_jetty, width=width, alpha=alpha, label="Pipelines jetty", color='#ffbb78', edgecolor='darkgrey') ax1.bar([x + 3 * width for x in years], storages, width=width, alpha=alpha, label="Storages", color='#9edae5', edgecolor='darkgrey') ax1.bar([x + 4 * width for x in years], h2retrievals, width=width, alpha=alpha, label="H2 retrievals", color='#DBDB8D', edgecolor='darkgrey') ax1.bar([x + 5 * width for x in years], pipelines_hinterland, width=width, alpha=alpha, label="Pipeline hinter", color='#c49c94', edgecolor='darkgrey') # added vertical lines for mentioning the different phases # plt.axvline(x=2025.6, color='k', linestyle='--') # plt.axvline(x=2023.4, color='k', linestyle='--') # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass #Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online #Making a second graph ax2 = ax1.twinx() ax2.step(years, demand['demand'].values, label="Demand [t/y]", where='mid', color='#ff9896') ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') # added boxes # props = dict(boxstyle='round', facecolor='white', alpha=0.5) # # place a text box in upper left in axes coords # ax1.text(0.30, 0.60, 'phase 1', transform=ax1.transAxes, fontsize=18, bbox=props) # ax1.text(0.55, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=18, bbox=props) # ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=18, bbox=props) # title and labels ax1.set_title('Terminal elements online', fontsize=fontsize) ax1.set_xlabel('Years', fontsize=fontsize) ax1.set_ylabel('Elements on line [nr]', fontsize=fontsize) ax2.set_ylabel('Demand/throughput[t/y]', fontsize=fontsize) # ticks and tick labels ax1.set_xticks([x for x in years]) ax1.set_xticklabels([int(x) for x in years], rotation='vertical', fontsize=fontsize) max_elements = max([max(berths), max(jettys), max(pipelines_jetty), max(storages), max(h2retrievals), max(pipelines_hinterland)]) ax1.set_yticks([x for x in range(0, max_elements + 1 + 2, 2)]) ax1.set_yticklabels([int(x) for x in range(0, max_elements + 1 + 2, 2)], fontsize=fontsize) # print legend fig.legend(loc='lower center', bbox_to_anchor=(0, -.01, .9, 0.7), fancybox=True, shadow=True, ncol=4, fontsize=fontsize) fig.subplots_adjust(bottom=0.2)
[docs] def demand_terminal_plot(self, width=0.1, alpha=0.6): # Adding the throughput years = [] throughputs_online = [] storage_capacity_online = [] h2retrievals_capacity = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) storage_capacity_online.append(0) h2retrievals_capacity.append(0) # Find storage capacity for element in self.elements: if isinstance(element, Storage): if year >= element.year_online: storage_capacity_online[-1] += element.capacity / self.allowable_dwelltime / 1.1 for element in self.elements: if isinstance(element, H2retrieval): if year >= element.year_online: h2retrievals_capacity[-1] += element.capacity * self.operational_hours # for element in self.elements: # if isinstance(element, Jetty): # if year >= element.year_online: # jettys_capacity[-1] += element.capacity * self.operational_hours # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online #Making a second graph # ax2 = ax1.twinx() fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x + 0 * width for x in years], storage_capacity_online, width=width, alpha=alpha, label="Storage capacity", color='#9edae5', edgecolor='darkgrey') ax1.bar([x + 1 * width for x in years], h2retrievals_capacity, width=width, alpha=alpha, label="H2 retrieval capacity", color='#dbdb8d', edgecolor='darkgrey') ax1.step(years, demand['demand'].values, label="Demand [t/y]", where='mid', color='#ff9896') ax1.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') # added vertical lines for mentioning the different phases plt.axvline(x=2025.6, color='k', linestyle='--') plt.axvline(x=2023.4, color='k', linestyle='--') # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.60, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.55, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Ton per annum') ax1.set_title('Demand vs Throughput') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def terminal_occupancy_plot(self, width=0.3, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # collect elements to add to plot years = [] berths_occupancy = [] waiting_factor = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) berths_occupancy.append(0) waiting_factor.append(0) smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, total_calls, total_vol, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned, total_calls_planned, total_vol_planned = self.calculate_vessel_calls( year) berth_occupancy_planned, berth_occupancy_online, unloading_occupancy_planned, unloading_occupancy_online = self.calculate_berth_occupancy( year, smallhydrogen_calls, largehydrogen_calls, smallammonia_calls, largeammonia_calls, handysize_calls, panamax_calls, vlcc_calls, smallhydrogen_calls_planned, largehydrogen_calls_planned, smallammonia_calls_planned, largeammonia_calls_planned, handysize_calls_planned, panamax_calls_planned, vlcc_calls_planned) factor = \ core.occupancy_to_waitingfactor(occupancy=berth_occupancy_online, nr_of_servers_chk=berths, poly_order=6) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: berths_occupancy[-1] = berth_occupancy_online for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: waiting_factor[-1] = factor # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass #Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x + 0 * width for x in years], berths_occupancy, width=width, alpha=alpha, label="Berth occupancy [-]", color='#aec7e8', edgecolor='darkgrey') # ax1.bar([x + 1 * width for x in years], waiting_factor, width=width, alpha=alpha, label="Berth occupancy [-]", color='grey', edgecolor='darkgrey') # added vertical lines for mentioning the different phases plt.axvline(x=2025.3, color='k', linestyle='--') plt.axvline(x=2023.3, color='k', linestyle='--') # Adding a horizontal line which shows the allowable berth occupancy horiz_line_data = np.array([self.allowable_berth_occupancy for i in range(len(years))]) plt.plot(years, horiz_line_data, 'r--', color='grey', label="Allowable berth occupancy [-]") for i, occ in enumerate(berths_occupancy): occ = occ if type(occ) != float else 0 ax1.text(x = years[i] - 0.1, y = occ + 0.01, s = "{:04.2f}".format(occ), size=15) # for i, occ in enumerate(waiting_factor): # occ = occ if type(occ) != float else 0 # ax1.text(x=years[i] - 0.1, y=occ + 0.01, s="{:04.2f}".format(occ), size=15) ax2 = ax1.twinx() ax2.step(years, demand['demand'].values, label="Demand [t/y]", where='mid', color='#ff9896') ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') plt.ylim(0, 6000000) # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.70, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.70, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.70, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Berth occupancy [-]') ax2.set_ylabel('Demand [t/y]') ax1.set_title('Berth occupancy') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def plant_occupancy_plot(self, width=0.3, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # collect elements to add to plot years = [] plants_occupancy = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) plants_occupancy.append(0) hydrogen_defaults_h2retrieval_data = self.h2retrieval_type_defaults plant_occupancy_planned, plant_occupancy_online, h2retrieval_capacity_planned, h2retrieval_capacity_online = self.calculate_h2retrieval_occupancy(year, hydrogen_defaults_h2retrieval_data) for element in self.elements: if isinstance(element, H2retrieval): if year >= element.year_online: plants_occupancy[-1] = plant_occupancy_online # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x for x in years], plants_occupancy, width=width, alpha=alpha, label="Plant occupancy [-]", color='#aec7e8', edgecolor='darkgrey') for i, occ in enumerate(plants_occupancy): ax1.text(x=years[i], y=occ + 0.01, s="{:04.2f}".format(occ), size=15) # added vertical lines for mentioning the different phases plt.axvline(x=2025.3, color='k', linestyle='--') plt.axvline(x=2023.3, color='k', linestyle='--') # Adding a horizontal line which shows the allowable plant occupancy horiz_line_data = np.array([self.h2retrieval_trigger for i in range(len(years))]) plt.plot(years, horiz_line_data, 'r--', color='grey', label="Allowable plant occupancy [-]") ax2 = ax1.twinx() ax2.step(years, demand['demand'].values, label="Demand [t/y]", where='mid', color='#ff9896') ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.70, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.70, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.70, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Plant occupancy [-]') ax2.set_ylabel('Demand [t/y]') ax1.set_title('Plant occupancy') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def Jetty_capacity_plot(self, width=0.3, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # collect elements to add to plot years = [] jettys = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) jettys.append(0) for element in self.elements: if isinstance(element, Jetty): if year >= element.year_online: jettys[-1] += 1 # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x for x in years], jettys, width=width, alpha=alpha, label="Jettys [nr]", color='#c7c7c7', edgecolor='darkgrey') # added vertical lines for mentioning the different phases plt.axvline(x=2025.3, color='k', linestyle='--') plt.axvline(x=2023.3, color='k', linestyle='--') for i, occ in enumerate(jettys): occ = occ if type(occ) != float else 0 ax1.text(x=years[i], y=occ + 0.02, s="{:01.0f}".format(occ), size=15) ax2 = ax1.twinx() ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') ax2.step(years, demand['demand'].values, label="Demand [t/y]", where='mid', color='#ff9896') # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.60, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Elements on line [nr]') ax2.set_ylabel('Demand [t/y]') ax1.set_title('Jettys') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def Pipeline1_capacity_plot(self, width=0.2, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # collect elements to add to plot years = [] pipeline_jetty = [] jettys = [] pipeline_jetty_cap = [] jettys_cap = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) pipeline_jetty.append(0) jettys.append(0) pipeline_jetty_cap.append(0) jettys_cap.append(0) for element in self.elements: if isinstance(element, Pipeline_Jetty): if year >= element.year_online: pipeline_jetty[-1] += 1 for element in self.elements: if isinstance(element, Jetty): if year >= element.year_online: jettys[-1] += 1 for element in self.elements: if isinstance(element, Pipeline_Jetty): if year >= element.year_online: pipeline_jetty_cap[-1] += element.capacity for element in core.find_elements(self, Jetty): if isinstance(element, Jetty): if year >= element.year_online: jettys_cap[-1] += hydrogen_defaults.largeammonia_data["pump_capacity"] # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x - 0.5 * width for x in years], jettys_cap, width=width, alpha=alpha, label="Jetty unloading capacity", color='#c7c7c7', edgecolor='darkgrey') ax1.bar([x + 0.5 * width for x in years], pipeline_jetty_cap, width=width, alpha=alpha, label="Pipeline Jetty - Storage capacity", color='#ffbb78', edgecolor='darkgrey') # added vertical lines for mentioning the different phases plt.axvline(x=2025.3, color='k', linestyle='--') plt.axvline(x=2023.3, color='k', linestyle='--') # Plot second ax ax2 = ax1.twinx() ax2.step(years, demand['demand'].values, label="Demand", where='mid', color='#ff9896') ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') plt.ylim(0, 6000000) # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.60, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Unloading capacity Jetty & capacity Pipeline [t/h]') ax2.set_ylabel('Demand [t/y]') ax1.set_title('Capacity Jetty & Pipeline') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def Storage_capacity_plot(self, width=0.25, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # get crane service capacity and storage capacity years = [] storages = [] storages_capacity = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) storages.append(0) storages_capacity.append(0) for element in self.elements: if isinstance(element, Storage): if year >= element.year_online: storages[-1] += 1 storages_capacity[-1] += element.capacity # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x for x in years], storages, width=width, alpha=alpha, label="Storages", color='#9edae5', edgecolor='darkgrey') # added vertical lines for mentioning the different phases plt.axvline(x=2025.5, color='k', linestyle='--') plt.axvline(x=2023.5, color='k', linestyle='--') for i, occ in enumerate(storages): occ = occ if type(occ) != float else 0 ax1.text(x = years[i] - 0.05, y = occ + 0.2, s = "{:01.0f}".format(occ), size=15) ax2 = ax1.twinx() ax2.step(years, demand['demand'].values, label="Demand", where='mid',color='#ff9896') ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') ax2.step(years, storages_capacity, label="Storages capacity", where='mid', linestyle = '--', color='steelblue') # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.60, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Storages [nr]') ax2.set_ylabel('Demand/Capacity [t/y]') ax1.set_title('Storage capacity') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def H2retrieval_capacity_plot(self, width=0.25, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # get crane service capacity and storage capacity years = [] h2retrievals = [] h2retrievals_capacity = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) h2retrievals.append(0) h2retrievals_capacity.append(0) for element in self.elements: if isinstance(element, H2retrieval): if year >= element.year_online: h2retrievals[-1] += 1 h2retrievals_capacity[-1] += element.capacity * self.operational_hours # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # Adding the throughput years = [] throughputs_online = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) throughputs_online.append(0) throughput_online, throughput_planned, throughput_planned_jetty, throughput_planned_pipej, throughput_planned_storage, throughput_planned_h2retrieval, throughput_planned_pipeh = self.throughput_elements( year) for element in self.elements: if isinstance(element, Berth): if year >= element.year_online: throughputs_online[-1] = throughput_online # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x for x in years], h2retrievals, width=width, alpha=alpha, label="H2 retrieval", color='#DBDB8D', edgecolor='darkgrey') #added vertical lines for mentioning the different phases plt.axvline(x=2025.3, color = 'k', linestyle = '--') plt.axvline(x=2023.3, color='k', linestyle='--') for i, occ in enumerate(h2retrievals): occ = occ if type(occ) != float else 0 ax1.text(x = years[i] - 0.05, y = occ + 0.2, s = "{:01.0f}".format(occ), size=15) ax2 = ax1.twinx() ax2.step(years, demand['demand'].values, label="Demand", where='mid',color='#ff9896') ax2.step(years, throughputs_online, label="Throughput [t/y]", where='mid', color='#aec7e8') ax2.step(years, h2retrievals_capacity, label="H2 retrieval capacity", where='mid', linestyle = '--', color='darkgrey') #added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.60,'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('H2 retrieval [nr]') ax2.set_ylabel('Demand/Capacity [t/y]') ax1.set_title('H2 retrieval capacity') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)
[docs] def Pipeline2_capacity_plot(self, width=0.2, alpha=0.6): """Gather data from Terminal and plot which elements come online when""" # collect elements to add to plot years = [] pipeline_hinterland = [] h2retrievals = [] pipeline_hinterland_cap = [] h2retrieval_cap = [] for year in range(self.startyear, self.startyear + self.lifecycle): years.append(year) pipeline_hinterland.append(0) h2retrievals.append(0) pipeline_hinterland_cap.append(0) h2retrieval_cap.append(0) for element in self.elements: if isinstance(element, Pipeline_Hinter): if year >= element.year_online: pipeline_hinterland[-1] += 1 for element in self.elements: if isinstance(element, H2retrieval): if year >= element.year_online: h2retrievals[-1] += 1 for element in self.elements: if isinstance(element, Pipeline_Hinter): if year >= element.year_online: pipeline_hinterland_cap[-1] += element.capacity for element in self.elements: if isinstance(element, H2retrieval): if year >= element.year_online: h2retrieval_cap[-1] += element.capacity # get demand demand = pd.DataFrame() demand['year'] = list(range(self.startyear, self.startyear + self.lifecycle)) demand['demand'] = 0 for commodity in core.find_elements(self, Commodity): try: for column in commodity.scenario_data.columns: if column in commodity.scenario_data.columns and column != "year": demand['demand'] += commodity.scenario_data[column] except: pass # generate plot fig, ax1 = plt.subplots(figsize=(20, 10)) ax1.bar([x - 0.5 * width for x in years], pipeline_hinterland, width=width, alpha=alpha, label="Number of pipeline H2 retrieval - Hinterland", color='#c49c94', edgecolor='darkgrey') ax1.bar([x + 0.5 * width for x in years], h2retrievals, width=width, alpha=alpha, label="Number of H2 retrievals", color='#DBDB8D', edgecolor='darkgrey') for i, occ in enumerate(pipeline_hinterland): occ = occ if type(occ) != float else 0 ax1.text(x=years[i] - 0.25, y=occ + 0.05, s="{:01.0f}".format(occ), size=15) for i, occ in enumerate(h2retrievals): occ = occ if type(occ) != float else 0 ax1.text(x=years[i] + 0.15, y=occ + 0.05, s="{:01.0f}".format(occ), size=15) # added vertical lines for mentioning the different phases plt.axvline(x=2025.3, color='k', linestyle='--') plt.axvline(x=2023.3, color='k', linestyle='--') # Plot second ax ax2 = ax1.twinx() ax2.step(years, pipeline_hinterland_cap, label="Pipeline hinterland capacity", where='mid', linestyle = '--', color='#c49c94') ax2.step(years, h2retrieval_cap, label="H2 retrieval capacity", where='mid', linestyle = '--', color='darkgrey') # added boxes props = dict(boxstyle='round', facecolor='white', alpha=0.5) # place a text box in upper left in axes coords ax1.text(0.30, 0.60, 'phase 1', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.57, 0.60, 'phase 2', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.text(0.82, 0.60, 'phase 3', transform=ax1.transAxes, fontsize=14, bbox=props) ax1.set_xlabel('Years') ax1.set_ylabel('Nr of elements') ax2.set_ylabel('Capacity Pipeline & loading capacity H2 retrieval [t/h]') ax1.set_title('Capacity Pipeline & H2 retrieval') ax1.set_xticks([x for x in years]) ax1.set_xticklabels(years) fig.legend(loc=1)