Check mrc header for sanity
authorChristian Blau <cblau@gwdg.de>
Thu, 21 Nov 2019 15:48:07 +0000 (16:48 +0100)
committerPaul Bauer <paul.bauer.q@gmail.com>
Mon, 25 Nov 2019 13:34:34 +0000 (14:34 +0100)
Add functionality to check mrc density map headers for sanity.

Required for endianess correction, because the endianess stamp is not
guaranteed to be right for most .mrc maps.

Change-Id: I8956bcbc4b9fd7b3b6346c3b4cdd0022c9e3332a

src/gromacs/fileio/mrcdensitymapheader.cpp
src/gromacs/fileio/mrcdensitymapheader.h
src/gromacs/fileio/tests/mrcdensitymapheader.cpp

index 28842078ba275962092c779c3786478979e9a7e9..faaefc5773a77b12c7289d99fb48c91513739acc 100644 (file)
 
 namespace gmx
 {
+
+namespace
+{
+
+//! Returns true if any of the argument values is smaller than zero.
+template<typename Container>
+bool anySmallerZero(Container values)
+{
+    return std::any_of(std::begin(values), std::end(values), [](auto v) { return v < 0; });
+}
+
+//! Returns true if any of the argument values is larger than a given boundary value.
+template<typename Container>
+bool anyLargerThanValue(Container values, typename Container::value_type boundaryValue)
+{
+    return std::any_of(std::begin(values), std::end(values),
+                       [boundaryValue](auto v) { return v > boundaryValue; });
+}
+
+} // namespace
+
 size_t numberOfExpectedDataItems(const MrcDensityMapHeader& header)
 {
-    if (std::any_of(std::begin(header.numColumnRowSection_), std::end(header.numColumnRowSection_),
-                    [](auto i) { return i < 0; }))
+    if (anySmallerZero(header.numColumnRowSection_))
     {
         GMX_THROW(
                 InternalError("Cannot determine data size, because the mrc "
@@ -95,4 +115,31 @@ dynamicExtents3D getDynamicExtents3D(const MrcDensityMapHeader& header)
              header.numColumnRowSection_[XX] };
 };
 
+bool mrcHeaderIsSane(const MrcDensityMapHeader& header)
+{
+    // Make sure all numbers of columns, row sections, extents and cell angles
+    // are positive
+    if (anySmallerZero(header.numColumnRowSection_) || anySmallerZero(header.cellAngles_)
+        || anySmallerZero(header.extent_))
+    {
+        return false;
+    }
+
+    // The maximum integer number in an mrc header to be considered sane
+    constexpr std::int32_t c_maxIntegerNumber = 100'000;
+    if (anyLargerThanValue(header.numColumnRowSection_, c_maxIntegerNumber)
+        || anyLargerThanValue(header.extent_, c_maxIntegerNumber))
+    {
+        return false;
+    }
+
+    constexpr std::int32_t c_maxCellAngle = 360;
+    if (anyLargerThanValue(header.cellAngles_, c_maxCellAngle))
+    {
+        return false; //NOLINT(readability-simplify-boolean-expr)
+    }
+
+    return true;
+}
+
 } // namespace gmx
index f358efbaa2e1ca56384987f485942dd7cf45a369..82160127fa62ccda348df20c5480730169f5974c 100644 (file)
@@ -190,5 +190,17 @@ TranslateAndScale getCoordinateTransformationToLattice(const MrcDensityMapHeader
  * \returns density data extents in three dimensions.
  */
 dynamicExtents3D getDynamicExtents3D(const MrcDensityMapHeader& header);
+
+/*! \brief Checks if the values in the header are sane.
+ *
+ * Checks extents and numbers of columns, rows and sections, as well as unit
+ * cell angles for positivity and to be within bounds.
+ *
+ * Bounds are set generously not to hamper future creative uses of mrc files.
+ *
+ * \returns true if all header values are within resonable albeit generous bounds
+ */
+bool mrcHeaderIsSane(const MrcDensityMapHeader& header);
+
 } // namespace gmx
 #endif /* end of include guard: GMX_FILEIO_MRCDENSITYMAPHEADER_H */
index ad303cad21672749cc815f76b2ab561e4bd7de9d..e9e17df359a87e4d0f267058456ed815e2964cd8 100644 (file)
@@ -140,4 +140,22 @@ TEST(MrcDensityMapHeaderTest, GetsCorrectExtents)
     EXPECT_EQ(expectedExtents[ZZ], extents.extent(ZZ));
 }
 
+TEST(MrcDensityMapHeaderTest, IsSane)
+{
+    MrcDensityMapHeader header;
+    EXPECT_TRUE(mrcHeaderIsSane(header));
+
+    header.numColumnRowSection_[YY] = -1;
+    EXPECT_FALSE(mrcHeaderIsSane(header));
+
+    header.numColumnRowSection_[YY] = 10'000'000;
+    EXPECT_FALSE(mrcHeaderIsSane(header));
+
+    header = {};
+    EXPECT_TRUE(mrcHeaderIsSane(header));
+
+    header.cellAngles_[XX] = -20;
+    EXPECT_FALSE(mrcHeaderIsSane(header));
+}
+
 } // namespace gmx