From 4715013fe9ff63cdd5f5a7d245d25552f5076473 Mon Sep 17 00:00:00 2001 From: Simon Exner <43469235+0815Creeper@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:21:19 +0100 Subject: [PATCH 1/5] removed docs, changed Examples to match FMI.jl --- .github/workflows/Documentation.yml | 71 --------------- .github/workflows/Example.yml | 129 +++++++++++++++++----------- .gitignore | 2 + docs/.gitignore | 1 - docs/Project.toml | 5 -- docs/make.jl | 40 --------- docs/src/contents.md | 3 - docs/src/examples/overview.md | 8 -- docs/src/faq.md | 6 -- docs/src/features.md | 5 -- docs/src/library.md | 3 - docs/src/related.md | 5 -- examples/Project.toml | 10 +++ examples/README.md | 39 +++++++++ examples/jupyter-src/.gitignore | 3 + 15 files changed, 132 insertions(+), 198 deletions(-) delete mode 100644 .github/workflows/Documentation.yml delete mode 100644 docs/.gitignore delete mode 100644 docs/Project.toml delete mode 100644 docs/make.jl delete mode 100644 docs/src/contents.md delete mode 100644 docs/src/examples/overview.md delete mode 100644 docs/src/faq.md delete mode 100644 docs/src/features.md delete mode 100644 docs/src/library.md delete mode 100644 docs/src/related.md create mode 100644 examples/Project.toml create mode 100644 examples/README.md create mode 100644 examples/jupyter-src/.gitignore diff --git a/.github/workflows/Documentation.yml b/.github/workflows/Documentation.yml deleted file mode 100644 index 3275050..0000000 --- a/.github/workflows/Documentation.yml +++ /dev/null @@ -1,71 +0,0 @@ -name: Documentation - -on: - workflow_run: - workflows: ["Run Examples"] - types: - - completed - push: - branches: - - main - paths: - - 'docs/**' - - 'README.md' - tags: - - '*' - -jobs: - trigger: - runs-on: ubuntu-latest - outputs: - flag_filter: ${{ steps.filter.outputs.test_workflow }} - steps: - - name: "Check out repository" - uses: actions/checkout@v4 - - - name: "Set path filter" - uses: dorny/paths-filter@v3 - id: filter - with: - list-files: shell - base: 'main' - filters: | - test_workflow: - - 'src/**' - - 'test/**' - - '.github/**' - - build: - needs: trigger - runs-on: ubuntu-latest - if: ${{ !(needs.trigger.outputs.flag_filter == 'true' && github.event_name == 'push') && github.event.workflow_run.conclusion == 'success'}} - steps: - - name: "Check out repository" - uses: actions/checkout@v4 - with: - ref: main - - - name: "Copy examples, readme" - env: - SRC_BRANCH: 'origin/examples' - SRC_FOLDER_PATH: 'examples/' - run: | - git fetch - git --work-tree=./docs/src checkout ${SRC_BRANCH/} -- $SRC_FOLDER_PATH/**.md - # git --work-tree=./docs/src checkout ${SRC_BRANCH/} -- $SRC_FOLDER_PATH/**.md $SRC_FOLDER_PATH/**.svg - cp ./README.md ./docs/src/index.md - - - name: "Set up Julia" - uses: julia-actions/setup-julia@v2 - with: - version: '1.6' - arch: x64 - - - name: "Install dependencies" - run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - - - name: "Build and deploy" - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # For authentication with GitHub Actions token - DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # For authentication with SSH deploy key - run: julia --project=docs/ docs/make.jl diff --git a/.github/workflows/Example.yml b/.github/workflows/Example.yml index 6a14379..ad8c7ca 100644 --- a/.github/workflows/Example.yml +++ b/.github/workflows/Example.yml @@ -1,33 +1,32 @@ -name: Examples v1 (latest) +name: Examples on: - workflow_run: - workflows: ["Test v1 (latest)"] - types: - - completed + workflow_dispatch: + pull_request: push: - branches: - - examples - + branches: + - main + paths: + - 'src/**' + - 'examples/**' + - '.github/workflows/Example.yml' + - 'Project.toml' + jobs: - sync-files: - if: | - ${{ (github.event.workflow_run.conclusion == 'success' && github.sha == github.event.workflow_run.head_sha) || - (github.event_name == 'push' && github.event.head_commit.committer.name != 'github-actions[bot]') }} + jupyter: runs-on: ${{ matrix.os }} strategy: - fail-fast: true + fail-fast: false matrix: - julia-version: ['1.9'] + os: [windows-latest] # ubuntu-latest + file-name: [BouncingBall, Manipulation] + julia-version: ['1.10'] # 1.9 julia-arch: [x64] - os: [windows-latest] - experimental: [false] + experimental: [false] # true steps: - name: "Check out repository" uses: actions/checkout@v4 - with: - ref: examples - name: "Set up Julia" uses: julia-actions/setup-julia@v2 @@ -35,42 +34,70 @@ jobs: version: ${{ matrix.julia-version }} arch: ${{ matrix.julia-arch }} - - name: "Build package" - uses: julia-actions/julia-buildpkg@v1 + - name: "Install dependencies" + run: julia --project=examples/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - name: "Install packages" run: pip install jupyter nbconvert + + - name: "Execute notebook" + env: + FILE: examples/jupyter-src/${{ matrix.file-name }}.ipynb + run: jupyter nbconvert --ExecutePreprocessor.kernel_name="julia-${{ matrix.julia-version }}" --to notebook --inplace --execute ${{ env.FILE }} - - name: "Set path filter" - uses: dorny/paths-filter@v3 - id: filter - with: - list-files: shell - filters: | - all: - - 'examples/**.ipynb' - changed: - - added|modified: 'examples/**.ipynb' - base: 'examples' - ref: 'examples' - - - name: "Execute and synchronize changed files" - if: ${{ github.event_name == 'push' && steps.filter.outputs.changed == 'true' }} + - name: "Export notebook to jl and md" + env: + FILE: examples/jupyter-src/${{ matrix.file-name }}.ipynb run: | - jupyter nbconvert --ExecutePreprocessor.kernel_name="julia-1.9" --to notebook --inplace --execute ${{ steps.filter.outputs.changed_files }} - jupyter nbconvert --to script ${{ steps.filter.outputs.changed_files }} - jupyter nbconvert --to markdown ${{ steps.filter.outputs.changed_files }} - - - name: "Execute and synchronize all files" - if: ${{ github.event_name == 'workflow_run' && steps.filter.outputs.all == 'true'}} - run: | - jupyter nbconvert --ExecutePreprocessor.kernel_name="julia-1.9" --to notebook --inplace --execute ${{ steps.filter.outputs.all_files }} - jupyter nbconvert --to script ${{ steps.filter.outputs.all_files }} - jupyter nbconvert --to markdown ${{ steps.filter.outputs.all_files }} - - - name: "Auto commit" - if: ${{ matrix.os == 'ubuntu-latest'}} - uses: stefanzweifel/git-auto-commit-action@v5 + jupyter nbconvert --to script ${{ env.FILE }} + jupyter nbconvert --to markdown ${{ env.FILE }} + + - name: "run generated jl script to determine success of example building" + run: julia --project=examples/ examples/jupyter-src/${{ matrix.file-name }}.jl + + - name: "auto-commit (retry on merge)" + if: success() && github.event_name != 'pull_request' && github.ref_name == 'main' + uses: nick-fields/retry@v3 + env: + CI_COMMIT_MESSAGE: jupyter-example-${{ matrix.file-name }}-${{ matrix.os }}-${{ matrix.julia-version }}-${{ matrix.julia-arch }}-${{ matrix.experimental }}[${{ github.ref_name }}] + CI_COMMIT_AUTHOR: github-actions[bot] + EXAMPLES_PATH: examples/jupyter-src + # Fetch all and clear the stash list. Include all files from the examples folder to the stash and switch the branch. + # Reset the branch and remove all current files in the examples folder. + # Checkout the last stash to restore the new notebooks and apply the stash index to restore all other new files in the folder. + with: + timeout_minutes: 999 + max_attempts: 10 + warning_on_retry: false + shell: bash + command: | + git fetch --all + git stash clear + git stash --include-untracked -- ${{ env.EXAMPLES_PATH }}/${{ matrix.file-name }}* + git switch examples + git reset --hard origin/examples + rm -r ${{ env.EXAMPLES_PATH }}/${{ matrix.file-name }}* + git checkout stash -f -- ${{ env.EXAMPLES_PATH }} + git stash apply --index + git stash drop + git config --global user.name "${{ env.CI_COMMIT_AUTHOR }}" + git config --global user.email "${{ env.CI_COMMIT_AUTHOR }}@users.noreply.github.com" + git config --global core.autocrlf false + git pull + git reset + git add ${{ env.EXAMPLES_PATH }}/${{ matrix.file-name }}* + git commit -m "${{ env.CI_COMMIT_MESSAGE }}" + git push origin examples || (git reset --soft HEAD~1 && (exit 1)) + + call-docu: + needs: [jupyter] + if: github.event_name != 'pull_request' && github.ref_name == 'main' + runs-on: ubuntu-latest + steps: + # Trigger an repoisitory dispath event + - name: Repository Dispatch + uses: peter-evans/repository-dispatch@v3 with: - commit_message: Jupyter nbconvert synch - modified, paired .ipynb files - push_options: '--force-with-lease' + token: ${{ secrets.FMI_DOC_TRIGGER_PAT }} + repository: 'ThummeTo/FMIExport.jl' + event-type: trigger-docu diff --git a/.gitignore b/.gitignore index c823245..8b93b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,9 @@ docs/site/ # committed for packages, but should be committed for applications that require a static # environment. Manifest.toml +LocalPreferences.toml # Custom tmp/ .vscode/settings.json +examples/jupyter-src/.ipynb_checkpoints/ diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 567609b..0000000 --- a/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -build/ diff --git a/docs/Project.toml b/docs/Project.toml deleted file mode 100644 index 67a236d..0000000 --- a/docs/Project.toml +++ /dev/null @@ -1,5 +0,0 @@ -[deps] -Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" - -[compat] -julia = "1.6" diff --git a/docs/make.jl b/docs/make.jl deleted file mode 100644 index 39ae102..0000000 --- a/docs/make.jl +++ /dev/null @@ -1,40 +0,0 @@ -# -# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher -# Licensed under the MIT license. See LICENSE file in the project root for details. -# - -using Documenter, FMIExport -using Documenter: GitHubActions - -makedocs(sitename="FMIExport.jl", - format = Documenter.HTML( - collapselevel = 1, - sidebar_sitename = false, - edit_link = nothing - ), - pages= Any[ - "Introduction" => "index.md" - "Features" => "features.md" - "FAQ" => "faq.md" - "Examples" => [ - "Overview" => "examples/overview.md" - "BouncingBall" => "examples/BouncingBall.md" - "Manipulation" => "examples/Manipulation.md" - ] - "Library Functions" => "library.md" - "Related Publication" => "related.md" - "Contents" => "contents.md" - ] - ) - -function deployConfig() - github_repository = get(ENV, "GITHUB_REPOSITORY", "") - github_event_name = get(ENV, "GITHUB_EVENT_NAME", "") - if github_event_name == "workflow_run" - github_event_name = "push" - end - github_ref = get(ENV, "GITHUB_REF", "") - return GitHubActions(github_repository, github_event_name, github_ref) -end - -deploydocs(repo = "github.com/ThummeTo/FMIExport.jl.git", devbranch = "main", deploy_config = deployConfig()) diff --git a/docs/src/contents.md b/docs/src/contents.md deleted file mode 100644 index 5e000f7..0000000 --- a/docs/src/contents.md +++ /dev/null @@ -1,3 +0,0 @@ -```@contents -Depth = 2 -``` diff --git a/docs/src/examples/overview.md b/docs/src/examples/overview.md deleted file mode 100644 index c13852d..0000000 --- a/docs/src/examples/overview.md +++ /dev/null @@ -1,8 +0,0 @@ -# Overview - -This section discusses the included examples of the FMI.jl library. So you can execute them on your machine and get detailed information about all the steps. If you require further information about the function calls, see the function sections of the [library](https://thummeto.github.io/FMI.jl/dev/library/). - -The examples are: - -- [__BouncingBall__](https://thummeto.github.io/FMIExport.jl/dev/examples/BouncingBall/): BouncingBall -- [__Manipulation__](https://thummeto.github.io/FMIExport.jl/dev/examples/Manipulation/): Manipulation diff --git a/docs/src/faq.md b/docs/src/faq.md deleted file mode 100644 index 04a9a77..0000000 --- a/docs/src/faq.md +++ /dev/null @@ -1,6 +0,0 @@ - -# FAQ - -This list contains some common errors: - -## ToDo \ No newline at end of file diff --git a/docs/src/features.md b/docs/src/features.md deleted file mode 100644 index af51520..0000000 --- a/docs/src/features.md +++ /dev/null @@ -1,5 +0,0 @@ - -# Features -Please note, that this guide focuses also on users, that are not familiar with FMI. The following feature explanations are written in an easy-to-read-fashion, so there might be some points that are scientifically only 95% correct. For further information on FMI and FMUs, see [fmi-standard.org](http://fmi-standard.org/). - -## ToDo \ No newline at end of file diff --git a/docs/src/library.md b/docs/src/library.md deleted file mode 100644 index 8a7e646..0000000 --- a/docs/src/library.md +++ /dev/null @@ -1,3 +0,0 @@ -# Library Functions - -## ToDo \ No newline at end of file diff --git a/docs/src/related.md b/docs/src/related.md deleted file mode 100644 index 49ae13f..0000000 --- a/docs/src/related.md +++ /dev/null @@ -1,5 +0,0 @@ -# Related Publications - -Tobias Thummerer, Josef Kircher, Lars Mikelsons 2021 **NeuralFMU: Towards Structural Integration of FMUs into Neural Networks** (14th Modelica Conference, Preprint, Accepted) [arXiv:2109.04351](https://arxiv.org/abs/2109.04351) - -Tobias Thummerer, Johannes Tintenherr, Lars Mikelsons 2021 **Hybrid modeling of the human cardiovascular system using NeuralFMUs** (10th International Conference on Mathematical Modeling in Physical Sciences, Preprint, Accepted) [arXiv:2109.04880](https://arxiv.org/abs/2109.04880) diff --git a/examples/Project.toml b/examples/Project.toml new file mode 100644 index 0000000..b962411 --- /dev/null +++ b/examples/Project.toml @@ -0,0 +1,10 @@ +name = "Examples" + +[deps] +FMIBuild = "226f0e26-6dd6-4589-ada7-1d32f6e1d800" +FMIExport = "31b88311-cab6-44ed-ba9c-fe5a9abbd67a" +IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" + +[compat] +FMIBuild = "0.3.2" +julia = "1.6" diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..98b316f --- /dev/null +++ b/examples/README.md @@ -0,0 +1,39 @@ +![FMI.jl Logo](https://github.com/ThummeTo/FMI.jl/blob/main/logo/dark/fmijl_logo_640_320.png?raw=true "FMI.jl Logo") + +# Structure + +Examples can be found in the [examples folder in the examples branch](https://github.com/ThummeTo/FMIExport.jl/tree/examples/examples) or the [examples section of the documentation](https://thummeto.github.io/FMI.jl/dev/examples/overview/). All examples are available as Julia-Script (*.jl*), Jupyter-Notebook (*.ipynb*) and Markdown (*.md*). + + +# Getting Started + +## Install Jupyter in Visual Studio Code +The Jupyter Notebooks extension for Visual Studio Code can be [here](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter). + +## Add Julia Kernel to Jupyter +To run Julia as kernel in a jupyter notebook it is necessary to add the **IJulia** package. + +1. Start the Julia REPL. + + ``` + julia + ``` + +2. Select your environment. + ```julia + using Pkg + Pkg.activate("Your Env") + ``` + +3. Add and build the IJulia package by typing inside the Julia REPL. + + ```julia + using Pkg + Pkg.add("IJulia") + Pkg.build("IJulia") + ``` + +4. Now you should be able to choose a Julia kernel in a Jupyter notebook. + + +More information can be found [here](https://towardsdatascience.com/how-to-best-use-julia-with-jupyter-82678a482677). diff --git a/examples/jupyter-src/.gitignore b/examples/jupyter-src/.gitignore new file mode 100644 index 0000000..c80ee6f --- /dev/null +++ b/examples/jupyter-src/.gitignore @@ -0,0 +1,3 @@ +tmp/ +.ipynb_checkpoints/ +*.png \ No newline at end of file From 1c70dde80b2c1b6486357d6ffd488fdf9b9c7477 Mon Sep 17 00:00:00 2001 From: Simon Exner <43469235+0815Creeper@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:07:29 +0100 Subject: [PATCH 2/5] removed examples from CI, as they are non existing --- .github/workflows/Example.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Example.yml b/.github/workflows/Example.yml index ad8c7ca..2d0f15d 100644 --- a/.github/workflows/Example.yml +++ b/.github/workflows/Example.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [windows-latest] # ubuntu-latest - file-name: [BouncingBall, Manipulation] + file-name: [] julia-version: ['1.10'] # 1.9 julia-arch: [x64] experimental: [false] # true From 44c98c1c5b1e3413dde76d2f52d00e598a962ab2 Mon Sep 17 00:00:00 2001 From: Simon Exner <43469235+0815Creeper@users.noreply.github.com> Date: Tue, 29 Oct 2024 19:10:59 +0100 Subject: [PATCH 3/5] added working export example --- .github/workflows/Example.yml | 55 ++- .github/workflows/Formatter.yml | 16 + examples/jupyter-src/.gitignore | 3 +- .../jupyter-src/Export-BouncingBall.ipynb | 385 ++++++++++++++++++ 4 files changed, 455 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/Formatter.yml create mode 100644 examples/jupyter-src/Export-BouncingBall.ipynb diff --git a/.github/workflows/Example.yml b/.github/workflows/Example.yml index 2d0f15d..b2d08b6 100644 --- a/.github/workflows/Example.yml +++ b/.github/workflows/Example.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false matrix: os: [windows-latest] # ubuntu-latest - file-name: [] + file-name: [Export-BouncingBall] julia-version: ['1.10'] # 1.9 julia-arch: [x64] experimental: [false] # true @@ -89,8 +89,57 @@ jobs: git commit -m "${{ env.CI_COMMIT_MESSAGE }}" git push origin examples || (git reset --soft HEAD~1 && (exit 1)) + pluto: + runs-on: ubuntu-latest + steps: + - name: "Check out repository" + uses: actions/checkout@v4 + + - name: "Set up Julia" + uses: julia-actions/setup-julia@v2 + with: + version: '1.10' + + - name: "Execute pluto notebooks" + if: ${{ hashFiles('examples/pluto-src/') != '' }} + run: julia -e 'using Pkg; Pkg.add("PlutoSliderServer"); using PlutoSliderServer; PlutoSliderServer.export_directory("examples/pluto-src")' + + - name: "auto-commit (retry on merge)" + if: success() && github.event_name != 'pull_request' && github.ref_name == 'main' + uses: nick-fields/retry@v3 + env: + CI_COMMIT_MESSAGE: pluto-examples[${{ github.ref_name }}] + CI_COMMIT_AUTHOR: github-actions[bot] + EXAMPLES_PATH: examples/pluto-src + # Fetch all and clear the stash list. Include all files from the examples folder to the stash and switch the branch. + # Reset the branch and remove all current files in the examples folder. + # Checkout the last stash to restore the new notebooks and apply the stash index to restore all other new files in the folder. + with: + timeout_minutes: 999 + max_attempts: 10 + warning_on_retry: false + shell: bash + command: | + git fetch --all + git stash clear + git stash --include-untracked -- ${{ env.EXAMPLES_PATH }} + git switch examples + git reset --hard origin/examples + rm -r ${{ env.EXAMPLES_PATH }}/* + git checkout stash -f -- ${{ env.EXAMPLES_PATH }} + git stash apply --index + git stash drop + git config --global user.name "${{ env.CI_COMMIT_AUTHOR }}" + git config --global user.email "${{ env.CI_COMMIT_AUTHOR }}@users.noreply.github.com" + git config --global core.autocrlf false + git pull + git reset + git add ${{ env.EXAMPLES_PATH }} + git commit -m "${{ env.CI_COMMIT_MESSAGE }}" + git push origin examples || (git reset --soft HEAD~1 && (exit 1)) + call-docu: - needs: [jupyter] + needs: [jupyter, pluto] if: github.event_name != 'pull_request' && github.ref_name == 'main' runs-on: ubuntu-latest steps: @@ -99,5 +148,5 @@ jobs: uses: peter-evans/repository-dispatch@v3 with: token: ${{ secrets.FMI_DOC_TRIGGER_PAT }} - repository: 'ThummeTo/FMIExport.jl' + repository: 'ThummeTo/FMI.jl' event-type: trigger-docu diff --git a/.github/workflows/Formatter.yml b/.github/workflows/Formatter.yml new file mode 100644 index 0000000..1465935 --- /dev/null +++ b/.github/workflows/Formatter.yml @@ -0,0 +1,16 @@ +name: Format suggestions +on: + pull_request: + # this argument is not required if you don't use the `suggestion-label` input + types: [ opened, reopened, synchronize, labeled, unlabeled ] + workflow_dispatch: + +jobs: + code-style: + runs-on: ubuntu-latest + steps: + - uses: julia-actions/julia-format@v3 + with: + version: '1' # Set `version` to '1.0.54' if you need to use JuliaFormatter.jl v1.0.54 (default: '1') + suggestion-label: 'format-suggest' # leave this unset or empty to show suggestions for all PRs + diff --git a/examples/jupyter-src/.gitignore b/examples/jupyter-src/.gitignore index c80ee6f..4e67b9b 100644 --- a/examples/jupyter-src/.gitignore +++ b/examples/jupyter-src/.gitignore @@ -1,3 +1,4 @@ tmp/ .ipynb_checkpoints/ -*.png \ No newline at end of file +*.png +*.fmu \ No newline at end of file diff --git a/examples/jupyter-src/Export-BouncingBall.ipynb b/examples/jupyter-src/Export-BouncingBall.ipynb new file mode 100644 index 0000000..421c5ca --- /dev/null +++ b/examples/jupyter-src/Export-BouncingBall.ipynb @@ -0,0 +1,385 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "db4395fe-0874-4976-a0de-206787f79e77", + "metadata": {}, + "source": [ + "# Create a Bouncing Ball FMU\n", + "\n", + "Tutorial by Johannes Stoljar, Tobias Thummerer, Simon Exner | Last edit: October 29 2024\n", + "\n", + "## License" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b5524a2-a822-431b-a3d0-d9b417b64c87", + "metadata": {}, + "outputs": [], + "source": [ + "# Copyright (c) 2021 Tobias Thummerer, Lars Mikelsons, Josef Kircher, Johannes Stoljar\n", + "# Licensed under the MIT license.\n", + "# See LICENSE (https://github.com/thummeto/FMIExport.jl/blob/main/LICENSE) file in the project root for details." + ] + }, + { + "cell_type": "markdown", + "id": "684f4ee9-af54-4781-a49d-3f5d0d89c4fe", + "metadata": {}, + "source": [ + "## Motivation\n", + "\n", + "This Julia Package FMIExport.jl is motivated by the export of simulation models in Julia. Here the FMI specification is implemented. FMI (Functional Mock-up Interface) is a free standard ([fmi-standard.org](https://fmi-standard.org)) that defines a container and an interface to exchange dynamic models using a combination of XML files, binaries and C code zipped into a single file. The user is able to create own FMUs (Functional Mock-up Units)." + ] + }, + { + "cell_type": "markdown", + "id": "be2ac71e-a3b0-4bc3-91d9-a4f0ec2a4377", + "metadata": {}, + "source": [ + "## Target group\n", + "\n", + "The example is primarily intended for users who work in the field of simulations. The example wants to show how simple it is to export FMUs in Julia." + ] + }, + { + "cell_type": "markdown", + "id": "7e21f7c8-d9d7-4259-9729-a69c6605b26d", + "metadata": {}, + "source": [ + "## Introduction to the example\n", + "\n", + "This example shows how to export a FMU from julia-code. It uses the BouncingBall FMU, that can be found on the main branch of FMIExport in [examples/FMI2/BouncingBall](https://github.com/ThummeTo/FMIExport.jl/tree/main/examples/FMI2/BouncingBall). This notebook will show you how to export it." + ] + }, + { + "cell_type": "markdown", + "id": "18e579c8-31c9-4753-8079-4bb1f082439b", + "metadata": {}, + "source": [ + "## Installation prerequisites\n", + "\n", + "| | Description | Command | Alternative |\n", + "|:----|:----------------------------------|:--------------------------|:-----------------------------------------------|\n", + "| 1. | Enter Package Manager via | ] | |\n", + "| 2. | Install FMIExport via | add FMIExport | add \"https://github.com/ThummeTo/FMIExport.jl\" |\n", + "| 3. | Install FMIBuild via | add FMIBuild | add \"https://github.com/ThummeTo/FMIBuild.jl\" |" + ] + }, + { + "cell_type": "markdown", + "id": "c354487b-99e1-4428-ad6f-cc535008df78", + "metadata": {}, + "source": [ + "## REPL-commands or build-script\n", + "\n", + "The way to do this usually will be the REPL, but if you plan on exporting FMUs in an automated way, you may want to use a jl script containing the following commands.\n", + "To run this example, the previously installed packages must be included." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1acea4a0-3817-4559-a47f-d880cc78d3e4", + "metadata": {}, + "outputs": [], + "source": [ + "using FMIExport\n", + "using FMIBuild: saveFMU" + ] + }, + { + "cell_type": "markdown", + "id": "a42e20da-526a-4d13-aeb4-6c758fc38003", + "metadata": {}, + "source": [ + "next we have to define where to put the generated files" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "edf9d24c-ee04-4371-9d62-70c6b6dfefa1", + "metadata": {}, + "outputs": [], + "source": [ + "tmpDir = mktempdir(; prefix=\"fmibuildjl_test_\", cleanup=false) \n", + "@info \"Saving example files at: $(tmpDir)\"\n", + "fmu_save_path = joinpath(tmpDir, \"BouncingBall.fmu\") " + ] + }, + { + "cell_type": "markdown", + "id": "8fb29e5a-0384-499c-807b-302a49193c95", + "metadata": {}, + "source": [ + "Remember, that we use the FMU-source stored at [examples/FMI2/BouncingBall](https://github.com/ThummeTo/FMIExport.jl/tree/main/examples/FMI2/BouncingBall). If you execute this notebook locally, make shure to ajust the fmu_source_path to where your FMU-Package resides. **It is important, that an absolute path is provided!** For this notebook to work in the automated bulid pipeline, this absolute path is obtained by the following instructions. If you run this example locally, you can provide the path manually, just make shure you use the correct directory seperator or just use just use julias `joinpath` function." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eb90aa47-5203-4480-bc5c-fa79ed240a6f", + "metadata": {}, + "outputs": [], + "source": [ + "working_dir = pwd() # current working directory\n", + "println(string(\"pwd() returns: \", working_dir))\n", + "\n", + "package_dir = split(working_dir, joinpath(\"examples\", \"jupyter-src\"))[1] # remove everything after and including \"examples\\jupyter-src\"\n", + "println(string(\"package_dir is \", package_dir))\n", + "\n", + "fmu_source_package = joinpath(package_dir, \"examples\", \"FMI2\", \"BouncingBall\") # add correct relative path\n", + "println(string(\"fmu_source_package is \", fmu_source_package))\n", + "\n", + "fmu_source_path = joinpath(fmu_source_package, \"src\", \"BouncingBall.jl\") # add correct relative path\n", + "println(string(\"fmu_source_path is \", fmu_source_path))" + ] + }, + { + "cell_type": "markdown", + "id": "625f5898-c269-4146-8284-c1bd0e005819", + "metadata": {}, + "source": [ + "TODO The following codecell contains *workardound* code that will be obsolete with the next release" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d86f0783-05fd-4cb3-9be6-5997ab9cfbdd", + "metadata": {}, + "outputs": [], + "source": [ + "using FMIExport.FMIBase.FMICore: fmi2True, fmi2False \n", + "\n", + "EPS = 1e-6\n", + "\n", + "FMU_FCT_INIT = function()\n", + " m = 1.0 # ball mass\n", + " r = 0.0 # ball radius\n", + " d = 0.7 # ball collision damping\n", + " v_min = 1e-1 # ball minimum velocity\n", + " g = 9.81 # gravity constant \n", + " sticking = fmi2False\n", + "\n", + " s = 1.0 # ball position\n", + " v = 0.0 # ball velocity\n", + " a = 0.0 # ball acceleration\n", + "\n", + " t = 0.0 \n", + " x_c = [s, v] \n", + " ẋ_c = [v, a]\n", + " x_d = [sticking]\n", + " u = []\n", + " p = [m, r, d, v_min, g]\n", + "\n", + " return (t, x_c, ẋ_c, x_d, u, p)\n", + "end\n", + "\n", + "FMU_FCT_EVALUATE = function(t, x_c, ẋ_c, x_d, u, p, eventMode)\n", + " m, r, d, v_min, g = p\n", + " s, v = x_c\n", + " sticking = x_d[1]\n", + " _, a = ẋ_c\n", + "\n", + " if sticking == fmi2True\n", + " a = 0.0\n", + " elseif sticking == fmi2False\n", + " if eventMode\n", + " if s < r && v < 0.0\n", + " s = r + EPS # so that indicator is not triggered again\n", + " v = -v*d \n", + " \n", + " # stop bouncing to prevent high frequency bouncing (and maybe tunneling the floor)\n", + " if abs(v) < v_min\n", + " sticking = fmi2True\n", + " v = 0.0\n", + " end\n", + " end\n", + " else\n", + " # no specials in continuos time mode\n", + " end\n", + "\n", + " a = (m * -g) / m # the system's physical equation (a little longer than necessary)\n", + " else\n", + " @error \"Unknown value for `sticking` == $(sticking).\"\n", + " return (x_c, ẋ_c, x_d, p)\n", + " end\n", + "\n", + " x_c = [s, v]\n", + " ẋ_c = [v, a]\n", + " x_d = [sticking]\n", + " p = [m, r, d, v_min, g]\n", + "\n", + " return (x_c, ẋ_c, x_d, p) # evaluation can't change discrete state!\n", + "end\n", + "\n", + "FMU_FCT_OUTPUT = function(t, x_c, ẋ_c, x_d, u, p)\n", + " m, r, d, v_min, g = p\n", + " s, v = x_c\n", + " _, a = ẋ_c\n", + " sticking = x_d[1]\n", + "\n", + " y = [s]\n", + "\n", + " return y\n", + "end\n", + "\n", + "FMU_FCT_EVENT = function(t, x_c, ẋ_c, x_d, u, p)\n", + " m, r, d, v_min, g = p\n", + " s, v = x_c\n", + " _, a = ẋ_c\n", + " sticking = x_d[1]\n", + " \n", + " if sticking == fmi2True\n", + " z1 = 1.0 # event 1: ball stay-on-ground\n", + " else\n", + " z1 = (s-r) # event 1: ball hits ground \n", + " end\n", + "\n", + " z = [z1]\n", + "\n", + " return z\n", + "end\n", + "FMIBUILD_CONSTRUCTOR = function(resPath=\"\")\n", + " fmu = fmi2CreateSimple(initializationFct=FMU_FCT_INIT,\n", + " evaluationFct=FMU_FCT_EVALUATE,\n", + " outputFct=FMU_FCT_OUTPUT,\n", + " eventFct=FMU_FCT_EVENT)\n", + "\n", + " fmu.modelDescription.modelName = \"BouncingBall\"\n", + "\n", + " # modes \n", + " fmi2ModelDescriptionAddModelExchange(fmu.modelDescription, \"BouncingBall\")\n", + "\n", + " # states [2]\n", + " fmi2AddStateAndDerivative(fmu, \"ball.s\"; stateDescr=\"Absolute position of ball center of mass\", derivativeDescr=\"Absolute velocity of ball center of mass\")\n", + " fmi2AddStateAndDerivative(fmu, \"ball.v\"; stateDescr=\"Absolute velocity of ball center of mass\", derivativeDescr=\"Absolute acceleration of ball center of mass\")\n", + "\n", + " # discrete state [1]\n", + " fmi2AddIntegerDiscreteState(fmu, \"sticking\"; description=\"Indicator (boolean) if the mass is sticking on the ground, as soon as abs(v) < v_min\")\n", + "\n", + " # outputs [1]\n", + " fmi2AddRealOutput(fmu, \"ball.s_out\"; description=\"Absolute position of ball center of mass\")\n", + "\n", + " # parameters [5]\n", + " fmi2AddRealParameter(fmu, \"m\"; description=\"Mass of ball\")\n", + " fmi2AddRealParameter(fmu, \"r\"; description=\"Radius of ball\")\n", + " fmi2AddRealParameter(fmu, \"d\"; description=\"Collision damping constant (velocity fraction after hitting the ground)\")\n", + " fmi2AddRealParameter(fmu, \"v_min\"; description=\"Minimal ball velocity to enter on-ground-state\")\n", + " fmi2AddRealParameter(fmu, \"g\"; description=\"Gravity constant\")\n", + "\n", + " fmi2AddEventIndicator(fmu)\n", + "\n", + " return fmu\n", + "end\n", + "fmu = FMIBUILD_CONSTRUCTOR()" + ] + }, + { + "cell_type": "markdown", + "id": "95a4ce82-9320-4776-b382-d9fe650d8d83", + "metadata": {}, + "source": [ + "TODO? It is questionable if this is the job of the library or the user... Currently it is not implemented and therefor the job of the user\n", + "\n", + "We need to make shure the fmu_source_package is instantiated" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7e04c11a-2afa-4d91-8bfc-1595aefa1aa6", + "metadata": {}, + "outputs": [], + "source": [ + "using Pkg\n", + "notebook_env = Base.active_project(); # save current enviroment to return to it after we are done\n", + "Pkg.activate(fmu_source_package); # activate the FMUs enviroment\n", + "\n", + "# make shure to use the same FMI source as in the enviroment of this example (\"notebook_env\"). \n", + "# As this example is automattically built using the local FMIExport package and not the one from the Juila registry, we need to add it using \"develop\". \n", + "Pkg.develop(PackageSpec(path=package_dir)); # If you added FMIExport using \"add FMIExport\", you have to remove this line and use instantiate instead.\n", + "# Pkg.instantiate(); # instantiate the FMUs enviroment only if develop was not previously called\n", + "\n", + "Pkg.activate(notebook_env); # return to the original notebooks enviroment" + ] + }, + { + "cell_type": "markdown", + "id": "c3c6ffee-d62f-40a1-826f-bccb5caffc36", + "metadata": {}, + "source": [ + "That is all the preperation, that was necessary. Now we can export the FMU. \n", + "\n", + "TODO The following codecell contains *workardound* code that will need to be modified with the next release" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ec7fe879-5ad4-4053-9676-eaf933b13804", + "metadata": {}, + "outputs": [], + "source": [ + "saveFMU(fmu, fmu_save_path, fmu_source_path; debug=false, compress=false) # feel free to set debug true, disabled for documentation building\n", + "#saveFMU(fmu_save_path, fmu_source_path; debug=false, compress=false) this meight be the format after the next release" + ] + }, + { + "cell_type": "markdown", + "id": "a511bfeb-df91-4aaf-85cf-5bb6b78a1b3f", + "metadata": {}, + "source": [ + "Now we will grab the generated FMU and move it to a path, where it will be included in this documentation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a4261b9-b5aa-4932-a5a5-6af70f5033a6", + "metadata": {}, + "outputs": [], + "source": [ + "mkpath(\"Export-BouncingBall_files\")\n", + "cp(fmu_save_path, joinpath(\"Export-BouncingBall_files\", \"BouncingBall.fmu\"))" + ] + }, + { + "cell_type": "markdown", + "id": "b4cee9f8-ec5c-433c-8f59-c1911583a305", + "metadata": {}, + "source": [ + "The FMU files generated by this example can be downloaded [here](https://github.com/ThummeTo/FMIExport.jl/tree/examples/examples/jupyter-src/Export-BouncingBall_files)" + ] + }, + { + "cell_type": "markdown", + "id": "1f982a2c-f7e6-45b9-a7d0-2e315ee0404e", + "metadata": {}, + "source": [ + "One current limitation of Julia-FMUs is, that they can not be imported back into Julia, as it is currently not allowed having two Julia-sys-images existing at the same time within the same process. (The Julia FMU comes bundeled with its own image) \n", + "\n", + "TODO Therefore we will test our generated FMU in Python unsing FMPy." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Julia 1.10.6", + "language": "julia", + "name": "julia-1.10" + }, + "language_info": { + "file_extension": ".jl", + "mimetype": "application/julia", + "name": "julia", + "version": "1.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 52606de219dc60f682867ef657ded4101a5a3eed Mon Sep 17 00:00:00 2001 From: Simon Exner <43469235+0815Creeper@users.noreply.github.com> Date: Mon, 4 Nov 2024 09:18:15 +0100 Subject: [PATCH 4/5] removed dummy link to fmu file download --- examples/jupyter-src/Export-BouncingBall.ipynb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/jupyter-src/Export-BouncingBall.ipynb b/examples/jupyter-src/Export-BouncingBall.ipynb index 421c5ca..3778e72 100644 --- a/examples/jupyter-src/Export-BouncingBall.ipynb +++ b/examples/jupyter-src/Export-BouncingBall.ipynb @@ -348,14 +348,6 @@ "cp(fmu_save_path, joinpath(\"Export-BouncingBall_files\", \"BouncingBall.fmu\"))" ] }, - { - "cell_type": "markdown", - "id": "b4cee9f8-ec5c-433c-8f59-c1911583a305", - "metadata": {}, - "source": [ - "The FMU files generated by this example can be downloaded [here](https://github.com/ThummeTo/FMIExport.jl/tree/examples/examples/jupyter-src/Export-BouncingBall_files)" - ] - }, { "cell_type": "markdown", "id": "1f982a2c-f7e6-45b9-a7d0-2e315ee0404e", From b3c6be1873e78774981c7079fa62616f252a59ab Mon Sep 17 00:00:00 2001 From: Simon Exner <43469235+0815Creeper@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:09:01 +0100 Subject: [PATCH 5/5] added placeholder info to example --- examples/jupyter-src/Export-BouncingBall.ipynb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/jupyter-src/Export-BouncingBall.ipynb b/examples/jupyter-src/Export-BouncingBall.ipynb index 3778e72..0fab250 100644 --- a/examples/jupyter-src/Export-BouncingBall.ipynb +++ b/examples/jupyter-src/Export-BouncingBall.ipynb @@ -9,6 +9,8 @@ "\n", "Tutorial by Johannes Stoljar, Tobias Thummerer, Simon Exner | Last edit: October 29 2024\n", "\n", + "### This is a placeholder example; it will be changed or replaced soon\n", + "\n", "## License" ] },