SYCL: Avoid using no_init read accessor in rocFFT
[alexxy/gromacs.git] / src / gromacs / selection / selectionoptionmanager.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5  * Copyright (c) 2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Implements gmx::SelectionOptionManager.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_selection
42  */
43 #include "gmxpre.h"
44
45 #include "selectionoptionmanager.h"
46
47 #include <cstdio>
48
49 #include "gromacs/selection/selection.h"
50 #include "gromacs/selection/selectioncollection.h"
51 #include "gromacs/selection/selectionoption.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/stringutil.h"
54
55 #include "selectionoptionstorage.h"
56
57 namespace gmx
58 {
59
60 /********************************************************************
61  * SelectionOptionManager::Impl
62  */
63
64 /*! \internal \brief
65  * Private implemention class for SelectionOptionManager.
66  *
67  * \ingroup module_selection
68  */
69 class SelectionOptionManager::Impl
70 {
71 public:
72     /*! \brief
73      * Request for postponed parsing of selections.
74      */
75     struct SelectionRequest
76     {
77         //! Initializes a request for the given option.
78         explicit SelectionRequest(SelectionOptionStorage* storage) : storage_(storage) {}
79
80         //! Returns name of the requested selection optin.
81         const std::string& name() const { return storage_->name(); }
82         //! Returns description for the requested selection option.
83         const std::string& description() const { return storage_->description(); }
84         /*! \brief
85          * Returns the number of selections requested in this request.
86          *
87          * -1 indicates no upper limit.
88          */
89         int count() const { return storage_->maxValueCount(); }
90
91         //! Storage object to which the selections will be added.
92         SelectionOptionStorage* storage_;
93     };
94
95     //! Collection for a list of selection requests.
96     typedef std::vector<SelectionRequest> RequestList;
97     //! Collection for list of option storage objects.
98     typedef std::vector<SelectionOptionStorage*> OptionList;
99
100     /*! \brief
101      * Helper class that clears a request list on scope exit.
102      *
103      * Methods in this class do not throw.
104      */
105     class RequestsClearer
106     {
107     public:
108         //! Constructs an object that clears given list on scope exit.
109         explicit RequestsClearer(RequestList* requests) : requests_(requests) {}
110         //! Clears the request list given to the constructor.
111         ~RequestsClearer() { requests_->clear(); }
112
113     private:
114         RequestList* requests_;
115     };
116
117     /*! \brief
118      * Creates a new selection collection.
119      *
120      * \throws  std::bad_alloc if out of memory.
121      */
122     explicit Impl(SelectionCollection* collection);
123
124     /*! \brief
125      * Assign selections from a list to pending requests.
126      *
127      * \param[in] selections  List of selections to assign.
128      * \throws    std::bad_alloc if out of memory.
129      * \throws    InvalidInputError if the assignment cannot be done
130      *      (see parseRequestedFromFile() for documented conditions).
131      *
132      * Loops through \p selections and the pending requests lists in order,
133      * and for each requests, assigns the first yet unassigned selections
134      * from the list.
135      */
136     void placeSelectionsInRequests(const SelectionList& selections);
137     /*! \brief
138      * Adds a request for each required option that is not yet set.
139      *
140      * \throws    std::bad_alloc if out of memory.
141      */
142     void requestUnsetRequiredOptions();
143
144     //! Selection collection to which selections are stored.
145     SelectionCollection& collection_;
146     //! List of selection options (storage objects) this manager manages.
147     OptionList options_;
148     //! List of selections requested for later parsing.
149     RequestList requests_;
150 };
151
152 SelectionOptionManager::Impl::Impl(SelectionCollection* collection) : collection_(*collection) {}
153
154 void SelectionOptionManager::Impl::placeSelectionsInRequests(const SelectionList& selections)
155 {
156     if (requests_.empty())
157     {
158         requestUnsetRequiredOptions();
159     }
160
161     RequestsClearer clearRequestsOnExit(&requests_);
162
163     SelectionList::const_iterator first = selections.begin();
164     SelectionList::const_iterator last  = first;
165     RequestList::const_iterator   i;
166     for (i = requests_.begin(); i != requests_.end(); ++i)
167     {
168         const SelectionRequest& request = *i;
169         if (request.count() > 0)
170         {
171             int remaining = selections.end() - first;
172             if (remaining < request.count())
173             {
174                 int assigned = first - selections.begin();
175                 GMX_THROW(InvalidInputError(
176                         formatString("Too few selections provided for '%s': "
177                                      "Expected %d selections, but only %d left "
178                                      "after assigning the first %d to other selections.",
179                                      request.name().c_str(),
180                                      request.count(),
181                                      remaining,
182                                      assigned)));
183             }
184             last = first + request.count();
185         }
186         else
187         {
188             RequestList::const_iterator nextRequest = i;
189             ++nextRequest;
190             if (nextRequest != requests_.end())
191             {
192                 const char* name         = request.name().c_str();
193                 const char* conflictName = nextRequest->name().c_str();
194                 GMX_THROW(InvalidInputError(
195                         formatString("Ambiguous selections for '%s' and '%s': "
196                                      "Any number of selections is acceptable for "
197                                      "'%s', but you have requested subsequent "
198                                      "selections to be assigned to '%s'. "
199                                      "Resolution for such cases is not implemented, "
200                                      "and may be impossible.",
201                                      name,
202                                      conflictName,
203                                      name,
204                                      conflictName)));
205             }
206             last = selections.end();
207         }
208         SelectionList curr(first, last);
209         request.storage_->addSelections(curr, true);
210         first = last;
211     }
212     if (last != selections.end())
213     {
214         int count     = selections.end() - selections.begin();
215         int remaining = selections.end() - last;
216         int assigned  = last - selections.begin();
217         GMX_THROW(InvalidInputError(
218                 formatString("Too many selections provided: "
219                              "Expected %d selections, but %d provided. "
220                              "Last %d selections could not be assigned to any option.",
221                              assigned,
222                              count,
223                              remaining)));
224     }
225 }
226
227 void SelectionOptionManager::Impl::requestUnsetRequiredOptions()
228 {
229     OptionList::const_iterator i;
230     for (i = options_.begin(); i != options_.end(); ++i)
231     {
232         SelectionOptionStorage& storage = **i;
233         if (storage.isRequired() && !storage.isSet())
234         {
235             requests_.emplace_back(&storage);
236         }
237     }
238 }
239
240
241 /********************************************************************
242  * SelectionOptionManager
243  */
244
245 SelectionOptionManager::SelectionOptionManager(SelectionCollection* collection) :
246     impl_(new Impl(collection))
247 {
248 }
249
250 SelectionOptionManager::~SelectionOptionManager() {}
251
252 void SelectionOptionManager::registerOption(SelectionOptionStorage* storage)
253 {
254     impl_->requests_.reserve(impl_->options_.size() + 1);
255     impl_->options_.push_back(storage);
256 }
257
258 void SelectionOptionManager::convertOptionValue(SelectionOptionStorage* storage,
259                                                 const std::string&      value,
260                                                 bool                    bFullValue)
261 {
262     SelectionList selections = impl_->collection_.parseFromString(value);
263     storage->addSelections(selections, bFullValue);
264 }
265
266 void SelectionOptionManager::requestOptionDelayedParsing(SelectionOptionStorage* storage)
267 {
268     impl_->requests_.emplace_back(storage);
269 }
270
271 bool SelectionOptionManager::hasRequestedSelections() const
272 {
273     return !impl_->requests_.empty();
274 }
275
276 void SelectionOptionManager::initOptions(IOptionsContainer* options)
277 {
278     bool                             allowOnlyAtomOutput = true;
279     Impl::OptionList::const_iterator iter;
280     for (iter = impl_->options_.begin(); iter != impl_->options_.end(); ++iter)
281     {
282         if (!(*iter)->allowsOnlyAtoms())
283         {
284             allowOnlyAtomOutput = false;
285         }
286     }
287
288     SelectionCollection::SelectionTypeOption typeOption =
289             allowOnlyAtomOutput ? SelectionCollection::AlwaysAtomSelections
290                                 : SelectionCollection::IncludeSelectionTypeOption;
291     impl_->collection_.initOptions(options, typeOption);
292 }
293
294 void SelectionOptionManager::parseRequestedFromStdin(bool bInteractive)
295 {
296     Impl::RequestsClearer clearRequestsOnExit(&impl_->requests_);
297
298     Impl::RequestList::const_iterator i;
299     for (i = impl_->requests_.begin(); i != impl_->requests_.end(); ++i)
300     {
301         const Impl::SelectionRequest& request = *i;
302         std::string                   context = formatString(
303                 "for option '%s'\n(%s)", request.name().c_str(), request.description().c_str());
304         SelectionList selections =
305                 impl_->collection_.parseFromStdin(request.count(), bInteractive, context);
306         request.storage_->addSelections(selections, true);
307     }
308 }
309
310 void SelectionOptionManager::parseRequestedFromFile(const std::string& filename)
311 {
312     SelectionList selections = impl_->collection_.parseFromFile(filename);
313     try
314     {
315         impl_->placeSelectionsInRequests(selections);
316     }
317     catch (GromacsException& ex)
318     {
319         ex.prependContext(formatString("Error in adding selections from file '%s'", filename.c_str()));
320         throw;
321     }
322 }
323
324 void SelectionOptionManager::parseRequestedFromString(const std::string& str)
325 {
326     SelectionList selections = impl_->collection_.parseFromString(str);
327     impl_->placeSelectionsInRequests(selections);
328 }
329
330 } // namespace gmx