From 10b3dd4d702d00556df7a6fbf0fdec644ffd1fd9 Mon Sep 17 00:00:00 2001 From: Katy Date: Thu, 7 Sep 2023 17:09:09 -0700 Subject: [PATCH 1/5] Adding serialization example --- examples/04_New Models.ipynb | 242 ++++++++++++++++++++++++++++++++++- 1 file changed, 241 insertions(+), 1 deletion(-) diff --git a/examples/04_New Models.ipynb b/examples/04_New Models.ipynb index aee5a51..5dd0634 100644 --- a/examples/04_New Models.ipynb +++ b/examples/04_New Models.ipynb @@ -333,6 +333,246 @@ "## Custom Events" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Serialization " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "ProgPy includes a feature to serialize models, which we highlight in this section. \n", + "\n", + "Model serialization has a variety of purposes. For example, serialization allows us to save a specific model to a file to be loaded later, or can aid us in sending a model to another machine over a network connection. In this section, we'll show how to serialize and deserialize model objects using `pickle` and `JSON` methods. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we'll import the necessary modules." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import matplotlib.pyplot as plt\n", + "import pickle\n", + "import numpy as np\n", + "from progpy.models import BatteryElectroChemEOD" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this example, we'll use the BatteryElectroChemEOD model. We'll start by creating a model object. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "batt = BatteryElectroChemEOD()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In order to illustrate a perfect match between the original and serialized versions of the model, we'll set both the `process_noise` and `measurement_noise` to 0. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "batt.parameters['process_noise'] = 0\n", + "batt.parameters['measurement_noise'] = 0" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we'll serialize the model in two different ways using 1) `pickle` and 2) `JSON`. Then, we'll plot the deserialized results to show equivalence of the methods. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To save using the `pickle` package, we'll serialize the model using the `dump` method. Once saved, we can then deserialize using the `load` method. In practice, deserializing will likely occur in a different file or in a later use-case, but here we deserialize to show equivalence of the saved model. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "pickle.dump(batt, open('save_pkl.pkl', 'wb')) # Serialize model\n", + "load_pkl = pickle.load(open('save_pkl.pkl', 'rb')) # Deserialize model " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we'll serialize using the `to_json` method. We deserialize by calling the model directly with the serialized result using the `from_json` method." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "save_json = batt.to_json() # Serialize model\n", + "json_1 = BatteryElectroChemEOD.from_json(save_json) # Deserialize model" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the serialized result can also be saved to a text file and uploaded for later use. We demonstrate this below:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "txtFile = open(\"save_json.txt\", \"w\")\n", + "txtFile.write(save_json)\n", + "txtFile.close()\n", + "\n", + "with open('save_json.txt') as infile: \n", + " load_json = infile.read()\n", + "\n", + "json_2 = BatteryElectroChemEOD.from_json(load_json)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We have now serialized and deserialized the model using `pickle` and `JSON` methods. Let's compare the resulting models. To do so, we'll use ProgPy's [simulation](https://nasa.github.io/progpy/prog_models_guide.html#simulation) to simulate the model to threshold and compare the results. \n", + "\n", + "First, we'll need to define our [future loading profile](https://nasa.github.io/progpy/prog_models_guide.html#future-loading)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def future_loading(t, x=None):\n", + " if (t < 600):\n", + " i = 3\n", + " elif (t < 1000):\n", + " i = 2\n", + " elif (t < 1500):\n", + " i = 1.5\n", + " else:\n", + " i = 4\n", + " return batt.InputContainer({'i': i})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let's simulate each model to threshold using the `simulate_to_threshold` method. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Original model \n", + "results_orig = batt.simulate_to_threshold(future_loading, save_freq = 1)\n", + "# Pickled version \n", + "results_pkl = load_pkl.simulate_to_threshold(future_loading, save_freq = 1)\n", + "# JSON versions\n", + "results_json_1 = json_1.simulate_to_threshold(future_loading, save_freq = 1)\n", + "results_json_2 = json_2.simulate_to_threshold(future_loading, save_freq = 1)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, let's plot the results for comparison." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "voltage_orig = [results_orig.outputs[iter]['v'] for iter in range(len(results_orig.times))]\n", + "voltage_pkl = [results_pkl.outputs[iter]['v'] for iter in range(len(results_pkl.times))]\n", + "voltage_json_1 = [results_json_1.outputs[iter]['v'] for iter in range(len(results_json_1.times))]\n", + "voltage_json_2 = [results_json_2.outputs[iter]['v'] for iter in range(len(results_json_2.times))]\n", + "\n", + "plt.plot(results_orig.times,voltage_orig,'-b',label='Original surrogate') \n", + "plt.plot(results_pkl.times,voltage_pkl,'--r',label='Pickled serialized surrogate') \n", + "plt.plot(results_json_1.times,voltage_json_1,'-.g',label='First JSON serialized surrogate') \n", + "plt.plot(results_json_2.times, voltage_json_2, '--y', label='Second JSON serialized surrogate')\n", + "plt.legend()\n", + "plt.xlabel('Time (sec)')\n", + "plt.ylabel('Voltage (volts)')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "All of the voltage curves overlap, showing that the different serialization methods produce the same results. \n", + "\n", + "Additionally, we can compare the output arrays directly, to ensure equivalence. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "# Check if the arrays are the same\n", + "are_arrays_same = np.array_equal(voltage_orig, voltage_pkl) and \\\n", + " np.array_equal(voltage_orig, voltage_json_1) and \\\n", + " np.array_equal(voltage_orig, voltage_json_2)\n", + "\n", + "print(f\"The simulated results from the original and serialized models are {'identical. This means that our serialization works!' if are_arrays_same else 'not identical. This means that our serialization does not work.'}!\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To conclude, we have shown how to serialize models in ProgPy using both `pickle` and `JSON` methods. Understanding how to serialize and deserialize models can be a powerful tool for prognostics developers. It enables the saving of models to a disk and the re-loading of these models back into memory at a later time. " + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -357,7 +597,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.9.6" }, "orig_nbformat": 4, "vscode": { From b7aec4fd4ec0189a670bda0313855e12cd2576a9 Mon Sep 17 00:00:00 2001 From: Katy Date: Wed, 11 Oct 2023 12:57:05 -0700 Subject: [PATCH 2/5] Updating serialization branch and adding conclusion --- examples/04_New Models.ipynb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/04_New Models.ipynb b/examples/04_New Models.ipynb index c8c7fd4..7c51b14 100644 --- a/examples/04_New Models.ipynb +++ b/examples/04_New Models.ipynb @@ -1415,6 +1415,15 @@ "source": [ "## Conclusions" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In these examples, we have described how to create new physics-based models. We have illustrated how to construct a generic physics-based model, as well as highlighted some specific types of models including linear models and direct models. We highlighted the matrix data access feature for using matrix operations more efficiently. Additionally, we discussed a few important components of any prognostics model including derived parameters, state limits, and events. \n", + "\n", + "With these tools, users are well-equipped to build their own prognostics models for their specific physics-based use-cases. In the next example, we'll discuss how to create data-driven models." + ] } ], "metadata": { From 022247e0af6b231949f9a45ed84d240633547ec2 Mon Sep 17 00:00:00 2001 From: Katy Date: Fri, 13 Oct 2023 13:13:04 -0700 Subject: [PATCH 3/5] Adding a few edits to serialization from PR suggestions --- examples/04_New Models.ipynb | 40 +++++++++--------------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/examples/04_New Models.ipynb b/examples/04_New Models.ipynb index d8d139f..2bb56d2 100644 --- a/examples/04_New Models.ipynb +++ b/examples/04_New Models.ipynb @@ -1406,7 +1406,9 @@ "source": [ "ProgPy includes a feature to serialize models, which we highlight in this section. \n", "\n", - "Model serialization has a variety of purposes. For example, serialization allows us to save a specific model to a file to be loaded later, or can aid us in sending a model to another machine over a network connection. In this section, we'll show how to serialize and deserialize model objects using `pickle` and `JSON` methods. " + "Model serialization has a variety of purposes. For example, serialization allows us to save a specific model or model configuration to a file to be loaded later, or can aid us in sending a model to another machine over a network connection. Some users maintain a directory or repository of configured models representing specific systems in their stock.\n", + "\n", + "In this section, we'll show how to serialize and deserialize model objects using `pickle` and `JSON` methods. " ] }, { @@ -1448,24 +1450,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "In order to illustrate a perfect match between the original and serialized versions of the model, we'll set both the `process_noise` and `measurement_noise` to 0. " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "batt.parameters['process_noise'] = 0\n", - "batt.parameters['measurement_noise'] = 0" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "First, we'll serialize the model in two different ways using 1) `pickle` and 2) `JSON`. Then, we'll plot the deserialized results to show equivalence of the methods. " + "First, we'll serialize the model in two different ways using 1) `pickle` and 2) `JSON`. Then, we'll plot the results from simulating the deserialized models to show equivalence of the methods. " ] }, { @@ -1531,7 +1516,7 @@ "source": [ "We have now serialized and deserialized the model using `pickle` and `JSON` methods. Let's compare the resulting models. To do so, we'll use ProgPy's [simulation](https://nasa.github.io/progpy/prog_models_guide.html#simulation) to simulate the model to threshold and compare the results. \n", "\n", - "First, we'll need to define our [future loading profile](https://nasa.github.io/progpy/prog_models_guide.html#future-loading)." + "First, we'll need to define our [future loading profile](https://nasa.github.io/progpy/prog_models_guide.html#future-loading) using the PiecewiseLoad class. " ] }, { @@ -1540,16 +1525,11 @@ "metadata": {}, "outputs": [], "source": [ - "def future_loading(t, x=None):\n", - " if (t < 600):\n", - " i = 3\n", - " elif (t < 1000):\n", - " i = 2\n", - " elif (t < 1500):\n", - " i = 1.5\n", - " else:\n", - " i = 4\n", - " return batt.InputContainer({'i': i})" + "# Variable (piecewise) future loading scheme\n", + "future_loading = Piecewise(\n", + " m.InputContainer,\n", + " [600, 1000, 1500, 3000],\n", + " {'i': [3, 2, 1.5, 4]})" ] }, { From 9a514be047a087a8c8d922c3edb196c79fe9f414 Mon Sep 17 00:00:00 2001 From: Katy Date: Fri, 13 Oct 2023 13:22:16 -0700 Subject: [PATCH 4/5] Adding new imports so code will run --- examples/04_New Models.ipynb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/04_New Models.ipynb b/examples/04_New Models.ipynb index 2bb56d2..0cd7256 100644 --- a/examples/04_New Models.ipynb +++ b/examples/04_New Models.ipynb @@ -1427,7 +1427,8 @@ "import matplotlib.pyplot as plt\n", "import pickle\n", "import numpy as np\n", - "from progpy.models import BatteryElectroChemEOD" + "from progpy.models import BatteryElectroChemEOD\n", + "from progpy.loading import Piecewise" ] }, { @@ -1527,7 +1528,7 @@ "source": [ "# Variable (piecewise) future loading scheme\n", "future_loading = Piecewise(\n", - " m.InputContainer,\n", + " batt.InputContainer,\n", " [600, 1000, 1500, 3000],\n", " {'i': [3, 2, 1.5, 4]})" ] @@ -1603,7 +1604,7 @@ " np.array_equal(voltage_orig, voltage_json_1) and \\\n", " np.array_equal(voltage_orig, voltage_json_2)\n", "\n", - "print(f\"The simulated results from the original and serialized models are {'identical. This means that our serialization works!' if are_arrays_same else 'not identical. This means that our serialization does not work.'}!\")" + "print(f\"The simulated results from the original and serialized models are {'identical. This means that our serialization works!' if are_arrays_same else 'not identical. This means that our serialization does not work.'}\")" ] }, { From 2cb179f405ca6da7ace9eb42f5e999ff81a61f74 Mon Sep 17 00:00:00 2001 From: Katy Date: Fri, 13 Oct 2023 14:47:46 -0700 Subject: [PATCH 5/5] Deleting default process_noise in BatteryElectroChemEOD; 0 used now --- src/progpy/models/battery_electrochem.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/progpy/models/battery_electrochem.py b/src/progpy/models/battery_electrochem.py index 2018237..4ce0262 100644 --- a/src/progpy/models/battery_electrochem.py +++ b/src/progpy/models/battery_electrochem.py @@ -301,8 +301,6 @@ class BatteryElectroChemEOD(PrognosticsModel): 'tb': 292.1 # in K, about 18.95 C }, - 'process_noise': 1e-3, - # End of discharge voltage threshold 'VEOD': 3.0, 'VDropoff': 0.1 # Voltage above EOD after which voltage will be considered in SOC calculation