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..b2d08b6 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: [Export-BouncingBall] + 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,119 @@ 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)) + + 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, pluto] + 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/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/.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..4e67b9b --- /dev/null +++ b/examples/jupyter-src/.gitignore @@ -0,0 +1,4 @@ +tmp/ +.ipynb_checkpoints/ +*.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..0fab250 --- /dev/null +++ b/examples/jupyter-src/Export-BouncingBall.ipynb @@ -0,0 +1,379 @@ +{ + "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", + "### This is a placeholder example; it will be changed or replaced soon\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": "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 +}