Updated selection documentation.
[gromacs/qmmm-gamess-us.git] / src / gmxlib / trajana / poscalc.c
blobbe6ccb23973a19cb5287835957375562f6cd352f
1 /*
3 * This source code is part of
5 * G R O M A C S
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
31 /*! \internal
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
57 * used.
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
61 * function is called.
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
66 * properly.
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
75 * as a string.
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.
81 /*! \internal \file
82 * \brief Implementation of functions in poscalc.h.
84 * \todo
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
88 * copying.
89 * The interface would need to be changed to make it possible to use the
90 * same output positions for several calculations.
92 * \todo
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
97 * same base).
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().
104 #ifdef HAVE_CONFIG_H
105 #include <config.h>
106 #endif
108 #include <string.h>
110 #include <macros.h>
111 #include <smalloc.h>
112 #include <typedefs.h>
113 #include <pbc.h>
114 #include <vec.h>
116 #include <centerofmass.h>
117 #include <indexutil.h>
118 #include <poscalc.h>
119 #include <position.h>
121 /*! \internal \brief
122 * Collection of \c gmx_ana_poscalc_t structures for the same topology.
124 * Calculations within the same structure are optimized to eliminate duplicate
125 * calculations.
127 struct gmx_ana_poscalc_coll_t
129 /*! \brief
130 * Topology data.
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.
135 t_topology *top;
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. */
141 bool bInit;
144 /*! \internal \brief
145 * Data structure for position calculation.
147 struct gmx_ana_poscalc_t
149 /*! \brief
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.
159 e_poscalc_t type;
160 /*! \brief
161 * Flags for calculation options.
163 * See \ref poscalc_flags "documentation of the flags".
165 int flags;
167 /*! \brief
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.
173 e_index_t itype;
174 /*! \brief
175 * Block data for the calculation.
177 t_blocka b;
178 /*! \brief
179 * Mapping from the blocks to the blocks of \p sbase.
181 * If \p sbase is NULL, this field is also.
183 int *baseid;
184 /*! \brief
185 * Maximum evaluation group.
187 gmx_ana_index_t gmax;
189 /** Position storage for calculations that are used as a base. */
190 gmx_ana_pos_t *p;
192 /** TRUE if the positions have been evaluated for the current frame. */
193 bool bEval;
194 /*! \brief
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. */
207 int refcount;
208 /** Collection this calculation belongs to. */
209 gmx_ana_poscalc_coll_t *coll;
212 static const char *const poscalc_enum_strings[] = {
213 "atom",
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",
222 NULL,
224 #define NENUM asize(poscalc_enum_strings)
226 /*! \brief
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.
232 static e_index_t
233 index_type_for_poscalc(e_poscalc_t type)
235 switch(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
256 * prefix is given).
257 * \returns 0 if \p post is one of the valid strings, EINVAL otherwise.
259 * \attention
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)
266 const char *ptr;
268 if (post[0] == 'a')
270 *type = POS_ATOM;
271 *flags &= ~(POS_MASS | POS_COMPLMAX | POS_COMPLWHOLE);
272 return 0;
275 /* Process the prefix */
276 ptr = post;
277 if (post[0] == 'w')
279 *flags &= ~POS_COMPLMAX;
280 *flags |= POS_COMPLWHOLE;
281 ptr = post + 6;
283 else if (post[0] == 'p')
285 *flags &= ~POS_COMPLWHOLE;
286 *flags |= POS_COMPLMAX;
287 ptr = post + 5;
289 else if (post[0] == 'd')
291 *flags &= ~(POS_COMPLMAX | POS_COMPLWHOLE);
292 ptr = post + 4;
295 if (ptr[0] == 'r')
297 *type = POS_RES;
299 else if (ptr[0] == 'm')
301 *type = POS_MOL;
303 else
305 gmx_incons("unknown position calculation type");
306 return EINVAL;
308 if (ptr[6] == 'm')
310 *flags |= POS_MASS;
312 else if (ptr[6] == 'g')
314 *flags &= ~POS_MASS;
316 else
318 gmx_incons("unknown position calculation type");
319 return EINVAL;
321 return 0;
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.
332 const char **
333 gmx_ana_poscalc_create_type_enum(bool bAtom)
335 const char **pcenum;
336 size_t i;
338 if (bAtom)
340 snew(pcenum, NENUM+1);
341 for (i = 0; i < NENUM; ++i)
343 pcenum[i+1] = poscalc_enum_strings[i];
346 else
348 snew(pcenum, NENUM+1-1);
349 for (i = 1; i < NENUM; ++i)
351 pcenum[i] = poscalc_enum_strings[i];
354 pcenum[0] = NULL;
355 return pcenum;
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;
367 snew(pcc, 1);
368 pcc->top = NULL;
369 pcc->first = NULL;
370 pcc->last = NULL;
371 pcc->bInit = FALSE;
372 *pccp = pcc;
373 return 0;
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.
384 void
385 gmx_ana_poscalc_coll_set_topology(gmx_ana_poscalc_coll_t *pcc, t_topology *top)
387 pcc->top = 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.
397 void
398 gmx_ana_poscalc_coll_free(gmx_ana_poscalc_coll_t *pcc)
400 while (pcc->first)
402 gmx_ana_poscalc_free(pcc->first);
404 sfree(pcc);
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.
414 void
415 gmx_ana_poscalc_coll_print_tree(FILE *fp, gmx_ana_poscalc_coll_t *pcc)
417 gmx_ana_poscalc_t *pc;
418 int i, j;
420 fprintf(fp, "Position calculations:\n");
421 i = 1;
422 pc = pcc->first;
423 while (pc)
425 fprintf(fp, "%2d ", i);
426 switch (pc->type)
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))
436 fprintf(fp, " (");
437 switch (pc->itype)
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;
445 fprintf(fp, ")");
447 fprintf(fp, " flg=");
448 if (pc->flags & POS_MASS)
450 fprintf(fp, "M");
452 if (pc->flags & POS_DYNAMIC)
454 fprintf(fp, "D");
456 if (pc->flags & POS_MASKONLY)
458 fprintf(fp, "A");
460 if (pc->flags & POS_COMPLMAX)
462 fprintf(fp, "Cm");
464 if (pc->flags & POS_COMPLWHOLE)
466 fprintf(fp, "Cw");
468 if (!pc->flags)
470 fprintf(fp, "0");
472 fprintf(fp, " nr=%d nra=%d", pc->b.nr, pc->b.nra);
473 fprintf(fp, " refc=%d", pc->refcount);
474 fprintf(fp, "\n");
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);
482 else
484 for (j = 0; j < pc->gmax.isize; ++j)
486 fprintf(fp, " %d", pc->gmax.index[j] + 1);
489 fprintf(fp, "\n");
491 if (pc->b.nalloc_a > 0)
493 fprintf(fp, " Atoms: ");
494 if (pc->b.nra > 20)
496 fprintf(fp, " %d atoms", pc->b.nra);
498 else
500 for (j = 0; j < pc->b.nra; ++j)
502 fprintf(fp, " %d", pc->b.a[j] + 1);
505 fprintf(fp, "\n");
507 if (pc->b.nalloc_index > 0)
509 fprintf(fp, " Blocks:");
510 if (pc->b.nr > 20)
512 fprintf(fp, " %d pcs", pc->b.nr);
514 else
516 for (j = 0; j <= pc->b.nr; ++j)
518 fprintf(fp, " %d", pc->b.index[j]);
521 fprintf(fp, "\n");
523 if (pc->sbase)
525 gmx_ana_poscalc_t *base;
527 fprintf(fp, " Base: ");
528 j = 1;
529 base = pcc->first;
530 while (base && base != pc->sbase)
532 ++j;
533 base = base->next;
535 fprintf(fp, "%d", j);
536 if (pc->baseid && pc->b.nr <= 20)
538 fprintf(fp, " id:");
539 for (j = 0; j < pc->b.nr; ++j)
541 fprintf(fp, " %d", pc->baseid[j]+1);
544 fprintf(fp, "\n");
546 ++i;
547 pc = pc->next;
551 /*! \brief
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.
561 static void
562 insert_poscalc(gmx_ana_poscalc_t *pc, gmx_ana_poscalc_t *before)
564 if (before == NULL)
566 pc->next = NULL;
567 pc->prev = pc->coll->last;
568 if (pc->coll->last)
570 pc->coll->last->next = pc;
572 pc->coll->last = pc;
574 else
576 pc->prev = before->prev;
577 pc->next = before;
578 if (before->prev)
580 before->prev->next = pc;
582 before->prev = pc;
584 if (!pc->prev)
586 pc->coll->first = pc;
590 /*! \brief
591 * Removes a position calculation structure from its collection.
593 * \param pc Data structure to remove.
595 * Removes \p pc from its collection.
597 static void
598 remove_poscalc(gmx_ana_poscalc_t *pc)
600 if (pc->prev)
602 pc->prev->next = pc->next;
604 else if (pc == pc->coll->first)
606 pc->coll->first = pc->next;
608 if (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;
619 /*! \brief
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.
628 static void
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)
635 pc->type = POS_ATOM;
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;
655 else
657 gmx_ana_index_set(&pc->gmax, pc->b.nra, pc->b.a, NULL, 0);
661 /*! \brief
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,
666 * FALSE otherwise.
668 static bool
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
672 * base. */
673 if (pc->type == POS_ATOM)
675 return FALSE;
677 /* For dynamic selections that do not use completion, it is not possible
678 * to use a base. */
679 if ((pc->type == POS_RES || pc->type == POS_MOL)
680 && (pc->flags & POS_DYNAMIC) && !(pc->flags & (POS_COMPLMAX | POS_COMPLWHOLE)))
682 return FALSE;
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))
688 return FALSE;
690 return TRUE;
693 /*! \brief
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
699 * \p pc1.
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.
705 static bool
706 should_merge(gmx_ana_poscalc_t *pc1, gmx_ana_poscalc_t *pc2,
707 gmx_ana_index_t *g1, gmx_ana_index_t *g)
709 gmx_ana_index_t g2;
711 /* Do not merge calculations with different mass weighting. */
712 if ((pc1->flags & POS_MASS) != (pc2->flags & POS_MASS))
714 return FALSE;
716 /* Avoid messing up complete calculations. */
717 if ((pc1->flags & POS_COMPLWHOLE) != (pc2->flags & POS_COMPLWHOLE))
719 return FALSE;
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. */
725 if (g->isize == 0)
727 return FALSE;
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)
733 return TRUE;
735 /* The calculations also match if the intersection consists of full
736 * blocks. */
737 if (gmx_ana_index_has_full_ablocks(g, &pc1->b)
738 && gmx_ana_index_has_full_ablocks(g, &pc2->b))
740 return TRUE;
742 return FALSE;
745 /*! \brief
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;
760 int flags;
761 int rc;
763 flags = pc->flags & ~(POS_DYNAMIC | POS_MASKONLY);
764 rc = gmx_ana_poscalc_create(&base, pc->coll, pc->type, flags);
765 if (rc != 0)
767 gmx_fatal(FARGS, "position calculation base creation failed");
769 set_poscalc_maxindex(base, &pc->gmax, TRUE);
771 snew(base->p, 1);
773 pc->sbase = base;
774 remove_poscalc(base);
775 insert_poscalc(base, pc);
777 return base;
780 /*! \brief
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.
792 static void
793 merge_to_base(gmx_ana_poscalc_t *base, gmx_ana_poscalc_t *pc)
795 gmx_ana_index_t gp, gb, g;
796 int isize, bnr;
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);
802 if (isize > 0)
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 */
809 i = bi = bnr = 0;
810 while (i < g.isize)
812 while (pc->b.a[pc->b.index[bi]] != g.index[i])
814 ++bi;
816 i += pc->b.index[bi+1] - pc->b.index[bi];
817 ++bnr;
818 ++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);
824 i = g.isize - 1;
825 bi = base->b.nr - 1;
826 bj = pc->b.nr - 1;
827 bo = base->b.nr + bnr - 1;
828 base->b.index[bo+1] = i + 1;
829 while (bo >= 0)
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];
834 --bj;
836 else
838 if (bj >= 0 && pc->b.a[pc->b.index[bj+1]-1] == g.index[i])
840 --bj;
842 i -= base->b.index[bi+1] - base->b.index[bi];
843 --bi;
845 base->b.index[bo] = i + 1;
846 --bo;
848 base->b.nr += bnr;
849 base->b.nalloc_index += bnr;
850 sfree(base->b.a);
851 base->b.nra = g.isize;
852 base->b.a = g.index;
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);
859 /*! \brief
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.
870 static void
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;
879 while (pc)
881 if (pc->sbase == mbase)
883 pc->sbase = tbase;
884 tbase->refcount++;
886 pc = pc->next;
888 /* Free mbase */
889 mbase->refcount = 0;
890 gmx_ana_poscalc_free(mbase);
893 /*! \brief
894 * Setups the static base calculation for a position calculation.
896 * \param[in,out] pc Position calculation to setup the base for.
898 static void
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))
907 return;
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);
913 pbase = pc;
914 base = pc->coll->first;
915 while (base)
917 /* Save the next calculation so that we can safely delete base */
918 next = base->next;
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
922 * merge.
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 */
928 if (pbase == pc)
930 /* Create a real base if one is not present */
931 if (!base->p)
933 pbase = create_simple_base(base);
935 else
937 pbase = base;
939 /* Make it a base for pc as well */
940 merge_to_base(pbase, pc);
941 pc->sbase = pbase;
942 pbase->refcount++;
944 else /* This was not the first base */
946 if (!base->p)
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);
951 base->sbase = pbase;
952 pbase->refcount++;
954 else
956 /* If base is a real base, merge it with the new base
957 * and delete it. */
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 */
965 base = next;
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;
992 snew(pc, 1);
993 pc->type = type;
994 pc->itype = index_type_for_poscalc(type);
995 gmx_ana_poscalc_set_flags(pc, flags);
996 pc->refcount = 1;
997 pc->coll = pcc;
998 insert_poscalc(pc, NULL);
999 *pcp = pc;
1000 return 0;
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)
1022 e_poscalc_t type;
1023 int cflags;
1024 int rc;
1026 cflags = flags;
1027 rc = gmx_ana_poscalc_type_from_enum(post, &type, &cflags);
1028 if (rc != 0)
1030 *pcp = NULL;
1031 return rc;
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
1042 * cleared.
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.
1048 void
1049 gmx_ana_poscalc_set_flags(gmx_ana_poscalc_t *pc, int flags)
1051 if (pc->type == POS_ATOM)
1053 flags &= ~POS_MASS;
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);
1063 pc->flags |= flags;
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
1074 * a member.
1076 void
1077 gmx_ana_poscalc_set_maxindex(gmx_ana_poscalc_t *pc, gmx_ana_index_t *g)
1079 set_poscalc_maxindex(pc, g, FALSE);
1080 setup_base(pc);
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().
1092 void
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);
1097 p->g = &pc->gmax;
1101 * \param pc Position calculation data to be freed.
1103 * The \p pc pointer is invalid after the call.
1105 void
1106 gmx_ana_poscalc_free(gmx_ana_poscalc_t *pc)
1108 if (!pc)
1110 return;
1113 pc->refcount--;
1114 if (pc->refcount > 0)
1116 return;
1119 remove_poscalc(pc);
1120 if (pc->b.nalloc_index > 0)
1122 sfree(pc->b.index);
1124 if (pc->b.nalloc_a > 0)
1126 sfree(pc->b.a);
1128 if (pc->flags & POS_COMPLWHOLE)
1130 gmx_ana_index_deinit(&pc->gmax);
1132 if (pc->p)
1134 gmx_ana_pos_free(pc->p);
1136 if (pc->sbase)
1138 gmx_ana_poscalc_free(pc->sbase);
1139 sfree(pc->baseid);
1141 sfree(pc);
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.
1149 bool
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)
1154 return TRUE;
1156 return FALSE;
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.
1171 void
1172 gmx_ana_poscalc_init_eval(gmx_ana_poscalc_coll_t *pcc)
1174 gmx_ana_poscalc_t *pc;
1175 int bi, bj;
1177 if (pcc->bInit)
1179 return;
1181 pc = pcc->first;
1182 while (pc)
1184 /* Initialize position storage for base calculations */
1185 if (pc->p)
1187 gmx_ana_poscalc_init_pos(pc, pc->p);
1189 /* Construct the mapping of the base positions */
1190 if (pc->sbase)
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]])
1197 ++bj;
1199 pc->baseid[bi] = bj;
1202 /* Free the block data for dynamic calculations */
1203 if ((pc->flags & POS_DYNAMIC) && pc->b.nalloc_index > 0)
1205 sfree(pc->b.index);
1206 sfree(pc->b.a);
1207 pc->b.nalloc_index = 0;
1208 pc->b.nalloc_a = 0;
1210 pc = pc->next;
1212 pcc->bInit = TRUE;
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
1223 * not being used.
1225 * This function calls gmx_ana_poscalc_init_eval() automatically if it has
1226 * not been called earlier.
1228 void
1229 gmx_ana_poscalc_init_frame(gmx_ana_poscalc_coll_t *pcc)
1231 gmx_ana_poscalc_t *pc;
1233 if (!pcc->bInit)
1235 gmx_ana_poscalc_init_eval(pcc);
1237 /* Clear the evaluation flags */
1238 pc = pcc->first;
1239 while (pc)
1241 pc->bEval = FALSE;
1242 pc = pc->next;
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
1255 * this function.
1257 void
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)
1261 int i, j, bi, bj;
1263 if (pc->bEval == TRUE && !(pc->flags & POS_MASKONLY))
1265 return;
1267 if (pc->sbase)
1269 gmx_ana_poscalc_update(pc->sbase, NULL, NULL, x, pbc);
1271 if (!p)
1273 p = pc->p;
1275 if (!g)
1277 g = &pc->gmax;
1279 p->g = g;
1281 /* Update the index map */
1282 if (pc->flags & POS_DYNAMIC)
1284 gmx_ana_indexmap_update(&p->m, g, FALSE);
1285 p->nr = p->m.nr;
1287 else if (pc->flags & POS_MASKONLY)
1289 gmx_ana_indexmap_update(&p->m, g, TRUE);
1290 if (pc->bEval)
1291 return;
1293 if (!(pc->flags & POS_DYNAMIC))
1295 pc->bEval = TRUE;
1298 /* Evaluate the positions */
1299 if (pc->sbase)
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]);
1311 else
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;
1327 pc->b.a = g->index;
1329 /* Here, we assume that the topology has been properly initialized,
1330 * and do not check the return values of gmx_calc_comg*(). */
1331 switch (pc->type)
1333 case POS_ATOM:
1334 for (i = 0; i < pc->b.nra; ++i)
1336 copy_rvec(x[pc->b.a[i]], p->x[i]);
1338 break;
1339 case POS_ALL:
1340 gmx_calc_comg(pc->coll->top, x, pc->b.nra, pc->b.a, pc->flags & POS_MASS, p->x[0]);
1341 break;
1342 case POS_ALL_PBC:
1343 gmx_calc_comg_pbc(pc->coll->top, x, pbc, pc->b.nra, pc->b.a, pc->flags & POS_MASS, p->x[0]);
1344 break;
1345 default:
1346 gmx_calc_comg_blocka(pc->coll->top, x, &pc->b, pc->flags & POS_MASS, p->x);
1347 break;