Add check for empty selections.
authorTeemu Murtola <teemu.murtola@gmail.com>
Fri, 12 Apr 2013 04:35:18 +0000 (07:35 +0300)
committerGerrit Code Review <gerrit@gerrit.gromacs.org>
Wed, 17 Apr 2013 07:27:33 +0000 (09:27 +0200)
The framework now checks that provided selections are not
unconditionally empty, and produces error messages in those cases.
Added an option for controlling this if there is some rare case where
selecting "none" would be reasonable input.

Change-Id: I7066fa89bcebfc883483cf2ddb769a31c3942260

src/gromacs/selection/selectioncollection.cpp
src/gromacs/selection/selectionenums.h
src/gromacs/selection/selectionoption.h
src/gromacs/selection/tests/selectionoption.cpp

index b23539a068b784524ac905f177f55632ced21814..e165c53c2e6b05d602fb08591990c1b14d1fd52d 100644 (file)
@@ -624,6 +624,16 @@ SelectionCollection::compile()
                 GMX_THROW(InvalidInputError(message));
             }
         }
+        if (sel.hasFlag(efSelection_DisallowEmpty))
+        {
+            if (sel.posCount() == 0)
+            {
+                std::string message = formatString(
+                            "Selection '%s' never matches any atoms.",
+                            sel.selectionText());
+                GMX_THROW(InvalidInputError(message));
+            }
+        }
     }
 }
 
index 41542f3a004045d1929512d55fbd1dfc0716912a..8b821b2611f13e368db993d677c86d865202f43f 100644 (file)
@@ -71,6 +71,8 @@ enum SelectionFlag
     efSelection_OnlyAtoms               = 1<<1,
     //! Whether ::POS_MASKONLY should be used for output position evaluation.
     efSelection_DynamicMask             = 1<<2,
+    //! If set, unconditionally empty selections result in compilation errors.
+    efSelection_DisallowEmpty           = 1<<3,
     //! Whether velocities of output positions should be evaluated.
     efSelection_EvaluateVelocities      = 1<<5,
     //! Whether forces on output positions should be evaluated.
index 7ade5a97752fb1d68bc267607c0bcef89f2f9704..3921957943c54fc81f8909aa1886e1c52ff8cd41 100644 (file)
@@ -78,7 +78,10 @@ class SelectionOption : public OptionTemplate<Selection, SelectionOption>
         typedef SelectionOptionInfo InfoType;
 
         //! Initializes an option with the given name.
-        explicit SelectionOption(const char *name) : MyBase(name) { }
+        explicit SelectionOption(const char *name)
+            : MyBase(name), selectionFlags_(efSelection_DisallowEmpty)
+        {
+        }
 
         /*! \brief
          * Request velocity evaluation for output positions.
@@ -112,6 +115,16 @@ class SelectionOption : public OptionTemplate<Selection, SelectionOption>
          */
         MyClass &dynamicMask()
         { selectionFlags_.set(efSelection_DynamicMask); return me(); }
+        /*! \brief
+         * Allow specifying an unconditionally empty selection for this option.
+         *
+         * If this option is not set, selections that are unconditionally empty
+         * (i.e., can never match any atoms) result in errors.
+         * Note that even without this option, it is still possible that a
+         * dynamic selection evaluates to zero atoms for some frames.
+         */
+        MyClass &allowEmpty()
+        { selectionFlags_.clear(efSelection_DisallowEmpty); return me(); }
 
     private:
         // Disable possibility to allow multiple occurrences, since it isn't
index 690f0b99ac01ffabb822586d20b55890b809c2ac..90f751170497a22d0392753a5f230025881359ec 100644 (file)
@@ -180,6 +180,26 @@ TEST_F(SelectionOptionTest, HandlesNonAtomicSelectionWhenAtomsRequired)
 }
 
 
+TEST_F(SelectionOptionTest, ChecksEmptySelections)
+{
+    gmx::Selection sel;
+    using gmx::SelectionOption;
+    ASSERT_NO_THROW_GMX(options_.addOption(
+                                SelectionOption("sel").store(&sel)));
+    setManager();
+
+    gmx::OptionsAssigner assigner(&options_);
+    EXPECT_NO_THROW_GMX(assigner.start());
+    ASSERT_NO_THROW_GMX(assigner.startOption("sel"));
+    EXPECT_NO_THROW_GMX(assigner.appendValue("none"));
+    EXPECT_NO_THROW_GMX(assigner.finishOption());
+    EXPECT_NO_THROW_GMX(assigner.finish());
+    EXPECT_NO_THROW_GMX(options_.finish());
+
+    EXPECT_THROW_GMX(sc_.compile(), gmx::InvalidInputError);
+}
+
+
 TEST_F(SelectionOptionTest, HandlesTooManySelections)
 {
     gmx::Selection sel;