From 0e6ad7937a6e67747907f991b4c2ca594610d178 Mon Sep 17 00:00:00 2001 From: alexdennis312 Date: Mon, 8 Jul 2024 13:46:34 -0500 Subject: [PATCH 1/7] Created PID generator. --- xopt/generators/pid/__init__.py | 0 xopt/generators/pid/pid.py | 49 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 xopt/generators/pid/__init__.py create mode 100644 xopt/generators/pid/pid.py diff --git a/xopt/generators/pid/__init__.py b/xopt/generators/pid/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/xopt/generators/pid/pid.py b/xopt/generators/pid/pid.py new file mode 100644 index 00000000..485288b8 --- /dev/null +++ b/xopt/generators/pid/pid.py @@ -0,0 +1,49 @@ +from pydantic import field_validator +from pydantic_core.core_schema import ValidationInfo +from simple_pid import PID +from xopt.generator import Generator + +class PIDGenerator(Generator): + #inputs when creating generator + target_value: float + Kp: float = 1.0 + Ki: float = 0.0 + Kd: float = 0.0 + sim_time: float = 1.0 #set to 0 if not a simulation + + #internal variables + pid: PID = None + time: float = 0 #this variable only used in simulations + + + @field_validator("vocs", mode="after") + def validate_vocs(cls, v, info: ValidationInfo): + if v.n_variables != 1: + raise ValueError("this generator only supports one variable") + + if v.n_observables != 1: + raise ValueError("this generator only supports one observable") + return v + + + def generate(self, n_candidates) -> list[dict]: + if self.pid is None: + self.pid = PID(self.Kp, self.Ki, self.Kd, self.target_value) + self.pid.output_limits = self.vocs.variables[self.vocs.variable_names[0]] + + if n_candidates != 1: + raise NotImplementedError() + + #get the last value + last_value = float(self.data[self.vocs.observable_names].iloc[-1,0]) + + #run pid controller + if self.sim_time>0: + self.time += self.sim_time + control_value = self.pid(last_value, self.time) + else: + control_value = self.pid(last_value) + + return [{self.vocs.variable_names[0]: control_value}] + + \ No newline at end of file From cd879f5620a440f77927ea8054a7b766264098cd Mon Sep 17 00:00:00 2001 From: alexdennis312 Date: Mon, 8 Jul 2024 13:47:32 -0500 Subject: [PATCH 2/7] Update pid.py --- xopt/generators/pid/pid.py | 1 - 1 file changed, 1 deletion(-) diff --git a/xopt/generators/pid/pid.py b/xopt/generators/pid/pid.py index 485288b8..113afce1 100644 --- a/xopt/generators/pid/pid.py +++ b/xopt/generators/pid/pid.py @@ -46,4 +46,3 @@ def generate(self, n_candidates) -> list[dict]: return [{self.vocs.variable_names[0]: control_value}] - \ No newline at end of file From ec7464e44ee9e220a5c223cd0459a57f2c0c95ad Mon Sep 17 00:00:00 2001 From: alexdennis312 Date: Mon, 29 Jul 2024 16:01:42 -0500 Subject: [PATCH 3/7] Fixed integral term Integral term of PID controller broken, fixed now. --- xopt/generators/pid/pid.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/xopt/generators/pid/pid.py b/xopt/generators/pid/pid.py index 113afce1..9afaf4dc 100644 --- a/xopt/generators/pid/pid.py +++ b/xopt/generators/pid/pid.py @@ -3,18 +3,17 @@ from simple_pid import PID from xopt.generator import Generator + class PIDGenerator(Generator): - #inputs when creating generator + # inputs when creating generator target_value: float Kp: float = 1.0 Ki: float = 0.0 Kd: float = 0.0 - sim_time: float = 1.0 #set to 0 if not a simulation + sim_time: float = 1.0 # set to 0 if not a simulation - #internal variables + # internal variables pid: PID = None - time: float = 0 #this variable only used in simulations - @field_validator("vocs", mode="after") def validate_vocs(cls, v, info: ValidationInfo): @@ -25,7 +24,6 @@ def validate_vocs(cls, v, info: ValidationInfo): raise ValueError("this generator only supports one observable") return v - def generate(self, n_candidates) -> list[dict]: if self.pid is None: self.pid = PID(self.Kp, self.Ki, self.Kd, self.target_value) @@ -34,15 +32,13 @@ def generate(self, n_candidates) -> list[dict]: if n_candidates != 1: raise NotImplementedError() - #get the last value - last_value = float(self.data[self.vocs.observable_names].iloc[-1,0]) + # get the last value + last_value = float(self.data[self.vocs.observable_names].iloc[-1, 0]) - #run pid controller - if self.sim_time>0: - self.time += self.sim_time - control_value = self.pid(last_value, self.time) + # run pid controller + if self.sim_time > 0: + control_value = self.pid(last_value, self.sim_time) else: control_value = self.pid(last_value) return [{self.vocs.variable_names[0]: control_value}] - From ad66b509eec4eca44635ffc9cbed0407474f3307 Mon Sep 17 00:00:00 2001 From: alexdennis312 <157322906+alexdennis312@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:28:44 -0500 Subject: [PATCH 4/7] Create pid --- docs/examples/pid/pid | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/examples/pid/pid diff --git a/docs/examples/pid/pid b/docs/examples/pid/pid new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/docs/examples/pid/pid @@ -0,0 +1 @@ + From e26b7461754f8f364862ef53fc48cb5c14ba96bb Mon Sep 17 00:00:00 2001 From: alexdennis312 <157322906+alexdennis312@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:29:05 -0500 Subject: [PATCH 5/7] added example --- docs/examples/pid/pid.ipynb | 228 ++++++++++++++++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 docs/examples/pid/pid.ipynb diff --git a/docs/examples/pid/pid.ipynb b/docs/examples/pid/pid.ipynb new file mode 100644 index 00000000..d59a3305 --- /dev/null +++ b/docs/examples/pid/pid.ipynb @@ -0,0 +1,228 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "a0a8a10c-2954-4fa8-baa4-a6725770144b", + "metadata": {}, + "source": [ + "# Xopt PID generator example\n", + "Here we will demonstrate how to create a simple PID controller to control the temperature of a room in a simulation.\n", + "\n", + "First we create the simulation:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b605d4e5-f512-44f5-819e-ef941884a81e", + "metadata": {}, + "outputs": [], + "source": [ + "# from xopt.generators.pid.pid import PIDGenerator\n", + "from xopt import Evaluator\n", + "from xopt import VOCS\n", + "from xopt import Xopt\n", + "import matplotlib.pyplot as plt\n", + "import pandas as pd\n", + "\n", + "\n", + "# Simulates a cooling room that starts at 95.6 degrees and decreases a certain amount depending on power output.\n", + "# Adds some temperature with every step to simulate a hot surrounding environment.\n", + "# Clearly not completely accurate to real life, but a good enough simulation.\n", + "\n", + "initial_temp = 95.6\n", + "current_temp = initial_temp\n", + "time_step = 1 # defines how much \"time\" passes per step in the simulation\n", + "\n", + "# Creating the simulation within the evaluate function\n", + "\n", + "\n", + "def evaluate_function(inputs: dict) -> dict:\n", + " global current_temp\n", + " power = inputs[\"power\"]\n", + " if power > 0:\n", + " current_temp -= (0.1 * power * time_step)\n", + " current_temp += time_step\n", + " return {\"f\": current_temp}\n", + "\n", + "\n", + "evaluator = Evaluator(function=evaluate_function)" + ] + }, + { + "cell_type": "markdown", + "id": "b1cd7870-b1e6-4985-b8b0-4aeb2475fa1f", + "metadata": {}, + "source": [ + "## Next define VOCS, generator, and Xopt object" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ab9cb793-0cbd-4440-a1d9-d4cbe84793a9", + "metadata": {}, + "outputs": [], + "source": [ + "# create generator and vocs\n", + "vocs = VOCS(\n", + " variables={\"power\": [0, 60]}, # [0,60] specifies the range that power can take\n", + " observables=[\"f\"],\n", + ")\n", + "\n", + "generator = PIDGenerator(target_value=80.0,\n", + " # We make these pid constants negative because we are decreasing the temperature.\n", + " # If trying to increase a value, all constants should be positive.\n", + " Kp=-6.0,\n", + " Ki=-0.2,\n", + " Kd=0.0,\n", + " sim_time=1.0, # defines how much time passes in a step. If not a simulation, set to 0.\n", + " vocs=vocs)\n", + "\n", + "X = Xopt(evaluator=evaluator, vocs=vocs, generator=generator)" + ] + }, + { + "cell_type": "markdown", + "id": "1a3cf534-a069-42f2-82ca-2fb7b8b36464", + "metadata": {}, + "source": [ + "## Now we run the pid controller to reduce the temperature to our target value." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b1dcac79-de91-44ad-a60b-c7e56dc046e5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " f power xopt_runtime xopt_error\n", + "0 95.600000 NaN NaN NaN\n", + "1 90.600000 60.000000 0.000015 False\n", + "2 85.600000 60.000000 0.000010 False\n", + "3 82.604000 39.960000 0.000009 False\n", + "4 81.353520 22.504800 0.000009 False\n", + "5 80.826258 15.272624 0.000011 False\n", + "6 80.598827 12.274301 0.000033 False\n", + "7 80.495879 11.029486 0.000011 False\n", + "8 80.444782 10.510970 0.000009 False\n", + "9 80.415447 10.293344 0.000009 False\n", + "10 80.395405 10.200427 0.000011 False\n", + "11 80.379480 10.159252 0.000009 False\n", + "12 80.365520 10.139597 0.000010 False\n", + "13 80.352626 10.128943 0.000010 False\n", + "14 80.340415 10.122102 0.000007 False\n", + "15 80.328723 10.116924 0.000007 False\n", + "16 80.317472 10.112514 0.000007 False\n", + "17 80.306622 10.108500 0.000006 False\n", + "18 80.296149 10.104724 0.000007 False\n", + "19 80.286037 10.101120 0.000007 False\n", + "20 80.276272 10.097655 0.000009 False\n", + "21 80.266840 10.094316 0.000012 False\n", + "22 80.257731 10.091095 0.000009 False\n", + "23 80.248932 10.087984 0.000009 False\n", + "24 80.240434 10.084980 0.000011 False\n", + "25 80.232226 10.082079 0.000010 False\n", + "26 80.224299 10.079277 0.000009 False\n", + "27 80.216642 10.076570 0.000009 False\n", + "28 80.209246 10.073956 0.000010 False\n", + "29 80.202103 10.071432 0.000016 False\n", + "30 80.195203 10.068993 0.000009 False\n", + "31 80.188540 10.066638 0.000010 False\n", + "32 80.182103 10.064363 0.000011 False\n", + "33 80.175887 10.062166 0.000010 False\n", + "34 80.169882 10.060044 0.000010 False\n", + "35 80.164083 10.057994 0.000011 False\n", + "36 80.158482 10.056014 0.000011 False\n", + "37 80.153071 10.054102 0.000008 False\n", + "38 80.147846 10.052255 0.000009 False\n", + "39 80.142799 10.050471 0.000007 False\n", + "40 80.137924 10.048748 0.000007 False\n", + "41 80.133215 10.047084 0.000004 False\n", + "42 80.128668 10.045477 0.000005 False\n", + "43 80.124275 10.043924 0.000006 False\n", + "44 80.120033 10.042425 0.000004 False\n", + "45 80.115935 10.040976 0.000009 False\n", + "46 80.111977 10.039578 0.000007 False\n", + "47 80.108155 10.038227 0.000009 False\n", + "48 80.104463 10.036922 0.000006 False\n", + "49 80.100897 10.035661 0.000007 False\n", + "50 80.097452 10.034444 0.000005 False\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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VcrNzVVmZqYqKyvV2NhoUcXJbdWqVZo4cWKg0VJ5eblefPHFwH6udWzdc889stlsWrx4cWAb19w8d955p2w2W9CrrKwssN/qa5304eMPf/iDli5dqjvuuENvvfWWJk2apNmzZ6upqcnq0pLe4cOHNWnSJK1cubLf/ffee68eeOABPfTQQ9q6datGjBih2bNnq729Pc6VDg/V1dWqqqpSTU2NXnnlFXV1delLX/qSDh8+HDhmyZIl2rhxo9avX6/q6mrV19drzpw5FladvIqLi3XPPfeotrZW27dv18yZM3Xttddq165dkrjWsbRt2zatXr1aEydODNrONTfXueeeq0OHDgVer7/+emCf5dfaSHKXXHKJUVVVFfi+p6fHKCoqMpYvX25hVcOPJGPDhg2B730+n1FQUGDcd999gW3Nzc2Gy+UynnrqKQsqHH6ampoMSUZ1dbVhGL3XNzU11Vi/fn3gmD179hiSjC1btlhV5rAycuRI49FHH+Vax1Bra6sxduxY45VXXjGuvPJKY9GiRYZh8O/bbHfccYcxadKkfvclwrVO6pGPzs5O1dbWqqKiIrDNbreroqJCW7ZssbCy4W///v1qaGgIuvYej0dTp07l2pukpaVFkpSTkyNJqq2tVVdXV9A1LysrU2lpKdc8Sj09PVq3bp0OHz6s8vJyrnUMVVVV6Stf+UrQtZX49x0Le/fuVVFRkc4880zNnTtXBw4ckJQY1zrhHiwXjn/+85/q6elRfn5+0Pb8/Hy99957FlV1amhoaJCkfq+9fx8i5/P5tHjxYl122WU677zzJPVec6fTqezs7KBjueaR27lzp8rLy9Xe3q7MzExt2LBBEyZM0I4dO7jWMbBu3Tq99dZb2rZt20n7+PdtrqlTp2rt2rUaN26cDh06pLvuuktXXHGF3n333YS41kkdPoDhqqqqSu+++27QPVqYb9y4cdqxY4daWlr09NNPa968eaqurra6rGHp4MGDWrRokV555RWlpaVZXc6wd9VVVwW+njhxoqZOnarRo0frj3/8o9LT0y2srFdS33Y57bTT5HA4Tpqh29jYqIKCAouqOjX4ry/X3nwLFy7UCy+8oL/85S8qLi4ObC8oKFBnZ6eam5uDjueaR87pdOrss8/W5MmTtXz5ck2aNEm//e1vudYxUFtbq6amJl100UVKSUlRSkqKqqur9cADDyglJUX5+flc8xjKzs7WOeeco3379iXEv++kDh9Op1OTJ0/Wpk2bAtt8Pp82bdqk8vJyCysb/saMGaOCgoKga+/1erV161aufYQMw9DChQu1YcMGvfrqqxozZkzQ/smTJys1NTXomtfV1enAgQNcc5P4fD51dHRwrWNg1qxZ2rlzp3bs2BF4TZkyRXPnzg18zTWPnba2Nn3wwQcqLCxMjH/fcZnWGkPr1q0zXC6XsXbtWmP37t3GggULjOzsbKOhocHq0pJea2ur8fbbbxtvv/22Icn4zW9+Y7z99tvGP/7xD8MwDOOee+4xsrOzjeeee87429/+Zlx77bXGmDFjjKNHj1pceXK65ZZbDI/HY2zevNk4dOhQ4HXkyJHAMTfffLNRWlpqvPrqq8b27duN8vJyo7y83MKqk9ePfvQjo7q62ti/f7/xt7/9zfjRj35k2Gw2489//rNhGFzreDhxtYthcM3N9P3vf9/YvHmzsX//fuOvf/2rUVFRYZx22mlGU1OTYRjWX+ukDx+GYRi/+93vjNLSUsPpdBqXXHKJUVNTY3VJw8Jf/vIXQ9JJr3nz5hmG0bvc9vbbbzfy8/MNl8tlzJo1y6irq7O26CTW37WWZKxZsyZwzNGjR43vfe97xsiRI42MjAzjX/7lX4xDhw5ZV3QS+853vmOMHj3acDqdRl5enjFr1qxA8DAMrnU89A0fXHPz3HDDDUZhYaHhdDqN008/3bjhhhuMffv2BfZbfa1thmEY8RljAQAASPI5HwAAIPkQPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFwRPgAAQFz9f+eJ0870HJj+AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Run optimization\n", + "\n", + "\n", + "# we need a start value so that the data set is not empty\n", + "# we will initialize the data set with the initial temperature of the system.\n", + "X.add_data(pd.DataFrame({\"f\": [initial_temp]}))\n", + "\n", + "# Run\n", + "for i in range(50):\n", + " X.step()\n", + "print(X.data)\n", + "\n", + "\n", + "# plot\n", + "X.data.plot(y=X.vocs.observable_names) # plots temperature with respect to 'time'\n", + "X.data.plot(y=X.vocs.variable_names) # plots power output with respect to 'time'\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "836da293-3889-43e9-939b-72c1eeebcabc", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 64ee85de227d929a3e115fc6135773756e5715b6 Mon Sep 17 00:00:00 2001 From: alexdennis312 <157322906+alexdennis312@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:29:41 -0500 Subject: [PATCH 6/7] Delete docs/examples/pid/pid --- docs/examples/pid/pid | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/examples/pid/pid diff --git a/docs/examples/pid/pid b/docs/examples/pid/pid deleted file mode 100644 index 8b137891..00000000 --- a/docs/examples/pid/pid +++ /dev/null @@ -1 +0,0 @@ - From b9ba09d82713be9f17c10e97436be3b8bbcba93e Mon Sep 17 00:00:00 2001 From: alexdennis312 <157322906+alexdennis312@users.noreply.github.com> Date: Mon, 29 Jul 2024 16:30:37 -0500 Subject: [PATCH 7/7] added updated example --- docs/examples/pid/pid.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples/pid/pid.ipynb b/docs/examples/pid/pid.ipynb index d59a3305..ef51421a 100644 --- a/docs/examples/pid/pid.ipynb +++ b/docs/examples/pid/pid.ipynb @@ -18,7 +18,7 @@ "metadata": {}, "outputs": [], "source": [ - "# from xopt.generators.pid.pid import PIDGenerator\n", + "from xopt.generators.pid.pid import PIDGenerator\n", "from xopt import Evaluator\n", "from xopt import VOCS\n", "from xopt import Xopt\n",