SYCL: Avoid using no_init read accessor in rocFFT
[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-2018, The GROMACS development team.
5  * Copyright (c) 2019,2021, 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 classes in basicoptions.h and basicoptionstorage.h.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_options
42  */
43 #include "gmxpre.h"
44
45 #include "basicoptions.h"
46
47 #include <cerrno>
48 #include <cstdio>
49 #include <cstdlib>
50
51 #include <algorithm>
52 #include <limits>
53 #include <string>
54 #include <vector>
55
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/exceptions.h"
58 #include "gromacs/utility/strconvert.h"
59 #include "gromacs/utility/stringutil.h"
60
61 #include "basicoptionstorage.h"
62
63 namespace
64 {
65
66 /*! \brief
67  * Expands a single value to a vector by copying the value.
68  *
69  * \tparam        ValueType  Type of values to process.
70  * \param[in]     length     Length of the resulting vector.
71  * \param[in,out] values     Values to process.
72  * \throws   std::bad_alloc    if out of memory.
73  * \throws   InvalidInputError if \p values has an invalid number of values.
74  *
75  * \p values should have 0, 1, or \p length values.
76  * If \p values has 1 value, it is expanded such that it has \p length
77  * identical values.  In other valid cases, nothing is done.
78  *
79  * \ingroup module_options
80  */
81 template<typename ValueType>
82 void expandVector(size_t length, std::vector<ValueType>* values)
83 {
84     if (length > 0 && !values->empty() && values->size() != length)
85     {
86         if (values->size() != 1)
87         {
88             GMX_THROW(gmx::InvalidInputError(
89                     gmx::formatString("Expected 1 or %zu values, got %zu", length, values->size())));
90         }
91         const ValueType& value = (*values)[0];
92         values->resize(length, value);
93     }
94 }
95
96 /*! \brief
97  * Finds an enumerated value from the list of allowed values.
98  *
99  * \param[in] allowedValues  List of allowed values.
100  * \param[in] value          Value to search for.
101  * \throws    gmx::InvalidInputError if \p value does not match anything in
102  *     \p allowedValues.
103  * \returns   Iterator to the found value.
104  *
105  * \ingroup module_options
106  */
107 std::vector<std::string>::const_iterator 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) : OptionInfo(option) {}
154
155 const BooleanOptionStorage& BooleanOptionInfo::option() const
156 {
157     return static_cast<const BooleanOptionStorage&>(OptionInfo::option());
158 }
159
160 bool BooleanOptionInfo::defaultValue() const
161 {
162     return option().defaultValue();
163 }
164
165 /********************************************************************
166  * BooleanOption
167  */
168
169 AbstractOptionStorage* BooleanOption::createStorage(const OptionManagerContainer& /*managers*/) const
170 {
171     return new BooleanOptionStorage(*this);
172 }
173
174
175 /********************************************************************
176  * IntegerOptionStorage
177  */
178
179 std::string IntegerOptionStorage::formatSingleValue(const int& value) const
180 {
181     return toString(value);
182 }
183
184 void IntegerOptionStorage::initConverter(ConverterType* converter)
185 {
186     converter->addConverter<std::string>(&fromStdString<int>);
187 }
188
189 void IntegerOptionStorage::processSetValues(ValueList* values)
190 {
191     if (isVector())
192     {
193         expandVector(maxValueCount(), values);
194     }
195 }
196
197 /********************************************************************
198  * IntegerOptionInfo
199  */
200
201 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage* option) : OptionInfo(option) {}
202
203 /********************************************************************
204  * IntegerOption
205  */
206
207 AbstractOptionStorage* IntegerOption::createStorage(const OptionManagerContainer& /*managers*/) const
208 {
209     return new IntegerOptionStorage(*this);
210 }
211
212
213 /********************************************************************
214  * Int64OptionStorage
215  */
216
217 std::string Int64OptionStorage::formatSingleValue(const int64_t& value) const
218 {
219     return toString(value);
220 }
221
222 void Int64OptionStorage::initConverter(ConverterType* converter)
223 {
224     converter->addConverter<std::string>(&fromStdString<int64_t>);
225 }
226
227 /********************************************************************
228  * Int64OptionInfo
229  */
230
231 Int64OptionInfo::Int64OptionInfo(Int64OptionStorage* option) : OptionInfo(option) {}
232
233 /********************************************************************
234  * Int64Option
235  */
236
237 AbstractOptionStorage* Int64Option::createStorage(const OptionManagerContainer& /*managers*/) const
238 {
239     return new Int64OptionStorage(*this);
240 }
241
242
243 /********************************************************************
244  * DoubleOptionStorage
245  */
246
247 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption& settings) :
248     MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
249 {
250 }
251
252 std::string DoubleOptionStorage::typeString() const
253 {
254     return isVector() ? "vector" : (isTime() ? "time" : "real");
255 }
256
257 std::string DoubleOptionStorage::formatSingleValue(const double& value) const
258 {
259     return toString(value / factor_);
260 }
261
262 void DoubleOptionStorage::initConverter(ConverterType* converter)
263 {
264     converter->addConverter<std::string>(&fromStdString<double>);
265     converter->addCastConversion<float>();
266 }
267
268 double DoubleOptionStorage::processValue(const double& value) const
269 {
270     // TODO: Consider testing for overflow when scaling with factor_.
271     return value * factor_;
272 }
273
274 void DoubleOptionStorage::processSetValues(ValueList* values)
275 {
276     if (isVector())
277     {
278         expandVector(maxValueCount(), values);
279     }
280 }
281
282 void DoubleOptionStorage::setScaleFactor(double factor)
283 {
284     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
285     if (!hasFlag(efOption_HasDefaultValue))
286     {
287         double scale = factor / factor_;
288         for (double& value : values())
289         {
290             value *= scale;
291         }
292     }
293     factor_ = factor;
294 }
295
296 /********************************************************************
297  * DoubleOptionInfo
298  */
299
300 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage* option) : OptionInfo(option) {}
301
302 DoubleOptionStorage& DoubleOptionInfo::option()
303 {
304     return static_cast<DoubleOptionStorage&>(OptionInfo::option());
305 }
306
307 const DoubleOptionStorage& DoubleOptionInfo::option() const
308 {
309     return static_cast<const DoubleOptionStorage&>(OptionInfo::option());
310 }
311
312 bool DoubleOptionInfo::isTime() const
313 {
314     return option().isTime();
315 }
316
317 void DoubleOptionInfo::setScaleFactor(double factor)
318 {
319     option().setScaleFactor(factor);
320 }
321
322 /********************************************************************
323  * DoubleOption
324  */
325
326 AbstractOptionStorage* DoubleOption::createStorage(const OptionManagerContainer& /*managers*/) const
327 {
328     return new DoubleOptionStorage(*this);
329 }
330
331
332 /********************************************************************
333  * FloatOptionStorage
334  */
335
336 FloatOptionStorage::FloatOptionStorage(const FloatOption& settings) :
337     MyBase(settings), info_(this), bTime_(settings.bTime_), factor_(1.0)
338 {
339 }
340
341 std::string FloatOptionStorage::typeString() const
342 {
343     return isVector() ? "vector" : (isTime() ? "time" : "real");
344 }
345
346 std::string FloatOptionStorage::formatSingleValue(const float& value) const
347 {
348     return toString(value / factor_);
349 }
350
351 void FloatOptionStorage::initConverter(ConverterType* converter)
352 {
353     converter->addConverter<std::string>(&fromStdString<float>);
354     converter->addCastConversion<double>();
355 }
356
357 float FloatOptionStorage::processValue(const float& value) const
358 {
359     // TODO: Consider testing for overflow when scaling with factor_.
360     return value * factor_;
361 }
362
363 void FloatOptionStorage::processSetValues(ValueList* values)
364 {
365     if (isVector())
366     {
367         expandVector(maxValueCount(), values);
368     }
369 }
370
371 void FloatOptionStorage::setScaleFactor(double factor)
372 {
373     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
374     if (!hasFlag(efOption_HasDefaultValue))
375     {
376         float scale = factor / factor_;
377         for (float& value : values())
378         {
379             value *= scale;
380         }
381     }
382     factor_ = factor;
383 }
384
385 /********************************************************************
386  * FloatOptionInfo
387  */
388
389 FloatOptionInfo::FloatOptionInfo(FloatOptionStorage* option) : OptionInfo(option) {}
390
391 FloatOptionStorage& FloatOptionInfo::option()
392 {
393     return static_cast<FloatOptionStorage&>(OptionInfo::option());
394 }
395
396 const FloatOptionStorage& FloatOptionInfo::option() const
397 {
398     return static_cast<const FloatOptionStorage&>(OptionInfo::option());
399 }
400
401 bool FloatOptionInfo::isTime() const
402 {
403     return option().isTime();
404 }
405
406 void FloatOptionInfo::setScaleFactor(double factor)
407 {
408     option().setScaleFactor(factor);
409 }
410
411 /********************************************************************
412  * FloatOption
413  */
414
415 AbstractOptionStorage* FloatOption::createStorage(const OptionManagerContainer& /*managers*/) const
416 {
417     return new FloatOptionStorage(*this);
418 }
419
420
421 /********************************************************************
422  * StringOptionStorage
423  */
424
425 StringOptionStorage::StringOptionStorage(const StringOption& settings) :
426     MyBase(settings), info_(this)
427 {
428     if (settings.defaultEnumIndex_ >= 0 && settings.enumValues_ == nullptr)
429     {
430         GMX_THROW(APIError("Cannot set default enum index without enum values"));
431     }
432     if (settings.enumValues_ != nullptr)
433     {
434         int count = settings.enumValuesCount_;
435         if (count < 0)
436         {
437             count = 0;
438             while (settings.enumValues_[count] != nullptr)
439             {
440                 ++count;
441             }
442         }
443         for (int i = 0; i < count; ++i)
444         {
445             if (settings.enumValues_[i] == nullptr)
446             {
447                 GMX_THROW(APIError("Enumeration value cannot be NULL"));
448             }
449             allowed_.emplace_back(settings.enumValues_[i]);
450         }
451         if (settings.defaultEnumIndex_ >= 0)
452         {
453             if (settings.defaultEnumIndex_ >= count)
454             {
455                 GMX_THROW(APIError("Default enumeration index is out of range"));
456             }
457             const std::string* defaultValue = settings.defaultValue();
458             if (defaultValue != nullptr && *defaultValue != allowed_[settings.defaultEnumIndex_])
459             {
460                 GMX_THROW(APIError("Conflicting default values"));
461             }
462             setDefaultValue(allowed_[settings.defaultEnumIndex_]);
463         }
464     }
465 }
466
467 std::string StringOptionStorage::formatExtraDescription() const
468 {
469     std::string result;
470     if (!allowed_.empty())
471     {
472         result.append(": ");
473         result.append(joinStrings(allowed_, ", "));
474     }
475     return result;
476 }
477
478 std::string StringOptionStorage::formatSingleValue(const std::string& value) const
479 {
480     return value;
481 }
482
483 void StringOptionStorage::initConverter(ConverterType* /*converter*/) {}
484
485 std::string StringOptionStorage::processValue(const std::string& value) const
486 {
487     if (!allowed_.empty())
488     {
489         return *findEnumValue(this->allowed_, value);
490     }
491     return value;
492 }
493
494 /********************************************************************
495  * StringOptionInfo
496  */
497
498 StringOptionInfo::StringOptionInfo(StringOptionStorage* option) : OptionInfo(option) {}
499
500 const StringOptionStorage& StringOptionInfo::option() const
501 {
502     return static_cast<const StringOptionStorage&>(OptionInfo::option());
503 }
504
505 bool StringOptionInfo::isEnumerated() const
506 {
507     return !allowedValues().empty();
508 }
509
510 const std::vector<std::string>& StringOptionInfo::allowedValues() const
511 {
512     return option().allowedValues();
513 }
514
515 /********************************************************************
516  * StringOption
517  */
518
519 AbstractOptionStorage* StringOption::createStorage(const OptionManagerContainer& /*managers*/) const
520 {
521     return new StringOptionStorage(*this);
522 }
523
524
525 /********************************************************************
526  * EnumOptionStorage
527  */
528
529 EnumOptionStorage::EnumOptionStorage(const AbstractOption& settings,
530                                      const char* const*    enumValues,
531                                      int                   count,
532                                      int                   defaultValue,
533                                      int                   defaultValueIfSet,
534                                      StorePointer          store) :
535     MyBase(settings, std::move(store)), info_(this)
536 {
537     if (enumValues == nullptr)
538     {
539         GMX_THROW(APIError("Allowed values must be provided to EnumOption"));
540     }
541
542     if (count < 0)
543     {
544         count = 0;
545         while (enumValues[count] != nullptr)
546         {
547             ++count;
548         }
549     }
550     for (int i = 0; i < count; ++i)
551     {
552         if (enumValues[i] == nullptr)
553         {
554             GMX_THROW(APIError("Enumeration value cannot be NULL"));
555         }
556         allowed_.emplace_back(enumValues[i]);
557     }
558
559     GMX_ASSERT(defaultValue < count, "Default enumeration value is out of range");
560     GMX_ASSERT(defaultValueIfSet < count, "Default enumeration value is out of range");
561     setFlag(efOption_HasDefaultValue);
562     if (defaultValue >= 0)
563     {
564         setDefaultValue(defaultValue);
565     }
566     if (defaultValueIfSet >= 0)
567     {
568         setDefaultValueIfSet(defaultValueIfSet);
569     }
570 }
571
572 std::string EnumOptionStorage::formatExtraDescription() const
573 {
574     std::string result;
575     result.append(": ");
576     result.append(joinStrings(allowed_, ", "));
577     return result;
578 }
579
580 std::string EnumOptionStorage::formatSingleValue(const int& value) const
581 {
582     if (value < 0 || value >= ssize(allowed_))
583     {
584         return std::string();
585     }
586     return allowed_[value];
587 }
588
589 Any EnumOptionStorage::normalizeValue(const int& value) const
590 {
591     return Any::create<std::string>(formatSingleValue(value));
592 }
593
594 void EnumOptionStorage::initConverter(ConverterType* converter)
595 {
596     converter->addConverter<std::string>([this](const std::string& value) {
597         return findEnumValue(this->allowed_, value) - this->allowed_.begin();
598     });
599 }
600
601 /********************************************************************
602  * EnumOptionInfo
603  */
604
605 EnumOptionInfo::EnumOptionInfo(EnumOptionStorage* option) : OptionInfo(option) {}
606
607 const EnumOptionStorage& EnumOptionInfo::option() const
608 {
609     return static_cast<const EnumOptionStorage&>(OptionInfo::option());
610 }
611
612 const std::vector<std::string>& EnumOptionInfo::allowedValues() const
613 {
614     return option().allowedValues();
615 }
616
617 /********************************************************************
618  * EnumOption helpers
619  */
620
621 namespace internal
622 {
623
624 //! \cond internal
625 AbstractOptionStorage* createEnumOptionStorage(const AbstractOption& option,
626                                                const char* const*    enumValues,
627                                                int                   count,
628                                                int                   defaultValue,
629                                                int                   defaultValueIfSet,
630                                                std::unique_ptr<IOptionValueStore<int>> store)
631 {
632     return new EnumOptionStorage(option, enumValues, count, defaultValue, defaultValueIfSet, move(store));
633 }
634 //! \endcond
635
636 } // namespace internal
637
638 } // namespace gmx