Update instructions in containers.rst
[gromacs.git] / src / gromacs / selection / sm_compare.cpp
blob1745d11ee3e6648f439d02e6f5d0ee79e85134e2
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
5 * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
6 * Copyright (c) 2019,2020, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 /*! \internal \file
38 * \brief
39 * Implements internal selection method for comparison expressions.
41 * \author Teemu Murtola <teemu.murtola@gmail.com>
42 * \ingroup module_selection
44 #include "gmxpre.h"
46 #include <cmath>
48 #include "gromacs/math/utilities.h"
49 #include "gromacs/utility/arraysize.h"
50 #include "gromacs/utility/basedefinitions.h"
51 #include "gromacs/utility/exceptions.h"
52 #include "gromacs/utility/smalloc.h"
54 #include "keywords.h"
55 #include "selmethod.h"
57 /** Defines the comparison operator for comparison expressions. */
58 typedef enum
60 CMP_INVALID, /**< Indicates an error */
61 CMP_LESS, /**< '<' */
62 CMP_LEQ, /**< '<=' */
63 CMP_GTR, /**< '>' */
64 CMP_GEQ, /**< '>=' */
65 CMP_EQUAL, /**< '==' */
66 CMP_NEQ /**< '!=' */
67 } e_comparison_t;
69 /** The operand has a single value. */
70 #define CMP_SINGLEVAL 1
71 /** The operand value is dynamic. */
72 #define CMP_DYNAMICVAL 2
73 /** The value is real. */
74 #define CMP_REALVAL 4
75 /** The integer array is allocated. */
76 #define CMP_ALLOCINT 16
77 /** The real array is allocated. */
78 #define CMP_ALLOCREAL 32
80 /*! \internal \brief
81 * Data structure for comparison expression operand values.
83 typedef struct
85 /** Flags that describe the type of the operand. */
86 int flags;
87 /** (Array of) integer value(s). */
88 int* i;
89 /** (Array of) real value(s). */
90 real* r;
91 } t_compare_value;
93 /*! \internal \brief
94 * Data structure for comparison expression evaluation.
96 typedef struct
98 /** Comparison operator as a string. */
99 char* cmpop;
100 /** Comparison operator type. */
101 e_comparison_t cmpt;
102 /** Left value. */
103 t_compare_value left;
104 /** Right value. */
105 t_compare_value right;
106 } t_methoddata_compare;
108 /*! \brief
109 * Allocates data for comparison expression evaluation.
111 * \param[in] npar Not used (should be 5).
112 * \param[in,out] param Method parameters (should point to a copy of
113 * \ref smparams_compare).
114 * \returns Pointer to the allocated data (\c t_methoddata_compare).
116 * Allocates memory for a \c t_methoddata_compare structure.
118 static void* init_data_compare(int npar, gmx_ana_selparam_t* param);
119 /*! \brief
120 * Initializes data for comparison expression evaluation.
122 * \param[in] top Not used.
123 * \param[in] npar Not used (should be 5).
124 * \param[in] param Method parameters (should point to \ref smparams_compare).
125 * \param[in] data Should point to a \c t_methoddata_compare.
126 * \returns 0 if the input data is valid, -1 on error.
128 static void init_compare(const gmx_mtop_t* top, int npar, gmx_ana_selparam_t* param, void* data);
129 /** Frees the memory allocated for comparison expression evaluation. */
130 static void free_data_compare(void* data);
131 /*! \brief
132 * Evaluates comparison expressions.
134 * \param[in] context Not used.
135 * \param[in] g Evaluation index group.
136 * \param[out] out Output data structure (\p out->u.g is used).
137 * \param[in] data Should point to a \c t_methoddata_compare.
139 static void evaluate_compare(const gmx::SelMethodEvalContext& context,
140 gmx_ana_index_t* g,
141 gmx_ana_selvalue_t* out,
142 void* data);
144 /** Parameters for comparison expression evaluation. */
145 static gmx_ana_selparam_t smparams_compare[] = {
146 { "int1", { INT_VALUE, -1, { nullptr } }, nullptr, SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL },
147 { "real1", { REAL_VALUE, -1, { nullptr } }, nullptr, SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL },
148 { "op", { STR_VALUE, 1, { nullptr } }, nullptr, 0 },
149 { "int2", { INT_VALUE, -1, { nullptr } }, nullptr, SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL },
150 { "real2", { REAL_VALUE, -1, { nullptr } }, nullptr, SPAR_OPTIONAL | SPAR_DYNAMIC | SPAR_ATOMVAL },
153 /** \internal Selection method data for comparison expression evaluation. */
154 gmx_ana_selmethod_t sm_compare = {
155 "cmp",
156 GROUP_VALUE,
157 SMETH_SINGLEVAL,
158 asize(smparams_compare),
159 smparams_compare,
160 &init_data_compare,
161 nullptr,
162 &init_compare,
163 nullptr,
164 &free_data_compare,
165 nullptr,
166 &evaluate_compare,
167 nullptr,
168 { nullptr, nullptr, 0, nullptr },
171 /*! \brief
172 * Returns a \c e_comparison_t value corresponding to an operator.
174 * \param[in] str String to process.
175 * \returns The comparison type corresponding to the first one or two
176 * characters of \p str.
178 * \p str can contain any number of characters; only the first two
179 * are used.
180 * If the beginning of \p str does not match any of the recognized types,
181 * \ref CMP_INVALID is returned.
183 static e_comparison_t comparison_type(const char* str)
185 switch (str[0])
187 case '<': return (str[1] == '=') ? CMP_LEQ : CMP_LESS;
188 case '>': return (str[1] == '=') ? CMP_GEQ : CMP_GTR;
189 case '=': return (str[1] == '=') ? CMP_EQUAL : CMP_INVALID;
190 case '!': return (str[1] == '=') ? CMP_NEQ : CMP_INVALID;
192 return CMP_INVALID;
195 /*! \brief
196 * Returns a string corresponding to a \c e_comparison_t value.
198 * \param[in] cmpt Comparison type to convert.
199 * \returns Pointer to a string that corresponds to \p cmpt.
201 * The return value points to a string constant and should not be \p free'd.
203 * The function returns NULL if \p cmpt is not one of the valid values.
205 static const char* comparison_type_str(e_comparison_t cmpt)
207 const char* p = nullptr;
208 switch (cmpt)
210 case CMP_INVALID: p = "INVALID"; break;
211 case CMP_LESS: p = "<"; break;
212 case CMP_LEQ: p = "<="; break;
213 case CMP_GTR: p = ">"; break;
214 case CMP_GEQ: p = ">="; break;
215 case CMP_EQUAL: p = "=="; break;
216 case CMP_NEQ:
217 p = "!=";
218 break;
219 // No default clause so we intentionally get compiler errors
220 // if new selection choices are added later.
222 return p;
226 * \param[in] fp File to receive the output.
227 * \param[in] data Should point to a \c t_methoddata_compare.
229 void _gmx_selelem_print_compare_info(FILE* fp, void* data)
231 t_methoddata_compare* d = static_cast<t_methoddata_compare*>(data);
233 fprintf(fp, " \"");
234 /* Print the left value */
235 if ((d->left.flags & CMP_SINGLEVAL) && !(d->left.flags & CMP_DYNAMICVAL))
237 if (d->left.flags & CMP_REALVAL)
239 fprintf(fp, "%f ", d->left.r[0]);
241 else
243 fprintf(fp, "%d ", d->left.i[0]);
246 /* Print the operator */
247 if (d->cmpt != CMP_INVALID)
249 fprintf(fp, "%s", comparison_type_str(d->cmpt));
251 else
253 fprintf(fp, "%s", d->cmpop);
255 /* Print the right value */
256 if ((d->right.flags & CMP_SINGLEVAL) && !(d->right.flags & CMP_DYNAMICVAL))
258 if (d->right.flags & CMP_REALVAL)
260 fprintf(fp, " %f", d->right.r[0]);
262 else
264 fprintf(fp, " %d", d->right.i[0]);
267 fprintf(fp, "\"");
270 static void* init_data_compare(int /* npar */, gmx_ana_selparam_t* param)
272 t_methoddata_compare* data;
274 snew(data, 1);
275 param[2].val.u.s = &data->cmpop;
276 return data;
279 /*! \brief
280 * Reverses a comparison operator.
282 * \param[in] type Comparison operator to reverse.
283 * \returns The correct comparison operator that equals \p type when the
284 * left and right sides are interchanged.
286 static e_comparison_t reverse_comparison_type(e_comparison_t type)
288 switch (type)
290 case CMP_LESS: return CMP_GTR;
291 case CMP_LEQ: return CMP_GEQ;
292 case CMP_GTR: return CMP_LESS;
293 case CMP_GEQ: return CMP_LEQ;
294 default: break;
296 return type;
299 /*! \brief
300 * Initializes the value storage for comparison expression.
302 * \param[out] val Value structure to initialize.
303 * \param[in] param Parameters to use for initialization.
304 * \returns The number of values provided for the value, 0 on error.
306 static int init_comparison_value(t_compare_value* val, gmx_ana_selparam_t param[2])
308 int n;
310 val->flags = 0;
311 if (param[0].flags & SPAR_SET)
313 val->flags |= (param[0].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
314 val->flags |= !(param[0].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL : 0;
315 n = param[0].val.nr;
316 val->i = param[0].val.u.i;
318 else if (param[1].flags & SPAR_SET)
320 val->flags |= (param[1].flags & SPAR_DYNAMIC) ? CMP_DYNAMICVAL : 0;
321 val->flags |= !(param[1].flags & SPAR_ATOMVAL) ? CMP_SINGLEVAL : 0;
322 val->flags |= CMP_REALVAL;
323 n = param[1].val.nr;
324 val->r = param[1].val.u.r;
326 else
328 n = 0;
329 val->i = nullptr;
330 val->r = nullptr;
332 return n;
335 /*! \brief
336 * Converts an integer value to floating point.
338 * \param[in] n Number of values in the \p val->u array.
339 * \param[in,out] val Value to convert.
341 static void convert_int_real(int n, t_compare_value* val)
343 int i;
344 real* rv;
346 snew(rv, n);
347 for (i = 0; i < n; ++i)
349 rv[i] = static_cast<real>(val->i[i]);
351 /* Free the previous value if one is present. */
352 sfree(val->r);
353 val->r = rv;
354 val->flags |= CMP_REALVAL | CMP_ALLOCREAL;
357 /*! \brief
358 * Converts a floating point value to integer.
360 * \param[in] n Number of values in the \p val->u array.
361 * \param[in,out] val Value to convert.
362 * \param[in] cmpt Comparison operator type.
363 * \param[in] bRight true if \p val appears on the right hand size of
364 * \p cmpt.
365 * \returns 0 on success, EINVAL on error.
367 * The values are rounded such that the same comparison operator can be used.
369 static void convert_real_int(int n, t_compare_value* val, e_comparison_t cmpt, bool bRight)
371 int i;
372 int* iv;
374 if (!bRight)
376 cmpt = reverse_comparison_type(cmpt);
378 snew(iv, n);
379 /* Round according to the comparison type */
380 for (i = 0; i < n; ++i)
382 switch (cmpt)
384 case CMP_LESS:
385 case CMP_GEQ: iv[i] = static_cast<int>(std::ceil(val->r[i])); break;
386 case CMP_GTR:
387 case CMP_LEQ: iv[i] = static_cast<int>(std::floor(val->r[i])); break;
388 case CMP_EQUAL:
389 case CMP_NEQ:
390 sfree(iv);
391 /* TODO: Implement, although it isn't typically very useful.
392 * Implementation is only a matter of proper initialization,
393 * the evaluation function can already handle this case with
394 * proper preparations. */
395 GMX_THROW(
396 gmx::NotImplementedError("Equality comparison between dynamic integer and "
397 "static real expressions not implemented"));
398 case CMP_INVALID: /* Should not be reached */
399 sfree(iv);
400 GMX_THROW(gmx::InternalError("Invalid comparison type"));
403 /* Free the previous value if one is present. */
404 sfree(val->i);
405 val->i = iv;
406 val->flags &= ~CMP_REALVAL;
407 val->flags |= CMP_ALLOCINT;
410 static void init_compare(const gmx_mtop_t* /* top */, int /* npar */, gmx_ana_selparam_t* param, void* data)
412 t_methoddata_compare* d = static_cast<t_methoddata_compare*>(data);
413 int n1, n2;
415 /* Store the values */
416 n1 = init_comparison_value(&d->left, &param[0]);
417 n2 = init_comparison_value(&d->right, &param[3]);
418 /* Store the comparison type */
419 d->cmpt = comparison_type(d->cmpop);
420 if (d->cmpt == CMP_INVALID)
422 GMX_THROW(gmx::InternalError("Invalid comparison type"));
424 /* Convert the values to the same type */
425 /* TODO: Currently, there are no dynamic integer-valued selection methods,
426 * which means that only the branches with convert_int_real() will ever be
427 * taken. It should be considered whether it is necessary to support these
428 * other cases at all.
430 if ((d->left.flags & CMP_REALVAL) && !(d->right.flags & CMP_REALVAL))
432 if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
434 /* Nothing can be done */
436 else if (!(d->right.flags & CMP_DYNAMICVAL))
438 convert_int_real(n2, &d->right);
440 else /* d->left is static */
442 convert_real_int(n1, &d->left, d->cmpt, false);
445 else if (!(d->left.flags & CMP_REALVAL) && (d->right.flags & CMP_REALVAL))
447 if (d->left.flags & d->right.flags & CMP_DYNAMICVAL)
449 /* Reverse the sides to place the integer on the right */
450 int flags;
451 d->left.r = d->right.r;
452 d->right.r = nullptr;
453 d->right.i = d->left.i;
454 d->left.i = nullptr;
455 flags = d->left.flags;
456 d->left.flags = d->right.flags;
457 d->right.flags = flags;
458 d->cmpt = reverse_comparison_type(d->cmpt);
460 else if (!(d->left.flags & CMP_DYNAMICVAL))
462 convert_int_real(n1, &d->left);
464 else /* d->right is static */
466 convert_real_int(n2, &d->right, d->cmpt, true);
472 * \param data Data to free (should point to a \c t_methoddata_compare).
474 * Frees the memory allocated for \c t_methoddata_compare.
476 static void free_data_compare(void* data)
478 t_methoddata_compare* d = static_cast<t_methoddata_compare*>(data);
480 sfree(d->cmpop);
481 if (d->left.flags & CMP_ALLOCINT)
483 sfree(d->left.i);
485 if (d->left.flags & CMP_ALLOCREAL)
487 sfree(d->left.r);
489 if (d->right.flags & CMP_ALLOCINT)
491 sfree(d->right.i);
493 if (d->right.flags & CMP_ALLOCREAL)
495 sfree(d->right.r);
497 sfree(d);
500 /*! \brief
501 * Implementation for evaluate_compare() for integer values.
503 * \param[in] g Evaluation index group.
504 * \param[out] out Output data structure (\p out->u.g is used).
505 * \param[in] data Should point to a \c t_methoddata_compare.
507 static void evaluate_compare_int(gmx_ana_index_t* g, gmx_ana_selvalue_t* out, void* data)
509 t_methoddata_compare* d = static_cast<t_methoddata_compare*>(data);
510 int i, i1, i2, ig;
511 int a, b;
512 bool bAccept;
514 for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
516 a = d->left.i[i1];
517 b = d->right.i[i2];
518 bAccept = false;
519 switch (d->cmpt)
521 case CMP_INVALID: break;
522 case CMP_LESS: bAccept = a < b; break;
523 case CMP_LEQ: bAccept = a <= b; break;
524 case CMP_GTR: bAccept = a > b; break;
525 case CMP_GEQ: bAccept = a >= b; break;
526 case CMP_EQUAL: bAccept = a == b; break;
527 case CMP_NEQ: bAccept = a != b; break;
529 if (bAccept)
531 out->u.g->index[ig++] = g->index[i];
533 if (!(d->left.flags & CMP_SINGLEVAL))
535 ++i1;
537 if (!(d->right.flags & CMP_SINGLEVAL))
539 ++i2;
542 out->u.g->isize = ig;
545 /*! \brief
546 * Implementation for evaluate_compare() if either value is non-integer.
548 * \param[in] g Evaluation index group.
549 * \param[out] out Output data structure (\p out->u.g is used).
550 * \param[in] data Should point to a \c t_methoddata_compare.
552 * Left value is assumed to be real-valued; right value can be either.
553 * This is ensured by the initialization method.
555 static void evaluate_compare_real(gmx_ana_index_t* g, gmx_ana_selvalue_t* out, void* data)
557 t_methoddata_compare* d = static_cast<t_methoddata_compare*>(data);
558 int i, i1, i2, ig;
559 real a, b;
560 bool bAccept;
562 for (i = i1 = i2 = ig = 0; i < g->isize; ++i)
564 a = d->left.r[i1];
565 b = (d->right.flags & CMP_REALVAL) ? d->right.r[i2] : d->right.i[i2];
566 bAccept = false;
567 switch (d->cmpt)
569 case CMP_INVALID: break;
570 case CMP_LESS: bAccept = a < b; break;
571 case CMP_LEQ: bAccept = a <= b; break;
572 case CMP_GTR: bAccept = a > b; break;
573 case CMP_GEQ: bAccept = a >= b; break;
574 case CMP_EQUAL: bAccept = gmx_within_tol(a, b, GMX_REAL_EPS); break;
575 case CMP_NEQ: bAccept = !gmx_within_tol(a, b, GMX_REAL_EPS); break;
577 if (bAccept)
579 out->u.g->index[ig++] = g->index[i];
581 if (!(d->left.flags & CMP_SINGLEVAL))
583 ++i1;
585 if (!(d->right.flags & CMP_SINGLEVAL))
587 ++i2;
590 out->u.g->isize = ig;
593 static void evaluate_compare(const gmx::SelMethodEvalContext& /*context*/,
594 gmx_ana_index_t* g,
595 gmx_ana_selvalue_t* out,
596 void* data)
598 t_methoddata_compare* d = static_cast<t_methoddata_compare*>(data);
600 if (!((d->left.flags | d->right.flags) & CMP_REALVAL))
602 evaluate_compare_int(g, out, data);
604 else
606 evaluate_compare_real(g, out, data);