diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 678bd7fd..89d9e970 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,5 @@ stages: + - examples - test - doc - code_quality @@ -8,6 +9,7 @@ stages: variables: COVERAGE_TYPE: "Dymola" PAGES_BRANCH: master + GIT_REPO: RWTH-EBC/ebcpy include: - project: 'EBC/EBC_all/gitlab_ci/templates' @@ -22,5 +24,8 @@ include: file: 'python/unit-tests/unittest.gitlab-ci.yml' - project: 'EBC/EBC_all/gitlab_ci/templates' file: 'python/unit-tests/coverage.gitlab-ci.yml' + - project: 'EBC/EBC_all/gitlab_ci/templates' + file: 'python/examples/examples.gitlab-ci.yml' + ref: '18-add-example-file-converter' - template: Dependency-Scanning.gitlab-ci.yml - template: SAST.gitlab-ci.yml diff --git a/examples/converter.toml b/examples/converter.toml new file mode 100644 index 00000000..41c66cd3 --- /dev/null +++ b/examples/converter.toml @@ -0,0 +1,23 @@ +[[ExampleFile]] +# Add the path of where data is +filename = "examples/e1_time_series_data_example.py" +func_name = "main" +docstrings = true + +[[ExampleFile]] +# Add the path of where data is +filename = "examples/e2_fmu_example.py" +func_name = "main" +docstrings = true + +[[ExampleFile]] +# Add the path of where data is +filename = "examples/e3_dymola_example.py" +func_name = "main" +docstrings = true + +[[ExampleFile]] +# Add the path of where data is +filename = "examples/e4_optimization_example.py" +func_name = "main" +docstrings = true \ No newline at end of file diff --git a/examples/e1_time_series_data_example.ipynb b/examples/e1_time_series_data_example.ipynb new file mode 100644 index 00000000..71913b9d --- /dev/null +++ b/examples/e1_time_series_data_example.ipynb @@ -0,0 +1,169 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "\"\"\"\nGoals of this part of the examples:\n1. Learn how to use `TimeSeriesData`\n2. Understand why we use `TimeSeriesData`\n3. Get to know the different processing functions\n\"\"\"\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Start by importing all relevant packages\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import pathlib\nimport numpy as np\nimport matplotlib.pyplot as plt\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Imports from ebcpy\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "from ebcpy import TimeSeriesData\n\n\nwith_plot = True\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " First get the path with relevant input files:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "basepath = pathlib.Path(__file__).parents[1].joinpath(\"tutorial\", \"data\")\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Note: We often use pathlib. If you're not familiar and want to learn more,\n just search for any of the many tutorials available online.\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Instantiation of TimeSeriesData ##########################\n First we open a simulation result file (.mat)\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_mat = TimeSeriesData(basepath.joinpath('simulatedData.mat'))\nprint(tsd_mat)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Now a .csv. .xlsx works as well.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_csv = TimeSeriesData(basepath.joinpath('excelData.csv'))\nprint(tsd_csv)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Or construct like any pandas DataFrame\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_df = TimeSeriesData({\"A\": np.random.rand(100), \"B\": np.random.rand(100)})\nprint(tsd_df)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Why do we use TimeSeriesData? ##########################\n As you may have guessed, TimeSeriesData is just a plain old DataFrame.\n It inherits the standard one and adds functionalities used typically on\n energy related time series.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"TimeSeriesData inherits from\", TimeSeriesData.__base__)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Processing TimeSeriesData ##########################\n Index changing:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(tsd_csv.index)\ntsd_csv.to_datetime_index(unit_of_index=\"s\")\nprint(tsd_csv.index)\ntsd_csv.to_float_index(offset=0)\nprint(tsd_csv.index)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Some filter options\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_csv.low_pass_filter(crit_freq=0.1, filter_order=2,\n variable=\"outputs.TRoom\", new_tag=\"lowPass2\")\nprint(tsd_csv)\ntsd_csv.moving_average(window=50, variable=\"outputs.TRoom\",\n tag=\"raw\", new_tag=\"MovingAverage\")\nprint(tsd_csv)\nfor tag in tsd_csv.get_tags(variable=\"outputs.TRoom\")[::-1]:\n plt.plot(tsd_csv.loc[:, (\"outputs.TRoom\", tag)], label=tag)\nplt.legend()\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " How-to re-sample your data:\n Call the function. Desired frequency is a string (s: seconds), 60: 60 seconds.\n Play around with this value to see what happens.\n First convert to DateTimeIndex (required for this function)\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_csv.to_datetime_index(unit_of_index=\"s\")\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Create a copy to later reference the change.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_csv_ref = tsd_csv.copy()\ntsd_csv.clean_and_space_equally(desired_freq=\"60s\")\nplt.figure()\nplt.plot(tsd_csv_ref.loc[:, (\"outputs.TRoom\", \"raw\")], label=\"Reference\", color=\"blue\")\nplt.plot(tsd_csv.loc[:, (\"outputs.TRoom\", \"raw\")], label=\"Resampled\", color=\"red\")\nplt.legend()\nif with_plot:\n plt.show()\n\n\n\n" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/examples/e1_time_series_data_example.md b/examples/e1_time_series_data_example.md new file mode 100644 index 00000000..d6753a59 --- /dev/null +++ b/examples/e1_time_series_data_example.md @@ -0,0 +1,95 @@ +```python +""" +Goals of this part of the examples: +1. Learn how to use `TimeSeriesData` +2. Understand why we use `TimeSeriesData` +3. Get to know the different processing functions +""" +``` + Start by importing all relevant packages +```python +import pathlib +import numpy as np +import matplotlib.pyplot as plt +``` + Imports from ebcpy +```python +from ebcpy import TimeSeriesData + + +with_plot = True + +``` + First get the path with relevant input files: +```python +basepath = pathlib.Path(__file__).parents[1].joinpath("tutorial", "data") +``` + Note: We often use pathlib. If you're not familiar and want to learn more, + just search for any of the many tutorials available online. + ######################### Instantiation of TimeSeriesData ########################## + First we open a simulation result file (.mat) +```python +tsd_mat = TimeSeriesData(basepath.joinpath('simulatedData.mat')) +print(tsd_mat) +``` + Now a .csv. .xlsx works as well. +```python +tsd_csv = TimeSeriesData(basepath.joinpath('excelData.csv')) +print(tsd_csv) +``` + Or construct like any pandas DataFrame +```python +tsd_df = TimeSeriesData({"A": np.random.rand(100), "B": np.random.rand(100)}) +print(tsd_df) +``` + ######################### Why do we use TimeSeriesData? ########################## + As you may have guessed, TimeSeriesData is just a plain old DataFrame. + It inherits the standard one and adds functionalities used typically on + energy related time series. +```python +print("TimeSeriesData inherits from", TimeSeriesData.__base__) + +``` + ######################### Processing TimeSeriesData ########################## + Index changing: +```python +print(tsd_csv.index) +tsd_csv.to_datetime_index(unit_of_index="s") +print(tsd_csv.index) +tsd_csv.to_float_index(offset=0) +print(tsd_csv.index) +``` + Some filter options +```python +tsd_csv.low_pass_filter(crit_freq=0.1, filter_order=2, + variable="outputs.TRoom", new_tag="lowPass2") +print(tsd_csv) +tsd_csv.moving_average(window=50, variable="outputs.TRoom", + tag="raw", new_tag="MovingAverage") +print(tsd_csv) +for tag in tsd_csv.get_tags(variable="outputs.TRoom")[::-1]: + plt.plot(tsd_csv.loc[:, ("outputs.TRoom", tag)], label=tag) +plt.legend() + +``` + How-to re-sample your data: + Call the function. Desired frequency is a string (s: seconds), 60: 60 seconds. + Play around with this value to see what happens. + First convert to DateTimeIndex (required for this function) +```python +tsd_csv.to_datetime_index(unit_of_index="s") +``` + Create a copy to later reference the change. +```python +tsd_csv_ref = tsd_csv.copy() +tsd_csv.clean_and_space_equally(desired_freq="60s") +plt.figure() +plt.plot(tsd_csv_ref.loc[:, ("outputs.TRoom", "raw")], label="Reference", color="blue") +plt.plot(tsd_csv.loc[:, ("outputs.TRoom", "raw")], label="Resampled", color="red") +plt.legend() +if with_plot: + plt.show() + + + +``` diff --git a/examples/e2_fmu_example.ipynb b/examples/e2_fmu_example.ipynb new file mode 100644 index 00000000..50eee220 --- /dev/null +++ b/examples/e2_fmu_example.ipynb @@ -0,0 +1,239 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": " # FMU Example:\n Goals of this part of the examples:\n\n 1. Learn how to use the `FMU_API`\n 2. Understand model variables\n 3. Learn how to change variables to store (`result_names`)\n 4. Learn how to change parameters of a simulation\n 5. Learn how to change inputs of a simulation\n 6. Learn how to run simulations in parallel\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Start by importing all relevant packages\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import pathlib\nimport numpy as np\nimport matplotlib.pyplot as plt\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Imports from ebcpy\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "from ebcpy import FMU_API, TimeSeriesData\n\n\ncd = None\nn_cpu = 1\nlog_fmu = True\nn_sim = 5\noutput_interval = 100\nwith_plot = True\n\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " General settings\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "if cd is None:\n cd = pathlib.Path(__file__).parent.joinpath(\"results\")\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Simulation API Instantiation\n %% Setup the FMU-API:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "model_name = pathlib.Path(__file__).parent.joinpath(\"data\", \"HeatPumpSystemWithInput.fmu\")\nfmu_api = FMU_API(model_name=model_name,\n cd=cd,\n n_cpu=n_cpu,\n log_fmu=log_fmu)\nprint(\"Number of variables:\", len(fmu_api.variables))\nprint(\"Number of outputs:\", len(fmu_api.outputs))\nprint(\"Number of inputs:\", len(fmu_api.inputs))\nprint(\"Number of parameters:\", len(fmu_api.parameters))\nprint(\"Number of states:\", len(fmu_api.states))\nprint(\"Variables to store when simulating:\", fmu_api.result_names)\nprint(\"Outputs of the fmu\", fmu_api.outputs)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Simulation Setup Part\n Change the simulation settings:\n Which settings can I change?\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Supported setup options:\", fmu_api.get_simulation_setup_fields())\nfmu_api.sim_setup.start_time = 0\nfmu_api.sim_setup.stop_time = 3600\nfmu_api.sim_setup.output_interval = output_interval\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Or pass a dictionary. This makes using configs (toml, json) much easier\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "simulation_setup = {\"start_time\": 0,\n \"stop_time\": 3600,\n \"output_interval\": output_interval}\nfmu_api.set_sim_setup(sim_setup=simulation_setup)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Parameters\n Let's get some parameter to change, e.g. the capacity of the thermal mass:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(fmu_api.parameters['heaCap.C'])\nhea_cap_c = fmu_api.parameters['heaCap.C'].value\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Let's alter it from 10% to 1000 % in n_sim simulations:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "sizings = np.linspace(0.1, 10, n_sim)\nparameters = []\nfor sizing in sizings:\n parameters.append({\"heaCap.C\": hea_cap_c * sizing})\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Inputs\n Let's also change the input of the simulation:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Inputs names are:\", fmu_api.inputs)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " We only have TDryBul (outdoor air temperature) as an input.\n Start with the setup of a time-index that matches our simulation setup\n Feel free to play around with the settings to see what happens if your time_index is malformed.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "time_index = np.arange(\n fmu_api.sim_setup.start_time,\n fmu_api.sim_setup.stop_time,\n fmu_api.sim_setup.output_interval\n)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Apply some sinus function for the outdoor air temperature\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "t_dry_bulb = np.sin(time_index/3600*np.pi) * 10 + 263.15\ndf_inputs = TimeSeriesData({\"TDryBul\": t_dry_bulb}, index=time_index)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Warning: If you enable the following line you will trigger an error.\n It only goes to show that inputs to the simulation must contain clear\n tags.\n df_inputs[('TDryBul', 'constant_0_degC')] = 275.15\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Results to store\n As we vary the heating capacity,\n let's plot the influence on the temperature of said capacity:\n Per default, all outputs will be stored:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Results that will be stored\", fmu_api.result_names)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " In our case, we are not interested in Pel but in other states:\n First, the temperature\n Second, the input outdoor air temperature to see if our input is correctly used.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "fmu_api.result_names = [\"heatCap.T\", \"TDryBul\"]\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Oops, `heatCap.T` is not part of the model. We warn you about such typos.\n This way, you can easier debug your simulations if something goes wrong.\n Set the correct names:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "fmu_api.result_names = [\"heaCap.T\", \"TDryBul\"]\nprint(\"Results that will be stored\", fmu_api.result_names)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Execute simulation\n Pass the created list to the simulate function\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "results = fmu_api.simulate(parameters=parameters,\n inputs=df_inputs)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Closing\n Close the fmu. If you forget to do so,\n we call this function at the exit of your script.\n It deleted all extracted FMU files.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "fmu_api.close()\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ### Visualization\n Plot the result\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "fig, ax = plt.subplots(2, sharex=True)\nax[0].set_ylabel(\"TDryBul in K\")\nax[1].set_ylabel(\"T_Cap in K\")\nax[1].set_xlabel(\"Time in s\")\nax[0].plot(df_inputs, label=\"Inputs\", linestyle=\"--\")\nfor res, sizing in zip(results, sizings):\n ax[0].plot(res['TDryBul'])\n ax[1].plot(res['heaCap.T'], label=sizing)\nfor _ax in ax:\n _ax.legend(bbox_to_anchor=(1, 1.05), loc=\"upper left\")\n\nif with_plot:\n plt.show()\n\n\n\n" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/examples/e2_fmu_example.md b/examples/e2_fmu_example.md new file mode 100644 index 00000000..4b4f9d21 --- /dev/null +++ b/examples/e2_fmu_example.md @@ -0,0 +1,163 @@ + # FMU Example: + Goals of this part of the examples: + + 1. Learn how to use the `FMU_API` + 2. Understand model variables + 3. Learn how to change variables to store (`result_names`) + 4. Learn how to change parameters of a simulation + 5. Learn how to change inputs of a simulation + 6. Learn how to run simulations in parallel + Start by importing all relevant packages +```python +import pathlib +import numpy as np +import matplotlib.pyplot as plt +``` + Imports from ebcpy +```python +from ebcpy import FMU_API, TimeSeriesData + + +cd = None +n_cpu = 1 +log_fmu = True +n_sim = 5 +output_interval = 100 +with_plot = True + + +``` + General settings +```python +if cd is None: + cd = pathlib.Path(__file__).parent.joinpath("results") + +``` + ### Simulation API Instantiation + %% Setup the FMU-API: +```python +model_name = pathlib.Path(__file__).parent.joinpath("data", "HeatPumpSystemWithInput.fmu") +fmu_api = FMU_API(model_name=model_name, + cd=cd, + n_cpu=n_cpu, + log_fmu=log_fmu) +print("Number of variables:", len(fmu_api.variables)) +print("Number of outputs:", len(fmu_api.outputs)) +print("Number of inputs:", len(fmu_api.inputs)) +print("Number of parameters:", len(fmu_api.parameters)) +print("Number of states:", len(fmu_api.states)) +print("Variables to store when simulating:", fmu_api.result_names) +print("Outputs of the fmu", fmu_api.outputs) + +``` + ### Simulation Setup Part + Change the simulation settings: + Which settings can I change? +```python +print("Supported setup options:", fmu_api.get_simulation_setup_fields()) +fmu_api.sim_setup.start_time = 0 +fmu_api.sim_setup.stop_time = 3600 +fmu_api.sim_setup.output_interval = output_interval +``` + Or pass a dictionary. This makes using configs (toml, json) much easier +```python +simulation_setup = {"start_time": 0, + "stop_time": 3600, + "output_interval": output_interval} +fmu_api.set_sim_setup(sim_setup=simulation_setup) + +``` + ### Parameters + Let's get some parameter to change, e.g. the capacity of the thermal mass: +```python +print(fmu_api.parameters['heaCap.C']) +hea_cap_c = fmu_api.parameters['heaCap.C'].value +``` + Let's alter it from 10% to 1000 % in n_sim simulations: +```python +sizings = np.linspace(0.1, 10, n_sim) +parameters = [] +for sizing in sizings: + parameters.append({"heaCap.C": hea_cap_c * sizing}) + +``` + ### Inputs + Let's also change the input of the simulation: +```python +print("Inputs names are:", fmu_api.inputs) +``` + We only have TDryBul (outdoor air temperature) as an input. + Start with the setup of a time-index that matches our simulation setup + Feel free to play around with the settings to see what happens if your time_index is malformed. +```python +time_index = np.arange( + fmu_api.sim_setup.start_time, + fmu_api.sim_setup.stop_time, + fmu_api.sim_setup.output_interval +) +``` + Apply some sinus function for the outdoor air temperature +```python +t_dry_bulb = np.sin(time_index/3600*np.pi) * 10 + 263.15 +df_inputs = TimeSeriesData({"TDryBul": t_dry_bulb}, index=time_index) +``` + Warning: If you enable the following line you will trigger an error. + It only goes to show that inputs to the simulation must contain clear + tags. + df_inputs[('TDryBul', 'constant_0_degC')] = 275.15 + ### Results to store + As we vary the heating capacity, + let's plot the influence on the temperature of said capacity: + Per default, all outputs will be stored: +```python +print("Results that will be stored", fmu_api.result_names) +``` + In our case, we are not interested in Pel but in other states: + First, the temperature + Second, the input outdoor air temperature to see if our input is correctly used. +```python +fmu_api.result_names = ["heatCap.T", "TDryBul"] +``` + Oops, `heatCap.T` is not part of the model. We warn you about such typos. + This way, you can easier debug your simulations if something goes wrong. + Set the correct names: +```python +fmu_api.result_names = ["heaCap.T", "TDryBul"] +print("Results that will be stored", fmu_api.result_names) + +``` + ### Execute simulation + Pass the created list to the simulate function +```python +results = fmu_api.simulate(parameters=parameters, + inputs=df_inputs) + +``` + ### Closing + Close the fmu. If you forget to do so, + we call this function at the exit of your script. + It deleted all extracted FMU files. +```python +fmu_api.close() + +``` + ### Visualization + Plot the result +```python +fig, ax = plt.subplots(2, sharex=True) +ax[0].set_ylabel("TDryBul in K") +ax[1].set_ylabel("T_Cap in K") +ax[1].set_xlabel("Time in s") +ax[0].plot(df_inputs, label="Inputs", linestyle="--") +for res, sizing in zip(results, sizings): + ax[0].plot(res['TDryBul']) + ax[1].plot(res['heaCap.T'], label=sizing) +for _ax in ax: + _ax.legend(bbox_to_anchor=(1, 1.05), loc="upper left") + +if with_plot: + plt.show() + + + +``` diff --git a/examples/e2_fmu_example.py b/examples/e2_fmu_example.py index 11f68e9e..193d4aea 100644 --- a/examples/e2_fmu_example.py +++ b/examples/e2_fmu_example.py @@ -1,13 +1,13 @@ -""" -Goals of this part of the examples: +# # FMU Example: +# Goals of this part of the examples: +# +# 1. Learn how to use the `FMU_API` +# 2. Understand model variables +# 3. Learn how to change variables to store (`result_names`) +# 4. Learn how to change parameters of a simulation +# 5. Learn how to change inputs of a simulation +# 6. Learn how to run simulations in parallel -1. Learn how to use the `FMU_API` -2. Understand model variables -3. Learn how to change variables to store (`result_names`) -4. Learn how to change parameters of a simulation -5. Learn how to change inputs of a simulation -6. Learn how to run simulations in parallel -""" # Start by importing all relevant packages import pathlib import numpy as np @@ -45,7 +45,7 @@ def main( if cd is None: cd = pathlib.Path(__file__).parent.joinpath("results") - # ######################### Simulation API Instantiation ########################## + # ### Simulation API Instantiation # %% Setup the FMU-API: model_name = pathlib.Path(__file__).parent.joinpath("data", "HeatPumpSystemWithInput.fmu") fmu_api = FMU_API(model_name=model_name, @@ -60,7 +60,7 @@ def main( print("Variables to store when simulating:", fmu_api.result_names) print("Outputs of the fmu", fmu_api.outputs) - # ######################### Simulation Setup Part ########################## + # ### Simulation Setup Part # Change the simulation settings: # Which settings can I change? print("Supported setup options:", fmu_api.get_simulation_setup_fields()) @@ -73,7 +73,7 @@ def main( "output_interval": output_interval} fmu_api.set_sim_setup(sim_setup=simulation_setup) - # ######################### Parameters ########################## + # ### Parameters # Let's get some parameter to change, e.g. the capacity of the thermal mass: print(fmu_api.parameters['heaCap.C']) hea_cap_c = fmu_api.parameters['heaCap.C'].value @@ -83,7 +83,7 @@ def main( for sizing in sizings: parameters.append({"heaCap.C": hea_cap_c * sizing}) - # ######################### Inputs ########################## + # ### Inputs # Let's also change the input of the simulation: print("Inputs names are:", fmu_api.inputs) # We only have TDryBul (outdoor air temperature) as an input. @@ -102,7 +102,7 @@ def main( # tags. # df_inputs[('TDryBul', 'constant_0_degC')] = 275.15 - # ######################### Results to store ########################## + # ### Results to store # As we vary the heating capacity, # let's plot the influence on the temperature of said capacity: # Per default, all outputs will be stored: @@ -117,18 +117,18 @@ def main( fmu_api.result_names = ["heaCap.T", "TDryBul"] print("Results that will be stored", fmu_api.result_names) - # ######################### Execute simulation ########################## + # ### Execute simulation # Pass the created list to the simulate function results = fmu_api.simulate(parameters=parameters, inputs=df_inputs) - # ######################### Closing ########################## + # ### Closing # Close the fmu. If you forget to do so, # we call this function at the exit of your script. # It deleted all extracted FMU files. fmu_api.close() - # ######################### Visualization ########################## + # ### Visualization # Plot the result fig, ax = plt.subplots(2, sharex=True) ax[0].set_ylabel("TDryBul in K") diff --git a/examples/e3_dymola_example.ipynb b/examples/e3_dymola_example.ipynb new file mode 100644 index 00000000..3b37fb80 --- /dev/null +++ b/examples/e3_dymola_example.ipynb @@ -0,0 +1,212 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "\"\"\"\nGoals of this part of the examples:\n1. Learn how to use the `DymolaAPI`\n2. Learn the different result options of the simulation\n3. Learn how to convert inputs into the Dymola format\n\"\"\"\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Start by importing all relevant packages\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import pathlib\nimport time\nimport numpy as np\nimport matplotlib.pyplot as plt\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Imports from ebcpy\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "from ebcpy import DymolaAPI, TimeSeriesData, FMU_API\nfrom ebcpy.utils.conversion import convert_tsd_to_modelica_txt\n\n\naixlib_mo\ncd = None\nn_cpu = 1\nwith_plot = True\n\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " General settings\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "if cd is None:\n cd = pathlib.Path(__file__).parent.joinpath(\"results\")\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Simulation API Instantiation ##########################\n %% Setup the Dymola-API:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "dym_api = DymolaAPI(\n model_name=\"AixLib.Systems.HeatPumpSystems.Examples.HeatPumpSystem\",\n cd=cd,\n n_cpu=n_cpu,\n packages=[aixlib_mo],\n show_window=True,\n n_restart=-1,\n equidistant_output=False,\n get_structural_parameters=True\n # Only necessary if you need a specific dymola version\n #dymola_path=None,\n #dymola_version=None\n)\nprint(\"Number of variables:\", len(dym_api.variables))\nprint(\"Number of outputs:\", len(dym_api.outputs))\nprint(\"Number of inputs:\", len(dym_api.inputs))\nprint(\"Number of parameters:\", len(dym_api.parameters))\nprint(\"Number of states:\", len(dym_api.states))\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Settings ##########################\n To understand what is happening here we refer to fmu_example.py\n As both simulation_apis are based on the same class, most interfaces are equal.\n Only difference is the simulation setup:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Fields of DymolaAPISetup\", DymolaAPI.get_simulation_setup_fields())\nprint(\"Fields of FMU_APISetup\", FMU_API.get_simulation_setup_fields())\n\nsimulation_setup = {\"start_time\": 0,\n \"stop_time\": 3600,\n \"output_interval\": 100}\ndym_api.set_sim_setup(sim_setup=simulation_setup)\np_el_name = \"heatPumpSystem.heatPump.sigBus.PelMea\"\ndym_api.result_names = [p_el_name]\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Inputs ##########################\n Sadly, setting inputs directly is not supported in Dymola.\n Hence, you have to use the model `Modelica.Blocks.Sources.CombiTimeTable`.\n In the model \"AixLib.Systems.HeatPumpSystems.Examples.HeatPumpSystem\" we\n already use this model to simulate heat gains into the room\n We called the instance of the model `timTab`.\n To get the output of the table, let's add it to the result names:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "dym_api.result_names = [p_el_name, 'timTab.y[1]']\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " In order to change the inputs, you have to change the model in Dymola by:\n 1. Double click on timTab\n 2. Set tableOnFile = true\n 3. Set tableName = \"myCustomInput\" (or any other nice string)\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "table_name = \"myCustomInput\"\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " 4. Enter the fileName where you want to store your input. This can be any filepath.\n For this tutorial to work, set\n fileName=Modelica.Utilities.Files.loadResource(\"modelica://AixLib/Resources/my_custom_input.txt\")\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "file_name = pathlib.Path(aixlib_mo).parent.joinpath(\"Resources\", \"my_custom_input.txt\")\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " This input generate is re-used from the fmu_example.py file.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "time_index = np.arange(\n dym_api.sim_setup.start_time,\n dym_api.sim_setup.stop_time,\n dym_api.sim_setup.output_interval\n)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Apply some sinus function for the outdoor air temperature\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "internal_gains = np.sin(time_index/3600*np.pi) * 1000\ntsd_input = TimeSeriesData({\"InternalGains\": internal_gains}, index=time_index)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " To generate the input in the correct format, use the convert_tsd_to_modelica_txt function:\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "filepath = convert_tsd_to_modelica_txt(\n tsd=tsd_input,\n table_name=table_name,\n save_path_file=file_name\n)\nprint(\"Successfully created Dymola input file at\", filepath)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Simulation options ##########################\n Look at the doc of simulate() in the website\n Besides parameters (explained in fmu_example), return_option is important\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "result_time_series = dym_api.simulate(\n return_option=\"time_series\",\n # Info: You would not need these following keyword-arguments,\n # as we've already created our file above.\n # However, you can also pass the arguments\n # from above directly into the function call:\n inputs=tsd_input,\n table_name=table_name,\n file_name=file_name\n)\nprint(type(result_time_series))\nprint(result_time_series)\nresult_last_point = dym_api.simulate(\n return_option=\"last_point\"\n)\nprint(type(result_last_point))\nprint(result_last_point)\nresult_sp = dym_api.simulate(\n return_option=\"savepath\"\n)\nprint(result_sp)\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Or change the savepath by using two keyword arguments.\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "result_sp_2 = dym_api.simulate(\n return_option=\"savepath\",\n savepath=r\"D:\\00_temp\",\n result_file_name=\"anotherResultFile\"\n)\nprint(result_sp_2)\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Simulation analysis ##########################\n Now let's load the TimeSeriesData\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "tsd_1 = TimeSeriesData(result_sp)\ntsd_2 = TimeSeriesData(result_sp_2)\nprint(\"Both .mat's are equal:\", all(tsd_1 == tsd_2))\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Let's look at both results. The .mat-file contains more indexes as events are stored as well.\n The return_option 'time_series' omits these events (as does fmpy). Thus, it's less accurate.\n But, it's much faster!\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "print(\"Number of points for option 'time_series':\", len(result_time_series.index))\nprint(\"Number of points for option 'savepath':\", len(tsd_1.index))\nplt.plot(tsd_1[p_el_name], color=\"blue\", label=\"savepath\", marker=\"^\")\nplt.plot(result_time_series[p_el_name], color=\"red\", label=\"time_series\", marker=\"^\")\nplt.scatter(result_last_point[\"Time\"], result_last_point[p_el_name],\n color=\"black\", label=\"last_point\", marker=\"^\")\nplt.legend()\nplt.title(\"Difference in output for different return_options\")\nplt.figure()\nplt.plot(tsd_1['timTab.y[1]'], color=\"blue\")\nplt.title(\"Input of CombiTimeTable 'timTab'\")\nif with_plot:\n plt.show()\n\n\n\n" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/examples/e3_dymola_example.md b/examples/e3_dymola_example.md new file mode 100644 index 00000000..167737e6 --- /dev/null +++ b/examples/e3_dymola_example.md @@ -0,0 +1,183 @@ +```python +""" +Goals of this part of the examples: +1. Learn how to use the `DymolaAPI` +2. Learn the different result options of the simulation +3. Learn how to convert inputs into the Dymola format +""" +``` + Start by importing all relevant packages +```python +import pathlib +import time +import numpy as np +import matplotlib.pyplot as plt +``` + Imports from ebcpy +```python +from ebcpy import DymolaAPI, TimeSeriesData, FMU_API +from ebcpy.utils.conversion import convert_tsd_to_modelica_txt + + +aixlib_mo +cd = None +n_cpu = 1 +with_plot = True + + +``` + General settings +```python +if cd is None: + cd = pathlib.Path(__file__).parent.joinpath("results") + +``` + ######################### Simulation API Instantiation ########################## + %% Setup the Dymola-API: +```python +dym_api = DymolaAPI( + model_name="AixLib.Systems.HeatPumpSystems.Examples.HeatPumpSystem", + cd=cd, + n_cpu=n_cpu, + packages=[aixlib_mo], + show_window=True, + n_restart=-1, + equidistant_output=False, + get_structural_parameters=True + # Only necessary if you need a specific dymola version + #dymola_path=None, + #dymola_version=None +) +print("Number of variables:", len(dym_api.variables)) +print("Number of outputs:", len(dym_api.outputs)) +print("Number of inputs:", len(dym_api.inputs)) +print("Number of parameters:", len(dym_api.parameters)) +print("Number of states:", len(dym_api.states)) + +``` + ######################### Settings ########################## + To understand what is happening here we refer to fmu_example.py + As both simulation_apis are based on the same class, most interfaces are equal. + Only difference is the simulation setup: +```python +print("Fields of DymolaAPISetup", DymolaAPI.get_simulation_setup_fields()) +print("Fields of FMU_APISetup", FMU_API.get_simulation_setup_fields()) + +simulation_setup = {"start_time": 0, + "stop_time": 3600, + "output_interval": 100} +dym_api.set_sim_setup(sim_setup=simulation_setup) +p_el_name = "heatPumpSystem.heatPump.sigBus.PelMea" +dym_api.result_names = [p_el_name] + +``` + ######################### Inputs ########################## + Sadly, setting inputs directly is not supported in Dymola. + Hence, you have to use the model `Modelica.Blocks.Sources.CombiTimeTable`. + In the model "AixLib.Systems.HeatPumpSystems.Examples.HeatPumpSystem" we + already use this model to simulate heat gains into the room + We called the instance of the model `timTab`. + To get the output of the table, let's add it to the result names: +```python +dym_api.result_names = [p_el_name, 'timTab.y[1]'] +``` + In order to change the inputs, you have to change the model in Dymola by: + 1. Double click on timTab + 2. Set tableOnFile = true + 3. Set tableName = "myCustomInput" (or any other nice string) +```python +table_name = "myCustomInput" +``` + 4. Enter the fileName where you want to store your input. This can be any filepath. + For this tutorial to work, set + fileName=Modelica.Utilities.Files.loadResource("modelica://AixLib/Resources/my_custom_input.txt") +```python +file_name = pathlib.Path(aixlib_mo).parent.joinpath("Resources", "my_custom_input.txt") +``` + This input generate is re-used from the fmu_example.py file. +```python +time_index = np.arange( + dym_api.sim_setup.start_time, + dym_api.sim_setup.stop_time, + dym_api.sim_setup.output_interval +) +``` + Apply some sinus function for the outdoor air temperature +```python +internal_gains = np.sin(time_index/3600*np.pi) * 1000 +tsd_input = TimeSeriesData({"InternalGains": internal_gains}, index=time_index) +``` + To generate the input in the correct format, use the convert_tsd_to_modelica_txt function: +```python +filepath = convert_tsd_to_modelica_txt( + tsd=tsd_input, + table_name=table_name, + save_path_file=file_name +) +print("Successfully created Dymola input file at", filepath) + +``` + ######################### Simulation options ########################## + Look at the doc of simulate() in the website + Besides parameters (explained in fmu_example), return_option is important +```python +result_time_series = dym_api.simulate( + return_option="time_series", + # Info: You would not need these following keyword-arguments, + # as we've already created our file above. + # However, you can also pass the arguments + # from above directly into the function call: + inputs=tsd_input, + table_name=table_name, + file_name=file_name +) +print(type(result_time_series)) +print(result_time_series) +result_last_point = dym_api.simulate( + return_option="last_point" +) +print(type(result_last_point)) +print(result_last_point) +result_sp = dym_api.simulate( + return_option="savepath" +) +print(result_sp) +``` + Or change the savepath by using two keyword arguments. +```python +result_sp_2 = dym_api.simulate( + return_option="savepath", + savepath=r"D:\00_temp", + result_file_name="anotherResultFile" +) +print(result_sp_2) + +``` + ######################### Simulation analysis ########################## + Now let's load the TimeSeriesData +```python +tsd_1 = TimeSeriesData(result_sp) +tsd_2 = TimeSeriesData(result_sp_2) +print("Both .mat's are equal:", all(tsd_1 == tsd_2)) +``` + Let's look at both results. The .mat-file contains more indexes as events are stored as well. + The return_option 'time_series' omits these events (as does fmpy). Thus, it's less accurate. + But, it's much faster! +```python +print("Number of points for option 'time_series':", len(result_time_series.index)) +print("Number of points for option 'savepath':", len(tsd_1.index)) +plt.plot(tsd_1[p_el_name], color="blue", label="savepath", marker="^") +plt.plot(result_time_series[p_el_name], color="red", label="time_series", marker="^") +plt.scatter(result_last_point["Time"], result_last_point[p_el_name], + color="black", label="last_point", marker="^") +plt.legend() +plt.title("Difference in output for different return_options") +plt.figure() +plt.plot(tsd_1['timTab.y[1]'], color="blue") +plt.title("Input of CombiTimeTable 'timTab'") +if with_plot: + plt.show() + + + +``` diff --git a/examples/e4_optimization_example.ipynb b/examples/e4_optimization_example.ipynb new file mode 100644 index 00000000..43c5e75f --- /dev/null +++ b/examples/e4_optimization_example.ipynb @@ -0,0 +1,68 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "\"\"\"\nGoals of this part of the examples:\n1. Learn how to create a custom `Optimizer` class\n2. Learn the different optimizer frameworks\n3. Learn the usage of `StatisticsAnalyzer`\n4. Understand the motivation behing `AixCaliBuHA`\n\"\"\"\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Start by importing all relevant packages\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import matplotlib.pyplot as plt\nimport numpy as np\nimport sklearn.metrics as skmetrics\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " Imports from ebcpy\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "from ebcpy.optimization import Optimizer\nfrom ebcpy.utils.statistics_analyzer import StatisticsAnalyzer\n\n\nstatistical_measure = \"MAE\"\nwith_plot = True\n\n\n" + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": " ######################### Class definition ##########################\n To create a custom optimizer, one needs to inherit from the Optimizer\n" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "class PolynomalFitOptimizer(Optimizer):\n \n" + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/examples/e4_optimization_example.md b/examples/e4_optimization_example.md new file mode 100644 index 00000000..70336484 --- /dev/null +++ b/examples/e4_optimization_example.md @@ -0,0 +1,32 @@ +```python +""" +Goals of this part of the examples: +1. Learn how to create a custom `Optimizer` class +2. Learn the different optimizer frameworks +3. Learn the usage of `StatisticsAnalyzer` +4. Understand the motivation behing `AixCaliBuHA` +""" +``` + Start by importing all relevant packages +```python +import matplotlib.pyplot as plt +import numpy as np +import sklearn.metrics as skmetrics +``` + Imports from ebcpy +```python +from ebcpy.optimization import Optimizer +from ebcpy.utils.statistics_analyzer import StatisticsAnalyzer + + +statistical_measure = "MAE" +with_plot = True + + +``` + ######################### Class definition ########################## + To create a custom optimizer, one needs to inherit from the Optimizer +```python +class PolynomalFitOptimizer(Optimizer): + +```