diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..3d3c581
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+ "recommendations": [
+ "ms-python.flake8",
+ "ms-python.black-formatter",
+ "ms-python.isort",
+ "njpwerner.autodocstring",
+ "ms-python.pylint",
+ "ms-python.mypy-type-checker"
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 707d449..dddfe92 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -3,10 +3,19 @@
"editor.defaultFormatter": "ms-python.black-formatter"
},
"cSpell.words": [
+ "autoapi",
+ "autodoc",
+ "autosummary",
+ "bibtex",
"figwidth",
+ "Ginter",
"jsonpickle",
+ "Korzeniowska",
+ "maxdepth",
"penalps",
- "pydantic"
+ "pydantic",
+ "targetname",
+ "timedelta"
],
"python.testing.pytestArgs": [
"."
@@ -14,4 +23,12 @@
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"python.testing.pytestPath": "C:\\Users\\Julian\\mambaforge",
+ "pylint.args": [
+ "--disable=W0611, W0719, C0114, C0115, W0105, C0116, C0123,C0301",
+ "--max-line-length=120"
+ ],
+ "flake8.args": [
+ "--max-line-length=120 ",
+ "--ignore=F401, W503,E501"
+ ],
}
\ No newline at end of file
diff --git a/README.md b/README.md
index 151594e..b4b562e 100644
--- a/README.md
+++ b/README.md
@@ -18,8 +18,8 @@ objects. After the material flow simulation is completed, a set of production or
![Main Component Overview](paper/main_component_overview.png)
*Depiction of the main components and workflow of ETHOS.PeNALPS*
-A further description of the model definition can be found [here](ethos_penalps_articles/model_description.md).
-Also two examples for a [toffee production process](examples/toffee_example.md) and a [b-pillar production process](examples/b_pillar_example.md) are available.
+A further description of the model definition can be found [here]([ethos_penalps_articles/model_description.md](https://ethospenalps.readthedocs.io/en/latest/ethos_penalps_articles/model_description.html)).
+Also two examples for a [toffee production process](https://ethospenalps.readthedocs.io/en/latest/examples/toffee_example.html) and a [b-pillar production process](https://ethospenalps.readthedocs.io/en/latest/examples/b_pillar_example.html) are available.
# Installation
diff --git a/asd.py b/asd.py
deleted file mode 100644
index 4ce8e5b..0000000
--- a/asd.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from pdf2image import convert_from_path
-
-import tempfile
-
-path_to_pdf = r"C:\Programming\ethos_penalps\examples\basic_examples.py\report_2024_02_21__11_43_50\tex_folder\enterprise_text_file.pdf"
-
-path_to_png = path_to_pdf[:-4] + ".png"
-with tempfile.TemporaryDirectory() as path:
- list_of_pillow_images = convert_from_path(path_to_pdf)
-
- for image in list_of_pillow_images:
- converted_image = image.convert("RGBA")
- converted_image.save(path_to_png)
diff --git a/documentation/_config.yml b/documentation/_config.yml
index 7af42b7..b40f482 100644
--- a/documentation/_config.yml
+++ b/documentation/_config.yml
@@ -1,7 +1,7 @@
# Book settings
# Learn more at https://jupyterbook.org/customize/config.html
-title: ETHOS.PeNALPS
+title: ETHOS.PeNALPS
author: Julian Belina
logo: ./visualizations/logos/fzj_logo.svg
@@ -19,12 +19,11 @@ latex:
# bibtex_bibfiles:
# - references.bib
-
# Information about where the book exists on the web
repository:
- url: https://github.com/FZJ-IEK3-VSA/ETHOS_PeNALPS # Online location of your book
- path_to_book: documentation # Optional path to your book, relative to the repository root
- branch: main # Which branch of the repository should be used when creating links (optional)
+ url: https://github.com/FZJ-IEK3-VSA/ETHOS_PeNALPS # Online location of your book
+ path_to_book: documentation # Optional path to your book, relative to the repository root
+ branch: main # Which branch of the repository should be used when creating links (optional)
# Add GitHub buttons to your book
# See https://jupyterbook.org/customize/config.html#add-a-link-to-your-repository
@@ -39,22 +38,18 @@ sphinx:
- sphinx.ext.autosummary
- sphinx.ext.inheritance_diagram
-
-
config:
- autoapi_dirs : ['../src/ethos_penalps']
+ autoapi_dirs: ["../src/ethos_penalps"]
autoapi_add_toctree_entry: false
- autoapi_file_patterns : ['*.py']
+ autoapi_file_patterns: ["*.py"]
autosummary_generate: false
autoapi_keep_files: true
autoapi_generate_api_docs: true
suppress_warnings: ["etoc.toctree"]
-
- # exclude_patterns : ['_build', '_templates','ethos_elpsi_articles','autoapi','visualizations','knowledge_articles','documentation']
- autoapi_options:
+ # exclude_patterns : ['_build', '_templates','autoapi','visualizations','knowledge_articles','documentation']
+
+ autoapi_options:
show-module-summary: true
bibtex_bibfiles:
- references.bib
-
-
diff --git a/documentation/_toc.yml b/documentation/_toc.yml
index 3ee4381..3275dc8 100644
--- a/documentation/_toc.yml
+++ b/documentation/_toc.yml
@@ -4,31 +4,31 @@
format: jb-book
root: contents
-
-options: # The options key will be applied to all chapters, but not sub-sections
+options: # The options key will be applied to all chapters, but not sub-sections
numbered: True
-
-
parts:
-- caption: Basic Articles
- maxdepth: 3
- chapters:
- - file: ethos_penalps_articles/simulation_workflow.md
- - file: ethos_penalps_articles/model_description.md
- - file: ethos_penalps_articles/bibliography.md
-- caption: API
- chapters:
- - file: autoapi/ethos_penalps/index.rst
-- caption: Examples
- chapters:
- - file: examples/b_pillar_example.md
- - file: examples/toffee_example.md
-
-
-
-
-
-
-
-
+ - caption: Tutorial
+ maxdepth: 3
+ chapters:
+ - file: ethos_penalps_tutorial/overview.md
+ - file: ethos_penalps_tutorial/single_cooker_process_chain.md
+ - file: ethos_penalps_tutorial/add_more_states.md
+ - file: ethos_penalps_tutorial/add_more_chains.md
+ - file: ethos_penalps_tutorial/connect_two_process_steps_exclusively.md
+ - file: ethos_penalps_tutorial/connect_three_or_more_process_steps.md
+ - caption: Basic Articles
+ maxdepth: 3
+ chapters:
+ - file: ethos_penalps_articles/simulation_workflow.md
+ - file: ethos_penalps_articles/model_description.md
+ - caption: API
+ chapters:
+ - file: autoapi/ethos_penalps/index.rst
+ - caption: Examples
+ chapters:
+ - file: examples/b_pillar_example.md
+ - file: examples/toffee_example.md
+ - caption: Bibliography
+ chapters:
+ - file: ethos_penalps_articles/bibliography.md
diff --git a/documentation/ethos_penalps_tutorial/add_more_chains.md b/documentation/ethos_penalps_tutorial/add_more_chains.md
new file mode 100644
index 0000000..33b0144
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/add_more_chains.md
@@ -0,0 +1,272 @@
+# Add More Cookers for Parallel Operation
+
+This sections explains how to model the parallel operation of two process steps using the previous cooker example which is shown in {numref}`cooking-example-two-cooker`. In this example two cookers are implemented.
+:::{figure-md} cooking-example-two-cooker
+
+
+Depiction of the cooker model with two parallel cookers.
+:::
+
+Therefore two process chains must be created. Each of the chains must hold an own instance of the cooker.
+
+## Create Two Process Chains
+The first change that must be applied is to the original example is to add an additional process chain.
+
+```
+process_chain_1 = network_level.create_process_chain(
+ process_chain_name="Cooker Chain 1"
+)
+process_chain_2 = network_level.create_process_chain(
+ process_chain_name="Cooker Chain 2"
+)
+
+
+```
+## Connect Chains with Sink and Source
+Both chains must then be connected to the sink and source
+
+```
+process_chain_1.add_sink(sink=sink)
+process_chain_1.add_source(source=source)
+process_chain_2.add_sink(sink=sink)
+process_chain_2.add_source(source=source)
+```
+## Create two process steps
+Then two process steps are created.
+```
+process_step_1 = process_chain_1.create_process_step(name="Cooker 1")
+process_step_2 = process_chain_2.create_process_step(name="Cooker 2")
+```
+
+## Create Streams
+Afterwards an own input and output stream must be created for each of the process steps.
+```
+raw_materials_to_cooking_stream_1 = process_chain_1.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=process_step_1.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=input_commodity,
+ maximum_batch_mass_value=0.0005,
+ )
+)
+cooking_to_sink_stream_1 = process_chain_1.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step_1.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.0005,
+ )
+)
+
+raw_materials_to_cooking_stream_2 = process_chain_2.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=process_step_2.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=input_commodity,
+ maximum_batch_mass_value=0.0005,
+ )
+)
+cooking_to_sink_stream_2 = process_chain_2.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step_2.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.0005,
+ )
+)
+```
+
+## Connect Streams
+Then both of the streams must be connected to respective sink and source. It is not necessary to add them to the respective process step.
+
+```
+source.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream_1,
+ process_chain_identifier=process_chain_1.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooking_to_sink_stream_1,
+ process_chain_identifier=process_chain_1.process_chain_identifier,
+)
+source.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream_2,
+ process_chain_identifier=process_chain_2.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooking_to_sink_stream_2,
+ process_chain_identifier=process_chain_2.process_chain_identifier,
+)
+```
+## Create Petri Nets
+Afterwards the petri net must be created for each of the process steps. For the sake of brevity the simpler petri net from the initial example is used:
+
+### Cooker 1:
+```
+idle_state_1 = process_step_1.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state_1 = (
+ process_step_1.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+cooking_state_1 = process_step_1.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+)
+
+discharge_goods_state_1 = (
+ process_step_1.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+activate_not_cooking_1 = process_step_1.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_1,
+ end_process_state=idle_state_1,
+)
+process_step_1.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking_1
+)
+
+activate_filling_1 = process_step_1.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_1,
+ end_process_state=fill_raw_materials_state_1,
+)
+
+process_step_1.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_1
+)
+
+activate_cooking_1 = process_step_1.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state_1,
+ end_process_state=cooking_state_1,
+ delay=datetime.timedelta(minutes=30),
+)
+
+process_step_1.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking_1
+)
+
+
+activate_discharging_1 = process_step_1.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state_1,
+ end_process_state=discharge_goods_state_1,
+)
+process_step_1.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_1
+)
+```
+
+### Cooker 2
+
+```
+# Process Step 2
+
+idle_state_2 = process_step_2.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state_2 = (
+ process_step_2.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+cooking_state_2 = process_step_2.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+)
+
+discharge_goods_state_2 = (
+ process_step_2.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+activate_not_cooking_2 = process_step_2.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_2,
+ end_process_state=idle_state_2,
+)
+process_step_2.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking_2
+)
+
+activate_filling_2 = process_step_2.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_2,
+ end_process_state=fill_raw_materials_state_2,
+)
+
+process_step_2.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_2
+)
+
+activate_cooking_2 = process_step_2.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state_2,
+ end_process_state=cooking_state_2,
+ delay=datetime.timedelta(minutes=30),
+)
+
+process_step_2.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking_2
+)
+
+
+activate_discharging_2 = process_step_2.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state_2,
+ end_process_state=discharge_goods_state_2,
+)
+process_step_2.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_2
+)
+```
+
+## Add Energy Data
+
+Now the energy data must be added two the states of each machine.
+```
+electricity_load = LoadType(name="Electricity")
+cooking_state_1.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=1.8,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream_1,
+)
+cooking_state_2.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=1.8,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream_2,
+)
+```
+
+## Create Mass Balances and Storages
+The last step is to add an own mass balance and storage to each of the process steps.
+```
+process_step_1.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream_1,
+ main_output_stream=cooking_to_sink_stream_1,
+)
+
+process_step_1.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+
+
+process_step_2.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream_2,
+ main_output_stream=cooking_to_sink_stream_2,
+)
+
+
+process_step_2.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+```
+
+The process to start the simulation is the same as in the previous example. It does not depend on the configuration of process steps, process chains and network level. The next example shows how to connect two process steps exclusively using a process chain.
+
diff --git a/documentation/ethos_penalps_tutorial/add_more_states.md b/documentation/ethos_penalps_tutorial/add_more_states.md
new file mode 100644
index 0000000..e1a7490
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/add_more_states.md
@@ -0,0 +1,136 @@
+# Add More States
+
+The energy relevant behavior of the cooker [of the previous example](single_cooker_process_chain.md) can be modeled in greater detail by adding more states to the petri net of the cooker. Instead of modeling a single cooking phase two phases are implemented. The first one is the heating phase which uses the maximum power of the cooker to reach the boiling temperature. The second phase uses less power and only holds the target temperature. Additionally, a cleaning phase is added after the discharge phase.
+
+```
+idle_state = process_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+heating_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Heating"
+)
+
+hold_temperature_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Hold Temperature"
+)
+
+discharge_goods_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+cleaning_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cleaning"
+)
+```
+Each of the new intermediate states is connected with an additional process state switch delay. To determine the length of the heating phase it is assumed that the cooker has a maximum power of 2000 Watt. The water must be heated from 20 °C to 100°C assuming a heat capacity o c_P=4.2 KJ/(Kg*K)
+
+$$
+E_{heating, state} = m_{potato,water}*c_{p,water}*(T_{target}-T_{start})
+=650 g *4.2kj/kg *(100°C-20°C)=0.06kWh
+$$
+$$
+t_{heating,state}=E_{heating, state}/P_{max}=0.06kWh/2000W=1.82 minutes
+$$
+
+The Assuming that the total cooking time does not change
+
+$$
+t_{hold_temperature,state}=t_{cooking, state}-t_{heating, state}=24 minutes -1.82 minutes= 22.18 minutes
+$$
+
+```
+activate_not_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=cleaning_state,
+ end_process_state=idle_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+)
+
+activate_filling = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling
+)
+
+activate_heating = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=heating_state,
+ delay=datetime.timedelta(minutes=1.82),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_heating
+)
+activate_hold_temperature = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=heating_state,
+ end_process_state=hold_temperature_state,
+ delay=datetime.timedelta(minutes=22.18),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_hold_temperature
+)
+
+
+activate_discharging = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=hold_temperature_state,
+ end_process_state=discharge_goods_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging
+)
+activate_hold_temperature = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=discharge_goods_state,
+ end_process_state=cleaning_state,
+ delay=datetime.timedelta(minutes=3),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_hold_temperature
+)
+```
+
+Now the the energy data must be updated to model the different energy usage in both states. Therefore the energy demand must be converted to MJ/t for each state. It is assumed that the total energy demand does not change. There fore the heat demand for hold temperature phase is:
+
+$$
+E_{hold_temperature, state} = E_{total}-E_{heating, state}= 0.15 kWH -0.06kWh= 0.09kWH
+$$
+
+The energy demand for both phase is converted into mass specific energy using the following conversion:
+$$
+SEC_{heating, state}=E_{heating, state}/m_{potato,water}=0.06kWh/650 g=332.31 MJ/t
+$$
+
+$$
+SEC_{hold_temperature, state}=E_{hold_temperature, state}/m_{potato,water}=0.09kWh/650 g=498.46 MJ/t
+$$
+
+In this case it is assumed that no energy is used during the cleaning.
+
+```
+electricity_load = LoadType(name="Electricity")
+heating_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=332.31,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+)
+hold_temperature_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=498.46,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+)
+```
+
+The next example shows how to implement multiple cookers that produced in parallel.
diff --git a/documentation/ethos_penalps_tutorial/connect_three_or_more_process_steps.md b/documentation/ethos_penalps_tutorial/connect_three_or_more_process_steps.md
new file mode 100644
index 0000000..0ac7bdd
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/connect_three_or_more_process_steps.md
@@ -0,0 +1,420 @@
+# Connect Three or More Process Steps
+
+In order to connect three or more process steps two network levels are required. In this example two blenders are connected to two cookers which are shown in figure {numref}`two-cooker-two-blender-example`. The process steps are connected through the storage which connects the two network levels. It replaces the sink the upper network level and the source of the lower network level. It distributes the outputs of both blenders to the inputs of both cookers evenly.
+
+:::{figure-md} two-cooker-two-blender-example
+
+
+Depiction of the cooker model with two two cooker and two blender.
+:::
+
+To prevent repetition the model is distributed between three modules. The module simulation_starter.py contains the definition of the time data, commodities, enterprise, network level, process chains, sources, sinks and storages. The process chains are filled using a function which can be applied to each process chain in a network level. The function for each network level is located in their own module. These functions are called fill_blending_process_chain and fill_cooking_process_chain. By calling these functions multiple times an arbitrary number of process chains can be created easily.
+
+## simulation_starter.py
+### Create Time Data and Orders
+First the commodities, time data and orders are created. Each process chain needs at least one order to start the simulation.
+```
+# Set simulation time data
+start_date = datetime.datetime(2022, 1, 2, hour=22, minute=30)
+end_date = datetime.datetime(2022, 1, 3)
+time_data = TimeData(
+ global_start_date=start_date,
+ global_end_date=end_date,
+)
+
+
+# Determine all relevant commodities
+raw_commodity = Commodity(name="Raw Goods")
+uncooked_commodity = Commodity(name="Uncooked Goods")
+output_commodity = Commodity(name="Cooked Goods")
+
+
+# Create all order for the simulation
+order_generator = NOrderGenerator(
+ commodity=output_commodity,
+ mass_per_order=0.00065,
+ production_deadline=end_date,
+ number_of_orders=4,
+)
+
+order_collection = order_generator.create_n_order_collection()
+```
+### Create Enterprise, Network Level and Process Chains
+In the next step the enterprise, network level and process chains are created.
+```
+cooking_network_level = enterprise.create_network_level()
+blending_network_level = enterprise.create_network_level()
+
+blending_chain_1 = blending_network_level.create_process_chain("Blending Chain 1")
+blending_chain_2 = blending_network_level.create_process_chain("Blending Chain 2")
+
+cooking_chain_1 = cooking_network_level.create_process_chain("Cooking Chain 1")
+cooking_chain_2 = cooking_network_level.create_process_chain("Cooking Chain 2")
+```
+
+### Create and Connect Source, Sink and Process Chain Storage
+The source and sink are created by their respective network level. The storage is created by network level which replaces a source with it. Here it is the cooking network level. The storage is then passed to the upper blender network level as a sink. Afterwards the sink, source and storages must be added to their respective process chain.
+
+```
+cooked_goods_sink = cooking_network_level.create_main_sink(
+ name="Cooked Goods Sink",
+ commodity=cooked_commodity,
+ order_collection=order_collection,
+)
+
+uncooked_goods_storage = cooking_network_level.create_process_chain_storage_as_source(
+ name="Uncooked Goods", commodity=uncooked_commodity
+)
+raw_goods_source = blending_network_level.create_main_source(
+ "Raw Goods", commodity=raw_commodity
+)
+blending_network_level.add_process_chain_storage_as_sink(
+ process_chain_storage=uncooked_goods_storage
+)
+
+blending_chain_1.add_sink(sink=uncooked_goods_storage)
+blending_chain_2.add_sink(sink=uncooked_goods_storage)
+blending_chain_1.add_source(source=raw_goods_source)
+blending_chain_2.add_source(source=raw_goods_source)
+cooking_chain_1.add_sink(sink=cooked_goods_sink)
+cooking_chain_1.add_source(source=uncooked_goods_storage)
+cooking_chain_2.add_sink(sink=cooked_goods_sink)
+cooking_chain_2.add_source(source=uncooked_goods_storage)
+```
+
+## Call Function to Fill Process Chains With Content
+In the next step the contents of the process chains are created by calls to the respective fill function. They are called as follows in the simulation_starter.py module:
+```
+from blending_process_chain import (
+ fill_blending_process_chain,
+)
+from cooking_process_chain import (
+ fill_cooking_process_chain,
+)
+fill_cooking_process_chain(
+ process_chain=cooking_chain_1,
+ uncooked_commodity=uncooked_commodity,
+ cooked_commodity=cooked_commodity,
+ cooked_goods_sink=cooked_goods_sink,
+ uncooked_goods_storage=uncooked_goods_storage,
+ process_step_name="Cooker 1",
+)
+fill_cooking_process_chain(
+ process_chain=cooking_chain_2,
+ uncooked_commodity=uncooked_commodity,
+ cooked_commodity=cooked_commodity,
+ cooked_goods_sink=cooked_goods_sink,
+ uncooked_goods_storage=uncooked_goods_storage,
+ process_step_name="Cooker 2",
+)
+
+fill_blending_process_chain(
+ process_chain=blending_chain_1,
+ raw_commodity=raw_commodity,
+ cooked_commodity=cooked_commodity,
+ raw_goods_source=raw_goods_source,
+ uncooked_storage=uncooked_goods_storage,
+ process_step_name="Blender 1",
+)
+fill_blending_process_chain(
+ process_chain=blending_chain_2,
+ raw_commodity=raw_commodity,
+ cooked_commodity=cooked_commodity,
+ raw_goods_source=raw_goods_source,
+ uncooked_storage=uncooked_goods_storage,
+ process_step_name="Blender 2",
+)
+```
+
+### Start the Simulation and Postprocessing
+
+Lastly the simulation and post processing is started.
+
+```
+# Start the simulation
+enterprise.start_simulation(number_of_iterations_in_chain=200)
+
+# Create report of the simulation results
+enterprise.create_post_simulation_report(
+ start_date=start_date,
+ end_date=end_date,
+ x_axis_time_delta=datetime.timedelta(hours=1),
+ resample_frequency="5min",
+ gantt_chart_end_date=end_date,
+ gantt_chart_start_date=start_date,
+)
+```
+## Fill Blender Process Chain
+Now the definition of the functions that fill the process chains are shown.
+
+### Pass Process Chain, Commodity and Process Step Name
+First the process chain instance and the commodities, source and storage which are shared among process chains are passed to the fill function.
+```
+def fill_blending_process_chain(
+ process_chain: ProcessChain,
+ raw_commodity: Commodity,
+ cooked_commodity: Commodity,
+ uncooked_storage: ProcessChainStorage,
+ raw_goods_source: Source,
+ process_step_name: str,
+):
+```
+### Create Process Step and Streams
+Then Process Steps and Streams are created. The only difference is that the argument from the functions are used for the process chain, process step name and commodities.
+```
+ # Create Process nodes
+ blender_step = process_chain.create_process_step(name=process_step_name)
+
+ # Streams
+ ## Process Chain 1
+ raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=raw_goods_source.name,
+ end_process_step_name=blender_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=raw_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+ cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=blender_step.name,
+ end_process_step_name=uncooked_storage.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=cooked_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+```
+### Define Petri Net of States
+The petri nets of the blender are defined as in the [example in which the blender was introduced](connect_two_process_steps_exclusively.md).
+```
+ idle_state = blender_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+ )
+ fill_raw_materials_state = (
+ blender_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+ )
+
+ blender_state = blender_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Mix"
+ )
+
+ discharge_goods_state_blender = (
+ blender_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+ )
+
+ # Petri net transitions
+
+ activate_not_blending = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_blender,
+ end_process_state=idle_state,
+ )
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_blending
+ )
+
+ activate_filling_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+ )
+
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_blender
+ )
+
+ activate_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=blender_state,
+ delay=datetime.timedelta(minutes=5),
+ )
+
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_blender
+ )
+
+ activate_discharging_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=blender_state,
+ end_process_state=discharge_goods_state_blender,
+ )
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_blender
+ )
+```
+### Add Energy Data
+The energy data did not change in comparison to the previous example.
+```
+ electricity_load = LoadType(name="Electricity")
+ blender_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=600,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+ )
+```
+
+### Add Mass Balance and Storage
+Also the mass balance and storage do not change compared to the minimal example.
+```
+ # Mass balances
+ blender_step.create_main_mass_balance(
+ commodity=cooked_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+ )
+
+ # Add internal storages (required)
+ blender_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+ )
+```
+
+## Fill Cooking Process Chain
+The fill cooking process chain function is very similar to the fill blender process chain function. It differs mainly in the arguments that are passed to it, the petri net and the energy data. However the petri net and energy data did not change compared to the [example](connect_two_process_steps_exclusively.md) in which the blender was introduced.
+
+### Pass process Chain, Commodity and Process Step Name
+The first argument is the process chain in which the cooker should be initiated. The commodities are the input and output commodity of the cooker. The sink and storage are required to connect it to the streams in the process chain. The process step name is required to distinguish the process steps from each other.
+```
+def fill_cooking_process_chain(
+ process_chain: ProcessChain,
+ uncooked_commodity: Commodity,
+ cooked_commodity: Commodity,
+ cooked_goods_sink: Sink,
+ uncooked_goods_storage: ProcessChainStorage,
+ process_step_name: str,
+):
+```
+
+### Create Process Step and Streams
+Then the process step and streams are created. Then the streams are connected.
+```
+ raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=uncooked_goods_storage.name,
+ end_process_step_name=process_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=uncooked_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+ cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step.name,
+ end_process_step_name=cooked_goods_sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=cooked_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+
+ # Add streams to sinks and sources
+ uncooked_goods_storage.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+ )
+ cooked_goods_sink.add_input_stream(
+ input_stream=cooking_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+ )
+```
+### Define Petri Net of States
+
+The Petri net did not change compared to the definition in the [single cooker example](single_cooker_process_chain.md).
+
+```
+ idle_state = process_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+ )
+ fill_raw_materials_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+ )
+
+ cooking_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+ )
+
+ discharge_goods_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+ )
+
+ # Petri net transitions
+
+ activate_not_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state,
+ end_process_state=idle_state,
+ )
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+ )
+
+ activate_filling = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+ )
+
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling
+ )
+
+ activate_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=cooking_state,
+ delay=datetime.timedelta(minutes=24),
+ )
+
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking
+ )
+
+ activate_discharging = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state,
+ end_process_state=discharge_goods_state,
+ )
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging
+ )
+```
+### Add Energy Data
+
+The energy data did also not change to the initial example.
+```
+ electricity_load = LoadType(name="Electricity")
+ mixing_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=600,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+ )
+
+ # Mass balances
+ mixer_step.create_main_mass_balance(
+ commodity=cooked_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+ )
+```
+
+### Create Mass Balance and Storage
+The mass balance and storage did not change compared to the previous example of the cooker.
+```
+ # Mass balances
+ mixer_step.create_main_mass_balance(
+ commodity=cooked_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+ )
+
+ # Add internal storages (required)
+ mixer_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+ )
+```
\ No newline at end of file
diff --git a/documentation/ethos_penalps_tutorial/connect_two_process_steps_exclusively.md b/documentation/ethos_penalps_tutorial/connect_two_process_steps_exclusively.md
new file mode 100644
index 0000000..70ad1a6
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/connect_two_process_steps_exclusively.md
@@ -0,0 +1,225 @@
+# Connect Two Process Steps Exclusively
+In order to connect two process steps sequentially they must be in the same process chain and be connected by streams . This will be demonstrated by adding a blender prior to the cooker which is shown in figure {numref}`cooking-blender-exclusive-example`.
+
+
+:::{figure-md} cooking-blender-exclusive-example
+
+
+Depiction of the cooker model with two parallel cookers.
+:::
+
+## Add Additional Commodity
+
+First additional commodities must be added for each of the new commodity states between source, process steps and sink.
+
+```
+raw_commodity = Commodity(name="Raw Goods")
+uncooked_commodity = Commodity(name="Uncooked Goods")
+output_commodity = Commodity(name="Cooked Goods")
+```
+
+## Create Both Process Steps
+Both process steps must be created.
+```
+blender_step = process_chain.create_process_step(name="Blender")
+cooker_step = process_chain.create_process_step(name="Cooker")
+```
+
+## Create Streams
+```
+raw_materials_to_blender_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=blender_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=raw_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+blender_to_cooker_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=blender_step.name,
+ end_process_step_name=cooker_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+cooker_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=cooker_step.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=raw_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+```
+## Add Streams
+```
+source.add_output_stream(
+ output_stream=raw_materials_to_blender_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooker_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+```
+
+## Create Petri Nets for Each Process Step:
+
+### Blender
+
+```
+activate_not_blending = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_blender,
+ end_process_state=idle_state_blender,
+)
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_blending
+)
+
+activate_filling_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_blender,
+ end_process_state=fill_raw_materials_state_1,
+)
+
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_blender
+)
+
+activate_blending = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state_1,
+ end_process_state=blending_state,
+ delay=datetime.timedelta(minutes=5),
+)
+
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_blending
+)
+
+
+activate_discharging_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=blending_state,
+ end_process_state=discharge_goods_state_blender,
+)
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_blender
+)
+```
+
+### Cooker
+
+```
+idle_state_cooker = cooker_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state_cooker = (
+ cooker_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+cooking_state = cooker_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+)
+
+discharge_goods_state_cooker = (
+ cooker_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+
+# Petri net transitions
+
+activate_not_cooking = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_cooker,
+ end_process_state=idle_state_cooker,
+)
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+)
+
+activate_filling_cooker = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_cooker,
+ end_process_state=fill_raw_materials_state_cooker,
+)
+
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_cooker
+)
+
+activate_cooking = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state_cooker,
+ end_process_state=cooking_state,
+ delay=datetime.timedelta(minutes=24),
+)
+
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking
+)
+
+
+activate_discharging_cooker = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state,
+ end_process_state=discharge_goods_state_cooker,
+)
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_cooker
+)
+```
+
+## Adapt Energy Data
+Now the energy demand for the mixer must be added. It is assumed that the blending occurs for 5 minutes at a maximum power 1300 Watt for 650 gram of input material. Therefore the specific energy demand of the state is calculated as follows:
+
+$$
+SEC_{blending, state}=(P_{blend, max}*t_{blend, max})/m_{potato,water}=(2000W*5min)/(650 g)=600 MJ/t
+$$
+
+```
+electricity_load = LoadType(name="Electricity")
+mixing_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=600,
+ load_type=electricity_load,
+ stream=raw_materials_to_blender_stream,
+)
+cooking_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=830.76,
+ load_type=electricity_load,
+ stream=blender_to_cooker_stream,
+)
+```
+## Add Mass Balances and Storages
+The names of the streams and process steps must be adapted to the new variable names.
+
+```
+# Mass balances
+blender_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_blender_stream,
+ main_output_stream=blender_to_cooker_stream,
+)
+
+# Add internal storages (required)
+blender_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+
+# Mass balances
+cooker_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=blender_to_cooker_stream,
+ main_output_stream=cooker_to_sink_stream,
+)
+
+# Add internal storages (required)
+cooker_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+```
+
+In the next example shows how to connect three or more process steps non exclusively.
\ No newline at end of file
diff --git a/documentation/ethos_penalps_tutorial/figures/blender_and_cooker_exclusively_connected.png b/documentation/ethos_penalps_tutorial/figures/blender_and_cooker_exclusively_connected.png
new file mode 100644
index 0000000..7288f00
Binary files /dev/null and b/documentation/ethos_penalps_tutorial/figures/blender_and_cooker_exclusively_connected.png differ
diff --git a/documentation/ethos_penalps_tutorial/figures/parallel_cooker_operation.png b/documentation/ethos_penalps_tutorial/figures/parallel_cooker_operation.png
new file mode 100644
index 0000000..2390bff
Binary files /dev/null and b/documentation/ethos_penalps_tutorial/figures/parallel_cooker_operation.png differ
diff --git a/documentation/ethos_penalps_tutorial/figures/single_cooker_process_chain_example.png b/documentation/ethos_penalps_tutorial/figures/single_cooker_process_chain_example.png
new file mode 100644
index 0000000..f4f2062
Binary files /dev/null and b/documentation/ethos_penalps_tutorial/figures/single_cooker_process_chain_example.png differ
diff --git a/documentation/ethos_penalps_tutorial/figures/two_blender_and_two_cooker.png b/documentation/ethos_penalps_tutorial/figures/two_blender_and_two_cooker.png
new file mode 100644
index 0000000..1001723
Binary files /dev/null and b/documentation/ethos_penalps_tutorial/figures/two_blender_and_two_cooker.png differ
diff --git a/documentation/ethos_penalps_tutorial/overview.md b/documentation/ethos_penalps_tutorial/overview.md
new file mode 100644
index 0000000..b493294
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/overview.md
@@ -0,0 +1,20 @@
+# Tutorial Overview
+
+This section gives an overview how to create simple models and how to create the complexity of the models.
+
+To setup an ETHOS.PeNALPS model the following object types must be instantiated
+
+1. Initialize Time Data
+2. Setup All Commodities
+3. Create Product Orders
+4. Create Container Classes
+5. Create Source, Sink and Process Step
+6. Create Streams, Sink, Source and Process Step
+7. Create Petri Net of States
+8. Initialize Energy Data
+9. Create Internal Storages and Mass Balances
+10. Start Simulation and Post Processing
+
+First it will be shown how to setup a [minimal cooking process simulation](ethos_penalps_in_10_minutes.md). Subsequently, [the cooking process will be modelled more rigorously ](single_cooker_process_chain.md) concerning the phases of energy demand by modifying the petri nets of states of the cooker. Afterwards it is shown how to model parallel production by [adding an additional simple cooker to a new process chain in the new level](add_more_chains.md). Then, it is shown (how to connect the inputs and outputs of two machines exclusively)[connect_two_process_steps_exclusvely.md] within a simple process chain by prepending a mixer to a cooker. Finally, it is [demonstrated how to connect the inputs and outputs of 2x2 process steps](connect_three_or_more_process_steps.md) using two network level.
+
+The executable examples files that are built during the tutorial can be found in the tutorial folder in the [examples in the ETHOS.PeNALPS repository](https://github.com/FZJ-IEK3-VSA/ETHOS_PeNALPS).
diff --git a/documentation/ethos_penalps_tutorial/single_cooker_process_chain.md b/documentation/ethos_penalps_tutorial/single_cooker_process_chain.md
new file mode 100644
index 0000000..416c3b1
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/single_cooker_process_chain.md
@@ -0,0 +1,256 @@
+# Simple Cooker Example
+
+This section shows how to setup a minimal simulation of a production system that simulates a single cooker. A depiction of the ETHOS.PeNALPS Model is shown in figure {numref}`cooking-example-single`. The energy, time and mass data is used from an experiment by {cite}`Korzeniowska_Ginter_2019`. Korzeniowska-Ginter et al . conducted a series of experiments to determine cooking length and energy of potatoes demand using household appliances. For this case the data from the electric stove with a metal plate is used.
+
+:::{figure-md} cooking-example-single
+
+
+Depiction of the minimal production system model in ETHOS.PeNALPS which simulates a simple cooker.
+:::
+
+## Initialize Time Data
+The first step is to setup the desired simulation period. The start time is only relevant for the period displayed. The simulation starts internally at the end date and terminates when all orders are created, which is shown in the following section.
+
+```
+import datetime
+from ethos_penalps.time_data import TimeData
+
+start_date = datetime.datetime(2022, 1, 2, hour=22, minute=30)
+end_date = datetime.datetime(2022, 1, 3)
+time_data = TimeData(
+ global_start_date=start_date,
+ global_end_date=end_date,
+)
+```
+
+## Setup All Commodities
+The second step is create all commodities which represent raw materials, intermediate products or final products of the production system. The modelled energy types and carriers for the load profiles are modelled later.
+
+```
+from ethos_penalps.data_classes import Commodity
+# Determine all relevant commodities
+output_commodity = Commodity(name="Cooked Goods")
+input_commodity = Commodity(name="Raw Goods")
+```
+## Create Product Orders
+As a third step the orders to be produced during the simulation must be created. The mass is passed as metric tones. The order consists of the commodity of the product, the deadline and the number of orders. The simulation attempts to fulfill the order just in time. If it is not possible due to capacity constraints, the production start is shifted to an earlier time. The order size is based on the mass that was used in a single experiment which consists of 200 gram potatoes and 450 ml of water. {cite}`Korzeniowska_Ginter_2019` p.3
+
+```
+# Create all order for the simulation
+order_generator = NOrderGenerator(
+ commodity=output_commodity,
+ mass_per_order=0.00065,
+ production_deadline=end_date,
+ number_of_orders=2,
+)
+order_collection = order_generator.create_n_order_collection()
+```
+## Create Container Classes
+The fourth step is to provide the minimum set of container classes. This consists of
+- an enterprise object
+- a network level
+- a process chain.
+
+These become relevant when multiple process steps should be modeled in a production system. [This is discussed in a later part of the tutorial](add_network_level_and_process_chains.md). It is important that the respective creator methods must be used as shown in this example. If the network level and process chain are instantiated directly from the class they are not connected properly.
+
+```
+from ethos_penalps.enterprise import Enterprise
+
+enterprise = Enterprise(time_data=time_data, name="Cooking Example")
+network_level = enterprise.create_network_level()
+process_chain = network_level.create_process_chain(process_chain_name="Cooker Chain")
+```
+## Create Source, Sink and Process Step
+In the next step source, sink and process step are created. The sink and source connect the production system to environment. The sink collects the requested products and the source provides the required raw materials. Additionally, the sink and source must be connected to process chain.
+
+```
+# Create all sources, sinks and network level storages
+sink = network_level.create_main_sink(
+ name="Cooked Goods Storage",
+ commodity=output_commodity,
+ order_collection=order_collection,
+)
+source = network_level.create_main_source(
+ name="Raw Material Storage",
+ commodity=input_commodity,
+)
+process_chain.add_sink(sink=continuous_sink)
+process_chain.add_source(source=batch_source)
+```
+
+Now the actual process step is created.
+
+```
+process_step = process_chain.create_process_step(name="Process Step")
+```
+
+## Create Streams, Sink, Source and Process Step
+The source, sink and process step must be connected by streams. These determine the material flow direction:
+
+```
+import datetime
+raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=process_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=input_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+
+source.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooking_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+```
+## Create Petri Net of States
+The behavior of the process step during the production is determined by a petri net of states. All possible states of the process step are modelled by a node of the petri net.
+These must be connected by transitions.
+
+### Create Nodes
+The simplest combination consists of an idle state and either a combined input and output state or two separate input and output states. In this case the model consists of:
+
+- idle state
+- input state
+- output state
+- intermediate state
+
+The intermediate state is not necessary to run the simulation, but is used to model the cooking phase of the cooking process. The input and output the states model the time phase in which input and output commodities are loaded or discharged.
+
+```
+idle_state = process_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+cooking_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+)
+
+discharge_goods_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+```
+
+### Create Transitions
+The transitions connect the newly created nodes. The transitions are called process state switches. In order to understand how the switches and states are connected it is important to note that the internal transition are conducted in reversed time direction. Thus, the condition is evaluated at the end process state. Currently there are four process state switches implemented which should be connected to the following end state:
+
+1. switch_at_next_discrete_event -> idle_state
+2. process_state_switch_at_input_stream -> input_state
+3. process_state_switch_at_output_stream --> output_state
+4. create_process_state_switch_delay --> intermediate_state
+
+Additionally, selectors are implemented to determine which transition are evaluated when multiple transitions could occur in the same state. In this example each state only has a single transition. Thus, the process state switch is passed to a so called single choice selector. The cooking time is assumed to be 24 minutes in total. {cite}`Korzeniowska_Ginter_2019` p.5
+
+```
+activate_not_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state,
+ end_process_state=idle_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+)
+
+activate_filling = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling
+)
+
+activate_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=cooking_state,
+ delay=datetime.timedelta(minutes=24),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking
+)
+
+
+activate_discharging = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state,
+ end_process_state=discharge_goods_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging
+)
+```
+## Initialize Energy Data
+
+Finally the energy data must be initialized. Therefore, the energy load type must be initialized, which is electricity in this example. It can either be addressed to a specific state or to the activity of a stream. Here the complete energy is consumed during the cooking state. The specific energy demand is provided in the units MJ/t. An energy demand of 830.76 MJ/t is assumed it is based on the energy demand of 0.15 kWh/650 gram {cite}`Korzeniowska_Ginter_2019` p.5 The input stream of the corresponding process step must be passed to the energy data to determine the mass that is processed in the state.
+
+```
+electricity_load = LoadType(name="Electricity")
+cooking_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=830.76,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+)
+```
+
+## Create Internal Storages and Mass Balances
+
+The last step before the simulation is to create mass balances and storages for each process step, which are required to start the simulation. These are used to model input to output conversions of the process step. They are mainly used for the internal processing of the output and input streams during the simulation.
+
+```
+process_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+)
+
+process_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+```
+## Start Simulation and Post Processing
+
+Lastly, the simulation and post processing must be started. Optionally, a maximum number of internal iterations can be determined to terminate ill defined simulations. The create_post_simulation_report method creates an HTML report of the simulation results. It contains:
+- A figure of the modelled process
+- The production plan
+ - Table
+ - downloadable CSV file
+- Load Profiles
+ - Table
+ - downloadable CSV file
+- Gantt chart of streams, process steps, sink and source
+- A carpet plot of the load profiles
+
+```
+enterprise.start_simulation(number_of_iterations_in_chain=200)
+enterprise.create_post_simulation_report(
+ start_date=start_date,
+ end_date=end_date,
+ x_axis_time_delta=datetime.timedelta(hours=1),
+ resample_frequency="5min",
+ gantt_chart_end_date=end_date,
+ gantt_chart_start_date=start_date,
+)
+```
+
+The next sections shows how the cooking process can be modelled in greater detail.
\ No newline at end of file
diff --git a/documentation/ethos_penalps_tutorial/unit_conversions.py b/documentation/ethos_penalps_tutorial/unit_conversions.py
new file mode 100644
index 0000000..7a782cb
--- /dev/null
+++ b/documentation/ethos_penalps_tutorial/unit_conversions.py
@@ -0,0 +1,40 @@
+from ethos_penalps.utilities.units import Units
+
+meter_quant = (0.15 * Units.unit_registry.Unit("kWh")) / (
+ 650 * Units.unit_registry.Unit("gram")
+)
+print(meter_quant.to("MJ/metric_ton"))
+
+
+a = 2000 * Units.unit_registry.Unit("W") * 6 * Units.unit_registry.Unit("minutes")
+print(a.to("kWh"))
+
+
+cp_water = 4.2 * Units.unit_registry.Unit("kJ/(kg*K)")
+energy_water = (
+ 650
+ * Units.unit_registry.Unit("gram")
+ * 80
+ * Units.unit_registry.Unit("K")
+ * cp_water
+)
+
+print("Energy", energy_water.to("kWh"))
+time = energy_water / (2000 * Units.unit_registry.Unit("W"))
+print(time.to("minute"))
+
+energy_output = (
+ 0.09 * Units.unit_registry.Unit("kWh") / (650 * Units.unit_registry.Unit("gram"))
+)
+print(energy_output.to("MJ/t"))
+
+energy_output = (
+ 0.06 * Units.unit_registry.Unit("kWh") / (650 * Units.unit_registry.Unit("gram"))
+)
+print(energy_output.to("MJ/t"))
+
+# Blender energy demand
+energy_output = (
+ 1300 * Units.unit_registry.Unit("W") * 5 * Units.unit_registry.Unit("minutes")
+) / (650 * Units.unit_registry.Unit("gram"))
+print(energy_output.to("MJ/t"))
diff --git a/documentation/references.bib b/documentation/references.bib
index 5c24a0e..9038586 100644
--- a/documentation/references.bib
+++ b/documentation/references.bib
@@ -1,170 +1,183 @@
% This file was created with Citavi 6.17.0.0
@book{.2006,
- year = {2006},
- title = {Batch processes},
- url = {http://www.loc.gov/catdir/enhancements/fy0648/2005043727-d.html},
- address = {Boca Raton, Fla.},
- volume = {106},
- publisher = {{CRC/Taylor {\&} Francis}},
- isbn = {978-0-8247-2522-8},
- series = {Chemical industries},
- editor = {Korovessi, Ekaterini and Linninger, Andreas A.},
- file = {[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li:Attachments/[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li.pdf:application/pdf}
+ year = {2006},
+ title = {Batch processes},
+ url = {http://www.loc.gov/catdir/enhancements/fy0648/2005043727-d.html},
+ address = {Boca Raton, Fla.},
+ volume = {106},
+ publisher = {{CRC/Taylor {\&} Francis}},
+ isbn = {978-0-8247-2522-8},
+ series = {Chemical industries},
+ editor = {Korovessi, Ekaterini and Linninger, Andreas A.},
+ file = {[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li:Attachments/[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li.pdf:application/pdf}
}
@misc{.ChocoTecCarastar,
- author = {{Chocotech GmbH}},
- year = {24.10.2023},
- title = {CARASTAR{\circledR} - CHOCOTECH :: The Candy Specialist},
- url = {https://www.chocotech.de/technologie/kochen/carastarr},
- urldate = {24.10.2023},
- file = {CARASTAR{\circledR} 24102023:Attachments/CARASTAR{\circledR} 24102023.pdf:application/pdf}
+ author = {{Chocotech GmbH}},
+ year = {24.10.2023},
+ title = {CARASTAR{\circledR} - CHOCOTECH :: The Candy Specialist},
+ url = {https://www.chocotech.de/technologie/kochen/carastarr},
+ urldate = {24.10.2023},
+ file = {CARASTAR{\circledR} 24102023:Attachments/CARASTAR{\circledR} 24102023.pdf:application/pdf}
}
@article{Cooper.2017,
- author = {Cooper, Daniel R. and Rossie, Kathleen E. and Gutowski, Timothy G.},
- year = {2017},
- title = {An Environmental and Cost Analysis of Stamping Sheet Metal Parts},
- volume = {139},
- number = {4},
- issn = {1087-1357},
- journal = {Journal of Manufacturing Science and Engineering},
- doi = {10.1115/1.4034670},
- file = {83232471:Attachments/83232471.pdf:application/pdf}
+ author = {Cooper, Daniel R. and Rossie, Kathleen E. and Gutowski, Timothy G.},
+ year = {2017},
+ title = {An Environmental and Cost Analysis of Stamping Sheet Metal Parts},
+ volume = {139},
+ number = {4},
+ issn = {1087-1357},
+ journal = {Journal of Manufacturing Science and Engineering},
+ doi = {10.1115/1.4034670},
+ file = {83232471:Attachments/83232471.pdf:application/pdf}
}
@book{Edwards.2000,
- author = {Edwards, William P.},
- year = {2000},
- title = {The Science of Sugar Confectionery},
- publisher = {{The Royal Society of Chemistry}},
- isbn = {978-0-85404-593-8},
- doi = {10.1039/9781847552167},
- file = {The Science of Sugar Confectionery:Attachments/The Science of Sugar Confectionery.pdf:application/pdf}
+ author = {Edwards, William P.},
+ year = {2000},
+ title = {The Science of Sugar Confectionery},
+ publisher = {{The Royal Society of Chemistry}},
+ isbn = {978-0-85404-593-8},
+ doi = {10.1039/9781847552167},
+ file = {The Science of Sugar Confectionery:Attachments/The Science of Sugar Confectionery.pdf:application/pdf}
}
@misc{Ganapathy.2017,
- abstract = {In the present work, a new grip design for the Gleeble Materials-Simulator has been developed to reduce the long-standing problem of the temperature gradient during thermo-mechanical tensile testing. The deformation behaviour of 22MnB5 boron steel for a range of temperatures and strain rates were evaluated using the new test grip. Deformation results showed higher strain hardening (n-value) behaviour at low temperature compared to that at high temperature deformation, which increases the drawability of the material at low temperature hot stamping conditions. Moreover, hot stamping at high temperatures takes a longer time to cool the material below the martensitic finish temperature, which increases the die holding time and decreases productivity. To improve the formability and productivity of the hot stamping processes, the new hot stamping process at low temperatures was demonstrated. An automotive B-Pillar component was hot stamped at a wide range of temperatures and forming speeds, which included temperatures much lower than that of a normal hot stamping temperature range. To understand the influence of tool-workpiece contact pressure, tool and tool surface temperatures, and the effect of initial work-piece stamping temperature on in-die cooling time and tool surface temperature were investigated. A series tests of low-temperature hot stamping and heat transfer at different initial workpiece temperatures and contact pressure were carried out. The results confirmed that hot stamping could be performed at a lower temperature (500 °C) without compromising the part quality. It could also benefit the reduction of in-die quenching time by over 50{\%}, which would increase productivity for automotive mass production. In the process, material microstructural and thermomechanical behaviours can be carefully controlled to improve drawability and at the same time, to maintain or even enhance the post-form properties. Finally, unified viscoplastic constitutive equations were discussed, and material constants were determined. The viscoplastic constitutive material model was input into the finite element code, LS-DYNA, through a user defined subroutine and used for the simulation of hot stamping processes. The implemented viscoplastic constitutive model was accurate enough to predict the thickness distribution of a hot stamped product.},
- author = {Ganapathy, Manikandan},
- date = {2017},
- title = {Hot stamping of complex shaped boron steel panels},
- publisher = {{Imperial College London}},
- doi = {10.25560/78569},
- file = {Ganapathy 2017 - Hot stamping of complex shaped:Attachments/Ganapathy 2017 - Hot stamping of complex shaped.pdf:application/pdf}
+ abstract = {In the present work, a new grip design for the Gleeble Materials-Simulator has been developed to reduce the long-standing problem of the temperature gradient during thermo-mechanical tensile testing. The deformation behaviour of 22MnB5 boron steel for a range of temperatures and strain rates were evaluated using the new test grip. Deformation results showed higher strain hardening (n-value) behaviour at low temperature compared to that at high temperature deformation, which increases the drawability of the material at low temperature hot stamping conditions. Moreover, hot stamping at high temperatures takes a longer time to cool the material below the martensitic finish temperature, which increases the die holding time and decreases productivity. To improve the formability and productivity of the hot stamping processes, the new hot stamping process at low temperatures was demonstrated. An automotive B-Pillar component was hot stamped at a wide range of temperatures and forming speeds, which included temperatures much lower than that of a normal hot stamping temperature range. To understand the influence of tool-workpiece contact pressure, tool and tool surface temperatures, and the effect of initial work-piece stamping temperature on in-die cooling time and tool surface temperature were investigated. A series tests of low-temperature hot stamping and heat transfer at different initial workpiece temperatures and contact pressure were carried out. The results confirmed that hot stamping could be performed at a lower temperature (500 °C) without compromising the part quality. It could also benefit the reduction of in-die quenching time by over 50{\%}, which would increase productivity for automotive mass production. In the process, material microstructural and thermomechanical behaviours can be carefully controlled to improve drawability and at the same time, to maintain or even enhance the post-form properties. Finally, unified viscoplastic constitutive equations were discussed, and material constants were determined. The viscoplastic constitutive material model was input into the finite element code, LS-DYNA, through a user defined subroutine and used for the simulation of hot stamping processes. The implemented viscoplastic constitutive model was accurate enough to predict the thickness distribution of a hot stamped product.},
+ author = {Ganapathy, Manikandan},
+ date = {2017},
+ title = {Hot stamping of complex shaped boron steel panels},
+ publisher = {{Imperial College London}},
+ doi = {10.25560/78569},
+ file = {Ganapathy 2017 - Hot stamping of complex shaped:Attachments/Ganapathy 2017 - Hot stamping of complex shaped.pdf:application/pdf}
}
@article{Ganapathy.2019,
- author = {Ganapathy, M. and Li, N. and Lin, J. and Abspoel, M. and Bhattacharjee, D.},
- year = {2019},
- title = {Experimental investigation of a new low-temperature hot stamping process for boron steels},
- pages = {669--682},
- volume = {105},
- number = {1-4},
- issn = {0268-3768},
- journal = {The International Journal of Advanced Manufacturing Technology},
- doi = {10.1007/s00170-019-04172-5},
- file = {s00170-019-04172-5:Attachments/s00170-019-04172-5.pdf:application/pdf}
+ author = {Ganapathy, M. and Li, N. and Lin, J. and Abspoel, M. and Bhattacharjee, D.},
+ year = {2019},
+ title = {Experimental investigation of a new low-temperature hot stamping process for boron steels},
+ pages = {669--682},
+ volume = {105},
+ number = {1-4},
+ issn = {0268-3768},
+ journal = {The International Journal of Advanced Manufacturing Technology},
+ doi = {10.1007/s00170-019-04172-5},
+ file = {s00170-019-04172-5:Attachments/s00170-019-04172-5.pdf:application/pdf}
}
@article{HeatherLiddell.,
- abstract = {The DOE Manufacturing Energy Bandwidth Studies, which have served as foundational data references for energy consumption savings opportunities in key manufacturing sectors for over two decades.},
- author = {{Heather Liddell} and {United States Department of Energy}},
- title = {Sustainable Materials Selection in Manufactured Products: A Framework for Design-Integrated Life Cycle Thinking with Case Studies},
- file = {Materials Substitution Working Report{\_}August 2023{\_}final{\_}compliant{\_}v2{\_}0:Attachments/Materials Substitution Working Report{\_}August 2023{\_}final{\_}compliant{\_}v2{\_}0.pdf:application/pdf}
+ abstract = {The DOE Manufacturing Energy Bandwidth Studies, which have served as foundational data references for energy consumption savings opportunities in key manufacturing sectors for over two decades.},
+ author = {{Heather Liddell} and {United States Department of Energy}},
+ title = {Sustainable Materials Selection in Manufactured Products: A Framework for Design-Integrated Life Cycle Thinking with Case Studies},
+ file = {Materials Substitution Working Report{\_}August 2023{\_}final{\_}compliant{\_}v2{\_}0:Attachments/Materials Substitution Working Report{\_}August 2023{\_}final{\_}compliant{\_}v2{\_}0.pdf:application/pdf}
}
@book{Hui.2007,
- author = {Hui, Y. H.},
- year = {2007},
- title = {Handbook of Food Products Manufacturing},
- publisher = {Wiley},
- isbn = {9780470049648},
- doi = {10.1002/0470113553},
- file = {0470113553:Attachments/0470113553.pdf:application/pdf}
+ author = {Hui, Y. H.},
+ year = {2007},
+ title = {Handbook of Food Products Manufacturing},
+ publisher = {Wiley},
+ isbn = {9780470049648},
+ doi = {10.1002/0470113553},
+ file = {0470113553:Attachments/0470113553.pdf:application/pdf}
}
@article{MezaGarcia.2019,
- author = {Meza-Garc{\'i}a, Enrique and Rautenstrauch, Anja and Br{\"a}unig, Michael and Kr{\"a}usel, Verena and Landgrebe, Dirk},
- year = {2019},
- title = {Energetic evaluation of press hardening processes},
- pages = {367--374},
- volume = {33},
- issn = {23519789},
- journal = {Procedia Manufacturing},
- doi = {10.1016/j.promfg.2019.04.045},
- file = {1-s2.0-S2351978919305220-main:Attachments/1-s2.0-S2351978919305220-main.pdf:application/pdf}
+ author = {Meza-Garc{\'i}a, Enrique and Rautenstrauch, Anja and Br{\"a}unig, Michael and Kr{\"a}usel, Verena and Landgrebe, Dirk},
+ year = {2019},
+ title = {Energetic evaluation of press hardening processes},
+ pages = {367--374},
+ volume = {33},
+ issn = {23519789},
+ journal = {Procedia Manufacturing},
+ doi = {10.1016/j.promfg.2019.04.045},
+ file = {1-s2.0-S2351978919305220-main:Attachments/1-s2.0-S2351978919305220-main.pdf:application/pdf}
}
@article{Neugebauer.2012,
- author = {Neugebauer, R. and Schieck, F. and Polster, S. and Mosel, A. and Rautenstrauch, A. and Sch{\"o}nherr, J. and Pierschel, N.},
- year = {2012},
- title = {Press hardening --- An innovative and challenging technology},
- pages = {113--118},
- volume = {12},
- number = {2},
- issn = {16449665},
- journal = {Archives of Civil and Mechanical Engineering},
- doi = {10.1016/j.acme.2012.04.013},
- file = {j.acme.2012.04.013:Attachments/j.acme.2012.04.013.pdf:application/pdf}
+ author = {Neugebauer, R. and Schieck, F. and Polster, S. and Mosel, A. and Rautenstrauch, A. and Sch{\"o}nherr, J. and Pierschel, N.},
+ year = {2012},
+ title = {Press hardening --- An innovative and challenging technology},
+ pages = {113--118},
+ volume = {12},
+ number = {2},
+ issn = {16449665},
+ journal = {Archives of Civil and Mechanical Engineering},
+ doi = {10.1016/j.acme.2012.04.013},
+ file = {j.acme.2012.04.013:Attachments/j.acme.2012.04.013.pdf:application/pdf}
}
@article{Pan.2010,
- author = {Pan, Feng and Zhu, Ping and Zhang, Yu},
- year = {2010},
- title = {Metamodel-based lightweight design of B-pillar with TWB structure via support vector regression},
- pages = {36--44},
- volume = {88},
- number = {1-2},
- issn = {00457949},
- journal = {Computers {\&} Structures},
- doi = {10.1016/j.compstruc.2009.07.008},
- file = {1-s2.0-S004579490900203X-main:Attachments/1-s2.0-S004579490900203X-main.pdf:application/pdf}
+ author = {Pan, Feng and Zhu, Ping and Zhang, Yu},
+ year = {2010},
+ title = {Metamodel-based lightweight design of B-pillar with TWB structure via support vector regression},
+ pages = {36--44},
+ volume = {88},
+ number = {1-2},
+ issn = {00457949},
+ journal = {Computers {\&} Structures},
+ doi = {10.1016/j.compstruc.2009.07.008},
+ file = {1-s2.0-S004579490900203X-main:Attachments/1-s2.0-S004579490900203X-main.pdf:application/pdf}
}
@incollection{Tomazi.2006,
- author = {Tomazi, G. Keith and Linninger, Andreas A. and Daniel, R. James},
- title = {Batch Processsing Industries},
- pages = {7--39},
- publisher = {{CRC/Taylor {\&} Francis}},
- isbn = {978-0-8247-2522-8},
- series = {Chemical industries},
- editor = {Korovessi, Ekaterini and Linninger, Andreas A.},
- booktitle = {Batch processes},
- year = {2006},
- address = {Boca Raton, Fla.},
- file = {[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li:Attachments/[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li.pdf:application/pdf}
+ author = {Tomazi, G. Keith and Linninger, Andreas A. and Daniel, R. James},
+ title = {Batch Processsing Industries},
+ pages = {7--39},
+ publisher = {{CRC/Taylor {\&} Francis}},
+ isbn = {978-0-8247-2522-8},
+ series = {Chemical industries},
+ editor = {Korovessi, Ekaterini and Linninger, Andreas A.},
+ booktitle = {Batch processes},
+ year = {2006},
+ address = {Boca Raton, Fla.},
+ file = {[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li:Attachments/[Chemical industries 106] Ekaterini Korovessi, Andreas A. Linninger - Batch Processes (2006, CRC{\_}Taylor {\&} Francis) - libgen.li.pdf:application/pdf}
}
@article{vskonicki.,
- author = {vskonicki},
- title = {Energy-Consumption and Carbon-Emission Analysis of Vehicle and Component Manufacturing},
- file = {68288 (1):Attachments/68288 (1).pdf:application/pdf}
+ author = {vskonicki},
+ title = {Energy-Consumption and Carbon-Emission Analysis of Vehicle and Component Manufacturing},
+ file = {68288 (1):Attachments/68288 (1).pdf:application/pdf}
}
@article{Wojdalski.2015,
- author = {Wojdalski, Janusz and Grochowicz, J{\'o}zef and Dr{\'o}{\.z}d{\.z}, Bogdan and Bartoszewska, Katarzyna and Zdanowska, Paulina and Kupczyk, Adam and Ekielski, Adam and Florczak, Iwona and Hasny, Aleksandra and W{\'o}jcik, Gra{\.z}yna},
- year = {2015},
- title = {Energy efficiency of a confectionery plant -- Case study},
- pages = {182--191},
- volume = {146},
- issn = {02608774},
- journal = {Journal of Food Engineering},
- doi = {10.1016/j.jfoodeng.2014.08.019},
- file = {EnergyefficiencyConf.Plant:Attachments/EnergyefficiencyConf.Plant.pdf:application/pdf}
+ author = {Wojdalski, Janusz and Grochowicz, J{\'o}zef and Dr{\'o}{\.z}d{\.z}, Bogdan and Bartoszewska, Katarzyna and Zdanowska, Paulina and Kupczyk, Adam and Ekielski, Adam and Florczak, Iwona and Hasny, Aleksandra and W{\'o}jcik, Gra{\.z}yna},
+ year = {2015},
+ title = {Energy efficiency of a confectionery plant -- Case study},
+ pages = {182--191},
+ volume = {146},
+ issn = {02608774},
+ journal = {Journal of Food Engineering},
+ doi = {10.1016/j.jfoodeng.2014.08.019},
+ file = {EnergyefficiencyConf.Plant:Attachments/EnergyefficiencyConf.Plant.pdf:application/pdf}
}
+@article{Korzeniowska_Ginter_2019,
+ author = {Korzeniowska-Ginter, R},
+ doi = {10.1088/1755-1315/214/1/012096},
+ issn = {1755-1315},
+ journal = {IOP Conference Series: Earth and Environmental Science},
+ month = {January},
+ pages = {012096},
+ publisher = {IOP Publishing},
+ title = {Energy consumption by cooking appliances used in Polish households},
+ url = {http://dx.doi.org/10.1088/1755-1315/214/1/012096},
+ volume = {214},
+ year = {2019}
+}
\ No newline at end of file
diff --git a/examples/basic_examples.py/batch_to_batch_1_node_example.py b/examples/basic_examples.py/batch_to_batch_1_node_example.py
index b9a7e65..7b9c2e2 100644
--- a/examples/basic_examples.py/batch_to_batch_1_node_example.py
+++ b/examples/basic_examples.py/batch_to_batch_1_node_example.py
@@ -34,17 +34,15 @@
# Create network level
network_level = enterprise.create_network_level()
-input_batch_delay = datetime.timedelta(minutes=20)
-output_batch_delay = datetime.timedelta(minutes=40)
# Determine all relevant commodities
-batch_product = Commodity(name="Continuous product")
-batch_raw_material = Commodity(name="Batch educt")
+output_commodity = Commodity(name="Product")
+input_commodity = Commodity(name="Educt")
# Create all order for the simulation
order_generator = NOrderGenerator(
- commodity=batch_product,
+ commodity=output_commodity,
mass_per_order=300,
production_deadline=end_date,
number_of_orders=1,
@@ -53,28 +51,26 @@
order_collection = order_generator.create_n_order_collection()
# Create all sources, sinks and network level storages
-continuous_sink = network_level.create_main_sink(
- name="Batch sink",
- commodity=batch_product,
+sink = network_level.create_main_sink(
+ name="Sink",
+ commodity=output_commodity,
order_collection=order_collection,
)
-batch_source = network_level.create_main_source(
- name="Batch source",
- commodity=batch_raw_material,
+source = network_level.create_main_source(
+ name="Source",
+ commodity=input_commodity,
)
# Create first process chain
-process_chain = network_level.create_process_chain(
- process_chain_name="Test process chain"
-)
+process_chain = network_level.create_process_chain(process_chain_name="Process Chain")
# Add sources and sinks to process chain
-process_chain.add_sink(sink=continuous_sink)
-process_chain.add_source(source=batch_source)
+process_chain.add_sink(sink=sink)
+process_chain.add_source(source=source)
# Create Process nodes
-batch_to_batch_step = process_chain.create_process_step(name="Batch to batch step")
+process_step = process_chain.create_process_step(name="Batch to batch process step")
""" Create petri net for process step
@@ -95,60 +91,64 @@
)
"""
-batch_idle_state = batch_to_batch_step.process_state_handler.create_idle_process_state(
+batch_idle_state = process_step.process_state_handler.create_idle_process_state(
process_state_name="Waiting process step"
)
-batch_input_state = batch_to_batch_step.process_state_handler.create_batch_input_stream_requesting_state(
- process_state_name="Batch input state"
+batch_input_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Batch input state"
+ )
)
-batch_output_state = batch_to_batch_step.process_state_handler.create_batch_output_stream_providing_state(
- process_state_name="Batch output state"
+batch_output_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Batch output state"
+ )
)
# Petri net transitions
-idle_state_activation = batch_to_batch_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+idle_state_activation = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
start_process_state=batch_output_state,
end_process_state=batch_idle_state,
)
-batch_to_batch_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
process_state_switch=idle_state_activation
)
-batch_input_request_state = batch_to_batch_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+batch_input_request_state = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
start_process_state=batch_idle_state,
end_process_state=batch_input_state,
)
-batch_to_batch_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
process_state_switch=batch_input_request_state
)
-output_providing_activation = batch_to_batch_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+output_providing_activation = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
start_process_state=batch_input_state,
end_process_state=batch_output_state,
)
-batch_to_batch_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
process_state_switch=output_providing_activation
)
# Streams
source_to_process_step = process_chain.stream_handler.create_batch_stream(
batch_stream_static_data=BatchStreamStaticData(
- start_process_step_name=batch_source.name,
- end_process_step_name=batch_to_batch_step.name,
- delay=input_batch_delay,
- commodity=batch_raw_material,
+ start_process_step_name=source.name,
+ end_process_step_name=process_step.name,
+ delay=datetime.timedelta(minutes=20),
+ commodity=input_commodity,
maximum_batch_mass_value=300,
)
)
process_step_to_sink = process_chain.stream_handler.create_batch_stream(
batch_stream_static_data=BatchStreamStaticData(
- start_process_step_name=batch_to_batch_step.name,
- end_process_step_name=continuous_sink.name,
- delay=output_batch_delay,
- commodity=batch_product,
+ start_process_step_name=process_step.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=40),
+ commodity=output_commodity,
maximum_batch_mass_value=300,
)
)
@@ -165,24 +165,24 @@
# Mass balances
-batch_to_batch_step.create_main_mass_balance(
- commodity=batch_product,
+process_step.create_main_mass_balance(
+ commodity=output_commodity,
input_to_output_conversion_factor=1,
main_input_stream=source_to_process_step,
main_output_stream=process_step_to_sink,
)
# Add internal storages (required)
-batch_to_batch_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+process_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
current_storage_level=0
)
# Add streams to sinks and sources
-batch_source.add_output_stream(
+source.add_output_stream(
output_stream=source_to_process_step,
process_chain_identifier=process_chain.process_chain_identifier,
)
-continuous_sink.add_input_stream(
+sink.add_input_stream(
input_stream=process_step_to_sink,
process_chain_identifier=process_chain.process_chain_identifier,
)
diff --git a/examples/tutorial/_1_cooking_example.py b/examples/tutorial/_1_cooking_example.py
new file mode 100644
index 0000000..6ba7b40
--- /dev/null
+++ b/examples/tutorial/_1_cooking_example.py
@@ -0,0 +1,206 @@
+import datetime
+import logging
+
+from ethos_penalps.data_classes import Commodity, LoadType
+from ethos_penalps.enterprise import Enterprise
+from ethos_penalps.order_generator import NOrderGenerator
+from ethos_penalps.stream import BatchStreamStaticData, ContinuousStreamStaticData
+from ethos_penalps.time_data import TimeData
+from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger
+
+logger = PeNALPSLogger.get_human_readable_logger(logging.INFO)
+
+# Enterprise structure
+
+# Set simulation time data
+start_date = datetime.datetime(2022, 1, 2, hour=22, minute=30)
+end_date = datetime.datetime(2022, 1, 3)
+time_data = TimeData(
+ global_start_date=start_date,
+ global_end_date=end_date,
+)
+
+
+# Determine all relevant commodities
+output_commodity = Commodity(name="Cooked Goods")
+input_commodity = Commodity(name="Raw Goods")
+
+# Create all order for the simulation
+order_generator = NOrderGenerator(
+ commodity=output_commodity,
+ mass_per_order=0.00065,
+ production_deadline=end_date,
+ number_of_orders=2,
+)
+
+order_collection = order_generator.create_n_order_collection()
+
+# Initialize enterprise
+enterprise = Enterprise(time_data=time_data, name="Cooking Example")
+
+# Create network level
+network_level = enterprise.create_network_level()
+# Create first process chain
+
+process_chain = network_level.create_process_chain(process_chain_name="Cooker Chain")
+
+# Create all sources, sinks and network level storages
+sink = network_level.create_main_sink(
+ name="Cooked Goods Storage",
+ commodity=output_commodity,
+ order_collection=order_collection,
+)
+source = network_level.create_main_source(
+ name="Raw Material Storage",
+ commodity=input_commodity,
+)
+
+
+# Add sources and sinks to process chain
+process_chain.add_sink(sink=sink)
+process_chain.add_source(source=source)
+
+# Create Process nodes
+process_step = process_chain.create_process_step(name="Cooker")
+
+# Streams
+raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=process_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=input_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+
+# Add streams to sinks and sources
+source.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooking_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+
+""" Create petri net for process step
+Each process state must have at least the following:
+- Either
+ - one combined production state
+ your_combined_state=process_step.process_state_handler.create_continuous_production_process_state_with_storage(
+ process_state_name="your process state name"
+ )
+ or
+ - an input stream requesting state
+ your_input_stream_requesting_state=process_step.process_state_handler.create_continuous_input_stream_requesting_state(process_state_name="your input stream providing state")
+ - and input stream requesting state
+ your_output_stream_providing_state=process_step.create_continuous_output_stream_providing_state(process_state_name="your output stream providing state")
+ also an idle state is required
+ your_idle_state=process_step..process_state_handler.create_idle_process_state(
+ process_state_name="Your idle state"
+ )
+"""
+
+idle_state = process_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+cooking_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+)
+
+discharge_goods_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+
+# Petri net transitions
+
+activate_not_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state,
+ end_process_state=idle_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+)
+
+activate_filling = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling
+)
+
+activate_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=cooking_state,
+ delay=datetime.timedelta(minutes=24),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking
+)
+
+
+activate_discharging = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state,
+ end_process_state=discharge_goods_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging
+)
+
+
+electricity_load = LoadType(name="Electricity")
+cooking_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=830.76,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+)
+
+
+# Mass balances
+process_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+)
+
+# Add internal storages (required)
+process_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+
+
+# Start the simulation
+enterprise.start_simulation(number_of_iterations_in_chain=200)
+
+# Create report of the simulation results
+enterprise.create_post_simulation_report(
+ start_date=start_date,
+ end_date=end_date,
+ x_axis_time_delta=datetime.timedelta(hours=1),
+ resample_frequency="5min",
+ gantt_chart_end_date=end_date,
+ gantt_chart_start_date=start_date,
+)
diff --git a/examples/tutorial/_2_cooking_example_more_states.py b/examples/tutorial/_2_cooking_example_more_states.py
new file mode 100644
index 0000000..c0086ef
--- /dev/null
+++ b/examples/tutorial/_2_cooking_example_more_states.py
@@ -0,0 +1,235 @@
+import datetime
+import logging
+
+from ethos_penalps.data_classes import Commodity, LoadType
+from ethos_penalps.enterprise import Enterprise
+from ethos_penalps.order_generator import NOrderGenerator
+from ethos_penalps.stream import BatchStreamStaticData, ContinuousStreamStaticData
+from ethos_penalps.time_data import TimeData
+from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger
+
+logger = PeNALPSLogger.get_human_readable_logger(logging.INFO)
+
+# Enterprise structure
+
+# Set simulation time data
+start_date = datetime.datetime(2022, 1, 2, hour=22, minute=30)
+end_date = datetime.datetime(2022, 1, 3)
+time_data = TimeData(
+ global_start_date=start_date,
+ global_end_date=end_date,
+)
+
+
+# Determine all relevant commodities
+output_commodity = Commodity(name="Cooked Goods")
+input_commodity = Commodity(name="Raw Goods")
+
+
+# Create all order for the simulation
+order_generator = NOrderGenerator(
+ commodity=output_commodity,
+ mass_per_order=0.0005,
+ production_deadline=end_date,
+ number_of_orders=2,
+)
+
+order_collection = order_generator.create_n_order_collection()
+
+# Initialize enterprise
+enterprise = Enterprise(time_data=time_data, name="Cooking Example")
+
+# Create network level
+network_level = enterprise.create_network_level()
+# Create first process chain
+
+process_chain = network_level.create_process_chain(process_chain_name="Cooker Chain")
+
+# Create all sources, sinks and network level storages
+sink = network_level.create_main_sink(
+ name="Cooked Goods Storage",
+ commodity=output_commodity,
+ order_collection=order_collection,
+)
+source = network_level.create_main_source(
+ name="Raw Material Storage",
+ commodity=input_commodity,
+)
+
+
+# Add sources and sinks to process chain
+process_chain.add_sink(sink=sink)
+process_chain.add_source(source=source)
+
+# Create Process nodes
+process_step = process_chain.create_process_step(name="Cooker")
+
+# Streams
+raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=process_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=input_commodity,
+ maximum_batch_mass_value=0.0005,
+ )
+)
+cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.0005,
+ )
+)
+
+# Add streams to sinks and sources
+source.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooking_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+
+""" Create petri net for process step
+Each process state must have at least the following:
+- Either
+ - one combined production state
+ your_combined_state=process_step.process_state_handler.create_continuous_production_process_state_with_storage(
+ process_state_name="your process state name"
+ )
+ or
+ - an input stream requesting state
+ your_input_stream_requesting_state=process_step.process_state_handler.create_continuous_input_stream_requesting_state(process_state_name="your input stream providing state")
+ - and input stream requesting state
+ your_output_stream_providing_state=process_step.create_continuous_output_stream_providing_state(process_state_name="your output stream providing state")
+ also an idle state is required
+ your_idle_state=process_step..process_state_handler.create_idle_process_state(
+ process_state_name="Your idle state"
+ )
+"""
+
+idle_state = process_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+heating_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Heating"
+)
+
+hold_temperature_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Hold Temperature"
+)
+
+discharge_goods_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+cleaning_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cleaning"
+)
+
+
+# Petri net transitions
+activate_not_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=cleaning_state,
+ end_process_state=idle_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+)
+
+activate_filling = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling
+)
+
+activate_heating = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=heating_state,
+ delay=datetime.timedelta(minutes=1.82),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_heating
+)
+activate_hold_temperature = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=heating_state,
+ end_process_state=hold_temperature_state,
+ delay=datetime.timedelta(minutes=22.18),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_hold_temperature
+)
+
+
+activate_discharging = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=hold_temperature_state,
+ end_process_state=discharge_goods_state,
+)
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging
+)
+activate_hold_temperature = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=discharge_goods_state,
+ end_process_state=cleaning_state,
+ delay=datetime.timedelta(minutes=3),
+)
+
+process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_hold_temperature
+)
+
+electricity_load = LoadType(name="Electricity")
+heating_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=332.31,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+)
+hold_temperature_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=498.46,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+)
+
+# Mass balances
+process_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+)
+
+# Add internal storages (required)
+process_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+
+
+# Start the simulation
+enterprise.start_simulation(number_of_iterations_in_chain=200)
+
+# Create report of the simulation results
+enterprise.create_post_simulation_report(
+ start_date=start_date,
+ end_date=end_date,
+ x_axis_time_delta=datetime.timedelta(hours=1),
+ resample_frequency="1min",
+ gantt_chart_end_date=end_date,
+ gantt_chart_start_date=start_date,
+)
diff --git a/examples/tutorial/_3_cooking_and_mixer_exclusive_example.py b/examples/tutorial/_3_cooking_and_mixer_exclusive_example.py
new file mode 100644
index 0000000..b6c8b70
--- /dev/null
+++ b/examples/tutorial/_3_cooking_and_mixer_exclusive_example.py
@@ -0,0 +1,299 @@
+import datetime
+import logging
+
+from ethos_penalps.data_classes import Commodity, LoadType
+from ethos_penalps.enterprise import Enterprise
+from ethos_penalps.order_generator import NOrderGenerator
+from ethos_penalps.stream import BatchStreamStaticData, ContinuousStreamStaticData
+from ethos_penalps.time_data import TimeData
+from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger
+
+logger = PeNALPSLogger.get_human_readable_logger(logging.DEBUG)
+
+# Enterprise structure
+
+# Set simulation time data
+start_date = datetime.datetime(2022, 1, 2, hour=22, minute=30)
+end_date = datetime.datetime(2022, 1, 3)
+time_data = TimeData(
+ global_start_date=start_date,
+ global_end_date=end_date,
+)
+
+
+# Determine all relevant commodities
+raw_commodity = Commodity(name="Raw Goods")
+uncooked_commodity = Commodity(name="Uncooked Goods")
+output_commodity = Commodity(name="Cooked Goods")
+
+
+# Create all order for the simulation
+order_generator = NOrderGenerator(
+ commodity=output_commodity,
+ mass_per_order=0.00065,
+ production_deadline=end_date,
+ number_of_orders=4,
+)
+
+order_collection = order_generator.create_n_order_collection()
+
+# Initialize enterprise
+enterprise = Enterprise(time_data=time_data, name="Cooking Example")
+
+# Create network level
+network_level = enterprise.create_network_level()
+# Create first process chain
+
+process_chain = network_level.create_process_chain(process_chain_name="Cooker Chain 1")
+# Create all sources, sinks and network level storages
+sink = network_level.create_main_sink(
+ name="Cooked Goods Storage",
+ commodity=output_commodity,
+ order_collection=order_collection,
+)
+source = network_level.create_main_source(
+ name="Raw Material Storage",
+ commodity=raw_commodity,
+)
+
+
+# Add sources and sinks to process chain
+process_chain.add_sink(sink=sink)
+process_chain.add_source(source=source)
+
+# Create Process nodes
+blender_step = process_chain.create_process_step(name="Blender")
+cooker_step = process_chain.create_process_step(name="Cooker")
+
+# Streams
+## Process Chain 1
+raw_materials_to_blender_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=source.name,
+ end_process_step_name=blender_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=raw_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+blender_to_cooker_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=blender_step.name,
+ end_process_step_name=cooker_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=output_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+
+cooker_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=cooker_step.name,
+ end_process_step_name=sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=raw_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+)
+
+# Add streams to sinks and sources
+source.add_output_stream(
+ output_stream=raw_materials_to_blender_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+sink.add_input_stream(
+ input_stream=cooker_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+)
+
+
+""" Create petri net for process step
+Each process state must have at least the following:
+- Either
+ - one combined production state
+ your_combined_state=process_step.process_state_handler.create_continuous_production_process_state_with_storage(
+ process_state_name="your process state name"
+ )
+ or
+ - an input stream requesting state
+ your_input_stream_requesting_state=process_step.process_state_handler.create_continuous_input_stream_requesting_state(process_state_name="your input stream providing state")
+ - and input stream requesting state
+ your_output_stream_providing_state=process_step.create_continuous_output_stream_providing_state(process_state_name="your output stream providing state")
+ also an idle state is required
+ your_idle_state=process_step..process_state_handler.create_idle_process_state(
+ process_state_name="Your idle state"
+ )
+"""
+# Process Step 1
+
+idle_state_blender = blender_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state_1 = (
+ blender_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+blending_state = blender_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Blend"
+)
+
+discharge_goods_state_blender = (
+ blender_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+
+# Petri net transitions
+
+activate_not_blending = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_blender,
+ end_process_state=idle_state_blender,
+)
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_blending
+)
+
+activate_filling_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_blender,
+ end_process_state=fill_raw_materials_state_1,
+)
+
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_blender
+)
+
+activate_blending = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state_1,
+ end_process_state=blending_state,
+ delay=datetime.timedelta(minutes=5),
+)
+
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_blending
+)
+
+
+activate_discharging_blender = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=blending_state,
+ end_process_state=discharge_goods_state_blender,
+)
+blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_blender
+)
+
+# Cooker
+
+idle_state_cooker = cooker_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+)
+fill_raw_materials_state_cooker = (
+ cooker_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+)
+
+cooking_state = cooker_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+)
+
+discharge_goods_state_cooker = (
+ cooker_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+)
+
+
+# Petri net transitions
+
+activate_not_cooking = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_cooker,
+ end_process_state=idle_state_cooker,
+)
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+)
+
+activate_filling_cooker = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_cooker,
+ end_process_state=fill_raw_materials_state_cooker,
+)
+
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_cooker
+)
+
+activate_cooking = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state_cooker,
+ end_process_state=cooking_state,
+ delay=datetime.timedelta(minutes=24),
+)
+
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking
+)
+
+
+activate_discharging_cooker = cooker_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state,
+ end_process_state=discharge_goods_state_cooker,
+)
+cooker_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_cooker
+)
+
+
+electricity_load = LoadType(name="Electricity")
+blending_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=600,
+ load_type=electricity_load,
+ stream=raw_materials_to_blender_stream,
+)
+cooking_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=830.76,
+ load_type=electricity_load,
+ stream=blender_to_cooker_stream,
+)
+
+
+# Mass balances
+blender_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_blender_stream,
+ main_output_stream=blender_to_cooker_stream,
+)
+
+# Add internal storages (required)
+blender_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+
+# Mass balances
+cooker_step.create_main_mass_balance(
+ commodity=output_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=blender_to_cooker_stream,
+ main_output_stream=cooker_to_sink_stream,
+)
+
+# Add internal storages (required)
+cooker_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+)
+
+
+# Start the simulation
+enterprise.start_simulation(number_of_iterations_in_chain=200)
+
+# Create report of the simulation results
+enterprise.create_post_simulation_report(
+ start_date=start_date,
+ end_date=end_date,
+ x_axis_time_delta=datetime.timedelta(hours=1),
+ resample_frequency="1min",
+ gantt_chart_end_date=end_date,
+ gantt_chart_start_date=start_date,
+)
diff --git a/examples/tutorial/_4_connect_four_process_steps/blending_process_chain.py b/examples/tutorial/_4_connect_four_process_steps/blending_process_chain.py
new file mode 100644
index 0000000..f7801db
--- /dev/null
+++ b/examples/tutorial/_4_connect_four_process_steps/blending_process_chain.py
@@ -0,0 +1,154 @@
+import datetime
+import logging
+
+from ethos_penalps.data_classes import Commodity, LoadType
+from ethos_penalps.enterprise import Enterprise
+from ethos_penalps.order_generator import NOrderGenerator
+from ethos_penalps.stream import BatchStreamStaticData, ContinuousStreamStaticData
+from ethos_penalps.time_data import TimeData
+from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger
+from ethos_penalps.process_chain import ProcessChain
+from ethos_penalps.process_nodes.sink import Sink
+from ethos_penalps.process_nodes.source import Source
+from ethos_penalps.process_nodes.process_chain_storage import ProcessChainStorage
+
+
+def fill_blending_process_chain(
+ process_chain: ProcessChain,
+ raw_commodity: Commodity,
+ cooked_commodity: Commodity,
+ uncooked_storage: ProcessChainStorage,
+ raw_goods_source: Source,
+ process_step_name: str,
+):
+
+ # Create all sources, sinks and network level storages
+
+ # Create Process nodes
+ blender_step = process_chain.create_process_step(name=process_step_name)
+
+ # Streams
+ ## Process Chain 1
+ raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=raw_goods_source.name,
+ end_process_step_name=blender_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=raw_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+ cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=blender_step.name,
+ end_process_step_name=uncooked_storage.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=cooked_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+
+ # Add streams to sinks and sources
+ raw_goods_source.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+ )
+ uncooked_storage.add_input_stream(
+ input_stream=cooking_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+ )
+
+ """ Create petri net for process step
+ Each process state must have at least the following:
+ - Either
+ - one combined production state
+ your_combined_state=process_step.process_state_handler.create_continuous_production_process_state_with_storage(
+ process_state_name="your process state name"
+ )
+ or
+ - an input stream requesting state
+ your_input_stream_requesting_state=process_step.process_state_handler.create_continuous_input_stream_requesting_state(process_state_name="your input stream providing state")
+ - and input stream requesting state
+ your_output_stream_providing_state=process_step.create_continuous_output_stream_providing_state(process_state_name="your output stream providing state")
+ also an idle state is required
+ your_idle_state=process_step..process_state_handler.create_idle_process_state(
+ process_state_name="Your idle state"
+ )
+ """
+ # Process Step 1
+
+ idle_state_mixer = blender_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+ )
+ fill_raw_materials_state = (
+ blender_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+ )
+
+ mixing_state = blender_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Mix"
+ )
+
+ discharge_goods_state_mixer = (
+ blender_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+ )
+
+ # Petri net transitions
+
+ activate_not_mixing = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state_mixer,
+ end_process_state=idle_state_mixer,
+ )
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_mixing
+ )
+
+ activate_filling_mixer = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state_mixer,
+ end_process_state=fill_raw_materials_state,
+ )
+
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling_mixer
+ )
+
+ activate_mixing = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=mixing_state,
+ delay=datetime.timedelta(minutes=5),
+ )
+
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_mixing
+ )
+
+ activate_discharging_mixer = blender_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=mixing_state,
+ end_process_state=discharge_goods_state_mixer,
+ )
+ blender_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging_mixer
+ )
+
+ electricity_load = LoadType(name="Electricity")
+ mixing_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=600,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+ )
+
+ # Mass balances
+ blender_step.create_main_mass_balance(
+ commodity=cooked_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+ )
+
+ # Add internal storages (required)
+ blender_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+ )
diff --git a/examples/tutorial/_4_connect_four_process_steps/cooking_process_chain.py b/examples/tutorial/_4_connect_four_process_steps/cooking_process_chain.py
new file mode 100644
index 0000000..de8a6f5
--- /dev/null
+++ b/examples/tutorial/_4_connect_four_process_steps/cooking_process_chain.py
@@ -0,0 +1,152 @@
+import datetime
+import logging
+
+from ethos_penalps.data_classes import Commodity, LoadType
+from ethos_penalps.enterprise import Enterprise
+from ethos_penalps.order_generator import NOrderGenerator
+from ethos_penalps.stream import BatchStreamStaticData, ContinuousStreamStaticData
+from ethos_penalps.time_data import TimeData
+from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger
+from ethos_penalps.process_chain import ProcessChain
+from ethos_penalps.process_nodes.sink import Sink
+from ethos_penalps.process_nodes.source import Source
+from ethos_penalps.process_nodes.process_chain_storage import ProcessChainStorage
+
+
+def fill_cooking_process_chain(
+ process_chain: ProcessChain,
+ uncooked_commodity: Commodity,
+ cooked_commodity: Commodity,
+ cooked_goods_sink: Sink,
+ uncooked_goods_storage: ProcessChainStorage,
+ process_step_name: str,
+):
+
+ # Create Process nodes
+ process_step = process_chain.create_process_step(name=process_step_name)
+
+ # Streams
+ ## Process Chain 1
+ raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=uncooked_goods_storage.name,
+ end_process_step_name=process_step.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=uncooked_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+ cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream(
+ batch_stream_static_data=BatchStreamStaticData(
+ start_process_step_name=process_step.name,
+ end_process_step_name=cooked_goods_sink.name,
+ delay=datetime.timedelta(minutes=1),
+ commodity=cooked_commodity,
+ maximum_batch_mass_value=0.00065,
+ )
+ )
+
+ # Add streams to sinks and sources
+ uncooked_goods_storage.add_output_stream(
+ output_stream=raw_materials_to_cooking_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+ )
+ cooked_goods_sink.add_input_stream(
+ input_stream=cooking_to_sink_stream,
+ process_chain_identifier=process_chain.process_chain_identifier,
+ )
+
+ """ Create petri net for process step
+ Each process state must have at least the following:
+ - Either
+ - one combined production state
+ your_combined_state=process_step.process_state_handler.create_continuous_production_process_state_with_storage(
+ process_state_name="your process state name"
+ )
+ or
+ - an input stream requesting state
+ your_input_stream_requesting_state=process_step.process_state_handler.create_continuous_input_stream_requesting_state(process_state_name="your input stream providing state")
+ - and input stream requesting state
+ your_output_stream_providing_state=process_step.create_continuous_output_stream_providing_state(process_state_name="your output stream providing state")
+ also an idle state is required
+ your_idle_state=process_step..process_state_handler.create_idle_process_state(
+ process_state_name="Your idle state"
+ )
+ """
+ # Process Step 1
+
+ idle_state = process_step.process_state_handler.create_idle_process_state(
+ process_state_name="Idle"
+ )
+ fill_raw_materials_state = (
+ process_step.process_state_handler.create_batch_input_stream_requesting_state(
+ process_state_name="Fill raw materials"
+ )
+ )
+
+ cooking_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass(
+ process_state_name="Cooking"
+ )
+
+ discharge_goods_state = (
+ process_step.process_state_handler.create_batch_output_stream_providing_state(
+ process_state_name="Discharge"
+ )
+ )
+
+ # Petri net transitions
+
+ activate_not_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_next_discrete_event(
+ start_process_state=discharge_goods_state,
+ end_process_state=idle_state,
+ )
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_not_cooking
+ )
+
+ activate_filling = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_input_stream(
+ start_process_state=idle_state,
+ end_process_state=fill_raw_materials_state,
+ )
+
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_filling
+ )
+
+ activate_cooking = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_delay(
+ start_process_state=fill_raw_materials_state,
+ end_process_state=cooking_state,
+ delay=datetime.timedelta(minutes=24),
+ )
+
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_cooking
+ )
+
+ activate_discharging = process_step.process_state_handler.process_state_switch_selector_handler.process_state_switch_handler.create_process_state_switch_at_output_stream(
+ start_process_state=cooking_state,
+ end_process_state=discharge_goods_state,
+ )
+ process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector(
+ process_state_switch=activate_discharging
+ )
+
+ electricity_load = LoadType(name="Electricity")
+ cooking_state.create_process_state_energy_data_based_on_stream_mass(
+ specific_energy_demand=830.76,
+ load_type=electricity_load,
+ stream=raw_materials_to_cooking_stream,
+ )
+
+ # Mass balances
+ process_step.create_main_mass_balance(
+ commodity=cooked_commodity,
+ input_to_output_conversion_factor=1,
+ main_input_stream=raw_materials_to_cooking_stream,
+ main_output_stream=cooking_to_sink_stream,
+ )
+
+ # Add internal storages (required)
+ process_step.process_state_handler.process_step_data.main_mass_balance.create_storage(
+ current_storage_level=0
+ )
diff --git a/examples/tutorial/_4_connect_four_process_steps/simulation_starter.py b/examples/tutorial/_4_connect_four_process_steps/simulation_starter.py
new file mode 100644
index 0000000..e35dddb
--- /dev/null
+++ b/examples/tutorial/_4_connect_four_process_steps/simulation_starter.py
@@ -0,0 +1,129 @@
+import datetime
+import logging
+
+from ethos_penalps.data_classes import Commodity, LoadType
+from ethos_penalps.enterprise import Enterprise
+from ethos_penalps.order_generator import NOrderGenerator
+from ethos_penalps.stream import BatchStreamStaticData, ContinuousStreamStaticData
+from ethos_penalps.time_data import TimeData
+from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger
+from examples.tutorial._4_connect_four_process_steps.blending_process_chain import (
+ fill_blending_process_chain,
+)
+from examples.tutorial._4_connect_four_process_steps.cooking_process_chain import (
+ fill_cooking_process_chain,
+)
+
+logger = PeNALPSLogger.get_human_readable_logger(logging.INFO)
+
+# Determine all relevant commodities
+raw_commodity = Commodity(name="Raw Goods")
+uncooked_commodity = Commodity(name="Uncooked Goods")
+cooked_commodity = Commodity(name="Cooked Goods")
+# Enterprise structure
+
+# Set simulation time data
+start_date = datetime.datetime(2022, 1, 2, hour=22, minute=30)
+end_date = datetime.datetime(2022, 1, 3)
+time_data = TimeData(
+ global_start_date=start_date,
+ global_end_date=end_date,
+)
+
+# Create all order for the simulation
+order_generator = NOrderGenerator(
+ commodity=cooked_commodity,
+ mass_per_order=0.00065,
+ production_deadline=end_date,
+ number_of_orders=4,
+)
+
+order_collection = order_generator.create_n_order_collection()
+
+# Initialize enterprise
+enterprise = Enterprise(time_data=time_data, name="Cooking Example")
+
+cooking_network_level = enterprise.create_network_level()
+blending_network_level = enterprise.create_network_level()
+
+
+blending_chain_1 = blending_network_level.create_process_chain("Blending Chain 1")
+blending_chain_2 = blending_network_level.create_process_chain("Blending Chain 2")
+
+cooking_chain_1 = cooking_network_level.create_process_chain("Cooking Chain 1")
+cooking_chain_2 = cooking_network_level.create_process_chain("Cooking Chain 2")
+
+cooked_goods_sink = cooking_network_level.create_main_sink(
+ name="Cooked Goods Sink",
+ commodity=cooked_commodity,
+ order_collection=order_collection,
+)
+
+uncooked_goods_storage = cooking_network_level.create_process_chain_storage_as_source(
+ name="Uncooked Goods", commodity=uncooked_commodity
+)
+raw_goods_source = blending_network_level.create_main_source(
+ "Raw Goods", commodity=raw_commodity
+)
+blending_network_level.add_process_chain_storage_as_sink(
+ process_chain_storage=uncooked_goods_storage
+)
+
+
+blending_chain_1.add_sink(sink=uncooked_goods_storage)
+blending_chain_2.add_sink(sink=uncooked_goods_storage)
+blending_chain_1.add_source(source=raw_goods_source)
+blending_chain_2.add_source(source=raw_goods_source)
+cooking_chain_1.add_sink(sink=cooked_goods_sink)
+cooking_chain_1.add_source(source=uncooked_goods_storage)
+cooking_chain_2.add_sink(sink=cooked_goods_sink)
+cooking_chain_2.add_source(source=uncooked_goods_storage)
+
+
+fill_cooking_process_chain(
+ process_chain=cooking_chain_1,
+ uncooked_commodity=uncooked_commodity,
+ cooked_commodity=cooked_commodity,
+ cooked_goods_sink=cooked_goods_sink,
+ uncooked_goods_storage=uncooked_goods_storage,
+ process_step_name="Cooker 1",
+)
+fill_cooking_process_chain(
+ process_chain=cooking_chain_2,
+ uncooked_commodity=uncooked_commodity,
+ cooked_commodity=cooked_commodity,
+ cooked_goods_sink=cooked_goods_sink,
+ uncooked_goods_storage=uncooked_goods_storage,
+ process_step_name="Cooker 2",
+)
+
+fill_blending_process_chain(
+ process_chain=blending_chain_1,
+ raw_commodity=raw_commodity,
+ cooked_commodity=cooked_commodity,
+ raw_goods_source=raw_goods_source,
+ uncooked_storage=uncooked_goods_storage,
+ process_step_name="Blender 1",
+)
+fill_blending_process_chain(
+ process_chain=blending_chain_2,
+ raw_commodity=raw_commodity,
+ cooked_commodity=cooked_commodity,
+ raw_goods_source=raw_goods_source,
+ uncooked_storage=uncooked_goods_storage,
+ process_step_name="Blender 2",
+)
+
+
+# Start the simulation
+enterprise.start_simulation(number_of_iterations_in_chain=200)
+
+# Create report of the simulation results
+enterprise.create_post_simulation_report(
+ start_date=start_date,
+ end_date=end_date,
+ x_axis_time_delta=datetime.timedelta(hours=1),
+ resample_frequency="5min",
+ gantt_chart_end_date=end_date,
+ gantt_chart_start_date=start_date,
+)
diff --git a/paper/paper.md b/paper/paper.md
index 02b0a3f..955037e 100644
--- a/paper/paper.md
+++ b/paper/paper.md
@@ -115,5 +115,8 @@ To overcome the lack of industrial load profiles, simulation tools and methods h
The software eLOAD employs an approach similar to that from Sandhaas. Instead of applying it to individual industries, @Bomann.2015 applies it at a national level. They also assume demand response flexibility for some appliances. The source code and appliance load profiles used have also not been published.
+# Authors Contribution
+
+**Julian Belina**:Software, Writing, Visualization, Methodology.**Noah Pflugradt**:Conceptualization, Methodology, Supervision, Writing - Review & Editing.**Detlef Stolten**:Conceptualization, Phd Supervision, Resources, Funding acquisition.
# References
\ No newline at end of file
diff --git a/pyproject.toml b/pyproject.toml
index 71accd9..d2a1bba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -2,6 +2,10 @@
requires = ["setuptools>=64.0.0", "wheel"]
build-backend = "setuptools.build_meta"
+[tool.setuptools.package-data]
+"ethos_penalps" = ["py.typed"]
+[tool.setuptools.packages.find]
+where = ["src"]
[project]
name = "ethos_penalps"
diff --git a/src/ethos_penalps/automatic_sizer/__init__.py b/src/ethos_penalps/automatic_sizer/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/ethos_penalps/data_classes.py b/src/ethos_penalps/data_classes.py
index e157ad6..3af10e8 100644
--- a/src/ethos_penalps/data_classes.py
+++ b/src/ethos_penalps/data_classes.py
@@ -70,8 +70,8 @@ class ProductEnergyData(DataClassJsonMixin):
product_commodity: Commodity
specific_energy_demand: float
load_type: LoadType
- mass_unit: str = Units.energy_unit.__str__()
- energy_unit: str = Units.energy_unit.__str__()
+ mass_unit: str = str(Units.energy_unit)
+ energy_unit: str = str(Units.energy_unit)
@dataclass(frozen=True, eq=True, unsafe_hash=True)
@@ -79,8 +79,8 @@ class StreamLoadEnergyData(DataClassJsonMixin):
stream_name: str
specific_energy_demand: float
load_type: LoadType
- mass_unit: str = Units.mass_unit.__str__()
- energy_unit: str = Units.energy_unit.__str__()
+ mass_unit: str = str(Units.mass_unit)
+ energy_unit: str = str(Units.energy_unit)
@dataclass(slots=True, frozen=True)
@@ -149,26 +149,22 @@ class CurrentProcessNode:
node_name: str = "Node not set yet"
-def get_new_uuid() -> str:
- return str(uuid.uuid4())
-
-
@dataclass(frozen=True, eq=True, slots=True)
class OutputBranchIdentifier:
branch_number: float
- global_unique_identifier: Optional[uuid.UUID] = field(default_factory=get_new_uuid)
+ global_unique_identifier: Optional[str] = field(default_factory=get_new_uuid)
@dataclass(frozen=True, eq=True, slots=True)
class TemporalBranchIdentifier:
branch_number: float
- global_unique_identifier: Optional[uuid.UUID] = field(default_factory=get_new_uuid)
+ global_unique_identifier: Optional[str] = field(default_factory=get_new_uuid)
@dataclass(frozen=True, eq=True, slots=True)
class StreamBranchIdentifier:
stream_name: str
- global_unique_identifier: Optional[uuid.UUID] = field(default_factory=get_new_uuid)
+ global_unique_identifier: Optional[str] = field(default_factory=get_new_uuid)
@dataclass(frozen=True, eq=True, slots=True)
@@ -191,7 +187,7 @@ def get_duration(self) -> datetime.timedelta:
class ProcessChainIdentifier:
chain_number: int
chain_name: str
- unique_identifier: uuid.UUID = get_new_uuid()
+ unique_identifier: str = get_new_uuid()
@dataclass
@@ -200,7 +196,7 @@ class ProductionOrder:
production_deadline: datetime.datetime
order_number: float
commodity: Commodity
- global_unique_identifier: Optional[uuid.UUID] = field(default_factory=get_new_uuid)
+ global_unique_identifier: str = field(default_factory=get_new_uuid)
produced_mass: float = 0
@@ -233,7 +229,7 @@ def sort_orders_by_deadline(self, ascending: bool = True):
)
self.order_data_frame.reset_index(inplace=True, drop=True)
- def append_order_collection(self, order_collection):
+ def append_order_collection(self, order_collection: "OrderCollection"):
if self.commodity != order_collection.commodity:
warnings.warn("Tried to append order collection with different commodity.")
self.order_data_frame = pandas.concat(
@@ -249,8 +245,8 @@ class ProcessStateEnergyLoadData(DataClassJsonMixin):
process_step_name: str
specific_energy_demand: float
load_type: LoadType
- mass_unit: str = Units.mass_unit.__str__()
- energy_unit: str = Units.energy_unit.__str__()
+ mass_unit: str = str(Units.mass_unit)
+ energy_unit: str = str(Units.energy_unit)
@dataclass(kw_only=True)
@@ -281,12 +277,12 @@ class ProcessStateEnergyData:
def add_process_state_energy_load_data(
self, process_state_energy_load_data: ProcessStateEnergyLoadData
):
- self.dict_of_loads[
- process_state_energy_load_data.load_type.uuid
- ] = process_state_energy_load_data.load_type
- self.dict_of_load_energy_data[
- process_state_energy_load_data.load_type.uuid
- ] = process_state_energy_load_data
+ self.dict_of_loads[process_state_energy_load_data.load_type.uuid] = (
+ process_state_energy_load_data.load_type
+ )
+ self.dict_of_load_energy_data[process_state_energy_load_data.load_type.uuid] = (
+ process_state_energy_load_data
+ )
def get_dict_of_loads(self) -> dict[str, LoadType]:
return self.dict_of_loads
diff --git a/src/ethos_penalps/organizational_agents/__init__.py b/src/ethos_penalps/organizational_agents/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/ethos_penalps/petri_net/__init__.py b/src/ethos_penalps/petri_net/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/ethos_penalps/post_processing/production_plan_post_processor.py b/src/ethos_penalps/post_processing/production_plan_post_processor.py
index d791e11..0ece66f 100644
--- a/src/ethos_penalps/post_processing/production_plan_post_processor.py
+++ b/src/ethos_penalps/post_processing/production_plan_post_processor.py
@@ -175,9 +175,9 @@ def __init__(
input_stream_post_processor: StreamPostProcessor,
output_stream_post_processor: StreamPostProcessor,
) -> None:
- self.list_of_process_step_entries: list[
- ProcessStepProductionPlanEntry
- ] = list_of_process_step_entries[::-1]
+ self.list_of_process_step_entries: list[ProcessStepProductionPlanEntry] = (
+ list_of_process_step_entries[::-1]
+ )
self.process_step: ProcessStep = process_step
self.input_stream_post_processor: StreamPostProcessor = (
input_stream_post_processor
@@ -198,7 +198,7 @@ def fill_from_date_to_start(
):
idle_state = self.process_step.process_state_handler.get_idle_state()
new_first_process_step_entry = (
- idle_state.create_process_step_production_plan_entry(
+ idle_state._create_process_step_production_plan_entry(
process_state_state=ProcessStateData(
process_state_name=idle_state.process_state_name,
start_time=start_date,
@@ -222,7 +222,7 @@ def fill_to_end_date(
):
idle_state = self.process_step.process_state_handler.get_idle_state()
new_last_process_step_entry = (
- idle_state.create_process_step_production_plan_entry(
+ idle_state._create_process_step_production_plan_entry(
process_state_state=ProcessStateData(
process_state_name=idle_state.process_state_name,
start_time=last_entry.end_time,
@@ -314,9 +314,9 @@ def __init__(
def create_all_process_step_processors(self) -> dict[str, ProcessStepPostProcessor]:
dict_of_process_step_processors = {}
for process_step_name in self.production_plan.process_step_states_dict:
- dict_of_process_step_processors[
- process_step_name
- ] = self.create_process_step_processor(process_step_name=process_step_name)
+ dict_of_process_step_processors[process_step_name] = (
+ self.create_process_step_processor(process_step_name=process_step_name)
+ )
return dict_of_process_step_processors
diff --git a/src/ethos_penalps/process_chain.py b/src/ethos_penalps/process_chain.py
index 861b1ca..c144cb3 100644
--- a/src/ethos_penalps/process_chain.py
+++ b/src/ethos_penalps/process_chain.py
@@ -119,9 +119,9 @@ def initialize_production_plan(self):
if process_node_name in self.production_plan.process_step_states_dict:
pass
else:
- self.production_plan.process_step_states_dict[
- process_node_name
- ] = []
+ self.production_plan.process_step_states_dict[process_node_name] = (
+ []
+ )
for stream_name in self.stream_handler.stream_dict:
if stream_name in self.production_plan.stream_state_dict:
pass
@@ -247,7 +247,13 @@ def create_process_chain_production_plan(
self.debugging_information_logger.add_node_operation(
node_operation=current_node_operation
)
- current_node_operation: UpstreamNewProductionOrder | DownstreamValidationOrder | DownstreamAdaptionOrder | UpstreamAdaptionOrder | TerminateProduction = current_node.process_input_order(
+ current_node_operation: (
+ UpstreamNewProductionOrder
+ | DownstreamValidationOrder
+ | DownstreamAdaptionOrder
+ | UpstreamAdaptionOrder
+ | TerminateProduction
+ ) = current_node.process_input_order(
input_node_operation=current_node_operation,
)
diff --git a/src/ethos_penalps/process_state.py b/src/ethos_penalps/process_state.py
index 4ed4e22..632251b 100644
--- a/src/ethos_penalps/process_state.py
+++ b/src/ethos_penalps/process_state.py
@@ -67,7 +67,7 @@ def __init__(
process_state_name=self.process_state_name,
)
- def create_process_step_production_plan_entry(
+ def _create_process_step_production_plan_entry(
self, process_state_state: ProcessStateData
) -> ProcessStepProductionPlanEntry:
entry = ProcessStepProductionPlanEntry(
@@ -300,8 +300,10 @@ def fulfill_order(self) -> ContinuousStreamState | BatchStreamState:
+ self.process_step_name
)
if isinstance(input_stream, BatchStream):
- input_stream: BatchStream = self.process_step_data.stream_handler.get_stream(
- stream_name=self.process_step_data.main_mass_balance.main_input_stream_name
+ input_stream: BatchStream = (
+ self.process_step_data.stream_handler.get_stream(
+ stream_name=self.process_step_data.main_mass_balance.main_input_stream_name
+ )
)
next_stream_end_time = (
self.process_step_data.time_data.get_next_stream_end_time()
@@ -344,8 +346,10 @@ def determine_required_input_stream_state(
self.process_step_data.time_data.set_next_stream_end_time(
next_stream_end_time=next_stream_end_time
)
- input_stream: BatchStream = self.process_step_data.stream_handler.get_stream(
- stream_name=self.process_step_data.main_mass_balance.main_input_stream_name
+ input_stream: BatchStream = (
+ self.process_step_data.stream_handler.get_stream(
+ stream_name=self.process_step_data.main_mass_balance.main_input_stream_name
+ )
)
next_stream_end_time = (
self.process_step_data.time_data.get_next_stream_end_time()
@@ -407,11 +411,12 @@ def __str__(self) -> str:
class IntermediateStateBasedOnEnergy(IntermediateState):
- def create_process_step_production_plan_entry(
+ def _create_process_step_production_plan_entry(
self,
process_state_state: ProcessStateData,
- input_stream_state: BatchStreamProductionPlanEntry
- | ContinuousStreamProductionPlanEntry,
+ input_stream_state: (
+ BatchStreamProductionPlanEntry | ContinuousStreamProductionPlanEntry
+ ),
) -> ProcessStepProductionPlanEntry:
if isinstance(input_stream_state, BatchStreamProductionPlanEntry):
total_stream_mass = input_stream_state.batch_mass_value
@@ -703,7 +708,7 @@ def create_storage_entries(self):
class BatchInputStreamRequestingStateWithStorageEnergyBasedOnStream(
BatchInputStreamRequestingStateWithStorage
):
- def create_process_step_production_plan_entry(
+ def _create_process_step_production_plan_entry(
self,
process_state_state: ProcessStateData,
input_stream_state: ContinuousStreamState | BatchStreamState,
@@ -757,9 +762,9 @@ def add_process_state_switch(self, process_state_switch: ProcessStateSwitch):
+ " is already in process state switch dictionary of :"
+ str(self.process_step_data.process_step_name)
)
- self.process_state_switch_dictionary[
- process_state_switch.state_connector
- ] = process_state_switch
+ self.process_state_switch_dictionary[process_state_switch.state_connector] = (
+ process_state_switch
+ )
def create_process_state_switch_at_next_discrete_event(
self, start_process_state: ProcessState, end_process_state: ProcessState
diff --git a/src/ethos_penalps/process_state_handler.py b/src/ethos_penalps/process_state_handler.py
index c998417..d8758c4 100644
--- a/src/ethos_penalps/process_state_handler.py
+++ b/src/ethos_penalps/process_state_handler.py
@@ -383,7 +383,11 @@ def add_process_state(
):
logger.debug("Process state: %s has been added:", process_state)
if process_state.process_state_name in self.process_state_dictionary:
- raise Exception("Process state is already in process state dictionary")
+ raise Exception(
+ "Process state: "
+ + process_state.process_state_name
+ + " is already in process state dictionary"
+ )
self.process_state_dictionary[process_state.process_state_name] = process_state
if isinstance(process_state, OutputStreamProvidingState):
@@ -416,7 +420,7 @@ def store_current_state_to_process_state_list(
)
production_plan_entry = (
- process_state.create_process_step_production_plan_entry()
+ process_state._create_process_step_production_plan_entry()
)
# self.list_of_production_plan_entries.append(production_plan_entry)
diff --git a/src/ethos_penalps/process_state_network_navigator.py b/src/ethos_penalps/process_state_network_navigator.py
index 5f3beea..864d5af 100644
--- a/src/ethos_penalps/process_state_network_navigator.py
+++ b/src/ethos_penalps/process_state_network_navigator.py
@@ -73,7 +73,11 @@ def __init__(
self.production_plan: ProductionPlan = production_plan
self.time_data_at_start: TimeData
- self.simulation_state_data_at_start: PreProductionStateData | PostProductionStateData | ValidatedPostProductionStateData
+ self.simulation_state_data_at_start: (
+ PreProductionStateData
+ | PostProductionStateData
+ | ValidatedPostProductionStateData
+ )
self.branch_data_at_start: OutputBranchData
def determine_if_output_stream_requires_adaption(
@@ -600,7 +604,7 @@ def create_process_state_entries(self):
temporary_production_plan.stream_state_dict[input_stream_name][-1]
)
process_state_entry = (
- process_state.create_process_step_production_plan_entry(
+ process_state._create_process_step_production_plan_entry(
process_state_state=process_state_state,
input_stream_state=input_stream_production_plan_entry,
)
@@ -608,7 +612,7 @@ def create_process_state_entries(self):
else:
process_state_entry = (
- process_state.create_process_step_production_plan_entry(
+ process_state._create_process_step_production_plan_entry(
process_state_state=process_state_state
)
)
@@ -618,9 +622,9 @@ def create_process_state_entries(self):
process_step_name
].extend(process_state_entry_list)
else:
- temporary_production_plan.process_step_states_dict[
- process_step_name
- ] = process_state_entry_list
+ temporary_production_plan.process_step_states_dict[process_step_name] = (
+ process_state_entry_list
+ )
self.process_state_handler.process_step_data.state_data_container.update_temporary_production_plan(
updated_temporary_production_plan=temporary_production_plan
diff --git a/src/ethos_penalps/py.typed b/src/ethos_penalps/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/src/ethos_penalps/utilities/logger_ethos_penalps.py b/src/ethos_penalps/utilities/logger_ethos_penalps.py
index 882e58a..ac01f71 100644
--- a/src/ethos_penalps/utilities/logger_ethos_penalps.py
+++ b/src/ethos_penalps/utilities/logger_ethos_penalps.py
@@ -28,9 +28,9 @@ def filter(self, record):
class PeNALPSLogger:
- logger_name = "ethos_elpsi"
+ logger_name = "ethos_penalps"
table_delimiter = "DELIMITER"
- preprend_loop_counter = True
+ prepend_loop_counter = True
has_been_called: bool = False
logger = logging.getLogger(logger_name)
@@ -54,7 +54,7 @@ def get_logger_without_handler() -> logging.Logger:
table_logger: logging.Logger = PeNALPSLogger.logger
return table_logger
- def get_human_readable_logger(logging_level=logging.INFO) -> logging.Logger:
+ def get_human_readable_logger(logging_level: int = logging.INFO) -> logging.Logger:
# create logger
logger: logging.Logger = PeNALPSLogger.logger
@@ -133,7 +133,7 @@ def read_log_to_data_frame(path_to_log_file: str | None = None) -> pd.DataFrame:
delimiter=PeNALPSLogger.table_delimiter,
engine="python",
)
- if PeNALPSLogger.preprend_loop_counter:
+ if PeNALPSLogger.prepend_loop_counter:
data_frame.columns = [
"Current node name",
"Main Loop Iteration",
@@ -142,7 +142,7 @@ def read_log_to_data_frame(path_to_log_file: str | None = None) -> pd.DataFrame:
"Module line",
"Log Message",
]
- elif PeNALPSLogger.preprend_loop_counter is False:
+ elif PeNALPSLogger.prepend_loop_counter is False:
data_frame.columns = [
"Module",
"Method",
diff --git a/src/ethos_penalps/utilities/units.py b/src/ethos_penalps/utilities/units.py
index 05dcd68..65dc037 100644
--- a/src/ethos_penalps/utilities/units.py
+++ b/src/ethos_penalps/utilities/units.py
@@ -5,10 +5,10 @@
class Units:
unit_registry = pint.UnitRegistry(system="SI")
unit_registry.default_format = "~"
- time_unit: pint.Unit = unit_registry.seconds
+ time_unit: pint.Unit = unit_registry.hour
mass_unit: pint.Unit = unit_registry.metric_ton
- power_unit: pint.Unit = unit_registry.watt
- energy_unit: pint.Unit = unit_registry.joule
+ power_unit: pint.Unit = unit_registry.MW
+ energy_unit: pint.Unit = unit_registry.MJ
def get_unit(unit_string: str) -> pint.Unit:
return Units.unit_registry.Unit(unit_string)
@@ -22,3 +22,12 @@ def compress_quantity(
def get_value_from_quantity(quantity: pint.Quantity) -> numbers.Number:
return quantity.m
+
+
+if __name__ == "__main__":
+ unit_registry = pint.UnitRegistry(system="SI")
+
+ meter_quant = (0.15 * Units.unit_registry.Unit("kWh")) / (
+ 650 * Units.unit_registry.Unit("gram")
+ )
+ print(meter_quant.to("MJ/metric_ton"))