Merge release-4-6 into master
[alexxy/gromacs.git] / src / gromacs / options / basicoptions.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements classes in basicoptions.h and basicoptionstorage.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_options
37  */
38 #include "basicoptions.h"
39 #include "basicoptionstorage.h"
40
41 #include <cerrno>
42 #include <cstdio>
43 #include <cstdlib>
44
45 #include <limits>
46 #include <string>
47 #include <vector>
48
49 #include "gromacs/utility/exceptions.h"
50 #include "gromacs/utility/stringutil.h"
51
52 namespace
53 {
54
55 /*! \brief
56  * Expands a single value to a vector by copying the value.
57  *
58  * \tparam        ValueType  Type of values to process.
59  * \param[in]     length     Length of the resulting vector.
60  * \param[in,out] values     Values to process.
61  * \throws   std::bad_alloc    if out of memory.
62  * \throws   InvalidInputError if \p values has an invalid number of values.
63  *
64  * \p values should have 0, 1, or \p length values.
65  * If \p values has 1 value, it is expanded such that it has \p length
66  * identical values.  In other valid cases, nothing is done.
67  */
68 template <typename ValueType>
69 void expandVector(size_t length, std::vector<ValueType> *values)
70 {
71     if (length > 0 && values->size() > 0 && values->size() != length)
72     {
73         if (values->size() != 1)
74         {
75             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
76                       "Expected 1 or %d values, got %d", length, values->size())));
77         }
78         const ValueType &value = (*values)[0];
79         values->resize(length, value);
80     }
81 }
82
83 } // namespace
84
85 namespace gmx
86 {
87
88 /********************************************************************
89  * BooleanOptionStorage
90  */
91
92 std::string BooleanOptionStorage::formatSingleValue(const bool &value) const
93 {
94     return value ? "yes" : "no";
95 }
96
97 void BooleanOptionStorage::convertValue(const std::string &value)
98 {
99     // TODO: Case-independence
100     if (value == "1" || value == "yes" || value == "true")
101     {
102         addValue(true);
103         return;
104     }
105     else if (value == "0" || value == "no" || value == "false")
106     {
107         addValue(false);
108         return;
109     }
110     GMX_THROW(InvalidInputError("Invalid value: '" + value + "'; supported values are: 1, 0, yes, no, true, false"));
111 }
112
113 /********************************************************************
114  * BooleanOptionInfo
115  */
116
117 BooleanOptionInfo::BooleanOptionInfo(BooleanOptionStorage *option)
118     : OptionInfo(option)
119 {
120 }
121
122 /********************************************************************
123  * BooleanOption
124  */
125
126 AbstractOptionStoragePointer BooleanOption::createStorage() const
127 {
128     return AbstractOptionStoragePointer(new BooleanOptionStorage(*this));
129 }
130
131
132 /********************************************************************
133  * IntegerOptionStorage
134  */
135
136 std::string IntegerOptionStorage::formatSingleValue(const int &value) const
137 {
138     return formatString("%d", value);
139 }
140
141 void IntegerOptionStorage::convertValue(const std::string &value)
142 {
143     const char *ptr = value.c_str();
144     char *endptr;
145     errno = 0;
146     long int ival = std::strtol(ptr, &endptr, 10);
147     if (errno == ERANGE
148         || ival < std::numeric_limits<int>::min()
149         || ival > std::numeric_limits<int>::max())
150     {
151         GMX_THROW(InvalidInputError("Invalid value: '" + value
152                                     + "'; it causes an integer overflow"));
153     }
154     if (*ptr == '\0' || *endptr != '\0')
155     {
156         GMX_THROW(InvalidInputError("Invalid value: '" + value
157                                     + "'; expected an integer"));
158     }
159     addValue(ival);
160 }
161
162 void IntegerOptionStorage::processSetValues(ValueList *values)
163 {
164     if (isVector())
165     {
166         expandVector(maxValueCount(), values);
167     }
168 }
169
170 /********************************************************************
171  * IntegerOptionInfo
172  */
173
174 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage *option)
175     : OptionInfo(option)
176 {
177 }
178
179 /********************************************************************
180  * IntegerOption
181  */
182
183 AbstractOptionStoragePointer IntegerOption::createStorage() const
184 {
185     return AbstractOptionStoragePointer(new IntegerOptionStorage(*this));
186 }
187
188
189 /********************************************************************
190  * DoubleOptionStorage
191  */
192
193 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
194     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
195 {
196 }
197
198 const char *DoubleOptionStorage::typeString() const
199 {
200     return isVector() ? "vector" : (isTime() ? "time" : "double");
201 }
202
203 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
204 {
205     return formatString("%g", value / factor_);
206 }
207
208 void DoubleOptionStorage::convertValue(const std::string &value)
209 {
210     const char *ptr = value.c_str();
211     char *endptr;
212     errno = 0;
213     double dval = std::strtod(ptr, &endptr);
214     if (errno == ERANGE)
215     {
216         GMX_THROW(InvalidInputError("Invalid value: '" + value
217                                     + "'; it causes an overflow/underflow"));
218     }
219     if (*ptr == '\0' || *endptr != '\0')
220     {
221         GMX_THROW(InvalidInputError("Invalid value: '" + value
222                                     + "'; expected a number"));
223     }
224     addValue(dval * factor_);
225 }
226
227 void DoubleOptionStorage::processSetValues(ValueList *values)
228 {
229     if (isVector())
230     {
231         expandVector(maxValueCount(), values);
232     }
233 }
234
235 void DoubleOptionStorage::processAll()
236 {
237 }
238
239 void DoubleOptionStorage::setScaleFactor(double factor)
240 {
241     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
242     if (!hasFlag(efOption_HasDefaultValue))
243     {
244         double scale = factor / factor_;
245         ValueList::iterator i;
246         for (i = values().begin(); i != values().end(); ++i)
247         {
248             (*i) *= scale;
249         }
250         refreshValues();
251     }
252     factor_ = factor;
253 }
254
255 /********************************************************************
256  * DoubleOptionInfo
257  */
258
259 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage *option)
260     : OptionInfo(option)
261 {
262 }
263
264 DoubleOptionStorage &DoubleOptionInfo::option()
265 {
266     return static_cast<DoubleOptionStorage &>(OptionInfo::option());
267 }
268
269 const DoubleOptionStorage &DoubleOptionInfo::option() const
270 {
271     return static_cast<const DoubleOptionStorage &>(OptionInfo::option());
272 }
273
274 bool DoubleOptionInfo::isTime() const
275 {
276     return option().isTime();
277 }
278
279 void DoubleOptionInfo::setScaleFactor(double factor)
280 {
281     option().setScaleFactor(factor);
282 }
283
284 /********************************************************************
285  * DoubleOption
286  */
287
288 AbstractOptionStoragePointer DoubleOption::createStorage() const
289 {
290     return AbstractOptionStoragePointer(new DoubleOptionStorage(*this));
291 }
292
293
294 /********************************************************************
295  * StringOptionStorage
296  */
297
298 StringOptionStorage::StringOptionStorage(const StringOption &settings)
299     : MyBase(settings), info_(this), enumIndexStore_(NULL)
300 {
301     if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == NULL)
302     {
303         GMX_THROW(APIError("Cannot set default enum index without enum values"));
304     }
305     if (settings.enumIndexStore_ != NULL && settings.enumValues_ == NULL)
306     {
307         GMX_THROW(APIError("Cannot set enum index store without enum values"));
308     }
309     if (settings.enumIndexStore_ != NULL && settings.maxValueCount_ < 0)
310     {
311         GMX_THROW(APIError("Cannot set enum index store with arbitrary number of values"));
312     }
313     if (settings.enumValues_ != NULL)
314     {
315         enumIndexStore_ = settings.enumIndexStore_;
316         const std::string *defaultValue = settings.defaultValue();
317         int match = -1;
318         for (int i = 0; settings.enumValues_[i] != NULL; ++i)
319         {
320             if (defaultValue != NULL && settings.enumValues_[i] == *defaultValue)
321             {
322                 match = i;
323             }
324             allowed_.push_back(settings.enumValues_[i]);
325         }
326         if (defaultValue != NULL)
327         {
328             if (match < 0)
329             {
330                 GMX_THROW(APIError("Default value is not one of allowed values"));
331             }
332         }
333         if (settings.defaultEnumIndex_ >= 0)
334         {
335             if (settings.defaultEnumIndex_ >= static_cast<int>(allowed_.size()))
336             {
337                 GMX_THROW(APIError("Default enumeration index is out of range"));
338             }
339             if (defaultValue != NULL && *defaultValue != allowed_[settings.defaultEnumIndex_])
340             {
341                 GMX_THROW(APIError("Conflicting default values"));
342             }
343         }
344         // If there is no default value, match is still -1.
345         if (enumIndexStore_ != NULL)
346         {
347             *enumIndexStore_ = match;
348         }
349     }
350     if (settings.defaultEnumIndex_ >= 0)
351     {
352         clear();
353         addValue(allowed_[settings.defaultEnumIndex_]);
354         commitValues();
355     }
356 }
357
358 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
359 {
360     return value;
361 }
362
363 void StringOptionStorage::convertValue(const std::string &value)
364 {
365     if (allowed_.size() == 0)
366     {
367         addValue(value);
368     }
369     else
370     {
371         ValueList::const_iterator  i;
372         ValueList::const_iterator  match = allowed_.end();
373         for (i = allowed_.begin(); i != allowed_.end(); ++i)
374         {
375             // TODO: Case independence.
376             if (i->find(value) == 0)
377             {
378                 if (match == allowed_.end() || i->size() < match->size())
379                 {
380                     match = i;
381                 }
382             }
383         }
384         if (match == allowed_.end())
385         {
386             GMX_THROW(InvalidInputError("Invalid value: " + value));
387         }
388         addValue(*match);
389     }
390 }
391
392 void StringOptionStorage::refreshValues()
393 {
394     MyBase::refreshValues();
395     if (enumIndexStore_ != NULL)
396     {
397         for (size_t i = 0; i < values().size(); ++i)
398         {
399             ValueList::const_iterator match =
400                 std::find(allowed_.begin(), allowed_.end(), values()[i]);
401             GMX_ASSERT(match != allowed_.end(),
402                        "Enum value not found (internal error)");
403             enumIndexStore_[i] = static_cast<int>(match - allowed_.begin());
404         }
405     }
406 }
407
408 /********************************************************************
409  * StringOptionInfo
410  */
411
412 StringOptionInfo::StringOptionInfo(StringOptionStorage *option)
413     : OptionInfo(option)
414 {
415 }
416
417 /********************************************************************
418  * StringOption
419  */
420
421 AbstractOptionStoragePointer StringOption::createStorage() const
422 {
423     return AbstractOptionStoragePointer(new StringOptionStorage(*this));
424 }
425
426 std::string StringOption::createDescription() const
427 {
428     std::string value(MyBase::createDescription());
429
430     if (enumValues_ != NULL)
431     {
432         value.append(": ");
433         for (int i = 0; enumValues_[i] != NULL; ++i)
434         {
435             value.append(enumValues_[i]);
436             if (enumValues_[i + 1] != NULL)
437             {
438                 value.append(enumValues_[i + 2] != NULL ? ", " : ", or ");
439             }
440         }
441     }
442     return value;
443 }
444
445 } // namespace gmx