3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
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.
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.
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.
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.
29 * For more info, check our website at http://www.gromacs.org
32 * \page poscalcengine Position calculation engine
34 * The header file \ref poscalc.h defines an API for calculating positions
35 * in an automated way. This is useful mostly in the selection engine, in
36 * particular with dynamic selections, because the same COM/COG positions
37 * may be needed in several contexts. The API makes it possible to
38 * optimize the evaluation such that any heavy calculation is only done once,
39 * and the results just copied if needed more than once.
40 * The functions also provide a convenient interface for keeping the whole
41 * \c gmx_ana_pos_t structure up-to-date.
43 * A new collection of position calculations is allocated with
44 * gmx_ana_poscalc_coll_create().
45 * Calculations within one collection should share the same topology, and
46 * they are optimized. Calculations in different collections do not interact.
47 * The topology for a collection can be set with
48 * gmx_ana_poscalc_coll_set_topology().
49 * This needs to be done before calling gmx_ana_poscalc_set_maxindex() for
50 * any calculation in the collection, unless that calculation does not
51 * require topology information.
52 * All memory allocated for a collection and the calculations in it can be
53 * freed with gmx_ana_poscalc_coll_free().
55 * A new calculation is created with gmx_ana_poscalc_create().
56 * If flags need to be adjusted later, gmx_ana_poscalc_set_flags() can be
58 * After the flags are final, the largest possible index group for which the
59 * positions are needed has to be set with gmx_ana_poscalc_set_maxindex().
60 * gmx_ana_poscalc_coll_set_topology() should have been called before this
62 * After the above calls, gmx_ana_poscalc_init_pos() can be used to initialize
63 * output to a \c gmx_ana_pos_t structure. Several different structures can be
64 * initialized for the same calculation; the only requirement is that the
65 * structure passed later to gmx_ana_poscalc_update() has been initialized
67 * The memory allocated for a calculation can be freed with
68 * gmx_ana_poscalc_free().
70 * The position evaluation is simple: gmx_ana_poscalc_init_frame() should be
71 * called once for each frame, and gmx_ana_poscalc_update() can then be called
72 * for each calculation that is needed for that frame.
74 * It is also possible to initialize the calculations based on a type provided
76 * The possible strings are returned by gmx_ana_poscalc_create_type_enum(),
77 * and the string can be converted to the parameters for
78 * gmx_ana_poscalc_create() using gmx_ana_poscalc_type_from_enum().
79 * gmx_ana_poscalc_create_enum() is also provided for convenience.
82 * \brief Implementation of functions in poscalc.h.
85 * There is probably some room for optimization in the calculation of
86 * positions with bases.
87 * In particular, the current implementation may do a lot of unnecessary
89 * The interface would need to be changed to make it possible to use the
90 * same output positions for several calculations.
93 * The current algorithm for setting up base calculations could be improved
94 * in cases when there are calculations that cannot use a common base but
95 * still overlap partially (e.g., with three calculations A, B, and C
96 * such that A could use both B and C as a base, but B and C cannot use the
98 * Setting up the bases in an optimal manner in every possible situation can
99 * be quite difficult unless several bases are allowed for one calculation,
100 * but better heuristics could probably be implemented.
101 * For best results, the setup should probably be postponed (at least
102 * partially) to gmx_ana_poscalc_init_eval().
112 #include <typedefs.h>
116 #include <centerofmass.h>
117 #include <indexutil.h>
119 #include <position.h>
122 * Collection of \c gmx_ana_poscalc_t structures for the same topology.
124 * Calculations within the same structure are optimized to eliminate duplicate
127 struct gmx_ana_poscalc_coll_t
132 * Can be NULL if none of the calculations require topology data or if
133 * gmx_ana_poscalc_coll_set_topology() has not been called.
136 /** Pointer to the first data structure. */
137 gmx_ana_poscalc_t
*first
;
138 /** Pointer to the last data structure. */
139 gmx_ana_poscalc_t
*last
;
140 /** Whether the collection has been initialized for evaluation. */
145 * Data structure for position calculation.
147 struct gmx_ana_poscalc_t
150 * Type of calculation.
152 * This field may differ from the type requested by the user, because
153 * it is changed internally to the most effective calculation.
154 * For example, if the user requests a COM calculation for residues
155 * consisting of single atoms, it is simply set to POS_ATOM.
156 * To provide a consistent interface to the user, the field \p itype
157 * should be used when information should be given out.
161 * Flags for calculation options.
163 * See \ref poscalc_flags "documentation of the flags".
168 * Type for the created indices.
170 * This field always agrees with the type that the user requested, but
171 * may differ from \p type.
175 * Block data for the calculation.
179 * Mapping from the blocks to the blocks of \p sbase.
181 * If \p sbase is NULL, this field is also.
185 * Maximum evaluation group.
187 gmx_ana_index_t gmax
;
189 /** Position storage for calculations that are used as a base. */
192 /** TRUE if the positions have been evaluated for the current frame. */
195 * Base position data for this calculation.
197 * If not NULL, the centers required by this calculation have
198 * already been calculated in \p sbase.
199 * The structure pointed by \p sbase is always a static calculation.
201 struct gmx_ana_poscalc_t
*sbase
;
202 /** Next structure in the linked list of calculations. */
203 struct gmx_ana_poscalc_t
*next
;
204 /** Previous structure in the linked list of calculations. */
205 struct gmx_ana_poscalc_t
*prev
;
206 /** Number of references to this structure. */
208 /** Collection this calculation belongs to. */
209 gmx_ana_poscalc_coll_t
*coll
;
212 static const char *const poscalc_enum_strings
[] = {
214 "res_com", "res_cog",
215 "mol_com", "mol_cog",
216 "whole_res_com", "whole_res_cog",
217 "whole_mol_com", "whole_mol_cog",
218 "part_res_com", "part_res_cog",
219 "part_mol_com", "part_mol_cog",
220 "dyn_res_com", "dyn_res_cog",
221 "dyn_mol_com", "dyn_mol_cog",
224 #define NENUM asize(poscalc_enum_strings)
227 * Returns the partition type for a given position type.
229 * \param [in] type \c e_poscalc_t value to convert.
230 * \returns Corresponding \c e_indet_t.
233 index_type_for_poscalc(e_poscalc_t type
)
237 case POS_ATOM
: return INDEX_ATOM
;
238 case POS_RES
: return INDEX_RES
;
239 case POS_MOL
: return INDEX_MOL
;
240 case POS_ALL
: return INDEX_ALL
;
241 case POS_ALL_PBC
: return INDEX_ALL
;
243 return INDEX_UNKNOWN
;
247 * \param[in] post String (typically an enum command-line argument).
248 * Allowed values: 'atom', 'res_com', 'res_cog', 'mol_com', 'mol_cog',
249 * or one of the last four prepended by 'whole_', 'part_', or 'dyn_'.
250 * \param[out] type \c e_poscalc_t corresponding to \p post.
251 * \param[in,out] flags Flags corresponding to \p post.
252 * On input, the flags should contain the default flags.
253 * On exit, the flags \ref POS_MASS, \ref POS_COMPLMAX and
254 * \ref POS_COMPLWHOLE have been set according to \p post
255 * (the completion flags are left at the default values if no completion
257 * \returns 0 if \p post is one of the valid strings, EINVAL otherwise.
260 * Checking is not complete, and other values than those listed above
261 * may be accepted for \p post, but the results are undefined.
264 gmx_ana_poscalc_type_from_enum(const char *post
, e_poscalc_t
*type
, int *flags
)
271 *flags
&= ~(POS_MASS
| POS_COMPLMAX
| POS_COMPLWHOLE
);
275 /* Process the prefix */
279 *flags
&= ~POS_COMPLMAX
;
280 *flags
|= POS_COMPLWHOLE
;
283 else if (post
[0] == 'p')
285 *flags
&= ~POS_COMPLWHOLE
;
286 *flags
|= POS_COMPLMAX
;
289 else if (post
[0] == 'd')
291 *flags
&= ~(POS_COMPLMAX
| POS_COMPLWHOLE
);
299 else if (ptr
[0] == 'm')
305 gmx_incons("unknown position calculation type");
312 else if (ptr
[6] == 'g')
318 gmx_incons("unknown position calculation type");
325 * \param[in] bAtom If TRUE, the "atom" value is included.
326 * \returns NULL-terminated array of strings that contains the string
327 * values acceptable for gmx_ana_poscalc_type_from_enum().
329 * The first string in the returned list is always NULL to allow the list to
330 * be used with Gromacs command-line parsing.
333 gmx_ana_poscalc_create_type_enum(bool bAtom
)
340 snew(pcenum
, NENUM
+1);
341 for (i
= 0; i
< NENUM
; ++i
)
343 pcenum
[i
+1] = poscalc_enum_strings
[i
];
348 snew(pcenum
, NENUM
+1-1);
349 for (i
= 1; i
< NENUM
; ++i
)
351 pcenum
[i
] = poscalc_enum_strings
[i
];
359 * \param[out] pccp Allocated position calculation collection.
360 * \returns 0 for success.
363 gmx_ana_poscalc_coll_create(gmx_ana_poscalc_coll_t
**pccp
)
365 gmx_ana_poscalc_coll_t
*pcc
;
377 * \param[in,out] pcc Position calculation collection data structure.
378 * \param[in] top Topology data structure.
380 * This function should be called to set the topology before using
381 * gmx_ana_poscalc_set_maxindex() for any calculation that requires
382 * topology information.
385 gmx_ana_poscalc_coll_set_topology(gmx_ana_poscalc_coll_t
*pcc
, t_topology
*top
)
391 * \param[in] pcc Position calculation collection to free.
393 * The pointer \p pcc is invalid after the call.
394 * Any calculations in the collection are also freed, no matter how many
395 * references to them are left.
398 gmx_ana_poscalc_coll_free(gmx_ana_poscalc_coll_t
*pcc
)
402 gmx_ana_poscalc_free(pcc
->first
);
408 * \param[in] fp File handle to receive the output.
409 * \param[in] pcc Position calculation collection to print.
411 * The output is very technical, making this function mainly useful for
412 * debugging purposes.
415 gmx_ana_poscalc_coll_print_tree(FILE *fp
, gmx_ana_poscalc_coll_t
*pcc
)
417 gmx_ana_poscalc_t
*pc
;
420 fprintf(fp
, "Position calculations:\n");
425 fprintf(fp
, "%2d ", i
);
428 case POS_ATOM
: fprintf(fp
, "ATOM"); break;
429 case POS_RES
: fprintf(fp
, "RES"); break;
430 case POS_MOL
: fprintf(fp
, "MOL"); break;
431 case POS_ALL
: fprintf(fp
, "ALL"); break;
432 case POS_ALL_PBC
: fprintf(fp
, "ALL_PBC"); break;
434 if (pc
->itype
!= index_type_for_poscalc(pc
->type
))
439 case INDEX_UNKNOWN
: fprintf(fp
, "???"); break;
440 case INDEX_ATOM
: fprintf(fp
, "ATOM"); break;
441 case INDEX_RES
: fprintf(fp
, "RES"); break;
442 case INDEX_MOL
: fprintf(fp
, "MOL"); break;
443 case INDEX_ALL
: fprintf(fp
, "ALL"); break;
447 fprintf(fp
, " flg=");
448 if (pc
->flags
& POS_MASS
)
452 if (pc
->flags
& POS_DYNAMIC
)
456 if (pc
->flags
& POS_MASKONLY
)
460 if (pc
->flags
& POS_COMPLMAX
)
464 if (pc
->flags
& POS_COMPLWHOLE
)
472 fprintf(fp
, " nr=%d nra=%d", pc
->b
.nr
, pc
->b
.nra
);
473 fprintf(fp
, " refc=%d", pc
->refcount
);
475 if (pc
->gmax
.nalloc_index
> 0)
477 fprintf(fp
, " Group: ");
478 if (pc
->gmax
.isize
> 20)
480 fprintf(fp
, " %d atoms", pc
->gmax
.isize
);
484 for (j
= 0; j
< pc
->gmax
.isize
; ++j
)
486 fprintf(fp
, " %d", pc
->gmax
.index
[j
] + 1);
491 if (pc
->b
.nalloc_a
> 0)
493 fprintf(fp
, " Atoms: ");
496 fprintf(fp
, " %d atoms", pc
->b
.nra
);
500 for (j
= 0; j
< pc
->b
.nra
; ++j
)
502 fprintf(fp
, " %d", pc
->b
.a
[j
] + 1);
507 if (pc
->b
.nalloc_index
> 0)
509 fprintf(fp
, " Blocks:");
512 fprintf(fp
, " %d pcs", pc
->b
.nr
);
516 for (j
= 0; j
<= pc
->b
.nr
; ++j
)
518 fprintf(fp
, " %d", pc
->b
.index
[j
]);
525 gmx_ana_poscalc_t
*base
;
527 fprintf(fp
, " Base: ");
530 while (base
&& base
!= pc
->sbase
)
535 fprintf(fp
, "%d", j
);
536 if (pc
->baseid
&& pc
->b
.nr
<= 20)
539 for (j
= 0; j
< pc
->b
.nr
; ++j
)
541 fprintf(fp
, " %d", pc
->baseid
[j
]+1);
552 * Inserts a position calculation structure into its collection.
554 * \param pc Data structure to insert.
555 * \param before Data structure before which to insert
556 * (NULL = insert at end).
558 * Inserts \p pc to its collection before \p before.
559 * If \p before is NULL, \p pc is appended to the list.
562 insert_poscalc(gmx_ana_poscalc_t
*pc
, gmx_ana_poscalc_t
*before
)
567 pc
->prev
= pc
->coll
->last
;
570 pc
->coll
->last
->next
= pc
;
576 pc
->prev
= before
->prev
;
580 before
->prev
->next
= pc
;
586 pc
->coll
->first
= pc
;
591 * Removes a position calculation structure from its collection.
593 * \param pc Data structure to remove.
595 * Removes \p pc from its collection.
598 remove_poscalc(gmx_ana_poscalc_t
*pc
)
602 pc
->prev
->next
= pc
->next
;
604 else if (pc
== pc
->coll
->first
)
606 pc
->coll
->first
= pc
->next
;
610 pc
->next
->prev
= pc
->prev
;
612 else if (pc
== pc
->coll
->last
)
614 pc
->coll
->last
= pc
->prev
;
616 pc
->prev
= pc
->next
= NULL
;
620 * Initializes position calculation using the maximum possible input index.
622 * \param[in,out] pc Position calculation data structure.
623 * \param[in] g Maximum index group for the calculation.
624 * \param[in] bBase Whether \p pc will be used as a base or not.
626 * \p bBase affects on how the \p pc->gmax field is initialized.
629 set_poscalc_maxindex(gmx_ana_poscalc_t
*pc
, gmx_ana_index_t
*g
, bool bBase
)
631 gmx_ana_index_make_block(&pc
->b
, pc
->coll
->top
, g
, pc
->itype
, pc
->flags
& POS_COMPLWHOLE
);
632 /* Set the type to POS_ATOM if the calculation in fact is such. */
633 if (pc
->b
.nr
== pc
->b
.nra
)
636 pc
->flags
&= ~(POS_MASS
| POS_COMPLMAX
| POS_COMPLWHOLE
);
638 /* Set the POS_COMPLWHOLE flag if the calculation in fact always uses
639 * complete residues and molecules. */
640 if (!(pc
->flags
& POS_COMPLWHOLE
)
641 && (!(pc
->flags
& POS_DYNAMIC
) || (pc
->flags
& POS_COMPLMAX
))
642 && (pc
->type
== POS_RES
|| pc
->type
== POS_MOL
)
643 && gmx_ana_index_has_complete_elems(g
, pc
->itype
, pc
->coll
->top
))
645 pc
->flags
&= ~POS_COMPLMAX
;
646 pc
->flags
|= POS_COMPLWHOLE
;
648 /* Setup the gmax field */
649 if ((pc
->flags
& POS_COMPLWHOLE
) && !bBase
&& pc
->b
.nra
> g
->isize
)
651 gmx_ana_index_copy(&pc
->gmax
, g
, TRUE
);
652 sfree(pc
->gmax
.name
);
653 pc
->gmax
.name
= NULL
;
657 gmx_ana_index_set(&pc
->gmax
, pc
->b
.nra
, pc
->b
.a
, NULL
, 0);
662 * Checks whether a position calculation should use a base at all.
664 * \param[in] pc Position calculation data to check.
665 * \returns TRUE if \p pc can use a base and gets some benefit out of it,
669 can_use_base(gmx_ana_poscalc_t
*pc
)
671 /* For atoms, it should be faster to do a simple copy, so don't use a
673 if (pc
->type
== POS_ATOM
)
677 /* For dynamic selections that do not use completion, it is not possible
679 if ((pc
->type
== POS_RES
|| pc
->type
== POS_MOL
)
680 && (pc
->flags
& POS_DYNAMIC
) && !(pc
->flags
& (POS_COMPLMAX
| POS_COMPLWHOLE
)))
684 /* Dynamic calculations for a single position cannot use a base. */
685 if ((pc
->type
== POS_ALL
|| pc
->type
== POS_ALL_PBC
)
686 && (pc
->flags
& POS_DYNAMIC
))
694 * Checks whether two position calculations should use a common base.
696 * \param[in] pc1 Calculation 1 to check for.
697 * \param[in] pc2 Calculation 2 to check for.
698 * \param[in] g1 Index group structure that contains the atoms from
700 * \param[in,out] g Working space, should have enough allocated memory to
701 * contain the intersection of the atoms in \p pc1 and \p pc2.
702 * \returns TRUE if the two calculations should be merged to use a common
703 * base, FALSE otherwise.
706 should_merge(gmx_ana_poscalc_t
*pc1
, gmx_ana_poscalc_t
*pc2
,
707 gmx_ana_index_t
*g1
, gmx_ana_index_t
*g
)
711 /* Do not merge calculations with different mass weighting. */
712 if ((pc1
->flags
& POS_MASS
) != (pc2
->flags
& POS_MASS
))
716 /* Avoid messing up complete calculations. */
717 if ((pc1
->flags
& POS_COMPLWHOLE
) != (pc2
->flags
& POS_COMPLWHOLE
))
721 /* Find the overlap between the calculations. */
722 gmx_ana_index_set(&g2
, pc2
->b
.nra
, pc2
->b
.a
, NULL
, 0);
723 gmx_ana_index_intersection(g
, g1
, &g2
);
724 /* Do not merge if there is no overlap. */
729 /* Full completion calculations always match if the type is correct. */
730 if ((pc1
->flags
& POS_COMPLWHOLE
) && (pc2
->flags
& POS_COMPLWHOLE
)
731 && pc1
->type
== pc2
->type
)
735 /* The calculations also match if the intersection consists of full
737 if (gmx_ana_index_has_full_ablocks(g
, &pc1
->b
)
738 && gmx_ana_index_has_full_ablocks(g
, &pc2
->b
))
746 * Creates a static base for position calculation.
748 * \param pc Data structure to copy.
749 * \returns Pointer to a newly allocated base for \p pc.
751 * Creates and returns a deep copy of \p pc, but clears the
752 * \ref POS_DYNAMIC and \ref POS_MASKONLY flags.
753 * The newly created structure is set as the base (\c gmx_ana_poscalc_t::sbase)
754 * of \p pc and inserted in the collection before \p pc.
756 static gmx_ana_poscalc_t
*
757 create_simple_base(gmx_ana_poscalc_t
*pc
)
759 gmx_ana_poscalc_t
*base
;
763 flags
= pc
->flags
& ~(POS_DYNAMIC
| POS_MASKONLY
);
764 rc
= gmx_ana_poscalc_create(&base
, pc
->coll
, pc
->type
, flags
);
767 gmx_fatal(FARGS
, "position calculation base creation failed");
769 set_poscalc_maxindex(base
, &pc
->gmax
, TRUE
);
774 remove_poscalc(base
);
775 insert_poscalc(base
, pc
);
781 * Merges a calculation into another calculation such that the new calculation
782 * can be used as a base.
784 * \param[in,out] base Base calculation to merge to.
785 * \param[in,out] pc Position calculation to merge to \p base.
787 * After the call, \p base can be used as a base for \p pc (or any calculation
788 * that used it as a base).
789 * It is assumed that any overlap between \p base and \p pc is in complete
790 * blocks, i.e., that the merge is possible.
793 merge_to_base(gmx_ana_poscalc_t
*base
, gmx_ana_poscalc_t
*pc
)
795 gmx_ana_index_t gp
, gb
, g
;
797 int i
, j
, bi
, bj
, bo
;
799 gmx_ana_index_set(&gp
, pc
->b
.nra
, pc
->b
.a
, NULL
, 0);
800 gmx_ana_index_set(&gb
, base
->b
.nra
, base
->b
.a
, NULL
, 0);
801 isize
= gmx_ana_index_difference_size(&gp
, &gb
);
804 gmx_ana_index_clear(&g
);
805 gmx_ana_index_reserve(&g
, base
->b
.nra
+ isize
);
806 /* Find the new blocks */
807 gmx_ana_index_difference(&g
, &gp
, &gb
);
808 /* Count the blocks in g */
812 while (pc
->b
.a
[pc
->b
.index
[bi
]] != g
.index
[i
])
816 i
+= pc
->b
.index
[bi
+1] - pc
->b
.index
[bi
];
820 /* Merge the atoms into a temporary structure */
821 gmx_ana_index_merge(&g
, &gb
, &g
);
822 /* Merge the blocks */
823 srenew(base
->b
.index
, base
->b
.nr
+ bnr
+ 1);
827 bo
= base
->b
.nr
+ bnr
- 1;
828 base
->b
.index
[bo
+1] = i
+ 1;
831 if (bi
< 0 || base
->b
.a
[base
->b
.index
[bi
+1]-1] != g
.index
[i
])
833 i
-= pc
->b
.index
[bj
+1] - pc
->b
.index
[bj
];
838 if (bj
>= 0 && pc
->b
.a
[pc
->b
.index
[bj
+1]-1] == g
.index
[i
])
842 i
-= base
->b
.index
[bi
+1] - base
->b
.index
[bi
];
845 base
->b
.index
[bo
] = i
+ 1;
849 base
->b
.nalloc_index
+= bnr
;
851 base
->b
.nra
= g
.isize
;
853 base
->b
.nalloc_a
= g
.isize
;
854 /* Refresh the gmax field */
855 gmx_ana_index_set(&base
->gmax
, base
->b
.nra
, base
->b
.a
, NULL
, 0);
860 * Merges two bases into one.
862 * \param[in,out] tbase Base calculation to merge to.
863 * \param[in] mbase Base calculation to merge to \p tbase.
865 * After the call, \p mbase has been freed and \p tbase is used as the base
866 * for all calculations that previously had \p mbase as their base.
867 * It is assumed that any overlap between \p tbase and \p mbase is in complete
868 * blocks, i.e., that the merge is possible.
871 merge_bases(gmx_ana_poscalc_t
*tbase
, gmx_ana_poscalc_t
*mbase
)
873 gmx_ana_poscalc_t
*pc
;
875 merge_to_base(tbase
, mbase
);
876 remove_poscalc(mbase
);
877 /* Set tbase as the base for all calculations that had mbase */
878 pc
= tbase
->coll
->first
;
881 if (pc
->sbase
== mbase
)
890 gmx_ana_poscalc_free(mbase
);
894 * Setups the static base calculation for a position calculation.
896 * \param[in,out] pc Position calculation to setup the base for.
899 setup_base(gmx_ana_poscalc_t
*pc
)
901 gmx_ana_poscalc_t
*base
, *pbase
, *next
;
902 gmx_ana_index_t gp
, g
;
904 /* Exit immediately if pc should not have a base. */
905 if (!can_use_base(pc
))
910 gmx_ana_index_set(&gp
, pc
->b
.nra
, pc
->b
.a
, NULL
, 0);
911 gmx_ana_index_clear(&g
);
912 gmx_ana_index_reserve(&g
, pc
->b
.nra
);
914 base
= pc
->coll
->first
;
917 /* Save the next calculation so that we can safely delete base */
919 /* Skip pc, calculations that already have a base (we should match the
920 * base instead), as well as calculations that should not have a base.
921 * If the above conditions are met, check whether we should do a
924 if (base
!= pc
&& !base
->sbase
&& can_use_base(base
)
925 && should_merge(pbase
, base
, &gp
, &g
))
927 /* Check whether this is the first base found */
930 /* Create a real base if one is not present */
933 pbase
= create_simple_base(base
);
939 /* Make it a base for pc as well */
940 merge_to_base(pbase
, pc
);
944 else /* This was not the first base */
948 /* If it is not a real base, just make the new base as
949 * the base for it as well. */
950 merge_to_base(pbase
, base
);
956 /* If base is a real base, merge it with the new base
958 merge_bases(pbase
, base
);
961 gmx_ana_index_set(&gp
, pbase
->b
.nra
, pbase
->b
.a
, NULL
, 0);
962 gmx_ana_index_reserve(&g
, pc
->b
.nra
);
964 /* Proceed to the next unchecked calculation */
968 gmx_ana_index_deinit(&g
);
970 /* If no base was found, create one if one is required */
971 if (!pc
->sbase
&& (pc
->flags
& POS_DYNAMIC
)
972 && (pc
->flags
& (POS_COMPLMAX
| POS_COMPLWHOLE
)))
974 create_simple_base(pc
);
979 * \param[out] pcp Position calculation data structure pointer to initialize.
980 * \param[in,out] pcc Position calculation collection.
981 * \param[in] type Type of calculation.
982 * \param[in] flags Flags for setting calculation options
983 * (see \ref poscalc_flags "documentation of the flags").
984 * \returns 0 on success.
987 gmx_ana_poscalc_create(gmx_ana_poscalc_t
**pcp
, gmx_ana_poscalc_coll_t
*pcc
,
988 e_poscalc_t type
, int flags
)
990 gmx_ana_poscalc_t
*pc
;
994 pc
->itype
= index_type_for_poscalc(type
);
995 gmx_ana_poscalc_set_flags(pc
, flags
);
998 insert_poscalc(pc
, NULL
);
1004 * \param[out] pcp Position calculation data structure pointer to initialize.
1005 * \param[in,out] pcc Position calculation collection.
1006 * \param[in] post One of the strings acceptable for
1007 * gmx_ana_poscalc_type_from_enum().
1008 * \param[in] flags Flags for setting calculation options
1009 * (see \ref poscalc_flags "documentation of the flags").
1010 * \returns 0 on success, a non-zero error value on error.
1012 * This is a convenience wrapper for gmx_ana_poscalc_create().
1013 * \p flags sets the default calculation options if not overridden by \p post;
1014 * see gmx_ana_poscalc_type_from_enum().
1016 * \see gmx_ana_poscalc_create(), gmx_ana_poscalc_type_from_enum()
1019 gmx_ana_poscalc_create_enum(gmx_ana_poscalc_t
**pcp
, gmx_ana_poscalc_coll_t
*pcc
,
1020 const char *post
, int flags
)
1027 rc
= gmx_ana_poscalc_type_from_enum(post
, &type
, &cflags
);
1033 return gmx_ana_poscalc_create(pcp
, pcc
, type
, cflags
);
1037 * \param[in,out] pc Position calculation data structure.
1038 * \param[in] flags New flags.
1040 * \p flags are added to the old flags.
1041 * If calculation type is \ref POS_ATOM, \ref POS_MASS is automatically
1043 * If both \ref POS_DYNAMIC and \ref POS_MASKONLY are provided,
1044 * \ref POS_DYNAMIC is cleared.
1045 * If calculation type is not \ref POS_RES or \ref POS_MOL,
1046 * \ref POS_COMPLMAX and \ref POS_COMPLWHOLE are automatically cleared.
1049 gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t
*pc
, int flags
)
1051 if (pc
->type
== POS_ATOM
)
1055 if (flags
& POS_MASKONLY
)
1057 flags
&= ~POS_DYNAMIC
;
1059 if (pc
->type
!= POS_RES
&& pc
->type
!= POS_MOL
)
1061 flags
&= ~(POS_COMPLMAX
| POS_COMPLWHOLE
);
1067 * \param[in,out] pc Position calculation data structure.
1068 * \param[in] g Maximum index group for the calculation.
1070 * Subsequent calls to gmx_ana_poscalc_update() should use only subsets of
1071 * \p g for evaluation.
1073 * The topology should have been set for the collection of which \p pc is
1077 gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t
*pc
, gmx_ana_index_t
*g
)
1079 set_poscalc_maxindex(pc
, g
, FALSE
);
1084 * \param[in] pc Position calculation data structure.
1085 * \param[out] p Output positions.
1087 * Calls to gmx_ana_poscalc_update() using \p pc should use only positions
1088 * initialized with this function.
1089 * The \c p->g pointer is initialized to point to an internal group that
1090 * contains the maximum index group set with gmx_ana_poscalc_set_maxindex().
1093 gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t
*pc
, gmx_ana_pos_t
*p
)
1095 gmx_ana_indexmap_init(&p
->m
, &pc
->gmax
, pc
->coll
->top
, pc
->itype
);
1096 gmx_ana_pos_reserve(p
, p
->m
.nr
, 0);
1101 * \param pc Position calculation data to be freed.
1103 * The \p pc pointer is invalid after the call.
1106 gmx_ana_poscalc_free(gmx_ana_poscalc_t
*pc
)
1114 if (pc
->refcount
> 0)
1120 if (pc
->b
.nalloc_index
> 0)
1124 if (pc
->b
.nalloc_a
> 0)
1128 if (pc
->flags
& POS_COMPLWHOLE
)
1130 gmx_ana_index_deinit(&pc
->gmax
);
1134 gmx_ana_pos_free(pc
->p
);
1138 gmx_ana_poscalc_free(pc
->sbase
);
1145 * \param[in] pc Position calculation data to query.
1146 * \returns TRUE if \p pc requires topology for initialization and/or
1147 * evaluation, FALSE otherwise.
1150 gmx_ana_poscalc_requires_top(gmx_ana_poscalc_t
*pc
)
1152 if ((pc
->flags
& POS_MASS
) || pc
->type
== POS_RES
|| pc
->type
== POS_MOL
)
1160 * \param[in,out] pcc Position calculation collection to initialize.
1162 * This function does some final initialization of the data structures in the
1163 * collection to prepare them for evaluation.
1164 * After this function has been called, it is no longer possible to add new
1165 * calculations to the collection.
1167 * This function is automatically called by gmx_ana_poscalc_init_frame()
1168 * if not called by the user earlier.
1169 * Multiple calls to the function are ignored.
1172 gmx_ana_poscalc_init_eval(gmx_ana_poscalc_coll_t
*pcc
)
1174 gmx_ana_poscalc_t
*pc
;
1184 /* Initialize position storage for base calculations */
1187 gmx_ana_poscalc_init_pos(pc
, pc
->p
);
1189 /* Construct the mapping of the base positions */
1192 snew(pc
->baseid
, pc
->b
.nr
);
1193 for (bi
= bj
= 0; bi
< pc
->b
.nr
; ++bi
, ++bj
)
1195 while (pc
->sbase
->b
.a
[pc
->sbase
->b
.index
[bj
]] != pc
->b
.a
[pc
->b
.index
[bi
]])
1199 pc
->baseid
[bi
] = bj
;
1202 /* Free the block data for dynamic calculations */
1203 if ((pc
->flags
& POS_DYNAMIC
) && pc
->b
.nalloc_index
> 0)
1207 pc
->b
.nalloc_index
= 0;
1216 * \param[in,out] pcc Position calculation collection to initialize.
1218 * Clears the evaluation flag for all calculations.
1219 * Should be called for each frame before calling gmx_ana_poscalc_update().
1221 * This function is automatically called by gmx_ana_do() for each
1222 * frame, and should not be called by the user unless gmx_ana_do() is
1225 * This function calls gmx_ana_poscalc_init_eval() automatically if it has
1226 * not been called earlier.
1229 gmx_ana_poscalc_init_frame(gmx_ana_poscalc_coll_t
*pcc
)
1231 gmx_ana_poscalc_t
*pc
;
1235 gmx_ana_poscalc_init_eval(pcc
);
1237 /* Clear the evaluation flags */
1247 * \param[in] pc Position calculation data.
1248 * \param[in,out] p Output positions, initialized previously with
1249 * gmx_ana_poscalc_init_pos() using \p pc.
1250 * \param[in] g Index group to use for the update.
1251 * \param[in] x Current positions of the atoms.
1252 * \param[in] pbc PBC data, or NULL if no PBC should be used.
1254 * gmx_ana_poscalc_init_frame() should be called for each frame before calling
1258 gmx_ana_poscalc_update(gmx_ana_poscalc_t
*pc
, gmx_ana_pos_t
*p
,
1259 gmx_ana_index_t
*g
, rvec x
[], t_pbc
*pbc
)
1263 if (pc
->bEval
== TRUE
&& !(pc
->flags
& POS_MASKONLY
))
1269 gmx_ana_poscalc_update(pc
->sbase
, NULL
, NULL
, x
, pbc
);
1281 /* Update the index map */
1282 if (pc
->flags
& POS_DYNAMIC
)
1284 gmx_ana_indexmap_update(&p
->m
, g
, FALSE
);
1287 else if (pc
->flags
& POS_MASKONLY
)
1289 gmx_ana_indexmap_update(&p
->m
, g
, TRUE
);
1293 if (!(pc
->flags
& POS_DYNAMIC
))
1298 /* Evaluate the positions */
1301 /* TODO: It might be faster to evaluate the positions within this
1302 * loop instead of in the beginning. */
1303 if (pc
->flags
& POS_DYNAMIC
)
1305 for (bi
= 0; bi
< p
->nr
; ++bi
)
1307 bj
= pc
->baseid
[p
->m
.refid
[bi
]];
1308 copy_rvec(pc
->sbase
->p
->x
[bj
], p
->x
[bi
]);
1313 for (bi
= 0; bi
< p
->nr
; ++bi
)
1315 bj
= pc
->baseid
[bi
];
1316 copy_rvec(pc
->sbase
->p
->x
[bj
], p
->x
[bi
]);
1320 else /* pc->sbase is NULL */
1322 if (pc
->flags
& POS_DYNAMIC
)
1324 pc
->b
.nr
= p
->m
.mapb
.nr
;
1325 pc
->b
.index
= p
->m
.mapb
.index
;
1326 pc
->b
.nra
= g
->isize
;
1329 /* Here, we assume that the topology has been properly initialized,
1330 * and do not check the return values of gmx_calc_comg*(). */
1334 for (i
= 0; i
< pc
->b
.nra
; ++i
)
1336 copy_rvec(x
[pc
->b
.a
[i
]], p
->x
[i
]);
1340 gmx_calc_comg(pc
->coll
->top
, x
, pc
->b
.nra
, pc
->b
.a
, pc
->flags
& POS_MASS
, p
->x
[0]);
1343 gmx_calc_comg_pbc(pc
->coll
->top
, x
, pbc
, pc
->b
.nra
, pc
->b
.a
, pc
->flags
& POS_MASS
, p
->x
[0]);
1346 gmx_calc_comg_blocka(pc
->coll
->top
, x
, &pc
->b
, pc
->flags
& POS_MASS
, p
->x
);