17f6af248e8cc19196d8e4a5a0d2f66ac5acc90e
[alexxy/gromacs.git] / src / gromacs / options / basicoptions.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010,2011,2012,2013,2014,2015,2016,2017,2018, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes in basicoptions.h and basicoptionstorage.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_options
41  */
42 #include "gmxpre.h"
43
44 #include "basicoptions.h"
45
46 #include <cerrno>
47 #include <cstdio>
48 #include <cstdlib>
49
50 #include <algorithm>
51 #include <limits>
52 #include <string>
53 #include <vector>
54
55 #include "gromacs/utility/cstringutil.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/strconvert.h"
58 #include "gromacs/utility/stringutil.h"
59
60 #include "basicoptionstorage.h"
61
62 namespace
63 {
64
65 /*! \brief
66  * Expands a single value to a vector by copying the value.
67  *
68  * \tparam        ValueType  Type of values to process.
69  * \param[in]     length     Length of the resulting vector.
70  * \param[in,out] values     Values to process.
71  * \throws   std::bad_alloc    if out of memory.
72  * \throws   InvalidInputError if \p values has an invalid number of values.
73  *
74  * \p values should have 0, 1, or \p length values.
75  * If \p values has 1 value, it is expanded such that it has \p length
76  * identical values.  In other valid cases, nothing is done.
77  *
78  * \ingroup module_options
79  */
80 template <typename ValueType>
81 void expandVector(size_t length, std::vector<ValueType> *values)
82 {
83     if (length > 0 && !values->empty() && values->size() != length)
84     {
85         if (values->size() != 1)
86         {
87             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
88                                                      "Expected 1 or %d values, got %d", length, values->size())));
89         }
90         const ValueType &value = (*values)[0];
91         values->resize(length, value);
92     }
93 }
94
95 /*! \brief
96  * Finds an enumerated value from the list of allowed values.
97  *
98  * \param[in] allowedValues  List of allowed values.
99  * \param[in] value          Value to search for.
100  * \throws    gmx::InvalidInputError if \p value does not match anything in
101  *     \p allowedValues.
102  * \returns   Iterator to the found value.
103  *
104  * \ingroup module_options
105  */
106 std::vector<std::string>::const_iterator
107 findEnumValue(const std::vector<std::string> &allowedValues,
108               const std::string              &value)
109 {
110     std::vector<std::string>::const_iterator  i;
111     std::vector<std::string>::const_iterator  match = allowedValues.end();
112     for (i = allowedValues.begin(); i != allowedValues.end(); ++i)
113     {
114         // TODO: Case independence.
115         if (gmx::startsWith(*i, value))
116         {
117             if (match == allowedValues.end() || i->size() < match->size())
118             {
119                 match = i;
120             }
121         }
122     }
123     if (match == allowedValues.end())
124     {
125         GMX_THROW(gmx::InvalidInputError("Invalid value: " + value));
126     }
127     return match;
128 }
129
130 } // namespace
131
132 namespace gmx
133 {
134
135 /********************************************************************
136  * BooleanOptionStorage
137  */
138
139 std::string BooleanOptionStorage::formatSingleValue(const bool &value) const
140 {
141     return value ? "yes" : "no";
142 }
143
144 void BooleanOptionStorage::initConverter(ConverterType *converter)
145 {
146     converter->addConverter<std::string>(&fromStdString<bool>);
147 }
148
149 /********************************************************************
150  * BooleanOptionInfo
151  */
152
153 BooleanOptionInfo::BooleanOptionInfo(BooleanOptionStorage *option)
154     : OptionInfo(option)
155 {
156 }
157
158 const BooleanOptionStorage &BooleanOptionInfo::option() const
159 {
160     return static_cast<const BooleanOptionStorage &>(OptionInfo::option());
161 }
162
163 bool BooleanOptionInfo::defaultValue() const
164 {
165     return option().defaultValue();
166 }
167
168 /********************************************************************
169  * BooleanOption
170  */
171
172 AbstractOptionStorage *
173 BooleanOption::createStorage(const OptionManagerContainer & /*managers*/) const
174 {
175     return new BooleanOptionStorage(*this);
176 }
177
178
179 /********************************************************************
180  * IntegerOptionStorage
181  */
182
183 std::string IntegerOptionStorage::formatSingleValue(const int &value) const
184 {
185     return toString(value);
186 }
187
188 void IntegerOptionStorage::initConverter(ConverterType *converter)
189 {
190     converter->addConverter<std::string>(&fromStdString<int>);
191 }
192
193 void IntegerOptionStorage::processSetValues(ValueList *values)
194 {
195     if (isVector())
196     {
197         expandVector(maxValueCount(), values);
198     }
199 }
200
201 /********************************************************************
202  * IntegerOptionInfo
203  */
204
205 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage *option)
206     : OptionInfo(option)
207 {
208 }
209
210 /********************************************************************
211  * IntegerOption
212  */
213
214 AbstractOptionStorage *
215 IntegerOption::createStorage(const OptionManagerContainer & /*managers*/) const
216 {
217     return new IntegerOptionStorage(*this);
218 }
219
220
221 /********************************************************************
222  * Int64OptionStorage
223  */
224
225 std::string Int64OptionStorage::formatSingleValue(const gmx_int64_t &value) const
226 {
227     return toString(value);
228 }
229
230 void Int64OptionStorage::initConverter(ConverterType *converter)
231 {
232     converter->addConverter<std::string>(&fromStdString<gmx_int64_t>);
233 }
234
235 /********************************************************************
236  * Int64OptionInfo
237  */
238
239 Int64OptionInfo::Int64OptionInfo(Int64OptionStorage *option)
240     : OptionInfo(option)
241 {
242 }
243
244 /********************************************************************
245  * Int64Option
246  */
247
248 AbstractOptionStorage *
249 Int64Option::createStorage(const OptionManagerContainer & /*managers*/) const
250 {
251     return new Int64OptionStorage(*this);
252 }
253
254
255 /********************************************************************
256  * DoubleOptionStorage
257  */
258
259 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
260     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
261 {
262 }
263
264 std::string DoubleOptionStorage::typeString() const
265 {
266     return isVector() ? "vector" : (isTime() ? "time" : "real");
267 }
268
269 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
270 {
271     return toString(value / factor_);
272 }
273
274 void DoubleOptionStorage::initConverter(ConverterType *converter)
275 {
276     converter->addConverter<std::string>(&fromStdString<double>);
277     converter->addCastConversion<float>();
278 }
279
280 double DoubleOptionStorage::processValue(const double &value) const
281 {
282     // TODO: Consider testing for overflow when scaling with factor_.
283     return value * factor_;
284 }
285
286 void DoubleOptionStorage::processSetValues(ValueList *values)
287 {
288     if (isVector())
289     {
290         expandVector(maxValueCount(), values);
291     }
292 }
293
294 void DoubleOptionStorage::setScaleFactor(double factor)
295 {
296     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
297     if (!hasFlag(efOption_HasDefaultValue))
298     {
299         double scale = factor / factor_;
300         for (double &value : values())
301         {
302             value *= scale;
303         }
304     }
305     factor_ = factor;
306 }
307
308 /********************************************************************
309  * DoubleOptionInfo
310  */
311
312 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage *option)
313     : OptionInfo(option)
314 {
315 }
316
317 DoubleOptionStorage &DoubleOptionInfo::option()
318 {
319     return static_cast<DoubleOptionStorage &>(OptionInfo::option());
320 }
321
322 const DoubleOptionStorage &DoubleOptionInfo::option() const
323 {
324     return static_cast<const DoubleOptionStorage &>(OptionInfo::option());
325 }
326
327 bool DoubleOptionInfo::isTime() const
328 {
329     return option().isTime();
330 }
331
332 void DoubleOptionInfo::setScaleFactor(double factor)
333 {
334     option().setScaleFactor(factor);
335 }
336
337 /********************************************************************
338  * DoubleOption
339  */
340
341 AbstractOptionStorage *
342 DoubleOption::createStorage(const OptionManagerContainer & /*managers*/) const
343 {
344     return new DoubleOptionStorage(*this);
345 }
346
347
348 /********************************************************************
349  * FloatOptionStorage
350  */
351
352 FloatOptionStorage::FloatOptionStorage(const FloatOption &settings)
353     : MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
354 {
355 }
356
357 std::string FloatOptionStorage::typeString() const
358 {
359     return isVector() ? "vector" : (isTime() ? "time" : "real");
360 }
361
362 std::string FloatOptionStorage::formatSingleValue(const float &value) const
363 {
364     return toString(value / factor_);
365 }
366
367 void FloatOptionStorage::initConverter(ConverterType *converter)
368 {
369     converter->addConverter<std::string>(&fromStdString<float>);
370     converter->addCastConversion<double>();
371 }
372
373 float FloatOptionStorage::processValue(const float &value) const
374 {
375     // TODO: Consider testing for overflow when scaling with factor_.
376     return value * factor_;
377 }
378
379 void FloatOptionStorage::processSetValues(ValueList *values)
380 {
381     if (isVector())
382     {
383         expandVector(maxValueCount(), values);
384     }
385 }
386
387 void FloatOptionStorage::setScaleFactor(double factor)
388 {
389     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
390     if (!hasFlag(efOption_HasDefaultValue))
391     {
392         float scale = factor / factor_;
393         for (float &value : values())
394         {
395             value *= scale;
396         }
397     }
398     factor_ = factor;
399 }
400
401 /********************************************************************
402  * FloatOptionInfo
403  */
404
405 FloatOptionInfo::FloatOptionInfo(FloatOptionStorage *option)
406     : OptionInfo(option)
407 {
408 }
409
410 FloatOptionStorage &FloatOptionInfo::option()
411 {
412     return static_cast<FloatOptionStorage &>(OptionInfo::option());
413 }
414
415 const FloatOptionStorage &FloatOptionInfo::option() const
416 {
417     return static_cast<const FloatOptionStorage &>(OptionInfo::option());
418 }
419
420 bool FloatOptionInfo::isTime() const
421 {
422     return option().isTime();
423 }
424
425 void FloatOptionInfo::setScaleFactor(double factor)
426 {
427     option().setScaleFactor(factor);
428 }
429
430 /********************************************************************
431  * FloatOption
432  */
433
434 AbstractOptionStorage *
435 FloatOption::createStorage(const OptionManagerContainer & /*managers*/) const
436 {
437     return new FloatOptionStorage(*this);
438 }
439
440
441 /********************************************************************
442  * StringOptionStorage
443  */
444
445 StringOptionStorage::StringOptionStorage(const StringOption &settings)
446     : MyBase(settings), info_(this)
447 {
448     if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == nullptr)
449     {
450         GMX_THROW(APIError("Cannot set default enum index without enum values"));
451     }
452     if (settings.enumValues_ != nullptr)
453     {
454         int count = settings.enumValuesCount_;
455         if (count < 0)
456         {
457             count = 0;
458             while (settings.enumValues_[count] != nullptr)
459             {
460                 ++count;
461             }
462         }
463         for (int i = 0; i < count; ++i)
464         {
465             if (settings.enumValues_[i] == nullptr)
466             {
467                 GMX_THROW(APIError("Enumeration value cannot be NULL"));
468             }
469             allowed_.emplace_back(settings.enumValues_[i]);
470         }
471         if (settings.defaultEnumIndex_ >= 0)
472         {
473             if (settings.defaultEnumIndex_ >= count)
474             {
475                 GMX_THROW(APIError("Default enumeration index is out of range"));
476             }
477             const std::string *defaultValue = settings.defaultValue();
478             if (defaultValue != nullptr && *defaultValue != allowed_[settings.defaultEnumIndex_])
479             {
480                 GMX_THROW(APIError("Conflicting default values"));
481             }
482             setDefaultValue(allowed_[settings.defaultEnumIndex_]);
483         }
484     }
485 }
486
487 std::string StringOptionStorage::formatExtraDescription() const
488 {
489     std::string result;
490     if (!allowed_.empty())
491     {
492         result.append(": ");
493         result.append(joinStrings(allowed_, ", "));
494     }
495     return result;
496 }
497
498 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
499 {
500     return value;
501 }
502
503 void StringOptionStorage::initConverter(ConverterType * /*converter*/)
504 {
505 }
506
507 std::string StringOptionStorage::processValue(const std::string &value) const
508 {
509     if (!allowed_.empty())
510     {
511         return *findEnumValue(this->allowed_, value);
512     }
513     return value;
514 }
515
516 /********************************************************************
517  * StringOptionInfo
518  */
519
520 StringOptionInfo::StringOptionInfo(StringOptionStorage *option)
521     : OptionInfo(option)
522 {
523 }
524
525 const StringOptionStorage &StringOptionInfo::option() const
526 {
527     return static_cast<const StringOptionStorage &>(OptionInfo::option());
528 }
529
530 bool StringOptionInfo::isEnumerated() const
531 {
532     return !allowedValues().empty();
533 }
534
535 const std::vector<std::string> &StringOptionInfo::allowedValues() const
536 {
537     return option().allowedValues();
538 }
539
540 /********************************************************************
541  * StringOption
542  */
543
544 AbstractOptionStorage *
545 StringOption::createStorage(const OptionManagerContainer & /*managers*/) const
546 {
547     return new StringOptionStorage(*this);
548 }
549
550
551 /********************************************************************
552  * EnumOptionStorage
553  */
554
555 EnumOptionStorage::EnumOptionStorage(const AbstractOption &settings,
556                                      const char *const *enumValues, int count,
557                                      int defaultValue, int defaultValueIfSet,
558                                      StorePointer store)
559     : MyBase(settings, std::move(store)), info_(this)
560 {
561     if (enumValues == nullptr)
562     {
563         GMX_THROW(APIError("Allowed values must be provided to EnumOption"));
564     }
565
566     if (count < 0)
567     {
568         count = 0;
569         while (enumValues[count] != nullptr)
570         {
571             ++count;
572         }
573     }
574     for (int i = 0; i < count; ++i)
575     {
576         if (enumValues[i] == nullptr)
577         {
578             GMX_THROW(APIError("Enumeration value cannot be NULL"));
579         }
580         allowed_.emplace_back(enumValues[i]);
581     }
582
583     GMX_ASSERT(defaultValue < count, "Default enumeration value is out of range");
584     GMX_ASSERT(defaultValueIfSet < count, "Default enumeration value is out of range");
585     setFlag(efOption_HasDefaultValue);
586     if (defaultValue >= 0)
587     {
588         setDefaultValue(defaultValue);
589     }
590     if (defaultValueIfSet >= 0)
591     {
592         setDefaultValueIfSet(defaultValueIfSet);
593     }
594 }
595
596 std::string EnumOptionStorage::formatExtraDescription() const
597 {
598     std::string result;
599     result.append(": ");
600     result.append(joinStrings(allowed_, ", "));
601     return result;
602 }
603
604 std::string EnumOptionStorage::formatSingleValue(const int &value) const
605 {
606     if (value < 0 || value >= static_cast<int>(allowed_.size()))
607     {
608         return std::string();
609     }
610     return allowed_[value];
611 }
612
613 Variant EnumOptionStorage::normalizeValue(const int &value) const
614 {
615     return Variant::create<std::string>(formatSingleValue(value));
616 }
617
618 void EnumOptionStorage::initConverter(ConverterType *converter)
619 {
620     converter->addConverter<std::string>(
621             [this] (const std::string &value)
622             {
623                 return findEnumValue(this->allowed_, value) - this->allowed_.begin();
624             });
625 }
626
627 /********************************************************************
628  * EnumOptionInfo
629  */
630
631 EnumOptionInfo::EnumOptionInfo(EnumOptionStorage *option)
632     : OptionInfo(option)
633 {
634 }
635
636 const EnumOptionStorage &EnumOptionInfo::option() const
637 {
638     return static_cast<const EnumOptionStorage &>(OptionInfo::option());
639 }
640
641 const std::vector<std::string> &EnumOptionInfo::allowedValues() const
642 {
643     return option().allowedValues();
644 }
645
646 /********************************************************************
647  * EnumOption helpers
648  */
649
650 namespace internal
651 {
652
653 //! \cond internal
654 AbstractOptionStorage *
655 createEnumOptionStorage(const AbstractOption &option,
656                         const char *const *enumValues, int count,
657                         int defaultValue, int defaultValueIfSet,
658                         IOptionValueStore<int> *store)
659 {
660     std::unique_ptr<IOptionValueStore<int> > storePtr(store);
661     return new EnumOptionStorage(option, enumValues, count, defaultValue,
662                                  defaultValueIfSet, move(storePtr));
663 }
664 //! \endcond
665
666 } // namespace internal
667
668 } // namespace gmx