Skip to content

Commit

Permalink
Merge pull request #107 from nasa/examples/serialization
Browse files Browse the repository at this point in the history
Adding serialization example
  • Loading branch information
teubert committed Oct 13, 2023
2 parents ac51178 + cb27026 commit 70ab878
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 2 deletions.
230 changes: 230 additions & 0 deletions examples/04_New Models.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1393,12 +1393,242 @@
"In this section, we have illustrated how to define custom [events](https://nasa.github.io/progpy/prog_models_guide.html#events) for prognostics models. Events can be used to define anything that a user is interested in predicting, including common values like Remaining Useful Life (RUL) and End of Discharge (EOD), as well as other values like special intermediate states or warning thresholds. "
]
},
{
"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 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. "
]
},
{
"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\n",
"from progpy.loading import Piecewise"
]
},
{
"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": [
"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. "
]
},
{
"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) using the PiecewiseLoad class. "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Variable (piecewise) future loading scheme\n",
"future_loading = Piecewise(\n",
" batt.InputContainer,\n",
" [600, 1000, 1500, 3000],\n",
" {'i': [3, 2, 1.5, 4]})"
]
},
{
"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": {},
"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": {
Expand Down
2 changes: 0 additions & 2 deletions src/progpy/models/battery_electrochem.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 70ab878

Please sign in to comment.