2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013, by the GROMACS development team, led by
5 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
6 * others, as listed in the AUTHORS file in the top-level source
7 * directory and at http://www.gromacs.org.
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.
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.
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.
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.
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.
37 * Implements gmx::PositionCalculationCollection and functions in poscalc.h.
40 * There is probably some room for optimization in the calculation of
41 * positions with bases.
42 * In particular, the current implementation may do a lot of unnecessary
44 * The interface would need to be changed to make it possible to use the
45 * same output positions for several calculations.
48 * The current algorithm for setting up base calculations could be improved
49 * in cases when there are calculations that cannot use a common base but
50 * still overlap partially (e.g., with three calculations A, B, and C
51 * such that A could use both B and C as a base, but B and C cannot use the
53 * Setting up the bases in an optimal manner in every possible situation can
54 * be quite difficult unless several bases are allowed for one calculation,
55 * but better heuristics could probably be implemented.
56 * For best results, the setup should probably be postponed (at least
57 * partially) to gmx_ana_poscalc_init_eval().
59 * \author Teemu Murtola <teemu.murtola@gmail.com>
60 * \ingroup module_selection
64 #include "gromacs/legacyheaders/smalloc.h"
65 #include "gromacs/legacyheaders/typedefs.h"
66 #include "gromacs/legacyheaders/pbc.h"
67 #include "gromacs/legacyheaders/vec.h"
69 #include "gromacs/selection/centerofmass.h"
70 #include "gromacs/selection/indexutil.h"
71 #include "gromacs/selection/poscalc.h"
72 #include "gromacs/selection/position.h"
73 #include "gromacs/utility/exceptions.h"
74 #include "gromacs/utility/gmxassert.h"
80 * Private implementation class for PositionCalculationCollection.
82 * \ingroup module_selection
84 class PositionCalculationCollection::Impl
91 * Inserts a position calculation structure into its collection.
93 * \param pc Data structure to insert.
94 * \param before Data structure before which to insert
95 * (NULL = insert at end).
97 * Inserts \p pc to its collection before \p before.
98 * If \p before is NULL, \p pc is appended to the list.
100 void insertCalculation(gmx_ana_poscalc_t *pc, gmx_ana_poscalc_t *before);
102 * Removes a position calculation structure from its collection.
104 * \param pc Data structure to remove.
106 * Removes \p pc from its collection.
108 void removeCalculation(gmx_ana_poscalc_t *pc);
110 /*! \copydoc PositionCalculationCollection::createCalculation()
112 * This method contains the actual implementation of the similarly
113 * named method in the parent class.
115 gmx_ana_poscalc_t *createCalculation(e_poscalc_t type, int flags);
120 * Can be NULL if none of the calculations require topology data or if
121 * setTopology() has not been called.
124 //! Pointer to the first data structure.
125 gmx_ana_poscalc_t *first_;
126 //! Pointer to the last data structure.
127 gmx_ana_poscalc_t *last_;
128 //! Whether the collection has been initialized for evaluation.
135 * Data structure for position calculation.
137 struct gmx_ana_poscalc_t
140 * Type of calculation.
142 * This field may differ from the type requested by the user, because
143 * it is changed internally to the most effective calculation.
144 * For example, if the user requests a COM calculation for residues
145 * consisting of single atoms, it is simply set to POS_ATOM.
146 * To provide a consistent interface to the user, the field \p itype
147 * should be used when information should be given out.
151 * Flags for calculation options.
153 * See \ref poscalc_flags "documentation of the flags".
158 * Type for the created indices.
160 * This field always agrees with the type that the user requested, but
161 * may differ from \p type.
165 * Block data for the calculation.
169 * Mapping from the blocks to the blocks of \p sbase.
171 * If \p sbase is NULL, this field is also.
175 * Maximum evaluation group.
177 gmx_ana_index_t gmax;
179 /** Position storage for calculations that are used as a base. */
182 /** true if the positions have been evaluated for the current frame. */
185 * Base position data for this calculation.
187 * If not NULL, the centers required by this calculation have
188 * already been calculated in \p sbase.
189 * The structure pointed by \p sbase is always a static calculation.
191 struct gmx_ana_poscalc_t *sbase;
192 /** Next structure in the linked list of calculations. */
193 struct gmx_ana_poscalc_t *next;
194 /** Previous structure in the linked list of calculations. */
195 struct gmx_ana_poscalc_t *prev;
196 /** Number of references to this structure. */
198 /** Collection this calculation belongs to. */
199 gmx::PositionCalculationCollection::Impl *coll;
202 const char * const gmx::PositionCalculationCollection::typeEnumValues[] = {
204 "res_com", "res_cog",
205 "mol_com", "mol_cog",
206 "whole_res_com", "whole_res_cog",
207 "whole_mol_com", "whole_mol_cog",
208 "part_res_com", "part_res_cog",
209 "part_mol_com", "part_mol_cog",
210 "dyn_res_com", "dyn_res_cog",
211 "dyn_mol_com", "dyn_mol_cog",
216 * Returns the partition type for a given position type.
218 * \param [in] type \c e_poscalc_t value to convert.
219 * \returns Corresponding \c e_indet_t.
222 index_type_for_poscalc(e_poscalc_t type)
226 case POS_ATOM: return INDEX_ATOM;
227 case POS_RES: return INDEX_RES;
228 case POS_MOL: return INDEX_MOL;
229 case POS_ALL: return INDEX_ALL;
230 case POS_ALL_PBC: return INDEX_ALL;
232 return INDEX_UNKNOWN;
240 PositionCalculationCollection::typeFromEnum(const char *post,
241 e_poscalc_t *type, int *flags)
246 *flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
250 /* Process the prefix */
251 const char *ptr = post;
254 *flags &= ~POS_COMPLMAX;
255 *flags |= POS_COMPLWHOLE;
258 else if (post[0] == 'p')
260 *flags &= ~POS_COMPLWHOLE;
261 *flags |= POS_COMPLMAX;
264 else if (post[0] == 'd')
266 *flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
274 else if (ptr[0] == 'm')
280 GMX_THROW(InternalError("Unknown position calculation type"));
284 GMX_THROW(InternalError("Unknown position calculation type"));
290 else if (ptr[6] == 'g')
296 GMX_THROW(InternalError("Unknown position calculation type"));
300 /********************************************************************
301 * PositionCalculationCollection::Impl
304 PositionCalculationCollection::Impl::Impl()
305 : top_(NULL), first_(NULL), last_(NULL), bInit_(false)
309 PositionCalculationCollection::Impl::~Impl()
311 // Loop backwards, because there can be internal references in that are
312 // correctly handled by this direction.
313 while (last_ != NULL)
315 GMX_ASSERT(last_->refcount == 1,
316 "Dangling references to position calculations");
317 gmx_ana_poscalc_free(last_);
322 PositionCalculationCollection::Impl::insertCalculation(gmx_ana_poscalc_t *pc,
323 gmx_ana_poscalc_t *before)
325 GMX_RELEASE_ASSERT(pc->coll == this, "Inconsistent collections");
338 pc->prev = before->prev;
342 before->prev->next = pc;
346 if (pc->prev == NULL)
353 PositionCalculationCollection::Impl::removeCalculation(gmx_ana_poscalc_t *pc)
355 GMX_RELEASE_ASSERT(pc->coll == this, "Inconsistent collections");
356 if (pc->prev != NULL)
358 pc->prev->next = pc->next;
360 else if (pc == first_)
364 if (pc->next != NULL)
366 pc->next->prev = pc->prev;
368 else if (pc == last_)
372 pc->prev = pc->next = NULL;
376 PositionCalculationCollection::Impl::createCalculation(e_poscalc_t type, int flags)
378 gmx_ana_poscalc_t *pc;
382 pc->itype = index_type_for_poscalc(type);
383 gmx_ana_poscalc_set_flags(pc, flags);
386 insertCalculation(pc, NULL);
391 /********************************************************************
392 * PositionCalculationCollection
395 PositionCalculationCollection::PositionCalculationCollection()
400 PositionCalculationCollection::~PositionCalculationCollection()
405 PositionCalculationCollection::setTopology(t_topology *top)
411 PositionCalculationCollection::printTree(FILE *fp) const
413 gmx_ana_poscalc_t *pc;
416 fprintf(fp, "Position calculations:\n");
421 fprintf(fp, "%2d ", i);
424 case POS_ATOM: fprintf(fp, "ATOM"); break;
425 case POS_RES: fprintf(fp, "RES"); break;
426 case POS_MOL: fprintf(fp, "MOL"); break;
427 case POS_ALL: fprintf(fp, "ALL"); break;
428 case POS_ALL_PBC: fprintf(fp, "ALL_PBC"); break;
430 if (pc->itype != index_type_for_poscalc(pc->type))
435 case INDEX_UNKNOWN: fprintf(fp, "???"); break;
436 case INDEX_ATOM: fprintf(fp, "ATOM"); break;
437 case INDEX_RES: fprintf(fp, "RES"); break;
438 case INDEX_MOL: fprintf(fp, "MOL"); break;
439 case INDEX_ALL: fprintf(fp, "ALL"); break;
443 fprintf(fp, " flg=");
444 if (pc->flags & POS_MASS)
448 if (pc->flags & POS_DYNAMIC)
452 if (pc->flags & POS_MASKONLY)
456 if (pc->flags & POS_COMPLMAX)
460 if (pc->flags & POS_COMPLWHOLE)
468 fprintf(fp, " nr=%d nra=%d", pc->b.nr, pc->b.nra);
469 fprintf(fp, " refc=%d", pc->refcount);
471 if (pc->gmax.nalloc_index > 0)
473 fprintf(fp, " Group: ");
474 if (pc->gmax.isize > 20)
476 fprintf(fp, " %d atoms", pc->gmax.isize);
480 for (j = 0; j < pc->gmax.isize; ++j)
482 fprintf(fp, " %d", pc->gmax.index[j] + 1);
487 if (pc->b.nalloc_a > 0)
489 fprintf(fp, " Atoms: ");
492 fprintf(fp, " %d atoms", pc->b.nra);
496 for (j = 0; j < pc->b.nra; ++j)
498 fprintf(fp, " %d", pc->b.a[j] + 1);
503 if (pc->b.nalloc_index > 0)
505 fprintf(fp, " Blocks:");
508 fprintf(fp, " %d pcs", pc->b.nr);
512 for (j = 0; j <= pc->b.nr; ++j)
514 fprintf(fp, " %d", pc->b.index[j]);
521 gmx_ana_poscalc_t *base;
523 fprintf(fp, " Base: ");
525 base = impl_->first_;
526 while (base && base != pc->sbase)
531 fprintf(fp, "%d", j);
532 if (pc->baseid && pc->b.nr <= 20)
535 for (j = 0; j < pc->b.nr; ++j)
537 fprintf(fp, " %d", pc->baseid[j]+1);
548 PositionCalculationCollection::createCalculation(e_poscalc_t type, int flags)
550 return impl_->createCalculation(type, flags);
554 PositionCalculationCollection::createCalculationFromEnum(const char *post, int flags)
558 typeFromEnum(post, &type, &cflags);
559 return impl_->createCalculation(type, cflags);
562 void PositionCalculationCollection::initEvaluation()
568 gmx_ana_poscalc_t *pc = impl_->first_;
571 /* Initialize position storage for base calculations */
574 gmx_ana_poscalc_init_pos(pc, pc->p);
576 /* Construct the mapping of the base positions */
581 snew(pc->baseid, pc->b.nr);
582 for (bi = bj = 0; bi < pc->b.nr; ++bi, ++bj)
584 while (pc->sbase->b.a[pc->sbase->b.index[bj]] != pc->b.a[pc->b.index[bi]])
591 /* Free the block data for dynamic calculations */
592 if (pc->flags & POS_DYNAMIC)
594 if (pc->b.nalloc_index > 0)
597 pc->b.nalloc_index = 0;
599 if (pc->b.nalloc_a > 0)
607 impl_->bInit_ = true;
610 void PositionCalculationCollection::initFrame()
616 /* Clear the evaluation flags */
617 gmx_ana_poscalc_t *pc = impl_->first_;
628 * Initializes position calculation using the maximum possible input index.
630 * \param[in,out] pc Position calculation data structure.
631 * \param[in] g Maximum index group for the calculation.
632 * \param[in] bBase Whether \p pc will be used as a base or not.
634 * \p bBase affects on how the \p pc->gmax field is initialized.
637 set_poscalc_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g, bool bBase)
639 t_topology *top = pc->coll->top_;
640 gmx_ana_index_make_block(&pc->b, top, g, pc->itype, pc->flags & POS_COMPLWHOLE);
641 /* Set the type to POS_ATOM if the calculation in fact is such. */
642 if (pc->b.nr == pc->b.nra)
645 pc->flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
647 /* Set the POS_COMPLWHOLE flag if the calculation in fact always uses
648 * complete residues and molecules. */
649 if (!(pc->flags & POS_COMPLWHOLE)
650 && (!(pc->flags & POS_DYNAMIC) || (pc->flags & POS_COMPLMAX))
651 && (pc->type == POS_RES || pc->type == POS_MOL)
652 && gmx_ana_index_has_complete_elems(g, pc->itype, top))
654 pc->flags &= ~POS_COMPLMAX;
655 pc->flags |= POS_COMPLWHOLE;
657 /* Setup the gmax field */
658 if ((pc->flags & POS_COMPLWHOLE) && !bBase && pc->b.nra > g->isize)
660 gmx_ana_index_copy(&pc->gmax, g, true);
664 gmx_ana_index_set(&pc->gmax, pc->b.nra, pc->b.a, 0);
669 * Checks whether a position calculation should use a base at all.
671 * \param[in] pc Position calculation data to check.
672 * \returns true if \p pc can use a base and gets some benefit out of it,
676 can_use_base(gmx_ana_poscalc_t *pc)
678 /* For atoms, it should be faster to do a simple copy, so don't use a
680 if (pc->type == POS_ATOM)
684 /* For dynamic selections that do not use completion, it is not possible
686 if ((pc->type == POS_RES || pc->type == POS_MOL)
687 && (pc->flags & POS_DYNAMIC) && !(pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
691 /* Dynamic calculations for a single position cannot use a base. */
692 if ((pc->type == POS_ALL || pc->type == POS_ALL_PBC)
693 && (pc->flags & POS_DYNAMIC))
701 * Checks whether two position calculations should use a common base.
703 * \param[in] pc1 Calculation 1 to check for.
704 * \param[in] pc2 Calculation 2 to check for.
705 * \param[in] g1 Index group structure that contains the atoms from
707 * \param[in,out] g Working space, should have enough allocated memory to
708 * contain the intersection of the atoms in \p pc1 and \p pc2.
709 * \returns true if the two calculations should be merged to use a common
710 * base, false otherwise.
713 should_merge(gmx_ana_poscalc_t *pc1, gmx_ana_poscalc_t *pc2,
714 gmx_ana_index_t *g1, gmx_ana_index_t *g)
718 /* Do not merge calculations with different mass weighting. */
719 if ((pc1->flags & POS_MASS) != (pc2->flags & POS_MASS))
723 /* Avoid messing up complete calculations. */
724 if ((pc1->flags & POS_COMPLWHOLE) != (pc2->flags & POS_COMPLWHOLE))
728 /* Find the overlap between the calculations. */
729 gmx_ana_index_set(&g2, pc2->b.nra, pc2->b.a, 0);
730 gmx_ana_index_intersection(g, g1, &g2);
731 /* Do not merge if there is no overlap. */
736 /* Full completion calculations always match if the type is correct. */
737 if ((pc1->flags & POS_COMPLWHOLE) && (pc2->flags & POS_COMPLWHOLE)
738 && pc1->type == pc2->type)
742 /* The calculations also match if the intersection consists of full
744 if (gmx_ana_index_has_full_ablocks(g, &pc1->b)
745 && gmx_ana_index_has_full_ablocks(g, &pc2->b))
753 * Creates a static base for position calculation.
755 * \param pc Data structure to copy.
756 * \returns Pointer to a newly allocated base for \p pc.
758 * Creates and returns a deep copy of \p pc, but clears the
759 * \ref POS_DYNAMIC and \ref POS_MASKONLY flags.
760 * The newly created structure is set as the base (\c gmx_ana_poscalc_t::sbase)
761 * of \p pc and inserted in the collection before \p pc.
763 static gmx_ana_poscalc_t *
764 create_simple_base(gmx_ana_poscalc_t *pc)
766 gmx_ana_poscalc_t *base;
769 flags = pc->flags & ~(POS_DYNAMIC | POS_MASKONLY);
770 base = pc->coll->createCalculation(pc->type, flags);
771 set_poscalc_maxindex(base, &pc->gmax, true);
776 pc->coll->removeCalculation(base);
777 pc->coll->insertCalculation(base, pc);
783 * Merges a calculation into another calculation such that the new calculation
784 * can be used as a base.
786 * \param[in,out] base Base calculation to merge to.
787 * \param[in,out] pc Position calculation to merge to \p base.
789 * After the call, \p base can be used as a base for \p pc (or any calculation
790 * that used it as a base).
791 * It is assumed that any overlap between \p base and \p pc is in complete
792 * blocks, i.e., that the merge is possible.
795 merge_to_base(gmx_ana_poscalc_t *base, gmx_ana_poscalc_t *pc)
797 gmx_ana_index_t gp, gb, g;
801 base->flags |= pc->flags & (POS_VELOCITIES | POS_FORCES);
802 gmx_ana_index_set(&gp, pc->b.nra, pc->b.a, 0);
803 gmx_ana_index_set(&gb, base->b.nra, base->b.a, 0);
804 isize = gmx_ana_index_difference_size(&gp, &gb);
807 gmx_ana_index_clear(&g);
808 gmx_ana_index_reserve(&g, base->b.nra + isize);
809 /* Find the new blocks */
810 gmx_ana_index_difference(&g, &gp, &gb);
811 /* Count the blocks in g */
815 while (pc->b.a[pc->b.index[bi]] != g.index[i])
819 i += pc->b.index[bi+1] - pc->b.index[bi];
823 /* Merge the atoms into a temporary structure */
824 gmx_ana_index_merge(&g, &gb, &g);
825 /* Merge the blocks */
826 srenew(base->b.index, base->b.nr + bnr + 1);
830 bo = base->b.nr + bnr - 1;
831 base->b.index[bo+1] = i + 1;
834 if (bi < 0 || base->b.a[base->b.index[bi+1]-1] != g.index[i])
836 i -= pc->b.index[bj+1] - pc->b.index[bj];
841 if (bj >= 0 && pc->b.a[pc->b.index[bj+1]-1] == g.index[i])
845 i -= base->b.index[bi+1] - base->b.index[bi];
848 base->b.index[bo] = i + 1;
852 base->b.nalloc_index += bnr;
854 base->b.nra = g.isize;
856 base->b.nalloc_a = g.isize;
857 /* Refresh the gmax field */
858 gmx_ana_index_set(&base->gmax, base->b.nra, base->b.a, 0);
863 * Merges two bases into one.
865 * \param[in,out] tbase Base calculation to merge to.
866 * \param[in] mbase Base calculation to merge to \p tbase.
868 * After the call, \p mbase has been freed and \p tbase is used as the base
869 * for all calculations that previously had \p mbase as their base.
870 * It is assumed that any overlap between \p tbase and \p mbase is in complete
871 * blocks, i.e., that the merge is possible.
874 merge_bases(gmx_ana_poscalc_t *tbase, gmx_ana_poscalc_t *mbase)
876 gmx_ana_poscalc_t *pc;
878 merge_to_base(tbase, mbase);
879 mbase->coll->removeCalculation(mbase);
880 /* Set tbase as the base for all calculations that had mbase */
881 pc = tbase->coll->first_;
884 if (pc->sbase == mbase)
893 gmx_ana_poscalc_free(mbase);
897 * Setups the static base calculation for a position calculation.
899 * \param[in,out] pc Position calculation to setup the base for.
902 setup_base(gmx_ana_poscalc_t *pc)
904 gmx_ana_poscalc_t *base, *pbase, *next;
905 gmx_ana_index_t gp, g;
907 /* Exit immediately if pc should not have a base. */
908 if (!can_use_base(pc))
913 gmx_ana_index_set(&gp, pc->b.nra, pc->b.a, 0);
914 gmx_ana_index_clear(&g);
915 gmx_ana_index_reserve(&g, pc->b.nra);
917 base = pc->coll->first_;
920 /* Save the next calculation so that we can safely delete base */
922 /* Skip pc, calculations that already have a base (we should match the
923 * base instead), as well as calculations that should not have a base.
924 * If the above conditions are met, check whether we should do a
927 if (base != pc && !base->sbase && can_use_base(base)
928 && should_merge(pbase, base, &gp, &g))
930 /* Check whether this is the first base found */
933 /* Create a real base if one is not present */
936 pbase = create_simple_base(base);
942 /* Make it a base for pc as well */
943 merge_to_base(pbase, pc);
947 else /* This was not the first base */
951 /* If it is not a real base, just make the new base as
952 * the base for it as well. */
953 merge_to_base(pbase, base);
959 /* If base is a real base, merge it with the new base
961 merge_bases(pbase, base);
964 gmx_ana_index_set(&gp, pbase->b.nra, pbase->b.a, 0);
965 gmx_ana_index_reserve(&g, pc->b.nra);
967 /* Proceed to the next unchecked calculation */
971 gmx_ana_index_deinit(&g);
973 /* If no base was found, create one if one is required */
974 if (!pc->sbase && (pc->flags & POS_DYNAMIC)
975 && (pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
977 create_simple_base(pc);
982 * \param[in,out] pc Position calculation data structure.
983 * \param[in] flags New flags.
985 * \p flags are added to the old flags.
986 * If calculation type is \ref POS_ATOM, \ref POS_MASS is automatically
988 * If both \ref POS_DYNAMIC and \ref POS_MASKONLY are provided,
989 * \ref POS_DYNAMIC is cleared.
990 * If calculation type is not \ref POS_RES or \ref POS_MOL,
991 * \ref POS_COMPLMAX and \ref POS_COMPLWHOLE are automatically cleared.
994 gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t *pc, int flags)
996 if (pc->type == POS_ATOM)
1000 if (flags & POS_MASKONLY)
1002 flags &= ~POS_DYNAMIC;
1004 if (pc->type != POS_RES && pc->type != POS_MOL)
1006 flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
1012 * \param[in,out] pc Position calculation data structure.
1013 * \param[in] g Maximum index group for the calculation.
1015 * Subsequent calls to gmx_ana_poscalc_update() should use only subsets of
1016 * \p g for evaluation.
1018 * The topology should have been set for the collection of which \p pc is
1022 gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g)
1024 set_poscalc_maxindex(pc, g, false);
1029 * \param[in] pc Position calculation data structure.
1030 * \param[out] p Output positions.
1032 * Calls to gmx_ana_poscalc_update() using \p pc should use only positions
1033 * initialized with this function.
1034 * The \c p->g pointer is initialized to point to an internal group that
1035 * contains the maximum index group set with gmx_ana_poscalc_set_maxindex().
1038 gmx_ana_poscalc_init_pos(gmx_ana_poscalc_t *pc, gmx_ana_pos_t *p)
1040 gmx_ana_indexmap_init(&p->m, &pc->gmax, pc->coll->top_, pc->itype);
1041 /* Only do the static optimization when there is no completion */
1042 if (!(pc->flags & POS_DYNAMIC) && pc->b.nra == pc->gmax.isize)
1044 gmx_ana_indexmap_set_static(&p->m, &pc->b);
1046 gmx_ana_pos_reserve(p, p->m.nr, 0);
1047 if (pc->flags & POS_VELOCITIES)
1049 gmx_ana_pos_reserve_velocities(p);
1051 if (pc->flags & POS_FORCES)
1053 gmx_ana_pos_reserve_forces(p);
1055 gmx_ana_pos_set_nr(p, p->m.nr);
1056 gmx_ana_pos_set_evalgrp(p, &pc->gmax);
1060 * \param pc Position calculation data to be freed.
1062 * The \p pc pointer is invalid after the call.
1065 gmx_ana_poscalc_free(gmx_ana_poscalc_t *pc)
1073 if (pc->refcount > 0)
1078 pc->coll->removeCalculation(pc);
1079 if (pc->b.nalloc_index > 0)
1083 if (pc->b.nalloc_a > 0)
1087 if (pc->flags & POS_COMPLWHOLE)
1089 gmx_ana_index_deinit(&pc->gmax);
1093 gmx_ana_pos_free(pc->p);
1097 gmx_ana_poscalc_free(pc->sbase);
1104 * \param[in] pc Position calculation data to query.
1105 * \returns true if \p pc requires topology for initialization and/or
1106 * evaluation, false otherwise.
1109 gmx_ana_poscalc_requires_top(gmx_ana_poscalc_t *pc)
1111 if ((pc->flags & POS_MASS) || pc->type == POS_RES || pc->type == POS_MOL)
1119 * \param[in] pc Position calculation data.
1120 * \param[in,out] p Output positions, initialized previously with
1121 * gmx_ana_poscalc_init_pos() using \p pc.
1122 * \param[in] g Index group to use for the update.
1123 * \param[in] fr Current frame.
1124 * \param[in] pbc PBC data, or NULL if no PBC should be used.
1126 * gmx_ana_poscalc_init_frame() should be called for each frame before calling
1130 gmx_ana_poscalc_update(gmx_ana_poscalc_t *pc, gmx_ana_pos_t *p,
1131 gmx_ana_index_t *g, t_trxframe *fr, t_pbc *pbc)
1135 if (pc->bEval == true && !(pc->flags & POS_MASKONLY))
1141 gmx_ana_poscalc_update(pc->sbase, NULL, NULL, fr, pbc);
1151 gmx_ana_pos_set_evalgrp(p, g);
1153 /* Update the index map */
1154 if (pc->flags & POS_DYNAMIC)
1156 gmx_ana_indexmap_update(&p->m, g, false);
1159 else if (pc->flags & POS_MASKONLY)
1161 gmx_ana_indexmap_update(&p->m, g, true);
1167 if (!(pc->flags & POS_DYNAMIC))
1172 /* Evaluate the positions */
1175 /* TODO: It might be faster to evaluate the positions within this
1176 * loop instead of in the beginning. */
1177 if (pc->flags & POS_DYNAMIC)
1179 for (bi = 0; bi < p->nr; ++bi)
1181 bj = pc->baseid[p->m.refid[bi]];
1182 copy_rvec(pc->sbase->p->x[bj], p->x[bi]);
1186 for (bi = 0; bi < p->nr; ++bi)
1188 bj = pc->baseid[p->m.refid[bi]];
1189 copy_rvec(pc->sbase->p->v[bj], p->v[bi]);
1194 for (bi = 0; bi < p->nr; ++bi)
1196 bj = pc->baseid[p->m.refid[bi]];
1197 copy_rvec(pc->sbase->p->f[bj], p->f[bi]);
1203 for (bi = 0; bi < p->nr; ++bi)
1205 bj = pc->baseid[bi];
1206 copy_rvec(pc->sbase->p->x[bj], p->x[bi]);
1210 for (bi = 0; bi < p->nr; ++bi)
1212 bj = pc->baseid[bi];
1213 copy_rvec(pc->sbase->p->v[bj], p->v[bi]);
1218 for (bi = 0; bi < p->nr; ++bi)
1220 bj = pc->baseid[bi];
1221 copy_rvec(pc->sbase->p->f[bj], p->f[bi]);
1226 else /* pc->sbase is NULL */
1228 if (pc->flags & POS_DYNAMIC)
1230 pc->b.nr = p->m.mapb.nr;
1231 pc->b.index = p->m.mapb.index;
1232 pc->b.nra = g->isize;
1235 if (p->v && !fr->bV)
1237 for (i = 0; i < pc->b.nra; ++i)
1239 clear_rvec(p->v[i]);
1242 if (p->f && !fr->bF)
1244 for (i = 0; i < pc->b.nra; ++i)
1246 clear_rvec(p->f[i]);
1249 /* Here, we assume that the topology has been properly initialized,
1250 * and do not check the return values of gmx_calc_comg*(). */
1251 t_topology *top = pc->coll->top_;
1252 bool bMass = pc->flags & POS_MASS;
1256 for (i = 0; i < pc->b.nra; ++i)
1258 copy_rvec(fr->x[pc->b.a[i]], p->x[i]);
1262 for (i = 0; i < pc->b.nra; ++i)
1264 copy_rvec(fr->v[pc->b.a[i]], p->v[i]);
1269 for (i = 0; i < pc->b.nra; ++i)
1271 copy_rvec(fr->f[pc->b.a[i]], p->f[i]);
1276 gmx_calc_comg(top, fr->x, pc->b.nra, pc->b.a, bMass, p->x[0]);
1279 gmx_calc_comg(top, fr->v, pc->b.nra, pc->b.a, bMass, p->v[0]);
1283 gmx_calc_comg_f(top, fr->f, pc->b.nra, pc->b.a, bMass, p->f[0]);
1287 gmx_calc_comg_pbc(top, fr->x, pbc, pc->b.nra, pc->b.a, bMass, p->x[0]);
1290 gmx_calc_comg(top, fr->v, pc->b.nra, pc->b.a, bMass, p->v[0]);
1294 gmx_calc_comg_f(top, fr->f, pc->b.nra, pc->b.a, bMass, p->f[0]);
1298 gmx_calc_comg_blocka(top, fr->x, &pc->b, bMass, p->x);
1301 gmx_calc_comg_blocka(top, fr->v, &pc->b, bMass, p->v);
1305 gmx_calc_comg_blocka(top, fr->f, &pc->b, bMass, p->f);