Repository: terrapower/armi
Pull request #1940 (2024-10-08 17:55:40)

Raising Error when loading inconsistent data from DB
## What is the change?

Raise a `ValueError` when loading data with special packing from a database and the size of the data does not match the length of the list of composites to which the data is supposed to be assigned.

## Why is the change being made?

If the length of `comps` and `data` did not match, there were two possibilities:

1. `if len(comps) < len(data)` the database load would fail and ARMI would crash. This is okay, since there is no risk of loading data incorrectly.
2. `if len(comps) > len(data)`, then the missing parameter values from `data` would be filled in with an empty string, `""`, because of the use of `itertools.zip_longest` with `fillvalue=""`. This is bad, because ARMI would silently fill in missing data with `""` without telling you, and the data that was assigned would likely be assigned to the wrong composite.

The length of the special `data` being unpacked should always match the number of composites to which it is being assigned. If these two things do not match, ARMI should raise a `ValueError` with a warning message and abort.

---

## Checklist

<!--
    You (the pull requester) should put an `x` in the boxes below you have completed.

    (If a checkbox doesn't apply to your PR, check it anyway.)
-->

- [x] This PR has only [one purpose or idea](https://terrapower.github.io/armi/developer/tooling.html#one-idea-one-pr).
- [x] [Tests](https://terrapower.github.io/armi/developer/tooling.html#test-it) have been added/updated to verify any new/changed code.

<!-- Check the code quality -->

- [x] The code style follows [good practices](https://terrapower.github.io/armi/developer/standards_and_practices.html).
- [x] The commit message(s) follow [good practices](https://terrapower.github.io/armi/developer/tooling.html).

<!-- Check the project-level cruft -->

- [ ] The [release notes](https://terrapower.github.io/armi/developer/tooling.html#add-release-notes) have been updated if necessary.
- [x] The [documentation](https://terrapower.github.io/armi/developer/tooling.html#document-it) is still up-to-date in the `doc` folder.
- [x] The dependencies are still up-to-date in `pyproject.toml`.


Patch:
diff --git a/armi/bookkeeping/db/database3.py b/armi/bookkeeping/db/database3.py
index 4f85a2b9..db4651a8 100644
--- a/armi/bookkeeping/db/database3.py
+++ b/armi/bookkeeping/db/database3.py
@@ -1065,9 +1065,24 @@ class Database3:
             if "linkedDims" in attrs:
                 linkedDims = np.char.decode(attrs["linkedDims"])
 
+            unpackedData = data.tolist()
+            if len(comps) != len(unpackedData):
+                msg = (
+                    "While unpacking special data for {}, encountered "
+                    "composites and parameter data with unmatched sizes.\n"
+                    "Length of composites list = {}\n"
+                    "Length of data list = {}\n"
+                    "This could indicate an error in data unpacking, which could "
+                    "result in faulty data on the resulting reactor model.".format(
+                        paramName, len(comps), len(unpackedData)
+                    )
+                )
+                runLog.error(msg)
+                raise ValueError(msg)
+
             # iterating of np is not fast...
             for c, val, linkedDim in itertools.zip_longest(
-                comps, data.tolist(), linkedDims, fillvalue=""
+                comps, unpackedData, linkedDims, fillvalue=""
             ):
                 try:
                     if linkedDim != "":
diff --git a/doc/release/0.4.rst b/doc/release/0.4.rst
index 392d0e0d..df592ec8 100644
--- a/doc/release/0.4.rst
+++ b/doc/release/0.4.rst
@@ -51,6 +51,7 @@ Quality Work
 ------------
 #. Removing deprecated code ``axialUnitGrid``. (`PR#1809 <https://github.com/terrapower/armi/pull/1809>`_)
 #. Refactoring ``axialExpansionChanger``. (`PR#1861 <https://github.com/terrapower/armi/pull/1861>`_)
+#. Raise a ValueError when database load fails. (`PR#1940 <https://github.com/terrapower/armi/pull/1940>`_)
 #. TBD
 
 Changes that Affect Requirements


Test patch:
diff --git a/armi/bookkeeping/db/tests/test_database3.py b/armi/bookkeeping/db/tests/test_database3.py
index e918ee7c..9a413da5 100644
--- a/armi/bookkeeping/db/tests/test_database3.py
+++ b/armi/bookkeeping/db/tests/test_database3.py
@@ -841,3 +841,20 @@ grids:
             self.assertEqual(len(evst.getChildren()), 1)
             b1 = evst.getChildren()[0].getChildren()[0]
             self.assertEqual(b1.p.power, 12345.6)
+
+    def test_badData(self):
+        # create a DB to be modified
+        self.db.writeToDB(self.r)
+        self.db.close()
+
+        # modify the HDF5 file to corrupt a dataset
+        with h5py.File(self.db.fileName, "r+") as hf:
+            circleGroup = hf["c00n00"]["Circle"]
+            circleMass = np.array(circleGroup["massHmBOL"][()])
+            badData = circleMass[:-1]
+            del circleGroup["massHmBOL"]
+            circleGroup.create_dataset("massHmBOL", data=badData)
+
+        with self.assertRaises(ValueError):
+            with database3.Database3(self.db.fileName, "r") as db:
+                _r = db.load(0, 0, allowMissing=True)