Skip to content
Snippets Groups Projects
Commit 12d52ee4 authored by Andi's avatar Andi
Browse files

Merge branch 'feature/cost_estimation_update' into 'develop'

Implemented changes to correct faulty imports due to old template

See merge request !56
parents f9b1d3c6 7d9f6632
No related branches found
No related tags found
5 merge requests!263Apply 1 suggestion(s) to 1 file(s),!261Reintruduced automatic flight condition selection,!245Apply 1 suggestion(s) to 1 file(s),!233Initial open source version,!56Implemented changes to correct faulty imports due to old template
Showing
with 50 additions and 107 deletions
......@@ -20,9 +20,9 @@ def read_energy_carrier(root_of_aircraft_exchange_tree, runtime_output):
# Attempt to extract information on energy carrier from aircraft exchange file.
try:
# Find all 'propulsor' nodes in aircraft exchange file.
# Find all 'energy_carrier' nodes in aircraft exchange file.
energy_carrier_node_list = root_of_aircraft_exchange_tree.findall('.//energy_carrier')
# Check, if propulsor nodes exist.
# Check, if 'energy_carrier' nodes exist.
if not energy_carrier_node_list:
# Raise error, if no energy carrier node exists.
raise ValueError('No energy carriers nodes found in the aircraft exchange file. Program aborted.')
......@@ -44,7 +44,7 @@ def read_energy_carrier(root_of_aircraft_exchange_tree, runtime_output):
energy_carrier = 'hybrid'
# Raise error, if 'energy_carrier_list' is empty.
else:
raise ValueError('No energy carrier node found in propulsor nodes. Program aborted.')
raise ValueError('No energy carrier node found. Program aborted.')
# Exception handling for ValueError.
except ValueError as e:
......@@ -52,35 +52,3 @@ def read_energy_carrier(root_of_aircraft_exchange_tree, runtime_output):
sys.exit(1)
return energy_carrier
def read_engine_configuration(root_of_aircraft_exchange_tree, runtime_output):
"""Read engine configuration.
Read engine configuration from aircraft exchange file.
:param ElementTree root_of_aircraft_exchange_tree: Root of aircraft exchange XML
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return str engine_configuration: Information on engine configuration
"""
engine_configuration = 'xyz'
print(engine_configuration)
return engine_configuration
def read_tank_configuration(root_of_aircraft_exchange_tree, runtime_output):
"""Read tank configuration.
Read tank configuration information from aircraft exchange file.
:param ElementTree root_of_aircraft_exchange_tree: Root of aircraft exchange XML
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return str tank_configuration: Information on tank configuration
"""
tank_configuration = 'xyz'
print(tank_configuration)
return tank_configuration
def determine_route_and_class_dependent_parameter(dict_ac_data, dict_operating_costs, runtime_output):
"""Determine parameter that depend on the route length and seating classes.
An absolute cost statement is not very meaningful. Only the reference to the revenue potential provides a good basis
for comparison. The transport work in the form of annual utilization is a significant factor. The calculation of the
DOC is often not based on the respective design range, but on standard route lengths typical for the operation.
An absolute cost statement is not very meaningful. Only the reference to the revenue potential provides a good
basis for comparison. The transport work in the form of annual utilization is a significant factor. The
calculation of the DOC is often not based on the respective design range, but on standard route lengths typical
for the operation.
For Europe, the standard ranges are
- regional (0 to 500 km),
- short range (500 to 1000 km),
- medium range (1000 km to 4000 km),
- long range (4000 to 6500 km), and
- ultra long range (more than 6500 km).
Both, the number of crew members and their salaries and the number of crews depend on these ranges. Since the annual
flight hours of an aircraft are far greater than the available dispatch time of a crew, a corresponding number of
crews must be provided for each aircraft. It is generally assumed that longer routes entail more crew members and
crews.
Both, the number of crew members and their salaries and the number of crews depend on these ranges. Since the
annual flight hours of an aircraft are far greater than the available dispatch time of a crew, a corresponding
number of crews must be provided for each aircraft. It is generally assumed that longer routes entail more crew
members and crews.
Since the experienced (higher paid) flight attendants/pilots generally transfer to long-haul operations after a
period of short-haul service, crew costs on long-haul routes are on average higher than on continental/short-haul
routes. Cost differences between the operational areas are even more pronounced for flight crews than for cabin
......@@ -31,7 +32,7 @@ def determine_route_and_class_dependent_parameter(dict_ac_data, dict_operating_c
- float salary_cockpit_crew_member: Salary of one cockpit crew member in EUR
- float revenue_per_seat_and_flight: Revenue per seat and flight in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -2,16 +2,16 @@ def calculate_capital_costs(dict_ac_data, dict_operating_costs, runtime_output):
"""Calculate capital costs.
The annuity formula, which is based on a modified mortgage equation, addresses both yearly depreciation and
interest. The reason for the annuity method modification is to include the residual aircraft value at the end of the
depreciation period into the capital costs. That does assume that an operator is buying an aircraft at a constant
price per kg and spends the corresponding capital cost constantly per year all over the depreciation period.
The capital costs are a major element of the route independent cost. They can be assumed as a linear function of the
operating empty weight if the aircraft market influence is considered negligible.
interest. The reason for the annuity method modification is to include the residual aircraft value at the end of
the depreciation period into the capital costs. That does assume that an operator is buying an aircraft at a
constant price per kg and spends the corresponding capital cost constantly per year all over the depreciation
period. The capital costs are a major element of the route independent cost. They can be assumed as a linear
function of the operating empty weight if the aircraft market influence is considered negligible.
This function adds the 'capital_costs' dict to the 'dict_operating_costs' that contains the following parameters:
- float capital_costs: Capital costs of the aircraft in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -29,7 +29,7 @@ def calculate_crew_costs(dict_ac_data, dict_operating_costs, runtime_output):
# Extract necessary variables from 'dict_ac_data'.
depreciation_period = dict_ac_data['depreciation_period']
n_cabin_crew_members = dict_ac_data['n_cabin_crew_members']['n_cabin_crew_members_ID0']
n_cockpit_crew_members = dict_ac_data['n_cockpit_crew_members']
n_cockpit_crew_members = dict_ac_data['n_cockpit_crew_members']['n_cabin_crew_members_ID0']
salary_increase = dict_ac_data['rate_inflation']
# Extract necessary variables from 'dict_operating_costs'.
cabin_crew_complement = dict_operating_costs['dependent_parameter']['cabin_crew_complement']
......
......@@ -11,7 +11,7 @@ def calculate_route_independent_costs(dict_ac_data, dict_operating_costs, runtim
- float route_independent_costs_annual: Route independent costs per year in EUR
- float route_independent_costs_depreciation_period: Route independent costs per depreciation period in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -22,7 +22,7 @@ def prepare_payload_range_diagram_data(dict_ac_data, dict_operating_costs, runti
- array payloads_for_prd: Payload mass (running from max. payload mass to zero, range dependent) in kg
- array ranges_for_prd: Range (running from 0 to ferry range) in kg
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -9,7 +9,7 @@ def calculate_fuel_costs(dict_ac_data, dict_operating_costs, runtime_output):
- array fuel_costs: Fuel costs (range dependent) in EUR
- float fuel_costs_design_point: Fuel costs at design point of current aircraft in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -8,7 +8,7 @@ def calculate_handling_costs(dict_ac_data, dict_operating_costs, runtime_output)
This function adds the 'handling_costs' dict to the 'dict_operating_costs' that contains the following parameters:
- float handling_costs: Handling costs per year in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -6,7 +6,7 @@ def calculate_landing_costs(dict_ac_data, dict_operating_costs, runtime_output):
This function adds the 'landing_costs' dict to the 'dict_operating_costs' that contains the following parameters:
- float landing_costs: Landing costs per year in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -20,7 +20,7 @@ def calculate_air_traffic_control_costs(dict_ac_data, dict_operating_costs, runt
URL: https://www.eurocontrol.int/sites/default/files/2022-11/eurocontrol-customer-guide-to-charges.pdf.
(Last access: 26.09.2023).
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -7,7 +7,8 @@ def calculate_maintenance_costs(dict_ac_data, dict_operating_costs, runtime_outp
- flight hour dependent costs, which consider primarily wear and the associated line maintenance work, and
- calendar time dependent costs, which are a constant share reflecting for example the rectification of
corrosion during overhaul, the latter being route independent and thus considered as constant.
Following the JADC-method an approximation for maintenance cost per flight cycle is given by the sum of three parts:
Following the JADC-method an approximation for maintenance cost per flight cycle is given by the sum of three
parts:
- 'airframe_material': Airframe material maintenance costs (repair and replacement)
- 'airframe_personnel': Airframe personnel maintenance costs (inspection and repair)
- 'engine_total': Engine total maintenance costs
......@@ -16,7 +17,7 @@ def calculate_maintenance_costs(dict_ac_data, dict_operating_costs, runtime_outp
parameters:
- float maintenance_costs: Maintenance costs per year in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
def calculate_route_dependent_costs(dict_ac_data, dict_operating_costs, runtime_output):
"""Calculate route dependent costs per year and depreciation period.
Route dependent costs include all cost components that are directly attributable to flight operations. These include
Route dependent costs include all cost components that are directly attributable to flight operations. These
include
- fuel,
- fees (handling, landing, air traffic control), and
- maintenance.
......@@ -14,10 +15,10 @@ def calculate_route_dependent_costs(dict_ac_data, dict_operating_costs, runtime_
dependent) in EUR
- float route_dependent_costs_annual_design_point: Route dependent costs per year at design point for current
aircraft in EUR
- float route_dependent_costs_depreciation_period_design_point: Route dependent costs per depreciation period at
design point for current aircraft in EUR
- float route_dependent_costs_depreciation_period_design_point: Route dependent costs per depreciation period
at design point for current aircraft in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -3,8 +3,8 @@ def calculate_direct_operating_costs(dict_ac_data, dict_operating_costs, runtime
// TODO Cost per depreciation period: Check comment below
The method implemented below is a simplified method based on "From Aircraft Performance to Aircraft Assessment“ by
J. Thorbeck [1]. The total operating cost are split into direct (DOC) and indirect operating cost (IOC). The DOC are
directly influenced by the parameters and the performance of the aircraft and are commonly used for aircraft
J. Thorbeck [1]. The total operating cost are split into direct (DOC) and indirect operating cost (IOC). The DOC
are directly influenced by the parameters and the performance of the aircraft and are commonly used for aircraft
evaluation. Two elements are required for the simplified DOC model: Route independent (fixed) and route dependent
(variable) costs. They are determined for one year and the entire depreciation period.
......@@ -20,7 +20,7 @@ def calculate_direct_operating_costs(dict_ac_data, dict_operating_costs, runtime
[1] J. Thorbeck: "From Aircraft Performance to Aircraft Assessment“ (YEAR)
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
def calculate_flight_kilometer_costs(dict_ac_data, dict_operating_costs, runtime_output):
"""Calculate flight kilometer costs.
The flight kilometer costs are very flexible and suitable for an extended consideration of changed route structures.
This parameter allows the range potential of the aircraft to be assessed.
The flight kilometer costs are very flexible and suitable for an extended consideration of changed route
structures. This parameter allows the range potential of the aircraft to be assessed.
This function adds the 'flight_kilometer_costs' dict to the 'dict_operating_costs' that contains the following
parameters:
- array flight_kilometer_costs: Flight kilometer costs (range dependent) in EUR/km
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -7,14 +7,14 @@ def calculate_seat_kilometer_costs(dict_ac_data, dict_operating_costs, runtime_o
The seat kilometer offered (SKO) is a measure of an aircraft's passenger carrying capacity or, in other words, its
potential to generate revenue by providing available seats to passengers. They are calculated by multiplying the
number of seats available by the range. The seat kilometer costs allow the analysis of a change in seat capacity and
thus the assessment of the passenger kilometer potential.
number of seats available by the range. The seat kilometer costs allow the analysis of a change in seat capacity
and thus the assessment of the passenger kilometer potential.
This function adds the 'seat_kilometer_costs' dict to the 'dict_operating_costs' that contains the following
parameters:
- array seat_kilometer_costs: Seat kilometer costs (range dependent) in EUR/SKO
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -14,7 +14,7 @@ def calculate_ton_kilometer_costs(dict_ac_data, dict_operating_costs, runtime_ou
- array ton_kilometer_costs: Ton kilometer costs (range dependent) in EUR/TKO
- array ton_kilometer_offered: Ton kilometer offered (range dependent) in EUR/(kg*km)
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -6,8 +6,8 @@ def calculate_revenue_passenger_kilometer_costs(dict_ac_data, dict_operating_cos
"""Calculate revenue passenger kilometer costs.
Revenue passenger kilometers (RPK) are a measure of how many kilometers the aircraft has carried paying passengers.
It is often referred to as "traffic" as it represents the actual demand for air transport. The RPK are determined by
multiplying the range by the number of paying passengers. The number of revenue passengers is calculated by
It is often referred to as "traffic" as it represents the actual demand for air transport. The RPK are determined
by multiplying the range by the number of paying passengers. The number of revenue passengers is calculated by
multiplying the maximum number of seats by the load factor. The DOC per revenue passenger kilometer additionally
take into account the overall performance of an airline, including its seat load factors.
Note that revenue is strongly dependent on market situation and therefore varying.
......@@ -16,7 +16,7 @@ def calculate_revenue_passenger_kilometer_costs(dict_ac_data, dict_operating_cos
following parameters:
- array revenue_passenger_kilometer_costs: Revenue passenger kilometer costs (range dependent) in EUR/RPK
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -7,7 +7,7 @@ def calculate_indirect_operating_costs(dict_ac_data, dict_operating_costs, runti
parameters:
- float indirect_operating_costs: Indirect operating costs in EUR
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module configuration file
:param dict dict_ac_data: Dict containing parameters and values from aircraft exchange and module config file
:param dict dict_operating_costs: Dict containing parameters from cost estimation calculation
:param logging.Logger runtime_output: Logging object used for capturing log messages in the module
:return dict dict_operating_costs: Dict containing parameters from cost estimation calculation
......
......@@ -19,28 +19,13 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output
:return: None
"""
runtime_output.print('Plots are generated and saved...')
# Check, if mission study exists.
# study_exists = 'study' in data_dict.keys()
# Extract necessary values from 'data_dict'.
prd_range_array = data_dict['design']['payload_range_diagram_data']['ranges_for_prd']/1000
prd_payload_array = data_dict['design']['payload_range_diagram_data']['payloads_for_prd']
seat_kilometer_costs_design = data_dict['design']['seat_kilometer_costs']['seat_kilometer_costs']
# seat_kilometer_costs_corrected_design = 0
ton_kilometers_offered_design = data_dict['design']['ton_kilometer_costs']['ton_kilometer_offered']
ton_kilometer_costs_design = data_dict['design']['ton_kilometer_costs']['ton_kilometer_costs']
# route_independent_costs_per_year_design = \
# data_dict['design']['route_independent_costs']['route_independent_costs_annual']
# route_dependent_costs_design = data_dict['design']['route_dependent_costs']['route_dependent_costs_annual']
# route_dependent_costs_design_design_point = \
# data_dict['design']['route_dependent_costs']['route_dependent_costs_annual_design_point']
# if study_exists:
# route_independent_costs_per_year_study = \
# data_dict['study']['route_independent_costs']['route_independent_costs_annual']
# route_dependent_costs_study = data_dict['study']['route_dependent_costs']['route_dependent_costs_annual']
# route_dependent_costs_study_design_point = \
# data_dict['study']['route_dependent_costs']['route_dependent_costs_annual_design_point']
""" Plot two values in the same diagram. """
create_two_axes_plot(seat_kilometer_costs_design, 300, "SKC in €/number of PAX * km * year",
......@@ -49,12 +34,6 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output
prd_range_array, "SKC and TKO for design mission",
"design_mission", paths_and_names)
# create_two_axes_plot(seat_kilometer_costs_design, 300, "SKC in €/number of PAX * km for design mission",
# seat_kilometer_costs_corrected_design, 300,
# "SKC corrected in €/number of PAX * km for mission study",
# prd_range_array, "Seat kilometer costs (SKC) vs. corrected SKC",
# "mission_study", paths_and_names)
create_two_axes_plot(ton_kilometer_costs_design, 3, "DOC per ton kilometers offered in €/payload weight * km",
ton_kilometers_offered_design, max(ton_kilometers_offered_design) * 1.1,
"Ton kilometers offered in payload weight * km",
......@@ -66,12 +45,6 @@ def method_plot(paths_and_names, routing_dict, data_dict, method_specific_output
prd_range_array, "Payload range diagram and SKC for design mission",
"design_mission", paths_and_names)
# Create pie charts showing direct operating costs.
# if study_exists:
# design_mission = [route_independent_costs_per_year_design, route_dependent_costs_design_design_point]
# mission_study = [route_independent_costs_per_year_study, route_dependent_costs_study_design_point]
# create_pie_chart(paths_and_names, design_mission, mission_study, 'Direct operating costs per year')
def create_two_axes_plot(y_value_1, y_limit_1, y_label_1, y_value_2, y_limit_2, y_label_2, range_array, plot_title,
mission_flag, paths_and_names):
......
......@@ -31,8 +31,7 @@ def method_xml_export(paths_and_names, routing_dict, data_dict, method_specific_
"""
runtime_output.print("Method-specific data are written to '" + routing_dict['module_name'] + "_results.xml'...")
# Function to write data to 'cost_estimation_results.xml'
# xml_export_tree = ET.parse(path_to_results_file)
# Function to write data to 'cost_estimation_results.xml'.
root_of_results_file = xml_export_tree.getroot()
parent = root_of_results_file.find('calculation_results')
# Add method node.
......@@ -48,8 +47,8 @@ def method_xml_export(paths_and_names, routing_dict, data_dict, method_specific_
specific_output_dict_design = method_specific_output_dict['specific_output_dict_design']
prepare_element_tree_for_module_specific_export(root_of_results_file, specific_output_dict_design)
# If study mission exists, extract study mission data from 'method_specific_output_dict' and prepare ElementTree for
# export to module-specific XML.
# If study mission exists, extract study mission data from 'method_specific_output_dict' and prepare ElementTree
# for export to module-specific XML.
study_exists = ('specific_output_dict_study' in method_specific_output_dict.keys())
if study_exists:
specific_output_dict_study = method_specific_output_dict['specific_output_dict_study']
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment