From ebb3b58dfc30524c3045f67bf079dddbd2acdfce Mon Sep 17 00:00:00 2001 From: "j.belina" Date: Fri, 9 Feb 2024 11:16:46 +0100 Subject: [PATCH 1/8] Adjust gitlab ci --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6fc4774..6561cf9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,14 +1,14 @@ variables: BRANCH_NAME: "prepare-deployment" -stages: # List of stages for jobs, and their order of execution +stages: # List of stages for jobs, and their order of execution - test # .deployment_rules: # Trigger job on push to master from other branch or Web UI # rules: # - if: $CI_COMMIT_BRANCH == $BRANCH_FOR_DEPLOYMENT && $CI_PIPELINE_SOURCE == "push" # - if: $CI_PIPELINE_SOURCE == "web" - + unit-test-job: # rules: # - if: $CI_PIPELINE_SOURCE == "merge_request_event" @@ -17,10 +17,10 @@ unit-test-job: image: mambaorg/micromamba:latest before_script: # - micromamba shell init --shell=bash --prefix=~/micromamba - - micromamba create -c conda-forge -f environment.yml + - micromamba create -f environment.yml - eval "$(micromamba shell hook --shell=bash)" - micromamba activate ethos_penalps - - pip install . + - pip install . --no-deps script: - echo "Running unit tests..." - pytest From 5e39a78f6c63e303a213a8b3f0c7bb19c0ef2489 Mon Sep 17 00:00:00 2001 From: Julian Belina Date: Wed, 21 Feb 2024 19:06:05 +0100 Subject: [PATCH 2/8] Adjust Github CI and remove pdftopng --- .github/workflows/test.yml | 6 +- asd.py | 13 + environment.yml | 2 +- ethos | 369 -------------- ittsc_lpg | 473 ------------------ pyproject.toml | 2 +- .../enterprise_graph_builder.py | 38 +- .../process_chain_graph_builder.py | 46 +- 8 files changed, 63 insertions(+), 886 deletions(-) create mode 100644 asd.py delete mode 100644 ethos delete mode 100644 ittsc_lpg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 813e59b..af25bc5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,8 +2,10 @@ name: Run ETHOS.PeNALPS Tests on: push: branches: main + branches: dev pull_request: branches: main + branches: dev # Allows to trigger the workflow manually workflow_dispatch: branches: main @@ -16,7 +18,7 @@ on: # - cron: 0 0 * * 0 jobs: - TestGeokitCondaForge: + TestPeNALPSCondaForge: name: Ex1 (${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: @@ -47,7 +49,7 @@ jobs: conda run -n test_env pytest echo "Pytest done" - TestGeokitDevLocal: + TestPeNALPSDevLocal: name: Ex1 (${{ matrix.python-version }}, ${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: diff --git a/asd.py b/asd.py new file mode 100644 index 0000000..4ce8e5b --- /dev/null +++ b/asd.py @@ -0,0 +1,13 @@ +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/environment.yml b/environment.yml index 95b3979..004092f 100644 --- a/environment.yml +++ b/environment.yml @@ -29,5 +29,5 @@ dependencies: - conda-forge::tectonic - conda-forge::typeguard - conda-forge::dataclasses-json - - conda-forge::pdftopng - conda-forge::jsonpickle + - conda-forge::pdf2image diff --git a/ethos b/ethos deleted file mode 100644 index 5a9835d..0000000 --- a/ethos +++ /dev/null @@ -1,369 +0,0 @@ -accessible-pygments 0.0.4 pyhd8ed1ab_0 conda-forge -alabaster 0.7.16 pyhd8ed1ab_0 conda-forge -altair 4.2.2 pyhd8ed1ab_0 conda-forge -asgiref 3.7.2 pyhd8ed1ab_0 conda-forge -astroid 2.15.8 py310h5588dad_0 conda-forge -asttokens 2.4.1 pyhd8ed1ab_0 conda-forge -attrs 23.2.0 pyh71513ae_0 conda-forge -aws-c-auth 0.7.14 ha04060b_3 conda-forge -aws-c-cal 0.6.9 hd33547d_3 conda-forge -aws-c-common 0.9.12 hcfcfb64_0 conda-forge -aws-c-compression 0.2.17 hd33547d_8 conda-forge -aws-c-event-stream 0.4.1 hf127292_5 conda-forge -aws-c-http 0.8.0 h0cc4be6_5 conda-forge -aws-c-io 0.14.3 hf372335_1 conda-forge -aws-c-mqtt 0.10.1 h189e261_3 conda-forge -aws-c-s3 0.5.0 h623a383_1 conda-forge -aws-c-sdkutils 0.1.14 hd33547d_0 conda-forge -aws-checksums 0.1.17 hd33547d_7 conda-forge -aws-crt-cpp 0.26.1 hb490d84_8 conda-forge -aws-sdk-cpp 1.11.210 hf72cfbd_11 conda-forge -babel 2.14.0 pyhd8ed1ab_0 conda-forge -backoff 1.11.1 pyhd8ed1ab_0 conda-forge -backports.zoneinfo 0.2.1 py310h5588dad_8 conda-forge -beautifulsoup4 4.12.3 pyha770c72_0 conda-forge -black 24.1.1 py310h5588dad_0 conda-forge -bleach 6.1.0 pyhd8ed1ab_0 conda-forge -boltons 21.0.0 pyhd8ed1ab_0 conda-forge -brotli-python 1.1.0 py310h00ffb61_1 conda-forge -bzip2 1.0.8 hcfcfb64_5 conda-forge -c-ares 1.26.0 hcfcfb64_0 conda-forge -ca-certificates 2024.2.2 h56e8100_0 conda-forge -cairo 1.18.0 h1fef639_0 conda-forge -certifi 2024.2.2 pyhd8ed1ab_0 conda-forge -chardet 5.2.0 py310h5588dad_1 conda-forge -charset-normalizer 3.3.2 pyhd8ed1ab_0 conda-forge -cheroot 9.0.0 pyhd8ed1ab_0 conda-forge -click 8.1.7 win_pyh7428d3b_0 conda-forge -click-spinner 0.1.10 pyh9f0ad1d_0 conda-forge -cloudpickle 3.0.0 pyhd8ed1ab_0 conda-forge -colorama 0.4.6 pyhd8ed1ab_0 conda-forge -colorlog 6.8.2 py310h5588dad_0 conda-forge -comm 0.2.1 pyhd8ed1ab_0 conda-forge -convertdate 2.4.0 pyhd8ed1ab_0 conda-forge -cssselect2 0.2.1 pyh9f0ad1d_1 conda-forge -cycler 0.12.1 pyhd8ed1ab_0 conda-forge -dacite 1.8.1 pyhd8ed1ab_0 conda-forge -dataclasses 0.8 pyhc8e2a94_3 conda-forge -dataclasses-json 0.6.4 pyhd8ed1ab_0 conda-forge -datapane 0.16.7 pyhd8ed1ab_0 conda-forge -datetimerange 2.2.0 pyhd8ed1ab_0 conda-forge -debugpy 1.8.1 py310h00ffb61_0 conda-forge -decorator 5.1.1 pyhd8ed1ab_0 conda-forge -defusedxml 0.7.1 pyhd8ed1ab_0 conda-forge -dill 0.3.8 pyhd8ed1ab_0 conda-forge -django 4.2.8 pyhd8ed1ab_0 conda-forge -docutils 0.18.1 py310h5588dad_1 conda-forge -dodgy 0.2.1 py_0 conda-forge -dominate 2.9.1 pyhd8ed1ab_0 conda-forge -dulwich 0.21.7 py310h8d17308_0 conda-forge -entrypoints 0.4 pyhd8ed1ab_0 conda-forge -et_xmlfile 1.1.0 pyhd8ed1ab_0 conda-forge -ethos-penalps 1.0.1 pypi_0 pypi -exceptiongroup 1.2.0 pyhd8ed1ab_2 conda-forge -executing 2.0.1 pyhd8ed1ab_0 conda-forge -expat 2.5.0 h63175ca_1 conda-forge -face 20.1.1 py_0 conda-forge -flake8 5.0.4 pyhd8ed1ab_0 conda-forge -flake8-polyfill 1.0.2 py_0 conda-forge -font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge -font-ttf-inconsolata 3.000 h77eed37_0 conda-forge -font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge -font-ttf-ubuntu 0.83 h77eed37_1 conda-forge -fontconfig 2.14.2 hbde0cde_0 conda-forge -fonts-conda-ecosystem 1 0 conda-forge -fonts-conda-forge 1 0 conda-forge -freetype 2.12.1 hdaf720e_2 conda-forge -freetype-py 2.3.0 pyhd8ed1ab_0 conda-forge -freezegun 1.4.0 pyhd8ed1ab_0 conda-forge -fribidi 1.0.10 h8d14728_0 conda-forge -furl 2.1.3 pyhd8ed1ab_0 conda-forge -getopt-win32 0.1 hcfcfb64_1 conda-forge -gettext 0.21.1 h5728263_0 conda-forge -gitdb 4.0.11 pyhd8ed1ab_0 conda-forge -gitpython 3.1.41 pyhd8ed1ab_0 conda-forge -glib 2.78.3 h12be248_0 conda-forge -glib-tools 2.78.3 h12be248_0 conda-forge -glom 23.5.0 pyhd8ed1ab_0 conda-forge -graphite2 1.3.13 1000 conda-forge -graphviz 9.0.0 h51cb2cd_1 conda-forge -greenlet 3.0.3 py310h00ffb61_0 conda-forge -gst-plugins-base 1.22.9 h001b923_0 conda-forge -gstreamer 1.22.9 hb4038d2_0 conda-forge -gts 0.7.6 h6b5321d_4 conda-forge -harfbuzz 8.3.0 h7ab893a_0 conda-forge -icu 73.2 h63175ca_0 conda-forge -idna 3.6 pyhd8ed1ab_0 conda-forge -imagesize 1.4.1 pyhd8ed1ab_0 conda-forge -importlib-metadata 7.0.1 pyha770c72_0 conda-forge -importlib_metadata 7.0.1 hd8ed1ab_0 conda-forge -importlib_resources 5.13.0 pyhd8ed1ab_0 conda-forge -iniconfig 2.0.0 pyhd8ed1ab_0 conda-forge -intel-openmp 2024.0.0 h57928b3_49841 conda-forge -ipykernel 6.29.2 pyha63f2e9_0 conda-forge -ipynbname 2023.2.0.0 pyhd8ed1ab_0 conda-forge -ipython 8.18.1 pyh7428d3b_3 conda-forge -isort 5.13.2 pyhd8ed1ab_0 conda-forge -jaraco.functools 4.0.0 pyhd8ed1ab_0 conda-forge -jedi 0.19.1 pyhd8ed1ab_0 conda-forge -jinja2 3.1.3 pyhd8ed1ab_0 conda-forge -jplephem 2.21 pyh864a33b_0 conda-forge -jsonpickle 3.0.2 pyhd8ed1ab_1 conda-forge -jsonschema 4.21.1 pyhd8ed1ab_0 conda-forge -jsonschema-specifications 2023.12.1 pyhd8ed1ab_0 conda-forge -jupyter-book 0.15.1 pyhd8ed1ab_0 conda-forge -jupyter-cache 0.6.1 pyhd8ed1ab_0 conda-forge -jupyter_client 8.6.0 pyhd8ed1ab_0 conda-forge -jupyter_core 5.7.1 py310h5588dad_0 conda-forge -jupyterlab_pygments 0.3.0 pyhd8ed1ab_1 conda-forge -kiwisolver 1.4.5 py310h232114e_1 conda-forge -krb5 1.21.2 heb0366b_0 conda-forge -latexcodec 2.0.1 pyh9f0ad1d_0 conda-forge -lazy-object-proxy 1.10.0 py310h8d17308_0 conda-forge -lcms2 2.16 h67d730c_0 conda-forge -lerc 4.0.0 h63175ca_0 conda-forge -libabseil 20230802.1 cxx17_h63175ca_0 conda-forge -libarrow 10.0.1 h1138768_57_cpu conda-forge -libblas 3.9.0 21_win64_mkl conda-forge -libbrotlicommon 1.1.0 hcfcfb64_1 conda-forge -libbrotlidec 1.1.0 hcfcfb64_1 conda-forge -libbrotlienc 1.1.0 hcfcfb64_1 conda-forge -libcblas 3.9.0 21_win64_mkl conda-forge -libclang 15.0.7 default_hde6756a_4 conda-forge -libclang13 15.0.7 default_h85b4d89_4 conda-forge -libcrc32c 1.1.2 h0e60522_0 conda-forge -libcurl 8.5.0 hd5e4a3a_0 conda-forge -libdeflate 1.19 hcfcfb64_0 conda-forge -libevent 2.1.12 h3671451_1 conda-forge -libexpat 2.5.0 h63175ca_1 conda-forge -libffi 3.4.2 h8ffe710_5 conda-forge -libgd 2.3.3 h312136b_9 conda-forge -libglib 2.78.3 h16e383f_0 conda-forge -libgoogle-cloud 2.12.0 h39f2fc6_4 conda-forge -libgrpc 1.59.3 h5bbd4a7_0 conda-forge -libhwloc 2.9.3 default_haede6df_1009 conda-forge -libiconv 1.17 hcfcfb64_2 conda-forge -libjpeg-turbo 3.0.0 hcfcfb64_1 conda-forge -liblapack 3.9.0 21_win64_mkl conda-forge -libogg 1.3.4 h8ffe710_1 conda-forge -libpng 1.6.42 h19919ed_0 conda-forge -libprotobuf 4.24.4 hb8276f3_0 conda-forge -libre2-11 2023.06.02 h8c5ae5e_0 conda-forge -libsodium 1.0.18 h8d14728_1 conda-forge -libsqlite 3.44.2 hcfcfb64_0 conda-forge -libssh2 1.11.0 h7dfc565_0 conda-forge -libthrift 0.19.0 ha2b3283_1 conda-forge -libtiff 4.6.0 h6e2ebb7_2 conda-forge -libutf8proc 2.8.0 h82a8f57_0 conda-forge -libvorbis 1.3.7 h0e60522_0 conda-forge -libwebp 1.3.2 hcfcfb64_1 conda-forge -libwebp-base 1.3.2 hcfcfb64_0 conda-forge -libxcb 1.15 hcd874cb_0 conda-forge -libxml2 2.12.5 hc3477c8_0 conda-forge -libxslt 1.1.39 h3df6e99_0 conda-forge -libzlib 1.2.13 hcfcfb64_5 conda-forge -linkify-it-py 2.0.3 pyhd8ed1ab_0 conda-forge -logilab-common 1.7.3 py_0 conda-forge -lunardate 0.2.0 py_0 conda-forge -lxml 4.9.4 py310hba208d0_0 conda-forge -lz4-c 1.9.4 hcfcfb64_0 conda-forge -m2w64-gcc-libgfortran 5.3.0 6 conda-forge -m2w64-gcc-libs 5.3.0 7 conda-forge -m2w64-gcc-libs-core 5.3.0 7 conda-forge -m2w64-gmp 6.1.0 2 conda-forge -m2w64-libwinpthread-git 5.0.0.4634.697f757 2 conda-forge -markdown-it-py 2.2.0 pyhd8ed1ab_0 conda-forge -markupsafe 2.1.5 py310h8d17308_0 conda-forge -marshmallow 3.20.2 pyhd8ed1ab_0 conda-forge -matplotlib 3.4.3 py310h5588dad_2 conda-forge -matplotlib-base 3.4.3 py310h79a7439_2 conda-forge -matplotlib-inline 0.1.6 pyhd8ed1ab_0 conda-forge -mbstrdecoder 1.1.3 pyhd8ed1ab_1 conda-forge -mccabe 0.7.0 pyhd8ed1ab_0 conda-forge -mdit-py-plugins 0.4.0 pyhd8ed1ab_0 conda-forge -mdurl 0.1.2 pyhd8ed1ab_0 conda-forge -micawber 0.5.5 pyhd8ed1ab_0 conda-forge -mistune 3.0.2 pyhd8ed1ab_0 conda-forge -mkl 2024.0.0 h66d3029_49657 conda-forge -monotonic 1.5 py_0 conda-forge -more-itertools 10.2.0 pyhd8ed1ab_0 conda-forge -msys2-conda-epoch 20160418 1 conda-forge -multimethod 1.9.1 pyhd8ed1ab_0 conda-forge -mypy_extensions 1.0.0 pyha770c72_0 conda-forge -myst-nb 0.17.2 pyhd8ed1ab_0 conda-forge -myst-parser 0.18.1 pyhd8ed1ab_0 conda-forge -nbclient 0.7.4 pyhd8ed1ab_0 conda-forge -nbconvert 7.16.0 pyhd8ed1ab_0 conda-forge -nbconvert-core 7.16.0 pyhd8ed1ab_0 conda-forge -nbconvert-pandoc 7.16.0 pyhd8ed1ab_0 conda-forge -nbformat 5.9.2 pyhd8ed1ab_0 conda-forge -nest-asyncio 1.6.0 pyhd8ed1ab_0 conda-forge -networkx 3.2 pyhd8ed1ab_0 conda-forge -numpy 1.23.5 py310h4a8f9c9_0 conda-forge -openjpeg 2.5.0 h3d672ee_3 conda-forge -openpyxl 3.1.2 py310h8d17308_1 conda-forge -openssl 3.2.1 hcfcfb64_0 conda-forge -orc 1.9.2 hf0b6bd4_0 conda-forge -orderedmultidict 1.0.1 pyhd8ed1ab_1 conda-forge -packaging 23.2 pyhd8ed1ab_0 conda-forge -pandas 1.5.3 py310h1c4a608_1 conda-forge -pandoc 3.1.3 h57928b3_0 conda-forge -pandocfilters 1.5.0 pyhd8ed1ab_0 conda-forge -pango 1.50.14 h07c897b_2 conda-forge -parso 0.8.3 pyhd8ed1ab_0 conda-forge -pathspec 0.12.1 pyhd8ed1ab_0 conda-forge -patsy 0.5.6 pyhd8ed1ab_0 conda-forge -pcre2 10.42 h17e33f8_0 conda-forge -pdftopng 0.2.3 py310hc893b6f_14 conda-forge -pep8-naming 0.10.0 pyh9f0ad1d_0 conda-forge -pickleshare 0.7.5 py_1003 conda-forge -pillow 10.2.0 py310h1e6a543_0 conda-forge -pint 0.23 pyhd8ed1ab_0 conda-forge -pip 24.0 pyhd8ed1ab_0 conda-forge -pixman 0.43.2 h63175ca_0 conda-forge -pkgutil-resolve-name 1.3.10 pyhd8ed1ab_1 conda-forge -platformdirs 4.2.0 pyhd8ed1ab_0 conda-forge -plotly 5.18.0 pyhd8ed1ab_0 conda-forge -pluggy 1.4.0 pyhd8ed1ab_0 conda-forge -ply 3.11 py_1 conda-forge -poppler 23.12.0 hc2f3c52_0 conda-forge -poppler-data 0.4.12 hd8ed1ab_0 conda-forge -posthog 2.4.2 pyhd8ed1ab_0 conda-forge -prompt-toolkit 3.0.42 pyha770c72_0 conda-forge -proplot 0.9.7 pyhd8ed1ab_0 conda-forge -prospector 1.10.3 pyhd8ed1ab_0 conda-forge -psutil 5.9.8 py310h8d17308_0 conda-forge -pthread-stubs 0.4 hcd874cb_1001 conda-forge -pthreads-win32 2.9.1 hfa6e2cd_3 conda-forge -pure_eval 0.2.2 pyhd8ed1ab_0 conda-forge -pyarrow 10.0.1 py310hd0bb7c2_57_cpu conda-forge -pybtex 0.24.0 pyhd8ed1ab_2 conda-forge -pybtex-docutils 1.0.3 py310h5588dad_1 conda-forge -pycairo 1.25.1 py310h42c1a3e_0 conda-forge -pycodestyle 2.9.1 pyhd8ed1ab_0 conda-forge -pydantic 1.10.13 py310h8d17308_1 conda-forge -pydata-sphinx-theme 0.15.2 pyhd8ed1ab_0 conda-forge -pydocstyle 6.3.0 pyhd8ed1ab_0 conda-forge -pyflakes 2.5.0 pyhd8ed1ab_0 conda-forge -pygments 2.17.2 pyhd8ed1ab_0 conda-forge -pylint 2.17.7 pyhd8ed1ab_0 conda-forge -pylint-celery 0.3 py_1 conda-forge -pylint-django 2.5.3 pyhd8ed1ab_0 conda-forge -pylint-flask 0.6 py_0 conda-forge -pylint-plugin-utils 0.7 pyhd8ed1ab_0 conda-forge -pyluach 2.2.0 pyhd8ed1ab_0 conda-forge -pymeeus 0.5.12 pyhd8ed1ab_0 conda-forge -pyngrok 5.2.2 pyhd8ed1ab_0 conda-forge -pyparsing 3.1.1 pyhd8ed1ab_0 conda-forge -pyqt 5.15.9 py310h1fd54f2_5 conda-forge -pyqt5-sip 12.12.2 py310h00ffb61_5 conda-forge -pysocks 1.7.1 pyh0701188_6 conda-forge -pytest 8.0.0 pyhd8ed1ab_0 conda-forge -python 3.10.13 h4de0772_1_cpython conda-forge -python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge -python-fastjsonschema 2.19.1 pyhd8ed1ab_0 conda-forge -python-graphviz 0.20.1 pyh22cad53_0 conda-forge -python_abi 3.10 4_cp310 conda-forge -pytz 2024.1 pyhd8ed1ab_0 conda-forge -pywin32 306 py310h00ffb61_2 conda-forge -pyyaml 6.0.1 py310h8d17308_1 conda-forge -pyzmq 25.1.2 py310h2849c00_0 conda-forge -qt-main 5.15.8 h9e85ed6_18 conda-forge -re2 2023.06.02 hcbb65ff_0 conda-forge -referencing 0.33.0 pyhd8ed1ab_0 conda-forge -reportlab 4.1.0 py310h8d17308_0 conda-forge -requests 2.31.0 pyhd8ed1ab_0 conda-forge -requests-toolbelt 1.0.0 pyhd8ed1ab_0 conda-forge -requirements-detector 1.2.2 pyhd8ed1ab_0 conda-forge -rlpycairo 0.2.0 pyhd8ed1ab_0 conda-forge -rpds-py 0.17.1 py310h87d50f1_0 conda-forge -scipy 1.12.0 py310hf667824_2 conda-forge -seaborn 0.13.2 hd8ed1ab_0 conda-forge -seaborn-base 0.13.2 pyhd8ed1ab_0 conda-forge -semver 3.0.2 pyhd8ed1ab_0 conda-forge -setoptconf-tmp 0.3.1 pyhd8ed1ab_0 conda-forge -setuptools 69.0.3 pyhd8ed1ab_0 conda-forge -sgp4 2.22 py310h220cb41_0 conda-forge -sip 6.7.12 py310h00ffb61_0 conda-forge -six 1.16.0 pyh6c4a22f_0 conda-forge -skyfield 1.45 pyh1a96a4e_0 conda-forge -skyfield-data 5.0.0 pyhd8ed1ab_0 conda-forge -smmap 5.0.0 pyhd8ed1ab_0 conda-forge -snappy 1.1.10 hfb803bf_0 conda-forge -snowballstemmer 2.2.0 pyhd8ed1ab_0 conda-forge -soupsieve 2.5 pyhd8ed1ab_1 conda-forge -sphinx 5.0.2 pyh6c4a22f_0 conda-forge -sphinx-autoapi 2.1.0 pyhd8ed1ab_0 conda-forge -sphinx-book-theme 1.0.1 pyhd8ed1ab_0 conda-forge -sphinx-comments 0.0.3 pyh9f0ad1d_0 conda-forge -sphinx-copybutton 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-design 0.3.0 pyhd8ed1ab_0 conda-forge -sphinx-external-toc 0.3.1 pyhd8ed1ab_1 conda-forge -sphinx-jupyterbook-latex 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-multitoc-numbering 0.1.3 pyhd8ed1ab_0 conda-forge -sphinx-thebe 0.2.1 pyhd8ed1ab_0 conda-forge -sphinx-togglebutton 0.3.2 pyhd8ed1ab_0 conda-forge -sphinxcontrib-applehelp 1.0.8 pyhd8ed1ab_0 conda-forge -sphinxcontrib-bibtex 2.5.0 pyhd8ed1ab_0 conda-forge -sphinxcontrib-devhelp 1.0.6 pyhd8ed1ab_0 conda-forge -sphinxcontrib-dotnetdomain 0.4 py_0 conda-forge -sphinxcontrib-golangdomain 0.2.0.dev0 py_0 conda-forge -sphinxcontrib-htmlhelp 2.0.5 pyhd8ed1ab_0 conda-forge -sphinxcontrib-jsmath 1.0.1 pyhd8ed1ab_0 conda-forge -sphinxcontrib-qthelp 1.0.7 pyhd8ed1ab_0 conda-forge -sphinxcontrib-serializinghtml 1.1.10 pyhd8ed1ab_0 conda-forge -sqlalchemy 1.4.49 py310h8d17308_1 conda-forge -sqlparse 0.4.4 pyhd8ed1ab_0 conda-forge -stack_data 0.6.2 pyhd8ed1ab_0 conda-forge -statsmodels 0.14.1 py310h3e78b6c_0 conda-forge -svglib 1.5.1 pyhd8ed1ab_0 conda-forge -tabulate 0.9.0 pyhd8ed1ab_1 conda-forge -tbb 2021.11.0 h91493d7_1 conda-forge -tectonic 0.15.0 h1cbdeb6_0 conda-forge -tenacity 8.2.3 pyhd8ed1ab_0 conda-forge -tinycss2 1.2.1 pyhd8ed1ab_0 conda-forge -tk 8.6.13 h5226925_1 conda-forge -toml 0.10.2 pyhd8ed1ab_0 conda-forge -tomli 2.0.1 pyhd8ed1ab_0 conda-forge -tomlkit 0.12.3 pyha770c72_0 conda-forge -toolz 0.12.1 pyhd8ed1ab_0 conda-forge -tornado 6.3.3 py310h8d17308_1 conda-forge -traitlets 5.14.1 pyhd8ed1ab_0 conda-forge -typeguard 4.1.5 pyhd8ed1ab_1 conda-forge -typepy 1.3.2 pyhd8ed1ab_0 conda-forge -typing-extensions 4.9.0 hd8ed1ab_0 conda-forge -typing_extensions 4.9.0 pyha770c72_0 conda-forge -typing_inspect 0.9.0 pyhd8ed1ab_0 conda-forge -tzdata 2024a h0c530f3_0 conda-forge -uc-micro-py 1.0.2 pyhd8ed1ab_0 conda-forge -ucrt 10.0.22621.0 h57928b3_0 conda-forge -unidecode 1.3.8 pyhd8ed1ab_0 conda-forge -urllib3 2.2.0 pyhd8ed1ab_0 conda-forge -vc 14.3 hcf57466_18 conda-forge -vc14_runtime 14.38.33130 h82b7239_18 conda-forge -vega_datasets 0.9.0 pyhd3deb0d_0 conda-forge -vs2015_runtime 14.38.33130 hcb4865c_18 conda-forge -wcwidth 0.2.13 pyhd8ed1ab_0 conda-forge -webencodings 0.5.1 pyhd8ed1ab_2 conda-forge -wheel 0.42.0 pyhd8ed1ab_0 conda-forge -win_inet_pton 1.1.0 pyhd8ed1ab_6 conda-forge -workalendar 17.0.0 pyhd8ed1ab_0 conda-forge -wrapt 1.16.0 py310h8d17308_0 conda-forge -xlsxwriter 3.1.9 pyhd8ed1ab_0 conda-forge -xorg-kbproto 1.0.7 hcd874cb_1002 conda-forge -xorg-libice 1.1.1 hcd874cb_0 conda-forge -xorg-libsm 1.2.4 hcd874cb_0 conda-forge -xorg-libx11 1.8.7 hefa74cf_0 conda-forge -xorg-libxau 1.0.11 hcd874cb_0 conda-forge -xorg-libxdmcp 1.1.3 hcd874cb_0 conda-forge -xorg-libxext 1.3.4 hcd874cb_2 conda-forge -xorg-libxpm 3.5.17 hcd874cb_0 conda-forge -xorg-libxt 1.3.0 hcd874cb_1 conda-forge -xorg-xextproto 7.3.0 hcd874cb_1003 conda-forge -xorg-xproto 7.0.31 hcd874cb_1007 conda-forge -xz 5.2.6 h8d14728_0 conda-forge -yaml 0.2.5 h8ffe710_2 conda-forge -zeromq 4.3.5 h63175ca_0 conda-forge -zipp 3.17.0 pyhd8ed1ab_0 conda-forge -zlib 1.2.13 hcfcfb64_5 conda-forge -zstd 1.5.5 h12be248_0 conda-forge \ No newline at end of file diff --git a/ittsc_lpg b/ittsc_lpg deleted file mode 100644 index 2e22db3..0000000 --- a/ittsc_lpg +++ /dev/null @@ -1,473 +0,0 @@ -accessible-pygments 0.0.4 pyhd8ed1ab_0 conda-forge -alabaster 0.7.13 pyhd8ed1ab_0 conda-forge -altair 4.2.2 pyhd8ed1ab_0 conda-forge -anyio 3.7.1 pyhd8ed1ab_0 conda-forge -appdirs 1.4.4 pyh9f0ad1d_0 conda-forge -argon2-cffi 23.1.0 pyhd8ed1ab_0 conda-forge -argon2-cffi-bindings 21.2.0 py310h8d17308_3 conda-forge -arrow 1.2.3 pyhd8ed1ab_0 conda-forge -arrow-cpp 10.0.1 h57928b3_24_cpu conda-forge -asgiref 3.7.2 pyhd8ed1ab_0 conda-forge -astroid 2.15.6 py310h5588dad_0 conda-forge -astropy 5.3.2 py310h9b08ddd_0 conda-forge -asttokens 2.2.1 pyhd8ed1ab_0 conda-forge -astunparse 1.6.3 pyhd8ed1ab_0 conda-forge -async-lru 2.0.4 pyhd8ed1ab_0 conda-forge -attrs 23.1.0 pyh71513ae_1 conda-forge -aws-c-auth 0.7.0 h6f3c987_2 conda-forge -aws-c-cal 0.6.0 h6ba3258_0 conda-forge -aws-c-common 0.8.23 hcfcfb64_0 conda-forge -aws-c-compression 0.2.17 h420beca_1 conda-forge -aws-c-event-stream 0.3.1 had47b81_1 conda-forge -aws-c-http 0.7.11 h72ba615_0 conda-forge -aws-c-io 0.13.28 ha35c040_0 conda-forge -aws-c-mqtt 0.8.14 h4941efa_2 conda-forge -aws-c-s3 0.3.13 he04eaa7_2 conda-forge -aws-c-sdkutils 0.1.11 h420beca_1 conda-forge -aws-checksums 0.1.16 h420beca_1 conda-forge -aws-crt-cpp 0.20.3 h247a981_4 conda-forge -aws-sdk-cpp 1.10.57 h1a0519f_17 conda-forge -babel 2.12.1 pyhd8ed1ab_1 conda-forge -backcall 0.2.0 pyh9f0ad1d_0 conda-forge -backoff 1.11.1 pyhd8ed1ab_0 conda-forge -backports 1.0 pyhd8ed1ab_3 conda-forge -backports.functools_lru_cache 1.6.5 pyhd8ed1ab_0 conda-forge -backports.zoneinfo 0.2.1 py310h5588dad_7 conda-forge -beautifulsoup4 4.12.2 pyha770c72_0 conda-forge -black 23.7.0 py310h5588dad_1 conda-forge -bleach 6.0.0 pyhd8ed1ab_0 conda-forge -boltons 21.0.0 pyhd8ed1ab_0 conda-forge -boost-cpp 1.78.0 h9f4b32c_4 conda-forge -brotli-python 1.0.9 py310h00ffb61_9 conda-forge -brotlipy 0.7.0 py310h8d17308_1005 conda-forge -bzip2 1.0.8 h8ffe710_4 conda-forge -c-ares 1.19.1 hcfcfb64_0 conda-forge -ca-certificates 2023.11.17 h56e8100_0 conda-forge -cached-property 1.5.2 hd8ed1ab_1 conda-forge -cached_property 1.5.2 pyha770c72_1 conda-forge -cairo 1.16.0 hd694305_1014 conda-forge -certifi 2023.11.17 pyhd8ed1ab_0 conda-forge -cffi 1.15.1 py310h628cb3f_3 conda-forge -chardet 5.2.0 py310h5588dad_0 conda-forge -charset-normalizer 3.2.0 pyhd8ed1ab_0 conda-forge -cheroot 9.0.0 pyhd8ed1ab_0 conda-forge -click 8.1.7 win_pyh7428d3b_0 conda-forge -click-completion 0.5.2 py310h5588dad_5 conda-forge -click-default-group 1.2.4 pyhd8ed1ab_0 conda-forge -click-log 0.4.0 pyhd8ed1ab_0 conda-forge -click-spinner 0.1.10 pyh9f0ad1d_0 conda-forge -cloudpickle 2.2.1 pyhd8ed1ab_0 conda-forge -colorama 0.4.6 pyhd8ed1ab_0 conda-forge -colorlog 6.7.0 py310h5588dad_1 conda-forge -comm 0.1.4 pyhd8ed1ab_0 conda-forge -commonmark 0.9.1 py_0 conda-forge -conda 23.1.0 py310h5588dad_0 conda-forge -conda-package-handling 2.2.0 pyh38be061_0 conda-forge -conda-package-streaming 0.9.0 pyhd8ed1ab_0 conda-forge -conda-tree 1.1.0 pyhd8ed1ab_2 conda-forge -convertdate 2.4.0 pyhd8ed1ab_0 conda-forge -cryptography 41.0.3 py310h6e82f81_0 conda-forge -cssselect2 0.2.1 pyh9f0ad1d_1 conda-forge -cycler 0.11.0 pyhd8ed1ab_0 conda-forge -dacite 1.8.0 pyhd8ed1ab_0 conda-forge -dataclass-wizard 0.22.0 pyhd8ed1ab_0 conda-forge -dataclasses 0.8 pyhc8e2a94_3 conda-forge -dataclasses-json 0.5.7 pyhd8ed1ab_0 conda-forge -datapane 0.16.7 pyhd8ed1ab_0 conda-forge -datetimerange 1.2.0 pyhd8ed1ab_0 conda-forge -debugpy 1.6.8 py310h00ffb61_0 conda-forge -decorator 5.1.1 pyhd8ed1ab_0 conda-forge -defusedxml 0.7.1 pyhd8ed1ab_0 conda-forge -dill 0.3.7 pyhd8ed1ab_0 conda-forge -django 4.2.4 pyhd8ed1ab_0 conda-forge -docutils 0.18.1 py310h5588dad_1 conda-forge -dodgy 0.2.1 py_0 conda-forge -dominate 2.8.0 pyhd8ed1ab_0 conda-forge -dulwich 0.21.5 py310h8d17308_0 conda-forge -entrypoints 0.4 pyhd8ed1ab_0 conda-forge -et_xmlfile 1.1.0 pyhd8ed1ab_0 conda-forge -ethos-penalps 1.0.1 pypi_0 pypi -exceptiongroup 1.1.3 pyhd8ed1ab_0 conda-forge -executing 1.2.0 pyhd8ed1ab_0 conda-forge -expat 2.5.0 h63175ca_1 conda-forge -face 20.1.1 py_0 conda-forge -flake8 5.0.4 pyhd8ed1ab_0 conda-forge -flake8-polyfill 1.0.2 py_0 conda-forge -flit-core 3.9.0 pyhd8ed1ab_0 conda-forge -font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge -font-ttf-inconsolata 3.000 h77eed37_0 conda-forge -font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge -font-ttf-ubuntu 0.83 hab24e00_0 conda-forge -fontconfig 2.14.2 hbde0cde_0 conda-forge -fonts-conda-ecosystem 1 0 conda-forge -fonts-conda-forge 1 0 conda-forge -fqdn 1.5.1 pyhd8ed1ab_0 conda-forge -freetype 2.12.1 h546665d_1 conda-forge -freetype-py 2.3.0 pyhd8ed1ab_0 conda-forge -freezegun 1.2.2 pyhd8ed1ab_0 conda-forge -fribidi 1.0.10 h8d14728_0 conda-forge -furl 2.1.3 pyhd8ed1ab_0 conda-forge -future 0.18.3 pyhd8ed1ab_0 conda-forge -getopt-win32 0.1 h8ffe710_0 conda-forge -gettext 0.21.1 h5728263_0 conda-forge -gflags 2.2.2 ha925a31_1004 conda-forge -gitdb 4.0.10 pyhd8ed1ab_0 conda-forge -gitpython 3.1.32 pyhd8ed1ab_0 conda-forge -glib 2.76.4 h12be248_0 conda-forge -glib-tools 2.76.4 h12be248_0 conda-forge -glog 0.6.0 h4797de2_0 conda-forge -glom 23.3.0 pyhd8ed1ab_1 conda-forge -graphite2 1.3.13 1000 conda-forge -graphviz 8.1.0 h51cb2cd_0 conda-forge -greenlet 2.0.2 py310h00ffb61_1 conda-forge -gst-plugins-base 1.22.5 h001b923_0 conda-forge -gstreamer 1.22.5 hb4038d2_0 conda-forge -gts 0.7.6 h6b5321d_4 conda-forge -harfbuzz 6.0.0 he256f1b_0 conda-forge -icu 70.1 h0e60522_0 conda-forge -idna 3.4 pyhd8ed1ab_0 conda-forge -imagesize 1.4.1 pyhd8ed1ab_0 conda-forge -importlib-metadata 6.8.0 pyha770c72_0 conda-forge -importlib_metadata 6.8.0 hd8ed1ab_0 conda-forge -importlib_resources 5.13.0 pyhd8ed1ab_0 conda-forge -inflection 0.5.1 pyh9f0ad1d_0 conda-forge -iniconfig 2.0.0 pyhd8ed1ab_0 conda-forge -intel-openmp 2023.2.0 h57928b3_49496 conda-forge -ipykernel 6.25.1 pyh6817e22_0 conda-forge -ipynbname 2023.2.0.0 pyhd8ed1ab_0 conda-forge -ipython 8.14.0 pyh08f2357_0 conda-forge -ipython_genutils 0.2.0 py_1 conda-forge -ipywidgets 8.1.0 pyhd8ed1ab_0 conda-forge -isoduration 20.11.0 pyhd8ed1ab_0 conda-forge -isort 5.12.0 pyhd8ed1ab_1 conda-forge -jaraco.functools 3.8.1 pyhd8ed1ab_0 conda-forge -jedi 0.19.0 pyhd8ed1ab_0 conda-forge -jinja2 3.1.2 pyhd8ed1ab_1 conda-forge -jpeg 9e hcfcfb64_3 conda-forge -jplephem 2.18 pyh78acc04_0 conda-forge -json5 0.9.14 pyhd8ed1ab_0 conda-forge -jsonpickle 3.0.2 pyhd8ed1ab_0 conda-forge -jsonpointer 2.0 py_0 conda-forge -jsonschema 4.19.0 pyhd8ed1ab_1 conda-forge -jsonschema-specifications 2023.7.1 pyhd8ed1ab_0 conda-forge -jsonschema-with-format-nongpl 4.19.0 pyhd8ed1ab_1 conda-forge -jupyter-book 0.15.1 pyhd8ed1ab_0 conda-forge -jupyter-cache 0.6.1 pyhd8ed1ab_0 conda-forge -jupyter-lsp 2.2.0 pyhd8ed1ab_0 conda-forge -jupyter-server-mathjax 0.2.6 pyh5bfe37b_1 conda-forge -jupyter-sphinx 0.4.0 pyhd8ed1ab_0 conda-forge -jupyter_client 8.3.0 pyhd8ed1ab_0 conda-forge -jupyter_core 5.3.1 py310h5588dad_0 conda-forge -jupyter_events 0.7.0 pyhd8ed1ab_2 conda-forge -jupyter_server 2.7.1 pyhd8ed1ab_0 conda-forge -jupyter_server_terminals 0.4.4 pyhd8ed1ab_1 conda-forge -jupyterlab 4.0.5 pyhd8ed1ab_0 conda-forge -jupyterlab_pygments 0.2.2 pyhd8ed1ab_0 conda-forge -jupyterlab_server 2.24.0 pyhd8ed1ab_0 conda-forge -jupyterlab_widgets 3.0.8 pyhd8ed1ab_0 conda-forge -kiwisolver 1.4.5 py310h232114e_0 conda-forge -krb5 1.20.1 heb0366b_0 conda-forge -latex-dependency-scanner 0.1.1 pyhd8ed1ab_1 conda-forge -latexcodec 2.0.1 pyh9f0ad1d_0 conda-forge -lazy-object-proxy 1.9.0 py310h8d17308_0 conda-forge -lcms2 2.15 ha5c8aab_0 conda-forge -lerc 4.0.0 h63175ca_0 conda-forge -libabseil 20230125.3 cxx17_h63175ca_0 conda-forge -libarrow 10.0.1 h0ac4353_24_cpu conda-forge -libblas 3.9.0 17_win64_mkl conda-forge -libbrotlicommon 1.0.9 hcfcfb64_9 conda-forge -libbrotlidec 1.0.9 hcfcfb64_9 conda-forge -libbrotlienc 1.0.9 hcfcfb64_9 conda-forge -libcblas 3.9.0 17_win64_mkl conda-forge -libclang 15.0.7 default_h77d9078_3 conda-forge -libclang13 15.0.7 default_h77d9078_3 conda-forge -libcrc32c 1.1.2 h0e60522_0 conda-forge -libcurl 8.1.2 h68f0423_0 conda-forge -libdeflate 1.17 hcfcfb64_0 conda-forge -libevent 2.1.12 h3671451_1 conda-forge -libexpat 2.5.0 h63175ca_1 conda-forge -libffi 3.4.2 h8ffe710_5 conda-forge -libgd 2.3.3 hf5a96e7_4 conda-forge -libglib 2.76.4 he8f3873_0 conda-forge -libgoogle-cloud 2.10.1 h00b2bdc_1 conda-forge -libgrpc 1.54.3 ha177ca7_0 conda-forge -libhwloc 2.9.2 nocuda_h15da153_1008 conda-forge -libiconv 1.17 h8ffe710_0 conda-forge -liblapack 3.9.0 17_win64_mkl conda-forge -libogg 1.3.4 h8ffe710_1 conda-forge -libpng 1.6.39 h19919ed_0 conda-forge -libprotobuf 3.21.12 h12be248_1 conda-forge -libsodium 1.0.18 h8d14728_1 conda-forge -libsqlite 3.43.0 hcfcfb64_0 conda-forge -libssh2 1.11.0 h7dfc565_0 conda-forge -libthrift 0.18.1 h06f6336_2 conda-forge -libtiff 4.5.0 hf8721a0_2 conda-forge -libutf8proc 2.8.0 h82a8f57_0 conda-forge -libvorbis 1.3.7 h0e60522_0 conda-forge -libwebp 1.3.1 hcfcfb64_0 conda-forge -libwebp-base 1.3.1 hcfcfb64_0 conda-forge -libxcb 1.13 hcd874cb_1004 conda-forge -libxml2 2.11.5 hc3477c8_1 conda-forge -libxslt 1.1.37 h6070c61_1 conda-forge -libzlib 1.2.13 hcfcfb64_5 conda-forge -linkify-it-py 2.0.0 pyhd8ed1ab_0 conda-forge -logilab-common 1.7.3 py_0 conda-forge -lolviz 1.4.4 pyhd8ed1ab_0 conda-forge -lunardate 0.2.0 py_0 conda-forge -lxml 4.9.3 py310h46d54dd_0 conda-forge -lz4-c 1.9.4 hcfcfb64_0 conda-forge -m2w64-gcc-libgfortran 5.3.0 6 conda-forge -m2w64-gcc-libs 5.3.0 7 conda-forge -m2w64-gcc-libs-core 5.3.0 7 conda-forge -m2w64-gmp 6.1.0 2 conda-forge -m2w64-libwinpthread-git 5.0.0.4634.697f757 2 conda-forge -markdown-it-py 2.2.0 pyhd8ed1ab_0 conda-forge -markupsafe 2.1.3 py310h8d17308_0 conda-forge -marshmallow 3.20.1 pyhd8ed1ab_0 conda-forge -marshmallow-enum 1.5.1 pyh9f0ad1d_3 conda-forge -matplotlib 3.4.3 py310h5588dad_2 conda-forge -matplotlib-base 3.4.3 py310h79a7439_2 conda-forge -matplotlib-inline 0.1.6 pyhd8ed1ab_0 conda-forge -mbstrdecoder 1.1.3 pyhd8ed1ab_1 conda-forge -mccabe 0.7.0 pyhd8ed1ab_0 conda-forge -mdit-py-plugins 0.4.0 pyhd8ed1ab_0 conda-forge -mdurl 0.1.0 pyhd8ed1ab_0 conda-forge -menuinst 1.4.19 py310h5588dad_1 conda-forge -micawber 0.5.5 pyhd8ed1ab_0 conda-forge -microbench 0.7 pyhd8ed1ab_0 conda-forge -mistune 3.0.1 pyhd8ed1ab_0 conda-forge -mkl 2022.1.0 h6a75c08_874 conda-forge -monotonic 1.5 py_0 conda-forge -more-itertools 10.1.0 pyhd8ed1ab_0 conda-forge -msys2-conda-epoch 20160418 1 conda-forge -multimethod 1.9.1 pyhd8ed1ab_0 conda-forge -munch 4.0.0 pyhd8ed1ab_0 conda-forge -mypy_extensions 1.0.0 pyha770c72_0 conda-forge -myst-nb 0.17.2 pyhd8ed1ab_0 conda-forge -myst-parser 0.18.1 pyhd8ed1ab_0 conda-forge -nbclassic 1.0.0 pyhb4ecaf3_1 conda-forge -nbclient 0.7.4 pyhd8ed1ab_0 conda-forge -nbconvert 7.7.4 pyhd8ed1ab_0 conda-forge -nbconvert-core 7.7.4 pyhd8ed1ab_0 conda-forge -nbconvert-pandoc 7.7.4 pyhd8ed1ab_0 conda-forge -nbdime 3.2.1 pyhd8ed1ab_0 conda-forge -nbformat 5.9.2 pyhd8ed1ab_0 conda-forge -nest-asyncio 1.5.6 pyhd8ed1ab_0 conda-forge -networkx 3.1 pyhd8ed1ab_0 conda-forge -notebook 7.0.2 pyhd8ed1ab_0 conda-forge -notebook-shim 0.2.3 pyhd8ed1ab_0 conda-forge -numpy 1.26.0 py310hf667824_0 conda-forge -openjpeg 2.5.0 ha2aaf27_2 conda-forge -openpyxl 3.1.2 py310h8d17308_0 conda-forge -openssl 3.1.4 hcfcfb64_0 conda-forge -orc 1.8.3 hada7b9e_1 conda-forge -orderedmultidict 1.0.1 py_0 conda-forge -overrides 7.4.0 pyhd8ed1ab_0 conda-forge -packaging 23.1 pyhd8ed1ab_0 conda-forge -pandas 1.5.3 py310h1c4a608_1 conda-forge -pandas-stubs 2.1.1.230928 pyhd8ed1ab_1 conda-forge -pandoc 3.1.3 h57928b3_0 conda-forge -pandocfilters 1.5.0 pyhd8ed1ab_0 conda-forge -pango 1.50.14 hdffb7b3_0 conda-forge -parquet-cpp 1.5.1 2 conda-forge -parso 0.8.3 pyhd8ed1ab_0 conda-forge -pathspec 0.11.2 pyhd8ed1ab_0 conda-forge -patsy 0.5.3 pyhd8ed1ab_0 conda-forge -pcre2 10.40 h17e33f8_0 conda-forge -pdftopng 0.2.3 py310h560bb75_9 conda-forge -pep8-naming 0.10.0 pyh9f0ad1d_0 conda-forge -pickleshare 0.7.5 py_1003 conda-forge -pillow 9.4.0 py310hdbb7713_1 conda-forge -pint 0.22 pyhd8ed1ab_1 conda-forge -pip 23.2.1 pyhd8ed1ab_0 conda-forge -pivottablejs 0.9.0 py310haa95532_0 anaconda -pixman 0.40.0 h8ffe710_0 conda-forge -pkgutil-resolve-name 1.3.10 pyhd8ed1ab_0 conda-forge -platformdirs 3.10.0 pyhd8ed1ab_0 conda-forge -plotly 5.16.1 pyhd8ed1ab_0 conda-forge -pluggy 1.3.0 pyhd8ed1ab_0 conda-forge -ply 3.11 py_1 conda-forge -pony 0.7.16 pyhd8ed1ab_0 conda-forge -pooch 1.7.0 pyha770c72_3 conda-forge -poppler 23.03.0 h183ae7b_0 conda-forge -poppler-data 0.4.12 hd8ed1ab_0 conda-forge -posthog 2.4.2 pyhd8ed1ab_0 conda-forge -prometheus_client 0.17.1 pyhd8ed1ab_0 conda-forge -prompt-toolkit 3.0.39 pyha770c72_0 conda-forge -prompt_toolkit 3.0.39 hd8ed1ab_0 conda-forge -proplot 0.9.7 pyhd8ed1ab_0 conda-forge -prospector 1.10.2 pyhd8ed1ab_0 conda-forge -psutil 5.9.5 py310h8d17308_0 conda-forge -pthread-stubs 0.4 hcd874cb_1001 conda-forge -pthreads-win32 2.9.1 hfa6e2cd_3 conda-forge -pure_eval 0.2.2 pyhd8ed1ab_0 conda-forge -py2puml 0.7.2 pyhd8ed1ab_0 conda-forge -pyarrow 10.0.1 py310hd1a9178_24_cpu conda-forge -pybaum 0.1.3 pyhd8ed1ab_0 conda-forge -pybtex 0.24.0 pyhd8ed1ab_2 conda-forge -pybtex-docutils 1.0.3 py310h5588dad_0 conda-forge -pycairo 1.24.0 py310h42c1a3e_0 conda-forge -pycodestyle 2.9.1 pyhd8ed1ab_0 conda-forge -pycosat 0.6.4 py310h8d17308_1 conda-forge -pycparser 2.21 pyhd8ed1ab_0 conda-forge -pydantic 1.10.12 py310h8d17308_1 conda-forge -pydata-sphinx-theme 0.13.3 pyhd8ed1ab_0 conda-forge -pydocstyle 6.3.0 pyhd8ed1ab_0 conda-forge -pydot 1.4.2 py310h5588dad_3 conda-forge -pyerfa 2.0.0.3 py310h9b08ddd_0 conda-forge -pyflakes 2.5.0 pyhd8ed1ab_0 conda-forge -pyflowchart 0.3.1 pyhd8ed1ab_0 conda-forge -pygments 2.16.1 pyhd8ed1ab_0 conda-forge -pylint 2.17.5 pyhd8ed1ab_0 conda-forge -pylint-celery 0.3 py_1 conda-forge -pylint-django 2.5.3 pyhd8ed1ab_0 conda-forge -pylint-flask 0.6 py_0 conda-forge -pylint-plugin-utils 0.7 pyhd8ed1ab_0 conda-forge -pyluach 2.2.0 pyhd8ed1ab_0 conda-forge -pymeeus 0.5.12 pyhd8ed1ab_0 conda-forge -pyngrok 5.2.2 pyhd8ed1ab_0 conda-forge -pynvml 11.4.1 pypi_0 pypi -pyopenssl 23.2.0 pyhd8ed1ab_1 conda-forge -pyparsing 3.1.1 pyhd8ed1ab_0 conda-forge -pyqt 5.15.9 py310h1fd54f2_4 conda-forge -pyqt5-sip 12.12.2 py310h00ffb61_4 conda-forge -pyrsistent 0.19.3 py310h8d17308_0 conda-forge -pysocks 1.7.1 pyh0701188_6 conda-forge -pytask 0.3.2 pyhd8ed1ab_0 conda-forge -pytask-latex 0.3.0 pyhd8ed1ab_0 conda-forge -pytest 7.4.2 pyhd8ed1ab_0 conda-forge -python 3.10.12 h4de0772_0_cpython conda-forge -python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge -python-fastjsonschema 2.18.0 pyhd8ed1ab_0 conda-forge -python-graphviz 0.20.1 pyh22cad53_0 conda-forge -python-json-logger 2.0.7 pyhd8ed1ab_0 conda-forge -python_abi 3.10 3_cp310 conda-forge -pytz 2023.3 pyhd8ed1ab_0 conda-forge -pywin32 304 py310h00ffb61_2 conda-forge -pywinpty 2.0.11 py310h00ffb61_0 conda-forge -pyyaml 5.4.1 py310h8d17308_4 conda-forge -pyzmq 25.1.1 py310hcd737a0_0 conda-forge -qt-main 5.15.8 h720456b_6 conda-forge -re2 2023.03.02 hd4eee63_0 conda-forge -referencing 0.30.2 pyhd8ed1ab_0 conda-forge -reportlab 4.0.4 py310h8d17308_0 conda-forge -requests 2.31.0 pyhd8ed1ab_0 conda-forge -requests-toolbelt 1.0.0 pyhd8ed1ab_0 conda-forge -requirements-detector 1.2.2 pyhd8ed1ab_0 conda-forge -rfc3339-validator 0.1.4 pyhd8ed1ab_0 conda-forge -rfc3986-validator 0.1.1 pyh9f0ad1d_0 conda-forge -rich 13.5.1 pyhd8ed1ab_0 conda-forge -rlpycairo 0.2.0 pyhd8ed1ab_0 conda-forge -rpds-py 0.9.2 py310h87d50f1_0 conda-forge -ruamel.yaml 0.17.32 py310h8d17308_0 conda-forge -ruamel.yaml.clib 0.2.7 py310h8d17308_1 conda-forge -scalene 1.5.26 pypi_0 pypi -scipy 1.11.2 py310h578b7cb_0 conda-forge -seaborn 0.12.2 hd8ed1ab_0 conda-forge -seaborn-base 0.12.2 pyhd8ed1ab_0 conda-forge -semver 3.0.1 pyhd8ed1ab_0 conda-forge -send2trash 1.8.2 pyh08f2357_0 conda-forge -setoptconf-tmp 0.3.1 pyhd8ed1ab_0 conda-forge -setuptools 58.5.3 py310h5588dad_0 conda-forge -sgp4 2.22 py310h220cb41_0 conda-forge -shellingham 1.5.3 pyhd8ed1ab_0 conda-forge -sip 6.7.11 py310h00ffb61_0 conda-forge -six 1.16.0 pyh6c4a22f_0 conda-forge -skyfield 1.45 pyh1a96a4e_0 conda-forge -skyfield-data 5.0.0 pyhd8ed1ab_0 conda-forge -smmap 3.0.5 pyh44b312d_0 conda-forge -snappy 1.1.10 hfb803bf_0 conda-forge -sniffio 1.3.0 pyhd8ed1ab_0 conda-forge -snowballstemmer 2.2.0 pyhd8ed1ab_0 conda-forge -soupsieve 2.3.2.post1 pyhd8ed1ab_0 conda-forge -sphinx 5.0.2 pyh6c4a22f_0 conda-forge -sphinx-autoapi 2.1.0 pyhd8ed1ab_0 conda-forge -sphinx-book-theme 1.0.1 pyhd8ed1ab_0 conda-forge -sphinx-comments 0.0.3 pyh9f0ad1d_0 conda-forge -sphinx-copybutton 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-design 0.3.0 pyhd8ed1ab_0 conda-forge -sphinx-external-toc 0.3.1 pyhd8ed1ab_1 conda-forge -sphinx-jupyterbook-latex 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-multitoc-numbering 0.1.3 pyhd8ed1ab_0 conda-forge -sphinx-thebe 0.2.1 pyhd8ed1ab_0 conda-forge -sphinx-togglebutton 0.3.2 pyhd8ed1ab_0 conda-forge -sphinxcontrib-applehelp 1.0.7 pyhd8ed1ab_0 conda-forge -sphinxcontrib-bibtex 2.5.0 pyhd8ed1ab_0 conda-forge -sphinxcontrib-devhelp 1.0.5 pyhd8ed1ab_0 conda-forge -sphinxcontrib-dotnetdomain 0.4 py_0 conda-forge -sphinxcontrib-golangdomain 0.2.0.dev0 py_0 conda-forge -sphinxcontrib-htmlhelp 2.0.4 pyhd8ed1ab_0 conda-forge -sphinxcontrib-jsmath 1.0.1 py_0 conda-forge -sphinxcontrib-qthelp 1.0.6 pyhd8ed1ab_0 conda-forge -sphinxcontrib-serializinghtml 1.1.9 pyhd8ed1ab_0 conda-forge -sqlalchemy 1.4.49 py310h8d17308_1 conda-forge -sqlparse 0.4.4 pyhd8ed1ab_0 conda-forge -stack_data 0.6.2 pyhd8ed1ab_0 conda-forge -statsmodels 0.14.0 py310h9b08ddd_1 conda-forge -stringcase 1.2.0 py_0 conda-forge -svglib 1.5.1 pyhd8ed1ab_0 conda-forge -tabulate 0.9.0 pyhd8ed1ab_1 conda-forge -tbb 2021.10.0 h91493d7_0 conda-forge -tectonic 0.12.0 hcab5949_1 conda-forge -tenacity 8.2.3 pyhd8ed1ab_0 conda-forge -terminado 0.17.0 pyh08f2357_0 conda-forge -tinycss2 1.2.1 pyhd8ed1ab_0 conda-forge -tk 8.6.12 h8ffe710_0 conda-forge -toml 0.10.2 pyhd8ed1ab_0 conda-forge -tomli 2.0.1 pyhd8ed1ab_0 conda-forge -tomlkit 0.12.1 pyha770c72_0 conda-forge -toolz 0.12.0 pyhd8ed1ab_0 conda-forge -tornado 6.3.3 py310h8d17308_0 conda-forge -tqdm 4.66.1 pyhd8ed1ab_0 conda-forge -traitlets 5.9.0 pyhd8ed1ab_0 conda-forge -treelib 1.7.0 pyhd8ed1ab_0 conda-forge -typeguard 4.1.5 pyhd8ed1ab_0 conda-forge -typepy 1.3.1 pyhd8ed1ab_0 conda-forge -types-datetimerange 2.0.0.6 pyhd8ed1ab_0 conda-forge -types-python-dateutil 2.8.19.14 pyhd8ed1ab_0 conda-forge -types-pytz 2023.3.1.1 pyhd8ed1ab_0 conda-forge -typing 3.10.0.0 pyhd8ed1ab_0 conda-forge -typing-extensions 4.7.1 hd8ed1ab_0 conda-forge -typing_extensions 4.7.1 pyha770c72_0 conda-forge -typing_inspect 0.9.0 pyhd8ed1ab_0 conda-forge -typing_utils 0.1.0 pyhd8ed1ab_0 conda-forge -tzdata 2023c h71feb2d_0 conda-forge -uc-micro-py 1.0.1 pyhd8ed1ab_0 conda-forge -ucrt 10.0.22621.0 h57928b3_0 conda-forge -unidecode 1.3.6 pyhd8ed1ab_0 conda-forge -uri-template 1.3.0 pyhd8ed1ab_0 conda-forge -urllib3 2.0.4 pyhd8ed1ab_0 conda-forge -validators 0.21.2 pyhd8ed1ab_0 conda-forge -vc 14.3 h64f974e_17 conda-forge -vc14_runtime 14.36.32532 hfdfe4a8_17 conda-forge -vega_datasets 0.9.0 pyhd3deb0d_0 conda-forge -vs2015_runtime 14.36.32532 h05e6639_17 conda-forge -wcwidth 0.2.6 pyhd8ed1ab_0 conda-forge -webcolors 1.13 pyhd8ed1ab_0 conda-forge -webencodings 0.5.1 py_1 conda-forge -websocket-client 1.6.2 pyhd8ed1ab_0 conda-forge -wheel 0.41.2 pyhd8ed1ab_0 conda-forge -widgetsnbextension 4.0.8 pyhd8ed1ab_0 conda-forge -win_inet_pton 1.1.0 pyhd8ed1ab_6 conda-forge -winpty 0.4.3 4 conda-forge -workalendar 17.0.0 pyhd8ed1ab_0 conda-forge -wrapt 1.15.0 py310h8d17308_0 conda-forge -xlsxwriter 3.1.2 pyhd8ed1ab_0 conda-forge -xorg-kbproto 1.0.7 hcd874cb_1002 conda-forge -xorg-libice 1.0.10 hcd874cb_0 conda-forge -xorg-libsm 1.2.3 hcd874cb_1000 conda-forge -xorg-libx11 1.8.4 hcd874cb_0 conda-forge -xorg-libxau 1.0.11 hcd874cb_0 conda-forge -xorg-libxdmcp 1.1.3 hcd874cb_0 conda-forge -xorg-libxext 1.3.4 hcd874cb_2 conda-forge -xorg-libxpm 3.5.16 hcd874cb_0 conda-forge -xorg-libxt 1.3.0 hcd874cb_0 conda-forge -xorg-xextproto 7.3.0 hcd874cb_1003 conda-forge -xorg-xproto 7.0.31 hcd874cb_1007 conda-forge -xz 5.2.6 h8d14728_0 conda-forge -yaml 0.2.5 h8ffe710_2 conda-forge -zeromq 4.3.4 h0e60522_1 conda-forge -zipp 3.16.2 pyhd8ed1ab_0 conda-forge -zlib 1.2.13 hcfcfb64_5 conda-forge -zstandard 0.19.0 py310h0009e47_2 conda-forge -zstd 1.5.2 h12be248_7 conda-forge diff --git a/pyproject.toml b/pyproject.toml index 23778f3..71accd9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "ethos_penalps" -version = "1.0.1" +version = "1.0.2" description = "A package to create energy load curves for industry locations in Germany" readme = "README.md" authors = [ diff --git a/src/ethos_penalps/post_processing/tikz_visualizations/enterprise_graph_builder.py b/src/ethos_penalps/post_processing/tikz_visualizations/enterprise_graph_builder.py index 8da5e58..434e6a6 100644 --- a/src/ethos_penalps/post_processing/tikz_visualizations/enterprise_graph_builder.py +++ b/src/ethos_penalps/post_processing/tikz_visualizations/enterprise_graph_builder.py @@ -1,11 +1,18 @@ import os import subprocess +import tempfile import uuid import webbrowser from dataclasses import dataclass, field import numpy import pandas +from pdf2image import convert_from_path +from pdf2image.exceptions import ( + PDFInfoNotInstalledError, + PDFPageCountError, + PDFSyntaxError, +) from ethos_penalps.network_level import NetworkLevel from ethos_penalps.post_processing.tikz_visualizations.tikz_wrapper import ( @@ -195,12 +202,12 @@ def __post_init__(self): self.list_of_optional_sinks = [] self.list_of_optional_sources = [] self.dictionary_of_tikz_process_node_names: dict[str, str] = {} - self.dictionary_of_tikz_process_node_names[ - self.main_sink.name - ] = self.unique_sink_name - self.dictionary_of_tikz_process_node_names[ - self.main_source.name - ] = self.unique_source_name + self.dictionary_of_tikz_process_node_names[self.main_sink.name] = ( + self.unique_sink_name + ) + self.dictionary_of_tikz_process_node_names[self.main_source.name] = ( + self.unique_source_name + ) for process_node in self.process_chain.process_node_dict.values(): if type(process_node) is ProcessStep: self.sorted_process_step_list.append(process_node) @@ -217,9 +224,9 @@ def __post_init__(self): node_options="ProcessStepNode", ) ) - self.dictionary_of_tikz_process_node_names[ - process_node.name - ] = unique_process_node_name + self.dictionary_of_tikz_process_node_names[process_node.name] = ( + unique_process_node_name + ) self.process_step_chain_length = len(self.sorted_process_step_list) @@ -528,14 +535,11 @@ def compile_pdf(self) -> str: def convert_pdf_to_png(self, path_to_pdf: str) -> str: path_to_png = path_to_pdf[:-4] + ".png" - subprocess.run( - [ - "pdftopng", - path_to_pdf, - path_to_png, - ], - check=True, - ) + 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) return path_to_png def create_enterprise_graph( diff --git a/src/ethos_penalps/post_processing/tikz_visualizations/process_chain_graph_builder.py b/src/ethos_penalps/post_processing/tikz_visualizations/process_chain_graph_builder.py index 226bd34..3745f22 100644 --- a/src/ethos_penalps/post_processing/tikz_visualizations/process_chain_graph_builder.py +++ b/src/ethos_penalps/post_processing/tikz_visualizations/process_chain_graph_builder.py @@ -1,10 +1,12 @@ import os import subprocess +import tempfile import uuid import webbrowser from dataclasses import dataclass, field import pandas +from pdf2image import convert_from_path from ethos_penalps.process_nodes.process_node import ProcessNode from ethos_penalps.process_nodes.process_step import ProcessStep @@ -212,9 +214,9 @@ class PathPortPair: @dataclass class EdgePath: - list_of_edge_node_pairs: list[ - ForwardEdge | BackwardEdge | IntermediateEdge - ] = field(default_factory=list) + list_of_edge_node_pairs: list[ForwardEdge | BackwardEdge | IntermediateEdge] = ( + field(default_factory=list) + ) @dataclass @@ -480,9 +482,9 @@ def add_junction(self, path_junction: PathJunction): self.list_of_initial_junction_state_connectors.append( path_junction.state_connector ) - self.dict_of_all_juctions[ - path_junction.state_path_connector - ] = path_junction + self.dict_of_all_juctions[path_junction.state_path_connector] = ( + path_junction + ) self.list_of_remaining_junctions.append(path_junction) def pop_path_junction(self) -> PathJunction: @@ -517,9 +519,9 @@ def __init__( display_names_dict, ) -> None: self.state_path_handler: StatePathHandler = StatePathHandler() - self.unique_process_state_names: dict[ - str, dict[str, str] | str - ] = unique_process_state_names + self.unique_process_state_names: dict[str, dict[str, str] | str] = ( + unique_process_state_names + ) self.display_names_dict: dict[str, dict[str, str] | str] = display_names_dict self.process_state_handler: ProcessStateHandler = process_state_handler self.output_stream_state_name = ( @@ -1089,9 +1091,9 @@ def create_unique_node_names(self): if isinstance(process_node, ProcessStep): unique_process_node_name = process_node_name.replace(" ", "-") unique_process_node_name = unique_process_node_name.replace("_", "-") - self.unique_process_node_name_dict[ - process_node_name - ] = unique_process_node_name + self.unique_process_node_name_dict[process_node_name] = ( + unique_process_node_name + ) self.unique_process_state_names[process_node_name] = {} for ( process_state_name @@ -1106,9 +1108,9 @@ def create_unique_node_names(self): elif isinstance(process_node, (Source, Sink)): unique_process_node_name = process_node_name.replace(" ", "") unique_process_node_name = unique_process_node_name.replace("_", "-") - self.unique_process_node_name_dict[ - process_node_name - ] = unique_process_node_name + self.unique_process_node_name_dict[process_node_name] = ( + unique_process_node_name + ) def create_display_names(self): """Remove whitespaces from node names""" @@ -1261,14 +1263,12 @@ def compile_pdf(self) -> str: def convert_pdf_to_png(self, path_to_pdf: str) -> str: path_to_png = path_to_pdf[:-4] + ".png" - subprocess.run( - [ - "pdftopng", - path_to_pdf, - path_to_png, - ], - check=True, - ) + 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) return path_to_png def create_enterprise_graph( From 8f3c697f538b2cf5d4524e8f9407d5f83d1a0259 Mon Sep 17 00:00:00 2001 From: Julian Belina Date: Wed, 21 Feb 2024 19:25:46 +0100 Subject: [PATCH 3/8] Update github ci --- .github/workflows/test.yml | 42 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index af25bc5..7035282 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,22 +1,28 @@ name: Run ETHOS.PeNALPS Tests -on: - push: - branches: main - branches: dev - pull_request: - branches: main - branches: dev - # Allows to trigger the workflow manually - workflow_dispatch: - branches: main - schedule: - # * is a special character in YAML so you have to quote this string - # Some Examples for cron syntax https://crontab.guru/examples.html - # Schedules job at any point after 12 pm - - cron: '0 0 * * *' - # Weekly after sunday - # - cron: 0 0 * * 0 - +on: + workflow_dispatch: + inputs: + tags: + description: 'Manual run' + push: + branches: + - main + - dev + pull_request: + branches: + - main + - dev + # Allows to trigger the workflow manually + + schedule: + # * is a special character in YAML so you have to quote this string + # Some Examples for cron syntax https://crontab.guru/examples.html + # Schedules job at any point after 12 pm + - cron: '0 0 * * *' + # Weekly after sunday + # - cron: 0 0 * * 0 + + jobs: TestPeNALPSCondaForge: name: Ex1 (${{ matrix.python-version }}, ${{ matrix.os }}) From 20fd154e1484f5ef16368dca6c8301a76c599980 Mon Sep 17 00:00:00 2001 From: "j.belina" Date: Mon, 26 Feb 2024 14:38:28 +0100 Subject: [PATCH 4/8] Update readme and paper2 --- ethos | 369 -------------------------------------- ittsc_lpg | 473 ------------------------------------------------- paper/paper.md | 3 + 3 files changed, 3 insertions(+), 842 deletions(-) delete mode 100644 ethos delete mode 100644 ittsc_lpg diff --git a/ethos b/ethos deleted file mode 100644 index 5a9835d..0000000 --- a/ethos +++ /dev/null @@ -1,369 +0,0 @@ -accessible-pygments 0.0.4 pyhd8ed1ab_0 conda-forge -alabaster 0.7.16 pyhd8ed1ab_0 conda-forge -altair 4.2.2 pyhd8ed1ab_0 conda-forge -asgiref 3.7.2 pyhd8ed1ab_0 conda-forge -astroid 2.15.8 py310h5588dad_0 conda-forge -asttokens 2.4.1 pyhd8ed1ab_0 conda-forge -attrs 23.2.0 pyh71513ae_0 conda-forge -aws-c-auth 0.7.14 ha04060b_3 conda-forge -aws-c-cal 0.6.9 hd33547d_3 conda-forge -aws-c-common 0.9.12 hcfcfb64_0 conda-forge -aws-c-compression 0.2.17 hd33547d_8 conda-forge -aws-c-event-stream 0.4.1 hf127292_5 conda-forge -aws-c-http 0.8.0 h0cc4be6_5 conda-forge -aws-c-io 0.14.3 hf372335_1 conda-forge -aws-c-mqtt 0.10.1 h189e261_3 conda-forge -aws-c-s3 0.5.0 h623a383_1 conda-forge -aws-c-sdkutils 0.1.14 hd33547d_0 conda-forge -aws-checksums 0.1.17 hd33547d_7 conda-forge -aws-crt-cpp 0.26.1 hb490d84_8 conda-forge -aws-sdk-cpp 1.11.210 hf72cfbd_11 conda-forge -babel 2.14.0 pyhd8ed1ab_0 conda-forge -backoff 1.11.1 pyhd8ed1ab_0 conda-forge -backports.zoneinfo 0.2.1 py310h5588dad_8 conda-forge -beautifulsoup4 4.12.3 pyha770c72_0 conda-forge -black 24.1.1 py310h5588dad_0 conda-forge -bleach 6.1.0 pyhd8ed1ab_0 conda-forge -boltons 21.0.0 pyhd8ed1ab_0 conda-forge -brotli-python 1.1.0 py310h00ffb61_1 conda-forge -bzip2 1.0.8 hcfcfb64_5 conda-forge -c-ares 1.26.0 hcfcfb64_0 conda-forge -ca-certificates 2024.2.2 h56e8100_0 conda-forge -cairo 1.18.0 h1fef639_0 conda-forge -certifi 2024.2.2 pyhd8ed1ab_0 conda-forge -chardet 5.2.0 py310h5588dad_1 conda-forge -charset-normalizer 3.3.2 pyhd8ed1ab_0 conda-forge -cheroot 9.0.0 pyhd8ed1ab_0 conda-forge -click 8.1.7 win_pyh7428d3b_0 conda-forge -click-spinner 0.1.10 pyh9f0ad1d_0 conda-forge -cloudpickle 3.0.0 pyhd8ed1ab_0 conda-forge -colorama 0.4.6 pyhd8ed1ab_0 conda-forge -colorlog 6.8.2 py310h5588dad_0 conda-forge -comm 0.2.1 pyhd8ed1ab_0 conda-forge -convertdate 2.4.0 pyhd8ed1ab_0 conda-forge -cssselect2 0.2.1 pyh9f0ad1d_1 conda-forge -cycler 0.12.1 pyhd8ed1ab_0 conda-forge -dacite 1.8.1 pyhd8ed1ab_0 conda-forge -dataclasses 0.8 pyhc8e2a94_3 conda-forge -dataclasses-json 0.6.4 pyhd8ed1ab_0 conda-forge -datapane 0.16.7 pyhd8ed1ab_0 conda-forge -datetimerange 2.2.0 pyhd8ed1ab_0 conda-forge -debugpy 1.8.1 py310h00ffb61_0 conda-forge -decorator 5.1.1 pyhd8ed1ab_0 conda-forge -defusedxml 0.7.1 pyhd8ed1ab_0 conda-forge -dill 0.3.8 pyhd8ed1ab_0 conda-forge -django 4.2.8 pyhd8ed1ab_0 conda-forge -docutils 0.18.1 py310h5588dad_1 conda-forge -dodgy 0.2.1 py_0 conda-forge -dominate 2.9.1 pyhd8ed1ab_0 conda-forge -dulwich 0.21.7 py310h8d17308_0 conda-forge -entrypoints 0.4 pyhd8ed1ab_0 conda-forge -et_xmlfile 1.1.0 pyhd8ed1ab_0 conda-forge -ethos-penalps 1.0.1 pypi_0 pypi -exceptiongroup 1.2.0 pyhd8ed1ab_2 conda-forge -executing 2.0.1 pyhd8ed1ab_0 conda-forge -expat 2.5.0 h63175ca_1 conda-forge -face 20.1.1 py_0 conda-forge -flake8 5.0.4 pyhd8ed1ab_0 conda-forge -flake8-polyfill 1.0.2 py_0 conda-forge -font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge -font-ttf-inconsolata 3.000 h77eed37_0 conda-forge -font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge -font-ttf-ubuntu 0.83 h77eed37_1 conda-forge -fontconfig 2.14.2 hbde0cde_0 conda-forge -fonts-conda-ecosystem 1 0 conda-forge -fonts-conda-forge 1 0 conda-forge -freetype 2.12.1 hdaf720e_2 conda-forge -freetype-py 2.3.0 pyhd8ed1ab_0 conda-forge -freezegun 1.4.0 pyhd8ed1ab_0 conda-forge -fribidi 1.0.10 h8d14728_0 conda-forge -furl 2.1.3 pyhd8ed1ab_0 conda-forge -getopt-win32 0.1 hcfcfb64_1 conda-forge -gettext 0.21.1 h5728263_0 conda-forge -gitdb 4.0.11 pyhd8ed1ab_0 conda-forge -gitpython 3.1.41 pyhd8ed1ab_0 conda-forge -glib 2.78.3 h12be248_0 conda-forge -glib-tools 2.78.3 h12be248_0 conda-forge -glom 23.5.0 pyhd8ed1ab_0 conda-forge -graphite2 1.3.13 1000 conda-forge -graphviz 9.0.0 h51cb2cd_1 conda-forge -greenlet 3.0.3 py310h00ffb61_0 conda-forge -gst-plugins-base 1.22.9 h001b923_0 conda-forge -gstreamer 1.22.9 hb4038d2_0 conda-forge -gts 0.7.6 h6b5321d_4 conda-forge -harfbuzz 8.3.0 h7ab893a_0 conda-forge -icu 73.2 h63175ca_0 conda-forge -idna 3.6 pyhd8ed1ab_0 conda-forge -imagesize 1.4.1 pyhd8ed1ab_0 conda-forge -importlib-metadata 7.0.1 pyha770c72_0 conda-forge -importlib_metadata 7.0.1 hd8ed1ab_0 conda-forge -importlib_resources 5.13.0 pyhd8ed1ab_0 conda-forge -iniconfig 2.0.0 pyhd8ed1ab_0 conda-forge -intel-openmp 2024.0.0 h57928b3_49841 conda-forge -ipykernel 6.29.2 pyha63f2e9_0 conda-forge -ipynbname 2023.2.0.0 pyhd8ed1ab_0 conda-forge -ipython 8.18.1 pyh7428d3b_3 conda-forge -isort 5.13.2 pyhd8ed1ab_0 conda-forge -jaraco.functools 4.0.0 pyhd8ed1ab_0 conda-forge -jedi 0.19.1 pyhd8ed1ab_0 conda-forge -jinja2 3.1.3 pyhd8ed1ab_0 conda-forge -jplephem 2.21 pyh864a33b_0 conda-forge -jsonpickle 3.0.2 pyhd8ed1ab_1 conda-forge -jsonschema 4.21.1 pyhd8ed1ab_0 conda-forge -jsonschema-specifications 2023.12.1 pyhd8ed1ab_0 conda-forge -jupyter-book 0.15.1 pyhd8ed1ab_0 conda-forge -jupyter-cache 0.6.1 pyhd8ed1ab_0 conda-forge -jupyter_client 8.6.0 pyhd8ed1ab_0 conda-forge -jupyter_core 5.7.1 py310h5588dad_0 conda-forge -jupyterlab_pygments 0.3.0 pyhd8ed1ab_1 conda-forge -kiwisolver 1.4.5 py310h232114e_1 conda-forge -krb5 1.21.2 heb0366b_0 conda-forge -latexcodec 2.0.1 pyh9f0ad1d_0 conda-forge -lazy-object-proxy 1.10.0 py310h8d17308_0 conda-forge -lcms2 2.16 h67d730c_0 conda-forge -lerc 4.0.0 h63175ca_0 conda-forge -libabseil 20230802.1 cxx17_h63175ca_0 conda-forge -libarrow 10.0.1 h1138768_57_cpu conda-forge -libblas 3.9.0 21_win64_mkl conda-forge -libbrotlicommon 1.1.0 hcfcfb64_1 conda-forge -libbrotlidec 1.1.0 hcfcfb64_1 conda-forge -libbrotlienc 1.1.0 hcfcfb64_1 conda-forge -libcblas 3.9.0 21_win64_mkl conda-forge -libclang 15.0.7 default_hde6756a_4 conda-forge -libclang13 15.0.7 default_h85b4d89_4 conda-forge -libcrc32c 1.1.2 h0e60522_0 conda-forge -libcurl 8.5.0 hd5e4a3a_0 conda-forge -libdeflate 1.19 hcfcfb64_0 conda-forge -libevent 2.1.12 h3671451_1 conda-forge -libexpat 2.5.0 h63175ca_1 conda-forge -libffi 3.4.2 h8ffe710_5 conda-forge -libgd 2.3.3 h312136b_9 conda-forge -libglib 2.78.3 h16e383f_0 conda-forge -libgoogle-cloud 2.12.0 h39f2fc6_4 conda-forge -libgrpc 1.59.3 h5bbd4a7_0 conda-forge -libhwloc 2.9.3 default_haede6df_1009 conda-forge -libiconv 1.17 hcfcfb64_2 conda-forge -libjpeg-turbo 3.0.0 hcfcfb64_1 conda-forge -liblapack 3.9.0 21_win64_mkl conda-forge -libogg 1.3.4 h8ffe710_1 conda-forge -libpng 1.6.42 h19919ed_0 conda-forge -libprotobuf 4.24.4 hb8276f3_0 conda-forge -libre2-11 2023.06.02 h8c5ae5e_0 conda-forge -libsodium 1.0.18 h8d14728_1 conda-forge -libsqlite 3.44.2 hcfcfb64_0 conda-forge -libssh2 1.11.0 h7dfc565_0 conda-forge -libthrift 0.19.0 ha2b3283_1 conda-forge -libtiff 4.6.0 h6e2ebb7_2 conda-forge -libutf8proc 2.8.0 h82a8f57_0 conda-forge -libvorbis 1.3.7 h0e60522_0 conda-forge -libwebp 1.3.2 hcfcfb64_1 conda-forge -libwebp-base 1.3.2 hcfcfb64_0 conda-forge -libxcb 1.15 hcd874cb_0 conda-forge -libxml2 2.12.5 hc3477c8_0 conda-forge -libxslt 1.1.39 h3df6e99_0 conda-forge -libzlib 1.2.13 hcfcfb64_5 conda-forge -linkify-it-py 2.0.3 pyhd8ed1ab_0 conda-forge -logilab-common 1.7.3 py_0 conda-forge -lunardate 0.2.0 py_0 conda-forge -lxml 4.9.4 py310hba208d0_0 conda-forge -lz4-c 1.9.4 hcfcfb64_0 conda-forge -m2w64-gcc-libgfortran 5.3.0 6 conda-forge -m2w64-gcc-libs 5.3.0 7 conda-forge -m2w64-gcc-libs-core 5.3.0 7 conda-forge -m2w64-gmp 6.1.0 2 conda-forge -m2w64-libwinpthread-git 5.0.0.4634.697f757 2 conda-forge -markdown-it-py 2.2.0 pyhd8ed1ab_0 conda-forge -markupsafe 2.1.5 py310h8d17308_0 conda-forge -marshmallow 3.20.2 pyhd8ed1ab_0 conda-forge -matplotlib 3.4.3 py310h5588dad_2 conda-forge -matplotlib-base 3.4.3 py310h79a7439_2 conda-forge -matplotlib-inline 0.1.6 pyhd8ed1ab_0 conda-forge -mbstrdecoder 1.1.3 pyhd8ed1ab_1 conda-forge -mccabe 0.7.0 pyhd8ed1ab_0 conda-forge -mdit-py-plugins 0.4.0 pyhd8ed1ab_0 conda-forge -mdurl 0.1.2 pyhd8ed1ab_0 conda-forge -micawber 0.5.5 pyhd8ed1ab_0 conda-forge -mistune 3.0.2 pyhd8ed1ab_0 conda-forge -mkl 2024.0.0 h66d3029_49657 conda-forge -monotonic 1.5 py_0 conda-forge -more-itertools 10.2.0 pyhd8ed1ab_0 conda-forge -msys2-conda-epoch 20160418 1 conda-forge -multimethod 1.9.1 pyhd8ed1ab_0 conda-forge -mypy_extensions 1.0.0 pyha770c72_0 conda-forge -myst-nb 0.17.2 pyhd8ed1ab_0 conda-forge -myst-parser 0.18.1 pyhd8ed1ab_0 conda-forge -nbclient 0.7.4 pyhd8ed1ab_0 conda-forge -nbconvert 7.16.0 pyhd8ed1ab_0 conda-forge -nbconvert-core 7.16.0 pyhd8ed1ab_0 conda-forge -nbconvert-pandoc 7.16.0 pyhd8ed1ab_0 conda-forge -nbformat 5.9.2 pyhd8ed1ab_0 conda-forge -nest-asyncio 1.6.0 pyhd8ed1ab_0 conda-forge -networkx 3.2 pyhd8ed1ab_0 conda-forge -numpy 1.23.5 py310h4a8f9c9_0 conda-forge -openjpeg 2.5.0 h3d672ee_3 conda-forge -openpyxl 3.1.2 py310h8d17308_1 conda-forge -openssl 3.2.1 hcfcfb64_0 conda-forge -orc 1.9.2 hf0b6bd4_0 conda-forge -orderedmultidict 1.0.1 pyhd8ed1ab_1 conda-forge -packaging 23.2 pyhd8ed1ab_0 conda-forge -pandas 1.5.3 py310h1c4a608_1 conda-forge -pandoc 3.1.3 h57928b3_0 conda-forge -pandocfilters 1.5.0 pyhd8ed1ab_0 conda-forge -pango 1.50.14 h07c897b_2 conda-forge -parso 0.8.3 pyhd8ed1ab_0 conda-forge -pathspec 0.12.1 pyhd8ed1ab_0 conda-forge -patsy 0.5.6 pyhd8ed1ab_0 conda-forge -pcre2 10.42 h17e33f8_0 conda-forge -pdftopng 0.2.3 py310hc893b6f_14 conda-forge -pep8-naming 0.10.0 pyh9f0ad1d_0 conda-forge -pickleshare 0.7.5 py_1003 conda-forge -pillow 10.2.0 py310h1e6a543_0 conda-forge -pint 0.23 pyhd8ed1ab_0 conda-forge -pip 24.0 pyhd8ed1ab_0 conda-forge -pixman 0.43.2 h63175ca_0 conda-forge -pkgutil-resolve-name 1.3.10 pyhd8ed1ab_1 conda-forge -platformdirs 4.2.0 pyhd8ed1ab_0 conda-forge -plotly 5.18.0 pyhd8ed1ab_0 conda-forge -pluggy 1.4.0 pyhd8ed1ab_0 conda-forge -ply 3.11 py_1 conda-forge -poppler 23.12.0 hc2f3c52_0 conda-forge -poppler-data 0.4.12 hd8ed1ab_0 conda-forge -posthog 2.4.2 pyhd8ed1ab_0 conda-forge -prompt-toolkit 3.0.42 pyha770c72_0 conda-forge -proplot 0.9.7 pyhd8ed1ab_0 conda-forge -prospector 1.10.3 pyhd8ed1ab_0 conda-forge -psutil 5.9.8 py310h8d17308_0 conda-forge -pthread-stubs 0.4 hcd874cb_1001 conda-forge -pthreads-win32 2.9.1 hfa6e2cd_3 conda-forge -pure_eval 0.2.2 pyhd8ed1ab_0 conda-forge -pyarrow 10.0.1 py310hd0bb7c2_57_cpu conda-forge -pybtex 0.24.0 pyhd8ed1ab_2 conda-forge -pybtex-docutils 1.0.3 py310h5588dad_1 conda-forge -pycairo 1.25.1 py310h42c1a3e_0 conda-forge -pycodestyle 2.9.1 pyhd8ed1ab_0 conda-forge -pydantic 1.10.13 py310h8d17308_1 conda-forge -pydata-sphinx-theme 0.15.2 pyhd8ed1ab_0 conda-forge -pydocstyle 6.3.0 pyhd8ed1ab_0 conda-forge -pyflakes 2.5.0 pyhd8ed1ab_0 conda-forge -pygments 2.17.2 pyhd8ed1ab_0 conda-forge -pylint 2.17.7 pyhd8ed1ab_0 conda-forge -pylint-celery 0.3 py_1 conda-forge -pylint-django 2.5.3 pyhd8ed1ab_0 conda-forge -pylint-flask 0.6 py_0 conda-forge -pylint-plugin-utils 0.7 pyhd8ed1ab_0 conda-forge -pyluach 2.2.0 pyhd8ed1ab_0 conda-forge -pymeeus 0.5.12 pyhd8ed1ab_0 conda-forge -pyngrok 5.2.2 pyhd8ed1ab_0 conda-forge -pyparsing 3.1.1 pyhd8ed1ab_0 conda-forge -pyqt 5.15.9 py310h1fd54f2_5 conda-forge -pyqt5-sip 12.12.2 py310h00ffb61_5 conda-forge -pysocks 1.7.1 pyh0701188_6 conda-forge -pytest 8.0.0 pyhd8ed1ab_0 conda-forge -python 3.10.13 h4de0772_1_cpython conda-forge -python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge -python-fastjsonschema 2.19.1 pyhd8ed1ab_0 conda-forge -python-graphviz 0.20.1 pyh22cad53_0 conda-forge -python_abi 3.10 4_cp310 conda-forge -pytz 2024.1 pyhd8ed1ab_0 conda-forge -pywin32 306 py310h00ffb61_2 conda-forge -pyyaml 6.0.1 py310h8d17308_1 conda-forge -pyzmq 25.1.2 py310h2849c00_0 conda-forge -qt-main 5.15.8 h9e85ed6_18 conda-forge -re2 2023.06.02 hcbb65ff_0 conda-forge -referencing 0.33.0 pyhd8ed1ab_0 conda-forge -reportlab 4.1.0 py310h8d17308_0 conda-forge -requests 2.31.0 pyhd8ed1ab_0 conda-forge -requests-toolbelt 1.0.0 pyhd8ed1ab_0 conda-forge -requirements-detector 1.2.2 pyhd8ed1ab_0 conda-forge -rlpycairo 0.2.0 pyhd8ed1ab_0 conda-forge -rpds-py 0.17.1 py310h87d50f1_0 conda-forge -scipy 1.12.0 py310hf667824_2 conda-forge -seaborn 0.13.2 hd8ed1ab_0 conda-forge -seaborn-base 0.13.2 pyhd8ed1ab_0 conda-forge -semver 3.0.2 pyhd8ed1ab_0 conda-forge -setoptconf-tmp 0.3.1 pyhd8ed1ab_0 conda-forge -setuptools 69.0.3 pyhd8ed1ab_0 conda-forge -sgp4 2.22 py310h220cb41_0 conda-forge -sip 6.7.12 py310h00ffb61_0 conda-forge -six 1.16.0 pyh6c4a22f_0 conda-forge -skyfield 1.45 pyh1a96a4e_0 conda-forge -skyfield-data 5.0.0 pyhd8ed1ab_0 conda-forge -smmap 5.0.0 pyhd8ed1ab_0 conda-forge -snappy 1.1.10 hfb803bf_0 conda-forge -snowballstemmer 2.2.0 pyhd8ed1ab_0 conda-forge -soupsieve 2.5 pyhd8ed1ab_1 conda-forge -sphinx 5.0.2 pyh6c4a22f_0 conda-forge -sphinx-autoapi 2.1.0 pyhd8ed1ab_0 conda-forge -sphinx-book-theme 1.0.1 pyhd8ed1ab_0 conda-forge -sphinx-comments 0.0.3 pyh9f0ad1d_0 conda-forge -sphinx-copybutton 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-design 0.3.0 pyhd8ed1ab_0 conda-forge -sphinx-external-toc 0.3.1 pyhd8ed1ab_1 conda-forge -sphinx-jupyterbook-latex 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-multitoc-numbering 0.1.3 pyhd8ed1ab_0 conda-forge -sphinx-thebe 0.2.1 pyhd8ed1ab_0 conda-forge -sphinx-togglebutton 0.3.2 pyhd8ed1ab_0 conda-forge -sphinxcontrib-applehelp 1.0.8 pyhd8ed1ab_0 conda-forge -sphinxcontrib-bibtex 2.5.0 pyhd8ed1ab_0 conda-forge -sphinxcontrib-devhelp 1.0.6 pyhd8ed1ab_0 conda-forge -sphinxcontrib-dotnetdomain 0.4 py_0 conda-forge -sphinxcontrib-golangdomain 0.2.0.dev0 py_0 conda-forge -sphinxcontrib-htmlhelp 2.0.5 pyhd8ed1ab_0 conda-forge -sphinxcontrib-jsmath 1.0.1 pyhd8ed1ab_0 conda-forge -sphinxcontrib-qthelp 1.0.7 pyhd8ed1ab_0 conda-forge -sphinxcontrib-serializinghtml 1.1.10 pyhd8ed1ab_0 conda-forge -sqlalchemy 1.4.49 py310h8d17308_1 conda-forge -sqlparse 0.4.4 pyhd8ed1ab_0 conda-forge -stack_data 0.6.2 pyhd8ed1ab_0 conda-forge -statsmodels 0.14.1 py310h3e78b6c_0 conda-forge -svglib 1.5.1 pyhd8ed1ab_0 conda-forge -tabulate 0.9.0 pyhd8ed1ab_1 conda-forge -tbb 2021.11.0 h91493d7_1 conda-forge -tectonic 0.15.0 h1cbdeb6_0 conda-forge -tenacity 8.2.3 pyhd8ed1ab_0 conda-forge -tinycss2 1.2.1 pyhd8ed1ab_0 conda-forge -tk 8.6.13 h5226925_1 conda-forge -toml 0.10.2 pyhd8ed1ab_0 conda-forge -tomli 2.0.1 pyhd8ed1ab_0 conda-forge -tomlkit 0.12.3 pyha770c72_0 conda-forge -toolz 0.12.1 pyhd8ed1ab_0 conda-forge -tornado 6.3.3 py310h8d17308_1 conda-forge -traitlets 5.14.1 pyhd8ed1ab_0 conda-forge -typeguard 4.1.5 pyhd8ed1ab_1 conda-forge -typepy 1.3.2 pyhd8ed1ab_0 conda-forge -typing-extensions 4.9.0 hd8ed1ab_0 conda-forge -typing_extensions 4.9.0 pyha770c72_0 conda-forge -typing_inspect 0.9.0 pyhd8ed1ab_0 conda-forge -tzdata 2024a h0c530f3_0 conda-forge -uc-micro-py 1.0.2 pyhd8ed1ab_0 conda-forge -ucrt 10.0.22621.0 h57928b3_0 conda-forge -unidecode 1.3.8 pyhd8ed1ab_0 conda-forge -urllib3 2.2.0 pyhd8ed1ab_0 conda-forge -vc 14.3 hcf57466_18 conda-forge -vc14_runtime 14.38.33130 h82b7239_18 conda-forge -vega_datasets 0.9.0 pyhd3deb0d_0 conda-forge -vs2015_runtime 14.38.33130 hcb4865c_18 conda-forge -wcwidth 0.2.13 pyhd8ed1ab_0 conda-forge -webencodings 0.5.1 pyhd8ed1ab_2 conda-forge -wheel 0.42.0 pyhd8ed1ab_0 conda-forge -win_inet_pton 1.1.0 pyhd8ed1ab_6 conda-forge -workalendar 17.0.0 pyhd8ed1ab_0 conda-forge -wrapt 1.16.0 py310h8d17308_0 conda-forge -xlsxwriter 3.1.9 pyhd8ed1ab_0 conda-forge -xorg-kbproto 1.0.7 hcd874cb_1002 conda-forge -xorg-libice 1.1.1 hcd874cb_0 conda-forge -xorg-libsm 1.2.4 hcd874cb_0 conda-forge -xorg-libx11 1.8.7 hefa74cf_0 conda-forge -xorg-libxau 1.0.11 hcd874cb_0 conda-forge -xorg-libxdmcp 1.1.3 hcd874cb_0 conda-forge -xorg-libxext 1.3.4 hcd874cb_2 conda-forge -xorg-libxpm 3.5.17 hcd874cb_0 conda-forge -xorg-libxt 1.3.0 hcd874cb_1 conda-forge -xorg-xextproto 7.3.0 hcd874cb_1003 conda-forge -xorg-xproto 7.0.31 hcd874cb_1007 conda-forge -xz 5.2.6 h8d14728_0 conda-forge -yaml 0.2.5 h8ffe710_2 conda-forge -zeromq 4.3.5 h63175ca_0 conda-forge -zipp 3.17.0 pyhd8ed1ab_0 conda-forge -zlib 1.2.13 hcfcfb64_5 conda-forge -zstd 1.5.5 h12be248_0 conda-forge \ No newline at end of file diff --git a/ittsc_lpg b/ittsc_lpg deleted file mode 100644 index 2e22db3..0000000 --- a/ittsc_lpg +++ /dev/null @@ -1,473 +0,0 @@ -accessible-pygments 0.0.4 pyhd8ed1ab_0 conda-forge -alabaster 0.7.13 pyhd8ed1ab_0 conda-forge -altair 4.2.2 pyhd8ed1ab_0 conda-forge -anyio 3.7.1 pyhd8ed1ab_0 conda-forge -appdirs 1.4.4 pyh9f0ad1d_0 conda-forge -argon2-cffi 23.1.0 pyhd8ed1ab_0 conda-forge -argon2-cffi-bindings 21.2.0 py310h8d17308_3 conda-forge -arrow 1.2.3 pyhd8ed1ab_0 conda-forge -arrow-cpp 10.0.1 h57928b3_24_cpu conda-forge -asgiref 3.7.2 pyhd8ed1ab_0 conda-forge -astroid 2.15.6 py310h5588dad_0 conda-forge -astropy 5.3.2 py310h9b08ddd_0 conda-forge -asttokens 2.2.1 pyhd8ed1ab_0 conda-forge -astunparse 1.6.3 pyhd8ed1ab_0 conda-forge -async-lru 2.0.4 pyhd8ed1ab_0 conda-forge -attrs 23.1.0 pyh71513ae_1 conda-forge -aws-c-auth 0.7.0 h6f3c987_2 conda-forge -aws-c-cal 0.6.0 h6ba3258_0 conda-forge -aws-c-common 0.8.23 hcfcfb64_0 conda-forge -aws-c-compression 0.2.17 h420beca_1 conda-forge -aws-c-event-stream 0.3.1 had47b81_1 conda-forge -aws-c-http 0.7.11 h72ba615_0 conda-forge -aws-c-io 0.13.28 ha35c040_0 conda-forge -aws-c-mqtt 0.8.14 h4941efa_2 conda-forge -aws-c-s3 0.3.13 he04eaa7_2 conda-forge -aws-c-sdkutils 0.1.11 h420beca_1 conda-forge -aws-checksums 0.1.16 h420beca_1 conda-forge -aws-crt-cpp 0.20.3 h247a981_4 conda-forge -aws-sdk-cpp 1.10.57 h1a0519f_17 conda-forge -babel 2.12.1 pyhd8ed1ab_1 conda-forge -backcall 0.2.0 pyh9f0ad1d_0 conda-forge -backoff 1.11.1 pyhd8ed1ab_0 conda-forge -backports 1.0 pyhd8ed1ab_3 conda-forge -backports.functools_lru_cache 1.6.5 pyhd8ed1ab_0 conda-forge -backports.zoneinfo 0.2.1 py310h5588dad_7 conda-forge -beautifulsoup4 4.12.2 pyha770c72_0 conda-forge -black 23.7.0 py310h5588dad_1 conda-forge -bleach 6.0.0 pyhd8ed1ab_0 conda-forge -boltons 21.0.0 pyhd8ed1ab_0 conda-forge -boost-cpp 1.78.0 h9f4b32c_4 conda-forge -brotli-python 1.0.9 py310h00ffb61_9 conda-forge -brotlipy 0.7.0 py310h8d17308_1005 conda-forge -bzip2 1.0.8 h8ffe710_4 conda-forge -c-ares 1.19.1 hcfcfb64_0 conda-forge -ca-certificates 2023.11.17 h56e8100_0 conda-forge -cached-property 1.5.2 hd8ed1ab_1 conda-forge -cached_property 1.5.2 pyha770c72_1 conda-forge -cairo 1.16.0 hd694305_1014 conda-forge -certifi 2023.11.17 pyhd8ed1ab_0 conda-forge -cffi 1.15.1 py310h628cb3f_3 conda-forge -chardet 5.2.0 py310h5588dad_0 conda-forge -charset-normalizer 3.2.0 pyhd8ed1ab_0 conda-forge -cheroot 9.0.0 pyhd8ed1ab_0 conda-forge -click 8.1.7 win_pyh7428d3b_0 conda-forge -click-completion 0.5.2 py310h5588dad_5 conda-forge -click-default-group 1.2.4 pyhd8ed1ab_0 conda-forge -click-log 0.4.0 pyhd8ed1ab_0 conda-forge -click-spinner 0.1.10 pyh9f0ad1d_0 conda-forge -cloudpickle 2.2.1 pyhd8ed1ab_0 conda-forge -colorama 0.4.6 pyhd8ed1ab_0 conda-forge -colorlog 6.7.0 py310h5588dad_1 conda-forge -comm 0.1.4 pyhd8ed1ab_0 conda-forge -commonmark 0.9.1 py_0 conda-forge -conda 23.1.0 py310h5588dad_0 conda-forge -conda-package-handling 2.2.0 pyh38be061_0 conda-forge -conda-package-streaming 0.9.0 pyhd8ed1ab_0 conda-forge -conda-tree 1.1.0 pyhd8ed1ab_2 conda-forge -convertdate 2.4.0 pyhd8ed1ab_0 conda-forge -cryptography 41.0.3 py310h6e82f81_0 conda-forge -cssselect2 0.2.1 pyh9f0ad1d_1 conda-forge -cycler 0.11.0 pyhd8ed1ab_0 conda-forge -dacite 1.8.0 pyhd8ed1ab_0 conda-forge -dataclass-wizard 0.22.0 pyhd8ed1ab_0 conda-forge -dataclasses 0.8 pyhc8e2a94_3 conda-forge -dataclasses-json 0.5.7 pyhd8ed1ab_0 conda-forge -datapane 0.16.7 pyhd8ed1ab_0 conda-forge -datetimerange 1.2.0 pyhd8ed1ab_0 conda-forge -debugpy 1.6.8 py310h00ffb61_0 conda-forge -decorator 5.1.1 pyhd8ed1ab_0 conda-forge -defusedxml 0.7.1 pyhd8ed1ab_0 conda-forge -dill 0.3.7 pyhd8ed1ab_0 conda-forge -django 4.2.4 pyhd8ed1ab_0 conda-forge -docutils 0.18.1 py310h5588dad_1 conda-forge -dodgy 0.2.1 py_0 conda-forge -dominate 2.8.0 pyhd8ed1ab_0 conda-forge -dulwich 0.21.5 py310h8d17308_0 conda-forge -entrypoints 0.4 pyhd8ed1ab_0 conda-forge -et_xmlfile 1.1.0 pyhd8ed1ab_0 conda-forge -ethos-penalps 1.0.1 pypi_0 pypi -exceptiongroup 1.1.3 pyhd8ed1ab_0 conda-forge -executing 1.2.0 pyhd8ed1ab_0 conda-forge -expat 2.5.0 h63175ca_1 conda-forge -face 20.1.1 py_0 conda-forge -flake8 5.0.4 pyhd8ed1ab_0 conda-forge -flake8-polyfill 1.0.2 py_0 conda-forge -flit-core 3.9.0 pyhd8ed1ab_0 conda-forge -font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge -font-ttf-inconsolata 3.000 h77eed37_0 conda-forge -font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge -font-ttf-ubuntu 0.83 hab24e00_0 conda-forge -fontconfig 2.14.2 hbde0cde_0 conda-forge -fonts-conda-ecosystem 1 0 conda-forge -fonts-conda-forge 1 0 conda-forge -fqdn 1.5.1 pyhd8ed1ab_0 conda-forge -freetype 2.12.1 h546665d_1 conda-forge -freetype-py 2.3.0 pyhd8ed1ab_0 conda-forge -freezegun 1.2.2 pyhd8ed1ab_0 conda-forge -fribidi 1.0.10 h8d14728_0 conda-forge -furl 2.1.3 pyhd8ed1ab_0 conda-forge -future 0.18.3 pyhd8ed1ab_0 conda-forge -getopt-win32 0.1 h8ffe710_0 conda-forge -gettext 0.21.1 h5728263_0 conda-forge -gflags 2.2.2 ha925a31_1004 conda-forge -gitdb 4.0.10 pyhd8ed1ab_0 conda-forge -gitpython 3.1.32 pyhd8ed1ab_0 conda-forge -glib 2.76.4 h12be248_0 conda-forge -glib-tools 2.76.4 h12be248_0 conda-forge -glog 0.6.0 h4797de2_0 conda-forge -glom 23.3.0 pyhd8ed1ab_1 conda-forge -graphite2 1.3.13 1000 conda-forge -graphviz 8.1.0 h51cb2cd_0 conda-forge -greenlet 2.0.2 py310h00ffb61_1 conda-forge -gst-plugins-base 1.22.5 h001b923_0 conda-forge -gstreamer 1.22.5 hb4038d2_0 conda-forge -gts 0.7.6 h6b5321d_4 conda-forge -harfbuzz 6.0.0 he256f1b_0 conda-forge -icu 70.1 h0e60522_0 conda-forge -idna 3.4 pyhd8ed1ab_0 conda-forge -imagesize 1.4.1 pyhd8ed1ab_0 conda-forge -importlib-metadata 6.8.0 pyha770c72_0 conda-forge -importlib_metadata 6.8.0 hd8ed1ab_0 conda-forge -importlib_resources 5.13.0 pyhd8ed1ab_0 conda-forge -inflection 0.5.1 pyh9f0ad1d_0 conda-forge -iniconfig 2.0.0 pyhd8ed1ab_0 conda-forge -intel-openmp 2023.2.0 h57928b3_49496 conda-forge -ipykernel 6.25.1 pyh6817e22_0 conda-forge -ipynbname 2023.2.0.0 pyhd8ed1ab_0 conda-forge -ipython 8.14.0 pyh08f2357_0 conda-forge -ipython_genutils 0.2.0 py_1 conda-forge -ipywidgets 8.1.0 pyhd8ed1ab_0 conda-forge -isoduration 20.11.0 pyhd8ed1ab_0 conda-forge -isort 5.12.0 pyhd8ed1ab_1 conda-forge -jaraco.functools 3.8.1 pyhd8ed1ab_0 conda-forge -jedi 0.19.0 pyhd8ed1ab_0 conda-forge -jinja2 3.1.2 pyhd8ed1ab_1 conda-forge -jpeg 9e hcfcfb64_3 conda-forge -jplephem 2.18 pyh78acc04_0 conda-forge -json5 0.9.14 pyhd8ed1ab_0 conda-forge -jsonpickle 3.0.2 pyhd8ed1ab_0 conda-forge -jsonpointer 2.0 py_0 conda-forge -jsonschema 4.19.0 pyhd8ed1ab_1 conda-forge -jsonschema-specifications 2023.7.1 pyhd8ed1ab_0 conda-forge -jsonschema-with-format-nongpl 4.19.0 pyhd8ed1ab_1 conda-forge -jupyter-book 0.15.1 pyhd8ed1ab_0 conda-forge -jupyter-cache 0.6.1 pyhd8ed1ab_0 conda-forge -jupyter-lsp 2.2.0 pyhd8ed1ab_0 conda-forge -jupyter-server-mathjax 0.2.6 pyh5bfe37b_1 conda-forge -jupyter-sphinx 0.4.0 pyhd8ed1ab_0 conda-forge -jupyter_client 8.3.0 pyhd8ed1ab_0 conda-forge -jupyter_core 5.3.1 py310h5588dad_0 conda-forge -jupyter_events 0.7.0 pyhd8ed1ab_2 conda-forge -jupyter_server 2.7.1 pyhd8ed1ab_0 conda-forge -jupyter_server_terminals 0.4.4 pyhd8ed1ab_1 conda-forge -jupyterlab 4.0.5 pyhd8ed1ab_0 conda-forge -jupyterlab_pygments 0.2.2 pyhd8ed1ab_0 conda-forge -jupyterlab_server 2.24.0 pyhd8ed1ab_0 conda-forge -jupyterlab_widgets 3.0.8 pyhd8ed1ab_0 conda-forge -kiwisolver 1.4.5 py310h232114e_0 conda-forge -krb5 1.20.1 heb0366b_0 conda-forge -latex-dependency-scanner 0.1.1 pyhd8ed1ab_1 conda-forge -latexcodec 2.0.1 pyh9f0ad1d_0 conda-forge -lazy-object-proxy 1.9.0 py310h8d17308_0 conda-forge -lcms2 2.15 ha5c8aab_0 conda-forge -lerc 4.0.0 h63175ca_0 conda-forge -libabseil 20230125.3 cxx17_h63175ca_0 conda-forge -libarrow 10.0.1 h0ac4353_24_cpu conda-forge -libblas 3.9.0 17_win64_mkl conda-forge -libbrotlicommon 1.0.9 hcfcfb64_9 conda-forge -libbrotlidec 1.0.9 hcfcfb64_9 conda-forge -libbrotlienc 1.0.9 hcfcfb64_9 conda-forge -libcblas 3.9.0 17_win64_mkl conda-forge -libclang 15.0.7 default_h77d9078_3 conda-forge -libclang13 15.0.7 default_h77d9078_3 conda-forge -libcrc32c 1.1.2 h0e60522_0 conda-forge -libcurl 8.1.2 h68f0423_0 conda-forge -libdeflate 1.17 hcfcfb64_0 conda-forge -libevent 2.1.12 h3671451_1 conda-forge -libexpat 2.5.0 h63175ca_1 conda-forge -libffi 3.4.2 h8ffe710_5 conda-forge -libgd 2.3.3 hf5a96e7_4 conda-forge -libglib 2.76.4 he8f3873_0 conda-forge -libgoogle-cloud 2.10.1 h00b2bdc_1 conda-forge -libgrpc 1.54.3 ha177ca7_0 conda-forge -libhwloc 2.9.2 nocuda_h15da153_1008 conda-forge -libiconv 1.17 h8ffe710_0 conda-forge -liblapack 3.9.0 17_win64_mkl conda-forge -libogg 1.3.4 h8ffe710_1 conda-forge -libpng 1.6.39 h19919ed_0 conda-forge -libprotobuf 3.21.12 h12be248_1 conda-forge -libsodium 1.0.18 h8d14728_1 conda-forge -libsqlite 3.43.0 hcfcfb64_0 conda-forge -libssh2 1.11.0 h7dfc565_0 conda-forge -libthrift 0.18.1 h06f6336_2 conda-forge -libtiff 4.5.0 hf8721a0_2 conda-forge -libutf8proc 2.8.0 h82a8f57_0 conda-forge -libvorbis 1.3.7 h0e60522_0 conda-forge -libwebp 1.3.1 hcfcfb64_0 conda-forge -libwebp-base 1.3.1 hcfcfb64_0 conda-forge -libxcb 1.13 hcd874cb_1004 conda-forge -libxml2 2.11.5 hc3477c8_1 conda-forge -libxslt 1.1.37 h6070c61_1 conda-forge -libzlib 1.2.13 hcfcfb64_5 conda-forge -linkify-it-py 2.0.0 pyhd8ed1ab_0 conda-forge -logilab-common 1.7.3 py_0 conda-forge -lolviz 1.4.4 pyhd8ed1ab_0 conda-forge -lunardate 0.2.0 py_0 conda-forge -lxml 4.9.3 py310h46d54dd_0 conda-forge -lz4-c 1.9.4 hcfcfb64_0 conda-forge -m2w64-gcc-libgfortran 5.3.0 6 conda-forge -m2w64-gcc-libs 5.3.0 7 conda-forge -m2w64-gcc-libs-core 5.3.0 7 conda-forge -m2w64-gmp 6.1.0 2 conda-forge -m2w64-libwinpthread-git 5.0.0.4634.697f757 2 conda-forge -markdown-it-py 2.2.0 pyhd8ed1ab_0 conda-forge -markupsafe 2.1.3 py310h8d17308_0 conda-forge -marshmallow 3.20.1 pyhd8ed1ab_0 conda-forge -marshmallow-enum 1.5.1 pyh9f0ad1d_3 conda-forge -matplotlib 3.4.3 py310h5588dad_2 conda-forge -matplotlib-base 3.4.3 py310h79a7439_2 conda-forge -matplotlib-inline 0.1.6 pyhd8ed1ab_0 conda-forge -mbstrdecoder 1.1.3 pyhd8ed1ab_1 conda-forge -mccabe 0.7.0 pyhd8ed1ab_0 conda-forge -mdit-py-plugins 0.4.0 pyhd8ed1ab_0 conda-forge -mdurl 0.1.0 pyhd8ed1ab_0 conda-forge -menuinst 1.4.19 py310h5588dad_1 conda-forge -micawber 0.5.5 pyhd8ed1ab_0 conda-forge -microbench 0.7 pyhd8ed1ab_0 conda-forge -mistune 3.0.1 pyhd8ed1ab_0 conda-forge -mkl 2022.1.0 h6a75c08_874 conda-forge -monotonic 1.5 py_0 conda-forge -more-itertools 10.1.0 pyhd8ed1ab_0 conda-forge -msys2-conda-epoch 20160418 1 conda-forge -multimethod 1.9.1 pyhd8ed1ab_0 conda-forge -munch 4.0.0 pyhd8ed1ab_0 conda-forge -mypy_extensions 1.0.0 pyha770c72_0 conda-forge -myst-nb 0.17.2 pyhd8ed1ab_0 conda-forge -myst-parser 0.18.1 pyhd8ed1ab_0 conda-forge -nbclassic 1.0.0 pyhb4ecaf3_1 conda-forge -nbclient 0.7.4 pyhd8ed1ab_0 conda-forge -nbconvert 7.7.4 pyhd8ed1ab_0 conda-forge -nbconvert-core 7.7.4 pyhd8ed1ab_0 conda-forge -nbconvert-pandoc 7.7.4 pyhd8ed1ab_0 conda-forge -nbdime 3.2.1 pyhd8ed1ab_0 conda-forge -nbformat 5.9.2 pyhd8ed1ab_0 conda-forge -nest-asyncio 1.5.6 pyhd8ed1ab_0 conda-forge -networkx 3.1 pyhd8ed1ab_0 conda-forge -notebook 7.0.2 pyhd8ed1ab_0 conda-forge -notebook-shim 0.2.3 pyhd8ed1ab_0 conda-forge -numpy 1.26.0 py310hf667824_0 conda-forge -openjpeg 2.5.0 ha2aaf27_2 conda-forge -openpyxl 3.1.2 py310h8d17308_0 conda-forge -openssl 3.1.4 hcfcfb64_0 conda-forge -orc 1.8.3 hada7b9e_1 conda-forge -orderedmultidict 1.0.1 py_0 conda-forge -overrides 7.4.0 pyhd8ed1ab_0 conda-forge -packaging 23.1 pyhd8ed1ab_0 conda-forge -pandas 1.5.3 py310h1c4a608_1 conda-forge -pandas-stubs 2.1.1.230928 pyhd8ed1ab_1 conda-forge -pandoc 3.1.3 h57928b3_0 conda-forge -pandocfilters 1.5.0 pyhd8ed1ab_0 conda-forge -pango 1.50.14 hdffb7b3_0 conda-forge -parquet-cpp 1.5.1 2 conda-forge -parso 0.8.3 pyhd8ed1ab_0 conda-forge -pathspec 0.11.2 pyhd8ed1ab_0 conda-forge -patsy 0.5.3 pyhd8ed1ab_0 conda-forge -pcre2 10.40 h17e33f8_0 conda-forge -pdftopng 0.2.3 py310h560bb75_9 conda-forge -pep8-naming 0.10.0 pyh9f0ad1d_0 conda-forge -pickleshare 0.7.5 py_1003 conda-forge -pillow 9.4.0 py310hdbb7713_1 conda-forge -pint 0.22 pyhd8ed1ab_1 conda-forge -pip 23.2.1 pyhd8ed1ab_0 conda-forge -pivottablejs 0.9.0 py310haa95532_0 anaconda -pixman 0.40.0 h8ffe710_0 conda-forge -pkgutil-resolve-name 1.3.10 pyhd8ed1ab_0 conda-forge -platformdirs 3.10.0 pyhd8ed1ab_0 conda-forge -plotly 5.16.1 pyhd8ed1ab_0 conda-forge -pluggy 1.3.0 pyhd8ed1ab_0 conda-forge -ply 3.11 py_1 conda-forge -pony 0.7.16 pyhd8ed1ab_0 conda-forge -pooch 1.7.0 pyha770c72_3 conda-forge -poppler 23.03.0 h183ae7b_0 conda-forge -poppler-data 0.4.12 hd8ed1ab_0 conda-forge -posthog 2.4.2 pyhd8ed1ab_0 conda-forge -prometheus_client 0.17.1 pyhd8ed1ab_0 conda-forge -prompt-toolkit 3.0.39 pyha770c72_0 conda-forge -prompt_toolkit 3.0.39 hd8ed1ab_0 conda-forge -proplot 0.9.7 pyhd8ed1ab_0 conda-forge -prospector 1.10.2 pyhd8ed1ab_0 conda-forge -psutil 5.9.5 py310h8d17308_0 conda-forge -pthread-stubs 0.4 hcd874cb_1001 conda-forge -pthreads-win32 2.9.1 hfa6e2cd_3 conda-forge -pure_eval 0.2.2 pyhd8ed1ab_0 conda-forge -py2puml 0.7.2 pyhd8ed1ab_0 conda-forge -pyarrow 10.0.1 py310hd1a9178_24_cpu conda-forge -pybaum 0.1.3 pyhd8ed1ab_0 conda-forge -pybtex 0.24.0 pyhd8ed1ab_2 conda-forge -pybtex-docutils 1.0.3 py310h5588dad_0 conda-forge -pycairo 1.24.0 py310h42c1a3e_0 conda-forge -pycodestyle 2.9.1 pyhd8ed1ab_0 conda-forge -pycosat 0.6.4 py310h8d17308_1 conda-forge -pycparser 2.21 pyhd8ed1ab_0 conda-forge -pydantic 1.10.12 py310h8d17308_1 conda-forge -pydata-sphinx-theme 0.13.3 pyhd8ed1ab_0 conda-forge -pydocstyle 6.3.0 pyhd8ed1ab_0 conda-forge -pydot 1.4.2 py310h5588dad_3 conda-forge -pyerfa 2.0.0.3 py310h9b08ddd_0 conda-forge -pyflakes 2.5.0 pyhd8ed1ab_0 conda-forge -pyflowchart 0.3.1 pyhd8ed1ab_0 conda-forge -pygments 2.16.1 pyhd8ed1ab_0 conda-forge -pylint 2.17.5 pyhd8ed1ab_0 conda-forge -pylint-celery 0.3 py_1 conda-forge -pylint-django 2.5.3 pyhd8ed1ab_0 conda-forge -pylint-flask 0.6 py_0 conda-forge -pylint-plugin-utils 0.7 pyhd8ed1ab_0 conda-forge -pyluach 2.2.0 pyhd8ed1ab_0 conda-forge -pymeeus 0.5.12 pyhd8ed1ab_0 conda-forge -pyngrok 5.2.2 pyhd8ed1ab_0 conda-forge -pynvml 11.4.1 pypi_0 pypi -pyopenssl 23.2.0 pyhd8ed1ab_1 conda-forge -pyparsing 3.1.1 pyhd8ed1ab_0 conda-forge -pyqt 5.15.9 py310h1fd54f2_4 conda-forge -pyqt5-sip 12.12.2 py310h00ffb61_4 conda-forge -pyrsistent 0.19.3 py310h8d17308_0 conda-forge -pysocks 1.7.1 pyh0701188_6 conda-forge -pytask 0.3.2 pyhd8ed1ab_0 conda-forge -pytask-latex 0.3.0 pyhd8ed1ab_0 conda-forge -pytest 7.4.2 pyhd8ed1ab_0 conda-forge -python 3.10.12 h4de0772_0_cpython conda-forge -python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge -python-fastjsonschema 2.18.0 pyhd8ed1ab_0 conda-forge -python-graphviz 0.20.1 pyh22cad53_0 conda-forge -python-json-logger 2.0.7 pyhd8ed1ab_0 conda-forge -python_abi 3.10 3_cp310 conda-forge -pytz 2023.3 pyhd8ed1ab_0 conda-forge -pywin32 304 py310h00ffb61_2 conda-forge -pywinpty 2.0.11 py310h00ffb61_0 conda-forge -pyyaml 5.4.1 py310h8d17308_4 conda-forge -pyzmq 25.1.1 py310hcd737a0_0 conda-forge -qt-main 5.15.8 h720456b_6 conda-forge -re2 2023.03.02 hd4eee63_0 conda-forge -referencing 0.30.2 pyhd8ed1ab_0 conda-forge -reportlab 4.0.4 py310h8d17308_0 conda-forge -requests 2.31.0 pyhd8ed1ab_0 conda-forge -requests-toolbelt 1.0.0 pyhd8ed1ab_0 conda-forge -requirements-detector 1.2.2 pyhd8ed1ab_0 conda-forge -rfc3339-validator 0.1.4 pyhd8ed1ab_0 conda-forge -rfc3986-validator 0.1.1 pyh9f0ad1d_0 conda-forge -rich 13.5.1 pyhd8ed1ab_0 conda-forge -rlpycairo 0.2.0 pyhd8ed1ab_0 conda-forge -rpds-py 0.9.2 py310h87d50f1_0 conda-forge -ruamel.yaml 0.17.32 py310h8d17308_0 conda-forge -ruamel.yaml.clib 0.2.7 py310h8d17308_1 conda-forge -scalene 1.5.26 pypi_0 pypi -scipy 1.11.2 py310h578b7cb_0 conda-forge -seaborn 0.12.2 hd8ed1ab_0 conda-forge -seaborn-base 0.12.2 pyhd8ed1ab_0 conda-forge -semver 3.0.1 pyhd8ed1ab_0 conda-forge -send2trash 1.8.2 pyh08f2357_0 conda-forge -setoptconf-tmp 0.3.1 pyhd8ed1ab_0 conda-forge -setuptools 58.5.3 py310h5588dad_0 conda-forge -sgp4 2.22 py310h220cb41_0 conda-forge -shellingham 1.5.3 pyhd8ed1ab_0 conda-forge -sip 6.7.11 py310h00ffb61_0 conda-forge -six 1.16.0 pyh6c4a22f_0 conda-forge -skyfield 1.45 pyh1a96a4e_0 conda-forge -skyfield-data 5.0.0 pyhd8ed1ab_0 conda-forge -smmap 3.0.5 pyh44b312d_0 conda-forge -snappy 1.1.10 hfb803bf_0 conda-forge -sniffio 1.3.0 pyhd8ed1ab_0 conda-forge -snowballstemmer 2.2.0 pyhd8ed1ab_0 conda-forge -soupsieve 2.3.2.post1 pyhd8ed1ab_0 conda-forge -sphinx 5.0.2 pyh6c4a22f_0 conda-forge -sphinx-autoapi 2.1.0 pyhd8ed1ab_0 conda-forge -sphinx-book-theme 1.0.1 pyhd8ed1ab_0 conda-forge -sphinx-comments 0.0.3 pyh9f0ad1d_0 conda-forge -sphinx-copybutton 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-design 0.3.0 pyhd8ed1ab_0 conda-forge -sphinx-external-toc 0.3.1 pyhd8ed1ab_1 conda-forge -sphinx-jupyterbook-latex 0.5.2 pyhd8ed1ab_0 conda-forge -sphinx-multitoc-numbering 0.1.3 pyhd8ed1ab_0 conda-forge -sphinx-thebe 0.2.1 pyhd8ed1ab_0 conda-forge -sphinx-togglebutton 0.3.2 pyhd8ed1ab_0 conda-forge -sphinxcontrib-applehelp 1.0.7 pyhd8ed1ab_0 conda-forge -sphinxcontrib-bibtex 2.5.0 pyhd8ed1ab_0 conda-forge -sphinxcontrib-devhelp 1.0.5 pyhd8ed1ab_0 conda-forge -sphinxcontrib-dotnetdomain 0.4 py_0 conda-forge -sphinxcontrib-golangdomain 0.2.0.dev0 py_0 conda-forge -sphinxcontrib-htmlhelp 2.0.4 pyhd8ed1ab_0 conda-forge -sphinxcontrib-jsmath 1.0.1 py_0 conda-forge -sphinxcontrib-qthelp 1.0.6 pyhd8ed1ab_0 conda-forge -sphinxcontrib-serializinghtml 1.1.9 pyhd8ed1ab_0 conda-forge -sqlalchemy 1.4.49 py310h8d17308_1 conda-forge -sqlparse 0.4.4 pyhd8ed1ab_0 conda-forge -stack_data 0.6.2 pyhd8ed1ab_0 conda-forge -statsmodels 0.14.0 py310h9b08ddd_1 conda-forge -stringcase 1.2.0 py_0 conda-forge -svglib 1.5.1 pyhd8ed1ab_0 conda-forge -tabulate 0.9.0 pyhd8ed1ab_1 conda-forge -tbb 2021.10.0 h91493d7_0 conda-forge -tectonic 0.12.0 hcab5949_1 conda-forge -tenacity 8.2.3 pyhd8ed1ab_0 conda-forge -terminado 0.17.0 pyh08f2357_0 conda-forge -tinycss2 1.2.1 pyhd8ed1ab_0 conda-forge -tk 8.6.12 h8ffe710_0 conda-forge -toml 0.10.2 pyhd8ed1ab_0 conda-forge -tomli 2.0.1 pyhd8ed1ab_0 conda-forge -tomlkit 0.12.1 pyha770c72_0 conda-forge -toolz 0.12.0 pyhd8ed1ab_0 conda-forge -tornado 6.3.3 py310h8d17308_0 conda-forge -tqdm 4.66.1 pyhd8ed1ab_0 conda-forge -traitlets 5.9.0 pyhd8ed1ab_0 conda-forge -treelib 1.7.0 pyhd8ed1ab_0 conda-forge -typeguard 4.1.5 pyhd8ed1ab_0 conda-forge -typepy 1.3.1 pyhd8ed1ab_0 conda-forge -types-datetimerange 2.0.0.6 pyhd8ed1ab_0 conda-forge -types-python-dateutil 2.8.19.14 pyhd8ed1ab_0 conda-forge -types-pytz 2023.3.1.1 pyhd8ed1ab_0 conda-forge -typing 3.10.0.0 pyhd8ed1ab_0 conda-forge -typing-extensions 4.7.1 hd8ed1ab_0 conda-forge -typing_extensions 4.7.1 pyha770c72_0 conda-forge -typing_inspect 0.9.0 pyhd8ed1ab_0 conda-forge -typing_utils 0.1.0 pyhd8ed1ab_0 conda-forge -tzdata 2023c h71feb2d_0 conda-forge -uc-micro-py 1.0.1 pyhd8ed1ab_0 conda-forge -ucrt 10.0.22621.0 h57928b3_0 conda-forge -unidecode 1.3.6 pyhd8ed1ab_0 conda-forge -uri-template 1.3.0 pyhd8ed1ab_0 conda-forge -urllib3 2.0.4 pyhd8ed1ab_0 conda-forge -validators 0.21.2 pyhd8ed1ab_0 conda-forge -vc 14.3 h64f974e_17 conda-forge -vc14_runtime 14.36.32532 hfdfe4a8_17 conda-forge -vega_datasets 0.9.0 pyhd3deb0d_0 conda-forge -vs2015_runtime 14.36.32532 h05e6639_17 conda-forge -wcwidth 0.2.6 pyhd8ed1ab_0 conda-forge -webcolors 1.13 pyhd8ed1ab_0 conda-forge -webencodings 0.5.1 py_1 conda-forge -websocket-client 1.6.2 pyhd8ed1ab_0 conda-forge -wheel 0.41.2 pyhd8ed1ab_0 conda-forge -widgetsnbextension 4.0.8 pyhd8ed1ab_0 conda-forge -win_inet_pton 1.1.0 pyhd8ed1ab_6 conda-forge -winpty 0.4.3 4 conda-forge -workalendar 17.0.0 pyhd8ed1ab_0 conda-forge -wrapt 1.15.0 py310h8d17308_0 conda-forge -xlsxwriter 3.1.2 pyhd8ed1ab_0 conda-forge -xorg-kbproto 1.0.7 hcd874cb_1002 conda-forge -xorg-libice 1.0.10 hcd874cb_0 conda-forge -xorg-libsm 1.2.3 hcd874cb_1000 conda-forge -xorg-libx11 1.8.4 hcd874cb_0 conda-forge -xorg-libxau 1.0.11 hcd874cb_0 conda-forge -xorg-libxdmcp 1.1.3 hcd874cb_0 conda-forge -xorg-libxext 1.3.4 hcd874cb_2 conda-forge -xorg-libxpm 3.5.16 hcd874cb_0 conda-forge -xorg-libxt 1.3.0 hcd874cb_0 conda-forge -xorg-xextproto 7.3.0 hcd874cb_1003 conda-forge -xorg-xproto 7.0.31 hcd874cb_1007 conda-forge -xz 5.2.6 h8d14728_0 conda-forge -yaml 0.2.5 h8ffe710_2 conda-forge -zeromq 4.3.4 h0e60522_1 conda-forge -zipp 3.16.2 pyhd8ed1ab_0 conda-forge -zlib 1.2.13 hcfcfb64_5 conda-forge -zstandard 0.19.0 py310h0009e47_2 conda-forge -zstd 1.5.2 h12be248_7 conda-forge diff --git a/paper/paper.md b/paper/paper.md index 02b0a3f..4f031c7 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**:**Noah Pflugradt**:conceptualisation, methododlogy, Supervision, Writing - Review & Editing # References \ No newline at end of file From eca85b56704a89fe1db500ed72e8fbbf042d2d7a Mon Sep 17 00:00:00 2001 From: "j.belina" Date: Mon, 26 Feb 2024 14:47:09 +0100 Subject: [PATCH 5/8] Add authors contribution, fix links in readme --- README.md | 4 ++-- paper/paper.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) 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/paper/paper.md b/paper/paper.md index 4f031c7..955037e 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -117,6 +117,6 @@ The software eLOAD employs an approach similar to that from Sandhaas. Instead of # Authors Contribution -**Julian Belina**:**Noah Pflugradt**:conceptualisation, methododlogy, Supervision, Writing - Review & Editing +**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 From 343e80d6c2112df92c51c4a79a1bf86d121cebc9 Mon Sep 17 00:00:00 2001 From: "j.belina" Date: Tue, 27 Feb 2024 18:33:53 +0100 Subject: [PATCH 6/8] Add tutorail and workspace recommendations --- .vscode/extensions.json | 11 + .vscode/settings.json | 8 + asd.py | 13 - documentation/_toc.yml | 45 ++- .../ethos_penalps_tutorial/add_more_states.md | 107 ++++++ .../add_network_level_and_process_chains.md | 6 + .../ethos_penalps_tutorial/overview.md | 18 + .../single_cooker_process_chain.md | 261 ++++++++++++++ .../single_cooker_process_chain_example.png | Bin 0 -> 29828 bytes .../batch_to_batch_1_node_example.py | 82 ++--- examples/tutorial/cooking_example.py | 213 ++++++++++++ .../cooking_example_multiple_process_step.py | 213 ++++++++++++ .../cooking_example_parallel_process_steps.py | 325 ++++++++++++++++++ .../production_plan_post_processor.py | 16 +- src/ethos_penalps/process_state.py | 29 +- src/ethos_penalps/process_state_handler.py | 8 +- .../process_state_network_navigator.py | 16 +- 17 files changed, 1264 insertions(+), 107 deletions(-) create mode 100644 .vscode/extensions.json delete mode 100644 asd.py create mode 100644 documentation/ethos_penalps_tutorial/add_more_states.md create mode 100644 documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md create mode 100644 documentation/ethos_penalps_tutorial/overview.md create mode 100644 documentation/ethos_penalps_tutorial/single_cooker_process_chain.md create mode 100644 documentation/ethos_penalps_tutorial/single_cooker_process_chain_example.png create mode 100644 examples/tutorial/cooking_example.py create mode 100644 examples/tutorial/cooking_example_multiple_process_step.py create mode 100644 examples/tutorial/cooking_example_parallel_process_steps.py diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..dff03d0 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + "recommendations": [ + "ms-python.flake8", + "matangover.mypy", + "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..5b38157 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,4 +14,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/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/_toc.yml b/documentation/_toc.yml index 3ee4381..39f070d 100644 --- a/documentation/_toc.yml +++ b/documentation/_toc.yml @@ -4,31 +4,26 @@ 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_network_level_and_process_chains.md + - 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 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..3073af4 --- /dev/null +++ b/documentation/ethos_penalps_tutorial/add_more_states.md @@ -0,0 +1,107 @@ +# Add more states + +The behavior of the cooker [of the previous example](single_cooker_process_chain.md) can be modeled in greater detail by adding more states. Instead of modeling a single cooking phase two phases are implemented. One heating phase which uses the maximum power to reach the desired temperature and seconds hold temperature phase in which needs less power to hold the 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 states is connected with an additional process state switch delay. + +``` +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=15), +) + +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=15), +) + +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. 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=1.8, + 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=1.8, + load_type=electricity_load, + stream=raw_materials_to_cooking_stream, +) +``` diff --git a/documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md b/documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md new file mode 100644 index 0000000..a049791 --- /dev/null +++ b/documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md @@ -0,0 +1,6 @@ +# Add more network level and process chains + +This sections explains how multiple process steps can be used in the simulation. They can be organized either sequentially or parallelly. Process steps that should be organized sequentially. + + +Multiple network level are used to model sequential process steps. Multiple process chains within a network level are used to model parallel operation of process steps. In the case of a single machine an instance of each is sufficient. \ No newline at end of file diff --git a/documentation/ethos_penalps_tutorial/overview.md b/documentation/ethos_penalps_tutorial/overview.md new file mode 100644 index 0000000..c8b818d --- /dev/null +++ b/documentation/ethos_penalps_tutorial/overview.md @@ -0,0 +1,18 @@ +# 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 Orders to be produced during the simulation +3. Create Organizational Container Objects +4. Create Process steps, sources, sinks, storages +5. Set up a petri net of states for each process step +6. Connect all objects from step 4 with Streams +7. Initialize Energy Data +8. Create Internal Storages and Mass balances +9. Setup Post Processing + +First it will be shown how to setup a [minimal cooking process simulation](ethos_penalps_in_10_minutes.md). In the following it shown how to add more details to cooking process step by modifying the petri nets of states of the cooker. The next step shows how to add more process steps to increase the complexity of the production system. + 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..16c7500 --- /dev/null +++ b/documentation/ethos_penalps_tutorial/single_cooker_process_chain.md @@ -0,0 +1,261 @@ +# Simple Cooker Example + +This section shows how to setup a minimalistic cooker example which is shown in figure {numref}`cooking-example`. + + + +:::{figure-md} cooking-example + + +Depiction of the simple cooker model. +::: + +## Initialize Time Data +The first thing todo is to setup the desired installation period. The start time is only relevant for the period displayed. The simulation starts internally at the end date and terminates when all order 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, +) + +``` + +(my-label)=heading-setup-commodities +## Setup All Commodities +The second step is create all commodities that raw materials, intermediate products or final products of the production system. + +``` +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 a 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 its not possible due to capacity constraints, the production is shifted to an earlier time. + +``` +# 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() + +``` +## 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 are used as shown in this example. Otherwise the objects are not connected appropriately. + +``` +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. Now 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=300, + ) +) +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=300, + ) +) + +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 +In 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 when the input commodities 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 +These states must be connected with transitions and selectors for transitions, which 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 is 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 model multiple transitions which can be chosen after a state. In this only single transition occur and the transition must be passed to a so called single choice selector. + +``` +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=30), +) + +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 case. It can be either addressed to a specific state or to the activity of a stream. In this case it is assumed that the loading and discharging of the cooker does not required energy because its done manually. The specific energy demand is provided in the units MJ/t. 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=1.8, + 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 and are mainly used for the internal processing streams. + +``` +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. 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 +- Load Profiles +- 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, +) +``` \ No newline at end of file diff --git a/documentation/ethos_penalps_tutorial/single_cooker_process_chain_example.png b/documentation/ethos_penalps_tutorial/single_cooker_process_chain_example.png new file mode 100644 index 0000000000000000000000000000000000000000..f4f206261f3631b8795f53ed0da1f90569d0fe6b GIT binary patch literal 29828 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 M;hKDfta;df0DYkezyJUM literal 0 HcmV?d00001 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/cooking_example.py b/examples/tutorial/cooking_example.py new file mode 100644 index 0000000..a39b461 --- /dev/null +++ b/examples/tutorial/cooking_example.py @@ -0,0 +1,213 @@ +import datetime +import logging +from typeguard import install_import_hook + +install_import_hook("ethos_penalps") + +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" + ) +) + +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=30), +) + +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=1.8, + 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/cooking_example_multiple_process_step.py b/examples/tutorial/cooking_example_multiple_process_step.py new file mode 100644 index 0000000..13d2763 --- /dev/null +++ b/examples/tutorial/cooking_example_multiple_process_step.py @@ -0,0 +1,213 @@ +import datetime +import logging +from typeguard import install_import_hook + +install_import_hook("ethos_penalps") + +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=23) +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=1, +) + +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=300, + ) +) +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=300, + ) +) + +# 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=30), +) + +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=1.8, + 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/cooking_example_parallel_process_steps.py b/examples/tutorial/cooking_example_parallel_process_steps.py new file mode 100644 index 0000000..e162e2f --- /dev/null +++ b/examples/tutorial/cooking_example_parallel_process_steps.py @@ -0,0 +1,325 @@ +import datetime +import logging + +from typeguard import install_import_hook + +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=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_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" +) +# 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_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 Process nodes +process_step_1 = process_chain_1.create_process_step(name="Cooker 1") +process_step_2 = process_chain_2.create_process_step(name="Cooker 2") + +# Streams +## Process Chain 1 +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, + ) +) + +## Process Chain 2 +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, + ) +) + +# Add streams to sinks and sources +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 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_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" + ) +) + + +# Petri net transitions + +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 +) + +# 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" + ) +) + + +# Petri net transitions + +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 +) + + +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, +) + + +# Mass balances +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, +) + +# Add internal storages (required) +process_step_1.process_state_handler.process_step_data.main_mass_balance.create_storage( + current_storage_level=0 +) + +# Mass balances +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, +) + +# Add internal storages (required) +process_step_2.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/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_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 From 897fe35ca8a11eeea8575f0bbdbe04a1ce1a1a8e Mon Sep 17 00:00:00 2001 From: "j.belina" Date: Thu, 29 Feb 2024 17:10:23 +0100 Subject: [PATCH 7/8] Add tutoral --- .vscode/extensions.json | 1 - .vscode/settings.json | 11 +- documentation/_config.yml | 23 +- documentation/_toc.yml | 9 +- .../ethos_penalps_tutorial/add_more_chains.md | 151 ++----- .../ethos_penalps_tutorial/add_more_states.md | 47 +- .../add_network_level_and_process_chains.md | 6 - .../connect_three_or_more_process_steps.md | 420 ++++++++++++++++++ .../connect_two_process_steps_exclusively.md | 225 ++++++++++ ...ender_and_cooker_exclusively_connected.png | Bin 0 -> 33833 bytes .../figures/parallel_cooker_operation.png | Bin 0 -> 38075 bytes .../single_cooker_process_chain_example.png | Bin .../figures/two_blender_and_two_cooker.png | Bin 0 -> 53212 bytes .../ethos_penalps_tutorial/overview.md | 20 +- .../single_cooker_process_chain.md | 65 ++- .../unit_conversions.py | 40 ++ documentation/references.bib | 237 +++++----- ...oking_example.py => _1_cooking_example.py} | 23 +- ...p.py => _2_cooking_example_more_states.py} | 74 +-- .../_3_cooking_and_mixer_exclusive_example.py | 299 +++++++++++++ .../blending_process_chain.py | 154 +++++++ .../cooking_process_chain.py | 152 +++++++ .../simulation_starter.py | 129 ++++++ pyproject.toml | 4 + src/ethos_penalps/automatic_sizer/__init__.py | 0 src/ethos_penalps/data_classes.py | 40 +- .../organizational_agents/__init__.py | 0 src/ethos_penalps/petri_net/__init__.py | 0 src/ethos_penalps/process_chain.py | 14 +- src/ethos_penalps/py.typed | 0 .../utilities/logger_ethos_penalps.py | 10 +- src/ethos_penalps/utilities/units.py | 15 +- 32 files changed, 1803 insertions(+), 366 deletions(-) rename examples/tutorial/cooking_example_parallel_process_steps.py => documentation/ethos_penalps_tutorial/add_more_chains.md (71%) delete mode 100644 documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md create mode 100644 documentation/ethos_penalps_tutorial/connect_three_or_more_process_steps.md create mode 100644 documentation/ethos_penalps_tutorial/connect_two_process_steps_exclusively.md create mode 100644 documentation/ethos_penalps_tutorial/figures/blender_and_cooker_exclusively_connected.png create mode 100644 documentation/ethos_penalps_tutorial/figures/parallel_cooker_operation.png rename documentation/ethos_penalps_tutorial/{ => figures}/single_cooker_process_chain_example.png (100%) create mode 100644 documentation/ethos_penalps_tutorial/figures/two_blender_and_two_cooker.png create mode 100644 documentation/ethos_penalps_tutorial/unit_conversions.py rename examples/tutorial/{cooking_example.py => _1_cooking_example.py} (94%) rename examples/tutorial/{cooking_example_multiple_process_step.py => _2_cooking_example_more_states.py} (75%) create mode 100644 examples/tutorial/_3_cooking_and_mixer_exclusive_example.py create mode 100644 examples/tutorial/_4_connect_four_process_steps/blending_process_chain.py create mode 100644 examples/tutorial/_4_connect_four_process_steps/cooking_process_chain.py create mode 100644 examples/tutorial/_4_connect_four_process_steps/simulation_starter.py create mode 100644 src/ethos_penalps/automatic_sizer/__init__.py create mode 100644 src/ethos_penalps/organizational_agents/__init__.py create mode 100644 src/ethos_penalps/petri_net/__init__.py create mode 100644 src/ethos_penalps/py.typed diff --git a/.vscode/extensions.json b/.vscode/extensions.json index dff03d0..3d3c581 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,6 @@ { "recommendations": [ "ms-python.flake8", - "matangover.mypy", "ms-python.black-formatter", "ms-python.isort", "njpwerner.autodocstring", diff --git a/.vscode/settings.json b/.vscode/settings.json index 5b38157..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": [ "." 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 39f070d..3275dc8 100644 --- a/documentation/_toc.yml +++ b/documentation/_toc.yml @@ -13,13 +13,15 @@ parts: chapters: - file: ethos_penalps_tutorial/overview.md - file: ethos_penalps_tutorial/single_cooker_process_chain.md - - file: ethos_penalps_tutorial/add_network_level_and_process_chains.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 - - file: ethos_penalps_articles/bibliography.md - caption: API chapters: - file: autoapi/ethos_penalps/index.rst @@ -27,3 +29,6 @@ parts: chapters: - file: examples/b_pillar_example.md - file: examples/toffee_example.md + - caption: Bibliography + chapters: + - file: ethos_penalps_articles/bibliography.md diff --git a/examples/tutorial/cooking_example_parallel_process_steps.py b/documentation/ethos_penalps_tutorial/add_more_chains.md similarity index 71% rename from examples/tutorial/cooking_example_parallel_process_steps.py rename to documentation/ethos_penalps_tutorial/add_more_chains.md index e162e2f..33b0144 100644 --- a/examples/tutorial/cooking_example_parallel_process_steps.py +++ b/documentation/ethos_penalps_tutorial/add_more_chains.md @@ -1,81 +1,46 @@ -import datetime -import logging +# Add More Cookers for Parallel Operation -from typeguard import install_import_hook +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 + -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 +Depiction of the cooker model with two parallel cookers. +::: -logger = PeNALPSLogger.get_human_readable_logger(logging.INFO) +Therefore two process chains must be created. Each of the chains must hold an own instance of the cooker. -# 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=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 +## 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" ) -# 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 +``` +## 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 Process nodes +``` +## 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") +``` -# Streams -## Process Chain 1 +## 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, @@ -95,7 +60,6 @@ ) ) -## Process Chain 2 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, @@ -114,8 +78,12 @@ 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. -# Add streams to sinks and sources +``` source.add_output_stream( output_stream=raw_materials_to_cooking_stream_1, process_chain_identifier=process_chain_1.process_chain_identifier, @@ -132,26 +100,12 @@ 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: -""" 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 - +### Cooker 1: +``` idle_state_1 = process_step_1.process_state_handler.create_idle_process_state( process_state_name="Idle" ) @@ -171,9 +125,6 @@ ) ) - -# Petri net transitions - 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, @@ -209,7 +160,11 @@ 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( @@ -231,9 +186,6 @@ ) ) - -# Petri net transitions - 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, @@ -269,8 +221,12 @@ 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, @@ -282,9 +238,11 @@ load_type=electricity_load, stream=raw_materials_to_cooking_stream_2, ) +``` - -# Mass balances +## 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, @@ -292,12 +250,11 @@ main_output_stream=cooking_to_sink_stream_1, ) -# Add internal storages (required) process_step_1.process_state_handler.process_step_data.main_mass_balance.create_storage( current_storage_level=0 ) -# Mass balances + process_step_2.create_main_mass_balance( commodity=output_commodity, input_to_output_conversion_factor=1, @@ -305,21 +262,11 @@ main_output_stream=cooking_to_sink_stream_2, ) -# Add internal storages (required) + 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. -# 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/documentation/ethos_penalps_tutorial/add_more_states.md b/documentation/ethos_penalps_tutorial/add_more_states.md index 3073af4..e1a7490 100644 --- a/documentation/ethos_penalps_tutorial/add_more_states.md +++ b/documentation/ethos_penalps_tutorial/add_more_states.md @@ -1,6 +1,6 @@ -# Add more states +# Add More States -The behavior of the cooker [of the previous example](single_cooker_process_chain.md) can be modeled in greater detail by adding more states. Instead of modeling a single cooking phase two phases are implemented. One heating phase which uses the maximum power to reach the desired temperature and seconds hold temperature phase in which needs less power to hold the temperature. Additionally a cleaning phase is added after the discharge phase. +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( @@ -30,8 +30,21 @@ cleaning_state = process_step.process_state_handler.create_intermediate_process_ 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) -Each of the new states is connected with an additional process state switch delay. +$$ +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( @@ -54,7 +67,7 @@ process_step.process_state_handler.process_state_switch_selector_handler.create_ 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=15), + delay=datetime.timedelta(minutes=1.82), ) process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector( @@ -63,7 +76,7 @@ process_step.process_state_handler.process_state_switch_selector_handler.create_ 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=15), + delay=datetime.timedelta(minutes=22.18), ) process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector( @@ -87,21 +100,37 @@ activate_hold_temperature = process_step.process_state_handler.process_state_swi 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. In this case it is assumed that no energy is used during the cleaning. +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=1.8, + 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=1.8, + 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/add_network_level_and_process_chains.md b/documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md deleted file mode 100644 index a049791..0000000 --- a/documentation/ethos_penalps_tutorial/add_network_level_and_process_chains.md +++ /dev/null @@ -1,6 +0,0 @@ -# Add more network level and process chains - -This sections explains how multiple process steps can be used in the simulation. They can be organized either sequentially or parallelly. Process steps that should be organized sequentially. - - -Multiple network level are used to model sequential process steps. Multiple process chains within a network level are used to model parallel operation of process steps. In the case of a single machine an instance of each is sufficient. \ No newline at end of file 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 0000000000000000000000000000000000000000..7288f0032b860919b2058d8926fd2191335dea3d GIT binary patch literal 33833 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2390bff5a804117d18be7a0f6fec525d38a6d5d3 GIT binary patch literal 38075 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 D*x}%t literal 0 HcmV?d00001 diff --git a/documentation/ethos_penalps_tutorial/single_cooker_process_chain_example.png b/documentation/ethos_penalps_tutorial/figures/single_cooker_process_chain_example.png similarity index 100% rename from documentation/ethos_penalps_tutorial/single_cooker_process_chain_example.png rename to documentation/ethos_penalps_tutorial/figures/single_cooker_process_chain_example.png 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 0000000000000000000000000000000000000000..10017231943b551e6a8cc633bab9bbec41d3631e GIT binary patch literal 53212 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 uT}@ejGQbpjyRy3aZ;|$T>| - -:::{figure-md} cooking-example - - -Depiction of the simple cooker model. +Depiction of the minimal production system model in ETHOS.PeNALPS which simulates a simple cooker. ::: ## Initialize Time Data -The first thing todo is to setup the desired installation period. The start time is only relevant for the period displayed. The simulation starts internally at the end date and terminates when all order are created, which is shown in the following section. +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 @@ -23,12 +21,10 @@ time_data = TimeData( global_start_date=start_date, global_end_date=end_date, ) - ``` -(my-label)=heading-setup-commodities ## Setup All Commodities -The second step is create all commodities that raw materials, intermediate products or final products of the production system. +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 @@ -37,19 +33,17 @@ output_commodity = Commodity(name="Cooked Goods") input_commodity = Commodity(name="Raw Goods") ``` ## Create Product Orders -As a third step a 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 its not possible due to capacity constraints, the production is shifted to an earlier time. +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.0005, + 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 @@ -57,7 +51,7 @@ The fourth step is to provide the minimum set of container classes. This consist - 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 are used as shown in this example. Otherwise the objects are not connected appropriately. +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 @@ -67,10 +61,9 @@ 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. Now the sink and source must be connected to process chain. +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", @@ -102,7 +95,7 @@ raw_materials_to_cooking_stream = process_chain.stream_handler.create_batch_stre end_process_step_name=process_step.name, delay=datetime.timedelta(minutes=1), commodity=input_commodity, - maximum_batch_mass_value=300, + maximum_batch_mass_value=0.00065, ) ) cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream( @@ -111,7 +104,7 @@ cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream( end_process_step_name=sink.name, delay=datetime.timedelta(minutes=1), commodity=output_commodity, - maximum_batch_mass_value=300, + maximum_batch_mass_value=0.00065, ) ) @@ -129,15 +122,14 @@ The behavior of the process step during the production is determined by a petri These must be connected by transitions. ### Create Nodes -In the simplest combination consists of an idle state and either a combined input and output state or two separate input and output states. +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: -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 when the input commodities and output commodities are loaded or discharged. +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( @@ -161,15 +153,14 @@ discharge_goods_state = ( ``` ### Create Transitions -These states must be connected with transitions and selectors for transitions, which 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 is 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: +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 model multiple transitions which can be chosen after a state. In this only single transition occur and the transition must be passed to a so called single choice selector. +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( @@ -192,7 +183,7 @@ process_step.process_state_handler.process_state_switch_selector_handler.create_ 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=30), + delay=datetime.timedelta(minutes=24), ) process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector( @@ -210,21 +201,20 @@ process_step.process_state_handler.process_state_switch_selector_handler.create_ ``` ## Initialize Energy Data -Finally the energy data must be initialized. Therefore the energy load type must be initialized, which is electricity in this case. It can be either addressed to a specific state or to the activity of a stream. In this case it is assumed that the loading and discharging of the cooker does not required energy because its done manually. The specific energy demand is provided in the units MJ/t. 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. +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=1.8, + 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 and are mainly used for the internal processing streams. +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( @@ -240,16 +230,19 @@ process_step.process_state_handler.process_step_data.main_mass_balance.create_st ``` ## Start Simulation and Post Processing -Lastly the simulation and post processing must be started. 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: +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 -- Load Profiles +- 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, @@ -258,4 +251,6 @@ enterprise.create_post_simulation_report( gantt_chart_end_date=end_date, gantt_chart_start_date=start_date, ) -``` \ No newline at end of file +``` + +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/tutorial/cooking_example.py b/examples/tutorial/_1_cooking_example.py similarity index 94% rename from examples/tutorial/cooking_example.py rename to examples/tutorial/_1_cooking_example.py index a39b461..6ba7b40 100644 --- a/examples/tutorial/cooking_example.py +++ b/examples/tutorial/_1_cooking_example.py @@ -1,16 +1,10 @@ import datetime import logging -from typeguard import install_import_hook - -install_import_hook("ethos_penalps") 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.stream import BatchStreamStaticData, ContinuousStreamStaticData from ethos_penalps.time_data import TimeData from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger @@ -31,11 +25,10 @@ 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, + mass_per_order=0.00065, production_deadline=end_date, number_of_orders=2, ) @@ -77,7 +70,7 @@ end_process_step_name=process_step.name, delay=datetime.timedelta(minutes=1), commodity=input_commodity, - maximum_batch_mass_value=0.0005, + maximum_batch_mass_value=0.00065, ) ) cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream( @@ -86,7 +79,7 @@ end_process_step_name=sink.name, delay=datetime.timedelta(minutes=1), commodity=output_commodity, - maximum_batch_mass_value=0.0005, + maximum_batch_mass_value=0.00065, ) ) @@ -102,12 +95,12 @@ """ Create petri net for process step Each process state must have at least the following: -- Either +- 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 + 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 @@ -160,7 +153,7 @@ 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=30), + delay=datetime.timedelta(minutes=24), ) process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector( @@ -179,7 +172,7 @@ electricity_load = LoadType(name="Electricity") cooking_state.create_process_state_energy_data_based_on_stream_mass( - specific_energy_demand=1.8, + specific_energy_demand=830.76, load_type=electricity_load, stream=raw_materials_to_cooking_stream, ) diff --git a/examples/tutorial/cooking_example_multiple_process_step.py b/examples/tutorial/_2_cooking_example_more_states.py similarity index 75% rename from examples/tutorial/cooking_example_multiple_process_step.py rename to examples/tutorial/_2_cooking_example_more_states.py index 13d2763..c0086ef 100644 --- a/examples/tutorial/cooking_example_multiple_process_step.py +++ b/examples/tutorial/_2_cooking_example_more_states.py @@ -1,16 +1,10 @@ import datetime import logging -from typeguard import install_import_hook - -install_import_hook("ethos_penalps") 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.stream import BatchStreamStaticData, ContinuousStreamStaticData from ethos_penalps.time_data import TimeData from ethos_penalps.utilities.logger_ethos_penalps import PeNALPSLogger @@ -19,7 +13,7 @@ # Enterprise structure # Set simulation time data -start_date = datetime.datetime(2022, 1, 2, hour=23) +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, @@ -37,7 +31,7 @@ commodity=output_commodity, mass_per_order=0.0005, production_deadline=end_date, - number_of_orders=1, + number_of_orders=2, ) order_collection = order_generator.create_n_order_collection() @@ -77,7 +71,7 @@ end_process_step_name=process_step.name, delay=datetime.timedelta(minutes=1), commodity=input_commodity, - maximum_batch_mass_value=300, + maximum_batch_mass_value=0.0005, ) ) cooking_to_sink_stream = process_chain.stream_handler.create_batch_stream( @@ -86,7 +80,7 @@ end_process_step_name=sink.name, delay=datetime.timedelta(minutes=1), commodity=output_commodity, - maximum_batch_mass_value=300, + maximum_batch_mass_value=0.0005, ) ) @@ -102,12 +96,12 @@ """ Create petri net for process step Each process state must have at least the following: -- Either +- 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 + 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 @@ -127,8 +121,12 @@ ) ) -cooking_state = process_step.process_state_handler.create_intermediate_process_state_energy_based_on_stream_mass( - process_state_name="Cooking" +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 = ( @@ -137,11 +135,14 @@ ) ) +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=discharge_goods_state, + start_process_state=cleaning_state, end_process_state=idle_state, ) process_step.process_state_handler.process_state_switch_selector_handler.create_single_choice_selector( @@ -157,33 +158,54 @@ 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( +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=cooking_state, - delay=datetime.timedelta(minutes=30), + 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_cooking + 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=cooking_state, + 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") -cooking_state.create_process_state_energy_data_based_on_stream_mass( - specific_energy_demand=1.8, +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( @@ -207,7 +229,7 @@ start_date=start_date, end_date=end_date, x_axis_time_delta=datetime.timedelta(hours=1), - resample_frequency="5min", + 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/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/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/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")) From 428ae3905b150fb82865ca1428fb41363f88eeca Mon Sep 17 00:00:00 2001 From: "j.belina" Date: Thu, 29 Feb 2024 19:02:26 +0100 Subject: [PATCH 8/8] Recompile paper, add community guidelines for contributions --- README.md | 52 ++++++++++++++++++++++++++++++++++++- paper/how_to_precompile.md | 11 +++++++- paper/paper.jats | 8 ++++++ paper/paper.md | 2 +- paper/paper.pdf | Bin 1304880 -> 1307236 bytes 5 files changed, 70 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b4b562e..c8e1f21 100644 --- a/README.md +++ b/README.md @@ -83,4 +83,54 @@ pytest # Documentation -A ReadTheDocs Documentation can be found [here](https://ethospenalps.readthedocs.io/en/latest/Introduction.html). \ No newline at end of file +A ReadTheDocs Documentation can be found [here](https://ethospenalps.readthedocs.io/en/latest/Introduction.html). + + +# Contributing + +Contributions are welcome, and they are greatly appreciated! Every little bit +helps, and credit will always be given. + +You can contribute in many ways: + +## Types of Contributions + + +### Report Bugs + + +Report bugs at https://github.com/FZJ-IEK3-VSA/ETHOS_PeNALPS/issues. + +If you are reporting a bug, please include: + +* Your operating system name and version. +* Any details about your local setup that might be helpful in troubleshooting. +* Detailed steps to reproduce the bug. + +### Fix Bugs + +Look through the GitHub issues for bugs. Anything tagged with "bug" and "help +wanted" is open to whoever wants to implement it. + +### Implement Features + +Look through the GitHub issues for features. Anything tagged with "enhancement" +and "help wanted" is open to whoever wants to implement it. + +### Write Documentation + +ETHOS.PeNALPS could always use more documentation, whether as part of the +official ETHOS.PeNALPS docs, in docstrings, or even on the web in blog posts, +articles, and such. + +### Submit Feedback + + +The best way to send feedback is to file an issue at https://github.com/FZJ-IEK3-VSA/ETHOS_PeNALPS/issues. + +If you are proposing a feature: + +- Explain in detail how it would work. +- Keep the scope as narrow as possible, to make it easier to implement. +- Remember that this is a volunteer-driven project, and that contributions + are welcome :) \ No newline at end of file diff --git a/paper/how_to_precompile.md b/paper/how_to_precompile.md index 942d86e..314da19 100644 --- a/paper/how_to_precompile.md +++ b/paper/how_to_precompile.md @@ -1,3 +1,12 @@ +# For Windows docker run --rm --volume %cd%/paper:/data --env JOURNAL=joss openjournals/inara Image Name: openjournals/inara -Volume path: Needs to be adjusted between windows and linux \ No newline at end of file +Volume path: Needs to be adjusted between windows and linux + + +# For Linux +docker run --rm \ + --volume $PWD/paper:/data \ + --user $(id -u):$(id -g) \ + --env JOURNAL=joss \ + openjournals/inara \ No newline at end of file diff --git a/paper/paper.jats b/paper/paper.jats index 74d644b..708cd80 100644 --- a/paper/paper.jats +++ b/paper/paper.jats @@ -367,6 +367,14 @@ a Creative Commons Attribution 4.0 International License (CC BY 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.

+
diff --git a/paper/paper.md b/paper/paper.md index 955037e..5919c60 100644 --- a/paper/paper.md +++ b/paper/paper.md @@ -117,6 +117,6 @@ The software eLOAD employs an approach similar to that from Sandhaas. Instead of # Authors Contribution -**Julian Belina**:Software, Writing, Visualization, Methodology.**Noah Pflugradt**:Conceptualization, Methodology, Supervision, Writing - Review & Editing.**Detlef Stolten**:Conceptualization, Phd Supervision, Resources, Funding acquisition. +**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/paper/paper.pdf b/paper/paper.pdf index f6a25b68d6d5c08cdd1766b7863e51ec11754051..f9535f493626f264b7284ca144ff63f1ed58c1be 100644 GIT binary patch delta 25092 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