}
catch (...)
{
- done_blocka(block);
- sfree(block);
for (int i = 0; i < block->nr; ++i)
{
sfree(names[i]);
}
sfree(names);
+ done_blocka(block);
+ sfree(block);
throw;
}
- done_blocka(block);
- sfree(block);
for (int i = 0; i < block->nr; ++i)
{
sfree(names[i]);
}
sfree(names);
+ done_blocka(block);
+ sfree(block);
}
/*!
SelectionTreeElementPointer p
= _gmx_sel_init_position(s, NULL, scanner);
if (!p) YYERROR;
- set((yyval.sel), _gmx_sel_init_selection(s->name().c_str(), p, scanner));
+ set((yyval.sel), _gmx_sel_init_selection(NULL, p, scanner));
END_ACTION;
}
break;
SelectionTreeElementPointer p
= _gmx_sel_init_position(s, NULL, scanner);
if (!p) YYERROR;
- set((yyval.sel), _gmx_sel_init_selection(s->name().c_str(), p, scanner));
+ set((yyval.sel), _gmx_sel_init_selection(NULL, p, scanner));
END_ACTION;
}
break;
SelectionTreeElementPointer p
= _gmx_sel_init_position(s, NULL, scanner);
if (!p) YYERROR;
- set($$, _gmx_sel_init_selection(s->name().c_str(), p, scanner));
+ set($$, _gmx_sel_init_selection(NULL, p, scanner));
END_ACTION;
}
| string
SelectionTreeElementPointer p
= _gmx_sel_init_position(s, NULL, scanner);
if (!p) YYERROR;
- set($$, _gmx_sel_init_selection(s->name().c_str(), p, scanner));
+ set($$, _gmx_sel_init_selection(NULL, p, scanner));
END_ACTION;
}
| selection
/* Update the flags */
_gmx_selelem_update_flags(root, scanner);
- /* If there is no name provided by the user, check whether the actual
- * selection given was from an external group, and if so, use the name
- * of the external group. */
- if (root->name().empty())
- {
- SelectionTreeElementPointer child = root->child;
- while (child->type == SEL_MODIFIER)
- {
- if (!child->child || child->child->type != SEL_SUBEXPRREF
- || !child->child->child)
- {
- break;
- }
- child = child->child->child;
- }
- if (child->type == SEL_EXPRESSION
- && child->child && child->child->type == SEL_SUBEXPRREF
- && child->child->child
- && child->child->child->type == SEL_CONST
- && child->child->child->v.type == GROUP_VALUE)
- {
- root->setName(child->child->child->name());
- }
- }
- /* If there still is no name, use the selection string */
- if (root->name().empty())
- {
- root->setName(_gmx_sel_lexer_pselstr(scanner));
- }
+ root->fillNameIfMissing(_gmx_sel_lexer_pselstr(scanner));
/* Print out some information if the parser is interactive */
if (_gmx_sel_is_lexer_interactive(scanner))
} // namespace
+void
+SelectionData::refreshName()
+{
+ rootElement_.fillNameIfMissing(selectionText_.c_str());
+ name_ = rootElement_.name();
+}
+
void
SelectionData::initializeMassesAndCharges(const t_topology *top)
{
SelectionData(SelectionTreeElement *elem, const char *selstr);
~SelectionData();
+ //! Returns the name for this selection.
+ const char *name() const { return name_.c_str(); }
//! Returns the string that was parsed to produce this selection.
const char *selectionText() const { return selectionText_.c_str(); }
//! Returns true if the size of the selection (posCount()) is dynamic.
//! \copydoc Selection::initCoveredFraction()
bool initCoveredFraction(e_coverfrac_t type);
+ /*! \brief
+ * Updates the name of the selection if missing.
+ *
+ * \throws std::bad_alloc if out of memory.
+ *
+ * If selections get their value from a group reference that cannot be
+ * resolved during parsing, the name is final only after group
+ * references have been resolved.
+ *
+ * This function is called by SelectionCollection::setIndexGroups().
+ */
+ void refreshName();
/*! \brief
* Computes total masses and charges for all selection positions.
*
explicit Selection(internal::SelectionData *sel) : sel_(sel) {}
//! Returns the name of the selection.
- const char *name() const { return data().name_.c_str(); }
+ const char *name() const { return data().name(); }
//! Returns the string that was parsed to produce this selection.
const char *selectionText() const { return data().selectionText(); }
//! Returns true if the size of the selection (posCount()) is dynamic.
char *name = root->u.gref.name;
bOk = gmx_ana_indexgrps_find(&root->u.cgrp, &foundName, grps_, name);
sfree(name);
- root->u.gref.name = NULL;
if (!bOk)
{
+ root->u.gref.name = NULL;
// TODO: Improve error messages
errors->append("Unknown group referenced in a selection");
}
{
GMX_THROW(InvalidInputError(errors.toString()));
}
+ for (size_t i = 0; i < impl_->sc_.sel.size(); ++i)
+ {
+ impl_->sc_.sel[i]->refreshName();
+ }
}
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2009,2010,2011,2012, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013, by the GROMACS development team, led by
* David van der Spoel, Berk Hess, Erik Lindahl, and including many
* others, as listed in the AUTHORS file in the top-level source
* directory and at http://www.gromacs.org.
}
}
+void SelectionTreeElement::fillNameIfMissing(const char *selectionText)
+{
+ GMX_RELEASE_ASSERT(type == SEL_ROOT,
+ "Should not be called for non-root elements");
+ if (name().empty())
+ {
+ // Check whether the actual selection given was from an external group,
+ // and if so, use the name of the external group.
+ SelectionTreeElementPointer child = this->child;
+ while (child->type == SEL_MODIFIER)
+ {
+ if (!child->child || child->child->type != SEL_SUBEXPRREF
+ || !child->child->child)
+ {
+ break;
+ }
+ child = child->child->child;
+ }
+ if (child->type == SEL_EXPRESSION
+ && child->child && child->child->type == SEL_SUBEXPRREF
+ && child->child->child)
+ {
+ if (child->child->child->type == SEL_CONST
+ && child->child->child->v.type == GROUP_VALUE)
+ {
+ setName(child->child->child->name());
+ return;
+ }
+ // If the group reference is still unresolved, leave the name empty
+ // and fill it later.
+ if (child->child->child->type == SEL_GROUPREF)
+ {
+ return;
+ }
+ }
+ // If there still is no name, use the selection string.
+ setName(selectionText);
+ }
+}
+
} // namespace gmx
/*!
/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2009,2010,2011,2012, by the GROMACS development team, led by
+ * Copyright (c) 2009,2010,2011,2012,2013, by the GROMACS development team, led by
* David van der Spoel, Berk Hess, Erik Lindahl, and including many
* others, as listed in the AUTHORS file in the top-level source
* directory and at http://www.gromacs.org.
void setName(const char *name) { name_ = (name != NULL ? name : ""); }
//! \copydoc setName(const char *)
void setName(const std::string &name) { name_ = name; }
+ /*! \brief
+ * Sets the name of a root element if it is missing.
+ *
+ * \param[in] selectionText Full selection text to use as a fallback.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * If index groups have not yet been set and the selection is a result
+ * of a group reference, the name may still be empty after this call.
+ *
+ * Strong exception safety guarantee.
+ */
+ void fillNameIfMissing(const char *selectionText);
//! Type of the element.
e_selelem_t type;
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <ParsedSelections Name="Parsed">
+ <ParsedSelection Name="Selection1">
+ <String Name="Input">group "GrpA"</String>
+ <String Name="Name">GrpA</String>
+ <String Name="Text">group "GrpA"</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection2">
+ <String Name="Input">GrpB</String>
+ <String Name="Name">GrpB</String>
+ <String Name="Text">GrpB</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection3">
+ <String Name="Input">1</String>
+ <String Name="Name">GrpB</String>
+ <String Name="Text">1</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection4">
+ <String Name="Input">group "GrpB" and resname RB</String>
+ <String Name="Name">group "GrpB" and resname RB</String>
+ <String Name="Text">group "GrpB" and resname RB</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ </ParsedSelections>
+ <CompiledSelections Name="Compiled">
+ <Selection Name="Selection1">
+ <String Name="Name">GrpA</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>0</Int>
+ <Int>1</Int>
+ <Int>2</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection2">
+ <String Name="Name">GrpB</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ <Int>6</Int>
+ <Int>7</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection3">
+ <String Name="Name">GrpB</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ <Int>6</Int>
+ <Int>7</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection4">
+ <String Name="Name">group "GrpB" and resname RB</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">3</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ </Sequence>
+ </Selection>
+ </CompiledSelections>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <ParsedSelections Name="Parsed">
+ <ParsedSelection Name="Selection1">
+ <String Name="Input">group "GrpA"</String>
+ <String Name="Name"/>
+ <String Name="Text">group "GrpA"</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection2">
+ <String Name="Input">GrpB</String>
+ <String Name="Name"/>
+ <String Name="Text">GrpB</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection3">
+ <String Name="Input">1</String>
+ <String Name="Name"/>
+ <String Name="Text">1</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection4">
+ <String Name="Input">group "GrpB" and resname RB</String>
+ <String Name="Name">group "GrpB" and resname RB</String>
+ <String Name="Text">group "GrpB" and resname RB</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ </ParsedSelections>
+ <CompiledSelections Name="Compiled">
+ <Selection Name="Selection1">
+ <String Name="Name">GrpA</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>0</Int>
+ <Int>1</Int>
+ <Int>2</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection2">
+ <String Name="Name">GrpB</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ <Int>6</Int>
+ <Int>7</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection3">
+ <String Name="Name">GrpB</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ <Int>6</Int>
+ <Int>7</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection4">
+ <String Name="Name">group "GrpB" and resname RB</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">3</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ </Sequence>
+ </Selection>
+ </CompiledSelections>
+</ReferenceData>
--- /dev/null
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+ <ParsedSelections Name="Parsed">
+ <ParsedSelection Name="Selection1">
+ <String Name="Input">"GroupSelection" group "GrpA"</String>
+ <String Name="Name">GroupSelection</String>
+ <String Name="Text">"GroupSelection" group "GrpA"</String>
+ <Bool Name="Dynamic">false</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection2">
+ <String Name="Input">"DynamicSelection" x < 5</String>
+ <String Name="Name">DynamicSelection</String>
+ <String Name="Text">"DynamicSelection" x < 5</String>
+ <Bool Name="Dynamic">true</Bool>
+ </ParsedSelection>
+ <ParsedSelection Name="Selection3">
+ <String Name="Input">y < 3</String>
+ <String Name="Name">y < 3</String>
+ <String Name="Text">y < 3</String>
+ <Bool Name="Dynamic">true</Bool>
+ </ParsedSelection>
+ </ParsedSelections>
+ <CompiledSelections Name="Compiled">
+ <Selection Name="Selection1">
+ <String Name="Name">GroupSelection</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">5</Int>
+ <Int>0</Int>
+ <Int>1</Int>
+ <Int>2</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection2">
+ <String Name="Name">DynamicSelection</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">10</Int>
+ <Int>0</Int>
+ <Int>1</Int>
+ <Int>2</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ <Int>6</Int>
+ <Int>7</Int>
+ <Int>8</Int>
+ <Int>9</Int>
+ </Sequence>
+ </Selection>
+ <Selection Name="Selection3">
+ <String Name="Name">y < 3</String>
+ <Sequence Name="Atoms">
+ <Int Name="Length">10</Int>
+ <Int>0</Int>
+ <Int>1</Int>
+ <Int>2</Int>
+ <Int>3</Int>
+ <Int>4</Int>
+ <Int>5</Int>
+ <Int>6</Int>
+ <Int>7</Int>
+ <Int>8</Int>
+ <Int>9</Int>
+ </Sequence>
+ </Selection>
+ </CompiledSelections>
+</ReferenceData>
<xsl:template match="Selection">
<h3><xsl:value-of select="@Name"/></h3>
- <p>
- Selection text:<br/>
- <xsl:value-of select="key('SelectionName', @Name)/String[@Name='Text']"/>
- </p>
- <xsl:apply-templates />
-</xsl:template>
-
-<xsl:template match="Selection/Sequence[@Name='Atoms']">
- <p>
- Atoms:
- <xsl:call-template name="SequenceAsHorizontalTable"/>
- </p>
+ <table>
+ <xsl:if test="String[@Name='Name']">
+ <tr>
+ <td>Name:</td>
+ <td><xsl:value-of select="String[@Name='Name']"/></td>
+ </tr>
+ </xsl:if>
+ <tr>
+ <td>Selection text:</td>
+ <td>
+ <xsl:value-of select="key('SelectionName', @Name)/String[@Name='Text']"/>
+ </td>
+ </tr>
+ <xsl:if test="Sequence[@Name='Atoms']">
+ <tr>
+ <td>Atoms (<xsl:value-of select="Sequence[@Name='Atoms']/Int[@Name='Length']"/>):</td>
+ <td>
+ <xsl:call-template name="SequenceAsCSV">
+ <xsl:with-param name="root" select="Sequence[@Name='Atoms']"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ </xsl:if>
+ </table>
+ <xsl:apply-templates select="Sequence[@Name='Positions']"/>
</xsl:template>
<xsl:template match="Selection/Sequence[@Name='Positions']">
#include "gromacs/options/basicoptions.h"
#include "gromacs/options/options.h"
+#include "gromacs/selection/indexutil.h"
#include "gromacs/selection/selectioncollection.h"
#include "gromacs/selection/selection.h"
#include "gromacs/utility/exceptions.h"
static int s_debugLevel;
SelectionCollectionTest();
+ ~SelectionCollectionTest();
void setAtomCount(int natoms)
{
}
void loadTopology(const char *filename);
void setTopology();
+ void loadIndexGroups(const char *filename);
gmx::test::TopologyManager topManager_;
gmx::SelectionCollection sc_;
gmx::SelectionList sel_;
t_topology *top_;
t_trxframe *frame_;
+ gmx_ana_indexgrps_t *grps_;
};
int SelectionCollectionTest::s_debugLevel = 0;
}
SelectionCollectionTest::SelectionCollectionTest()
- : top_(NULL), frame_(NULL)
+ : top_(NULL), frame_(NULL), grps_(NULL)
{
topManager_.requestFrame();
sc_.setDebugLevel(s_debugLevel);
sc_.setOutputPosType("atom");
}
+SelectionCollectionTest::~SelectionCollectionTest()
+{
+ if (grps_ != NULL)
+ {
+ gmx_ana_indexgrps_free(grps_);
+ }
+}
+
void
SelectionCollectionTest::loadTopology(const char *filename)
{
ASSERT_NO_THROW_GMX(sc_.setTopology(top_, -1));
}
+void
+SelectionCollectionTest::loadIndexGroups(const char *filename)
+{
+ GMX_RELEASE_ASSERT(grps_ == NULL,
+ "External groups can only be loaded once");
+ std::string fullpath =
+ gmx::test::TestFileManager::getInputFilePath(filename);
+ gmx_ana_indexgrps_init(&grps_, NULL, fullpath.c_str());
+ sc_.setIndexGroups(grps_);
+}
+
/********************************************************************
* Test fixture for selection testing with reference data
efTestPositionMapping = 1<<3,
efTestPositionMasses = 1<<4,
efTestPositionCharges = 1<<5,
+ efTestSelectionNames = 1<<6,
efDontTestCompiledAtoms = 1<<8
};
typedef gmx::FlagsTemplate<TestFlag> TestFlags;
std::string id = gmx::formatString("Selection%d", static_cast<int>(i + 1));
TestReferenceChecker selcompound(
compound.checkCompound("Selection", id.c_str()));
+ if (flags_.test(efTestSelectionNames))
+ {
+ selcompound.checkString(sel_[i].name(), "Name");
+ }
if (!flags_.test(efDontTestCompiledAtoms))
{
checkSelection(&selcompound, sel_[i], flags_ & mask);
// TODO: Tests for more parser errors
-TEST_F(SelectionCollectionTest, RecoversFromUnknownGroupReference)
+TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceParser1)
+{
+ ASSERT_NO_THROW_GMX(sc_.setIndexGroups(NULL));
+ EXPECT_THROW_GMX(sc_.parseFromString("group \"foo\""), gmx::InvalidInputError);
+ EXPECT_THROW_GMX(sc_.parseFromString("4"), gmx::InvalidInputError);
+}
+
+TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceParser2)
+{
+ ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
+ EXPECT_THROW_GMX(sc_.parseFromString("group \"foo\""), gmx::InvalidInputError);
+ EXPECT_THROW_GMX(sc_.parseFromString("4"), gmx::InvalidInputError);
+}
+
+TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceDelayed1)
{
ASSERT_NO_THROW_GMX(sc_.parseFromString("group \"foo\""));
ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
}
+TEST_F(SelectionCollectionTest, HandlesUnknownGroupReferenceDelayed2)
+{
+ ASSERT_NO_THROW_GMX(sc_.parseFromString("group 4; group \"foo\""));
+ ASSERT_NO_FATAL_FAILURE(setAtomCount(10));
+ EXPECT_THROW_GMX(loadIndexGroups("simple.ndx"), gmx::InvalidInputError);
+ EXPECT_THROW_GMX(sc_.compile(), gmx::APIError);
+}
+
TEST_F(SelectionCollectionTest, RecoversFromMissingMoleculeInfo)
{
ASSERT_NO_THROW_GMX(sc_.parseFromString("molindex 1 to 5"));
* Tests for selection syntactic constructs
*/
+TEST_F(SelectionCollectionDataTest, HandlesSelectionNames)
+{
+ static const char * const selections[] = {
+ "\"GroupSelection\" group \"GrpA\"",
+ "\"DynamicSelection\" x < 5",
+ "y < 3"
+ };
+ setFlags(TestFlags() | efTestSelectionNames);
+ ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
+ runTest(10, selections);
+}
+
+TEST_F(SelectionCollectionDataTest, HandlesIndexGroupsInSelections)
+{
+ static const char * const selections[] = {
+ "group \"GrpA\"",
+ "GrpB",
+ "1",
+ "group \"GrpB\" and resname RB"
+ };
+ setFlags(TestFlags() | efTestSelectionNames);
+ ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
+ runTest("simple.gro", selections);
+}
+
+TEST_F(SelectionCollectionDataTest, HandlesIndexGroupsInSelectionsDelayed)
+{
+ static const char * const selections[] = {
+ "group \"GrpA\"",
+ "GrpB",
+ "1",
+ "group \"GrpB\" and resname RB"
+ };
+ setFlags(TestFlags() | efTestSelectionNames);
+ ASSERT_NO_FATAL_FAILURE(runParser(selections));
+ ASSERT_NO_FATAL_FAILURE(loadTopology("simple.gro"));
+ ASSERT_NO_THROW_GMX(loadIndexGroups("simple.ndx"));
+ ASSERT_NO_FATAL_FAILURE(runCompiler());
+}
+
TEST_F(SelectionCollectionDataTest, HandlesConstantPositions)
{
static const char * const selections[] = {
--- /dev/null
+[ GrpA ]
+1 2 3 4 5
+
+[ GrpB ]
+4 5 6 7 8
+
+[ GrpUnsorted ]
+1 2 4 3 2