root/Multipole/trunk/multipole.c

Revision 1, 57.0 KB (checked in by cristy, 2 months ago)


Line 
1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%       M   M  U   U  L      TTTTT  IIIII  PPPP    OOO   L      EEEEE         %
6%       MM MM  U   U  L        T      I    P   P  O   O  L      E             %
7%       M M M  U   U  L        T      I    PPPP   O   O  L      EEE           %
8%       M   M  U   U  L        T      I    P      O   O  L      E             %
9%       M   M   UUU   LLLLL    T    IIIII  P       OOO   LLLLL  EEEEE         %
10%                                                                             %
11%                                                                             %
12%          An O(N) Algorithm for Three-Dimensional N-Body Simulations         %
13%                                                                             %
14%                                                                             %
15%                                                                             %
16%                           Software Design                                   %
17%                             John Cristy                                     %
18%                             January 1991                                    %
19%                                                                             %
20%                          Algorithm Analysis                                 %
21%                             Steve Lustig                                    %
22%                                                                             %
23%                                                                             %
24%  Copyright 1999-2008 ImageMagick Studio LLC, a non-profit organization      %
25%  dedicated to making software imaging solutions freely available.           %
26%                                                                             %
27%  You may not use this file except in compliance with the License.  You may  %
28%  obtain a copy of the License at                                            %
29%                                                                             %
30%    http://www.imagemagick.org/script/license.php                            %
31%                                                                             %
32%  Unless required by applicable law or agreed to in writing, software        %
33%  distributed under the License is distributed on an "AS IS" BASIS,          %
34%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
35%  See the License for the specific language governing permissions and        %
36%  limitations under the License.                                             %
37%                                                                             %
38%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
39%
40%  N-body implements a sequential multipole algorithm that solves the
41%  interparticle gravitational potential 1/r of N particles to within
42%  round-off error and whose computational effort grows only linearly with
43%  increasing N, i.e. O(N).  This makes possible simulations of a large
44%  number of atoms on the order of 500,000 or more.  Typically, thousands
45%  to millions of time steps are required for a realistic dynamic
46%  simulation.  The linear nature of the cost of computing the potential
47%  with the sequential multipole algorithm allows many more time-steps, in
48%  less time, than previously possible.
49%
50%  The multipole-expansion algorithm computes the potential by
51%  partitioning them into two parts: close-by particles (`near-field
52%  potentials') and the potential of far-away particles (`far-field
53%  potentials').  The near-field potential is computed using direct
54%  evaluation.  The far-field potential is represented as an absolutely
55%  convergent Taylor series.  Given a required precision e, the number of
56%  terms p in the Taylor series can be determined in advance to obtain a
57%  particular accuracy.
58%
59%  The goal of the multipole computation is to compute the far-field with
60%  O(N) work.  This is achieved by propagating values, first upwards
61%  through the computational tree, then downward in two distinct phases.
62%  In the first (upward) phase the far-field interactions are computed by
63%  shifting the multipole expansions of the child nodes to the center of
64%  the parent node and adding the results.  The far-field interaction is
65%  calculated for all cubes at all levels, beginning at the finest level
66%  of refinement (the leaf nodes).  The multipole-expansion of the field
67%  due to each particle is calculated relative to the center of each leaf
68%  node and summed.  Proceeding upward, the expansions of the eight child
69%  nodes are shifted relative to the parent's center and summed.  This
70%  computation is performed at each level propagating values upward
71%  through the tree.  At the end of this phase of the algorithm, each node
72%  at every level has a multipole-expansion representation of the field
73%  due to all particles within its boundaries.  These expansions are only
74%  valid outside the region defined by the near field of each cube.
75%
76%  In the second (downward) phase, we form the local expansions for all
77%  cubes at all levels beginning at the coarsest level (root node).  The
78%  far-field multipole-expansions of all nodes in the interactive field of
79%  the node under consideration are converted into local expansions
80%  relative to the node's center and are summed.  Next, shift the
81%  expansion of the parent cube to the center of the current cube under
82%  consideration and sum with the local expansions computed from the
83%  interactive field.  Note, the root node does not have a parent or
84%  interactive field so it's local expansion is defined to be zero.  The
85%  local expansions are propagated down the tree.  They describe the field
86%  due to all particles in the system that are not contained in the
87%  current node or its nearest neighbors.
88%
89%  Finally, for each cube i at the finest level n, we evaluate the local
90%  expansion for each particle in cube i.  Each local expansion is
91%  described by the coefficients of a p-term Taylor series.  Direct
92%  evaluation of this series at a particle yields the potential.
93%  Summating the local expansion at each leaf node generates the potential
94%  or force due to all particles outside the nearest neighbor cubes. The
95%  interactions of each particle in cube i are computed directly and
96%  summed to generate the potential due to the nearest neightbors.
97%  Finally summating far-field and near-field potential gives us the
98%  desired potential.
99%
100%  A discussion and formal description of the multipole-expansion algorithm
101%  can be found in these references:
102%
103%    "Computational Simulation of Multi-Body Interactions with O(N) Scaling",
104%    John Cristy and David A. Pensak, Central Research and Development,
105%    E.I. Du Pont de Nemours, Technical Report, June 1991.
106%
107%    "An O(N) Algorithm for Three-dimensional N-body Simulations", Feng Zhao,
108%    MIT Artificial Intelligence Lab, Technical Report 995, 1987.
109%
110%  The formula for the lexicographical ordering of a tetrahedral array was
111%  derived by Richard Merrifield.
112%
113%
114*/
115
116/*
117  Include declarations.
118*/
119#include <multipole.h>
120
121/*
122  Global variables.
123*/
124static Cube
125  cube;
126
127/*
128%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
129%                                                                             %
130%                                                                             %
131%                                                                             %
132%  B i n o m i a l                                                            %
133%                                                                             %
134%                                                                             %
135%                                                                             %
136%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
137%
138%  Binomial() returns the binomial coefficient (n,k).
139%
140%  The format of the Binomial routine is:
141%
142%    Binomial(long n,long k)
143%
144%  A description of each parameter follows:
145%
146%    o n,k: Specifies an unsigned long.
147%
148%
149*/
150static unsigned long Binomial(long n,long k)
151{
152  register long
153    i,
154    j;
155
156  unsigned long
157    binomial;
158
159  if (n < k)
160    return(0);
161  binomial=1;
162  k=Min(k,n-k);
163  j=n;
164  for (i=1; i <= k; i++)
165  {
166    binomial=(unsigned long) ((binomial*(float) j/i)+0.5);
167    j--;
168  }
169  return(binomial);
170}
171
172/*
173%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174%                                                                             %
175%                                                                             %
176%                                                                             %
177%  C l a s s i f i c a t i o n                                                %
178%                                                                             %
179%                                                                             %
180%                                                                             %
181%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182%
183%  Classification() creates a linked list of particles in the leaf nodes of the
184%  particle cube.
185%
186%  The format of the Classification routine is:
187%
188%    Classification(Particle *particles,unsigned long number_particles)
189%
190%  A description of each parameter follows:
191%
192%    o particles: Specifies a pointer to an array of Particles structures.
193%
194%    o number_particles: The number of particules in the Particles array.
195%
196*/
197static void Classification(Particle *particles,unsigned long number_particles)
198{
199  register long
200    i,
201    level;
202
203  register Node
204    *node;
205
206  register Particle
207    *p;
208
209  unsigned long
210    id;
211
212  p=particles;
213  for (i=0; i < (long) number_particles; i++)
214  {
215    /*
216      Descend the tree to a particular leaf node.
217    */
218    node=cube.root;
219    for (level=1; level < (long) cube.depth; level++)
220    {
221      id=(p->x > node->mid_x) | (p->y > node->mid_y) << 1 |
222        (p->z > node->mid_z) << 2;
223      node=node->child[id];
224    }
225    p->next=node->particle;
226    node->particle=p;
227    p++;
228  }
229}
230
231/*
232%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233%                                                                             %
234%                                                                             %
235%                                                                             %
236%  C o m p u t e P h i                                                        %
237%                                                                             %
238%                                                                             %
239%                                                                             %
240%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241%
242%  ComputePhi() forms the multipole expansions of the potential field due to
243%  particle in each node.
244%
245%  The format of the ComputePhi routine is:
246%
247%    ComputePhi(register Node *node)
248%
249%  A description of each parameter follows:
250%
251%    o node: Specifies a pointer to a Node structure.
252%
253*/
254static void ComputePhi(register Node *node)
255{
256  double
257    x_distance,
258    y_distance,
259    z_distance;
260
261  long
262    i,
263    j,
264    k;
265
266  register double
267    sum;
268
269  register float
270    *ijk,
271    *phi;
272
273  register long
274    a,
275    b,
276    g;
277
278  unsigned long
279    id;
280
281  /*
282    Recursively descend the particle cube.
283  */
284  if (node->level < (cube.depth-1))
285    for (id=0; id < MaxChildren; id++)
286      ComputePhi(node->child[id]);
287  if (node->level == (cube.depth-1))
288    if (node->particle != (Particle *) NULL)
289      {
290        register Particle
291          *p;
292
293        /*
294          Form multipole expansions of potential field due to particles
295          in each cube about the cube center at the leaf nodes.
296        */
297        for (p=node->particle; p != (Particle *) NULL; p=p->next)
298        {
299          x_distance=p->x-node->mid_x;
300          y_distance=p->y-node->mid_y;
301          z_distance=p->z-node->mid_z;
302          for (i=0; i <= (long) cube.precision; i++)
303          {
304            cube.x_power[i]=pow(x_distance,(double) i);
305            cube.y_power[i]=pow(y_distance,(double) i);
306            cube.z_power[i]=pow(z_distance,(double) i);
307          }
308          phi=node->phi;
309          ijk=cube.ijk_factorial;
310          for (i=0; i <= (long) cube.precision; i++)
311            for (j=0; j <= (long) (cube.precision-i); j++)
312              for (k=0; k <= (long) (cube.precision-i-j); k++)
313              {
314                sum=cube.x_power[i]*cube.y_power[j]*cube.z_power[k]*
315                  cube.one_power[i+j+k]*(*ijk++);
316                *phi+=sum;
317                phi++;
318              }
319        }
320      }
321  if (node->level > 1)
322    {
323      /*
324        Form multipole expansions about the centers of each cube at all
325        internal nodes, each expansion representing the potential field
326        due to all particles contained in the cube.  Shift the center
327        of the cube's expansion to the parent cube center using the
328        `Translation of a Multipole Expansion' lemma.
329      */
330      MultipoleExpansion(node,node->parent,cube.phi_tilde);
331      phi=node->parent->phi;
332      for (i=0; i <= (long) cube.precision; i++)
333        for (j=0; j <= (long) (cube.precision-i); j++)
334          for (k=0; k <= (long) (cube.precision-i-j); k++)
335          {
336            sum=0.0;
337            for (a=0; a <= i; a++)
338              for (b=0; b <= j; b++)
339                for (g=0; g <= k; g++)
340                  sum+=TetrahedralArray(node->phi,a,b,g)*
341                    TetrahedralArray(cube.phi_tilde,i-a,j-b,k-g);
342            *phi+=sum;
343            phi++;
344          }
345    }
346}
347
348/*
349%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
350%                                                                             %
351%                                                                             %
352%                                                                             %
353%  C o m p u t e P s i                                                        %
354%                                                                             %
355%                                                                             %
356%                                                                             %
357%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358%
359%  ComputePsi() forms a local expansion which describes the potential field due
360%  to all particles in the system that are not contained in the current cube or
361%  its near-field.
362%
363%  The format of the ComputePsi routine is:
364%
365%    ComputePsi(register Node *node)
366%
367%  A description of each parameter follows:
368%
369%    o node: Specifies a pointer to a Node structure.
370%
371*/
372static void ComputePsi(register Node *node)
373{
374  double
375    x_distance,
376    y_distance,
377    z_distance;
378
379  long
380    i,
381    j,
382    k,
383    n;
384
385  Node
386    *parent;
387
388  register long
389    a,
390    b,
391    g;
392
393  register double
394    sum;
395
396  register float
397    *ijk,
398    *phi,
399    *psi;
400
401  register Node
402    **interactive_neighbor;
403
404  unsigned long
405    id;
406
407  if (node->level == (cube.depth-1))
408    {
409      /*
410        Form a local expansion by using the `Conversion of Multipole
411        into a Local Expansion' lemma to convert the multipole expansion
412        of each cube in the interaction-field of the current leaf cube
413        to a local expansion about the center of the leaf.
414      */
415      interactive_neighbor=node->interactive_field;
416      while (*interactive_neighbor != (Node *) NULL)
417      {
418        /*
419          Shift multipole expansion of the interactive cube to the
420          center of the current leaf cube.
421        */
422        LocalExpansion(*interactive_neighbor,node,cube.psi_tilde);
423        psi=node->psi;
424        phi=(*interactive_neighbor)->phi;
425        ijk=cube.ijk_factorial;
426        for (i=0; i <= (long) cube.precision; i++)
427          for (j=0; j <= (long) (cube.precision-i); j++)
428            for (k=0; k <= (long) (cube.precision-i-j); k++)
429            {
430              sum=0.0;
431              n=i+j+k;
432              for (a=0; a <= (long) (cube.precision-n); a++)
433                for (b=0; b <= (long) (cube.precision-n-a); b++)
434                  for (g=0; g <= (long) (cube.precision-n-a-b); g++)
435                    sum+=TetrahedralArray(phi,a,b,g)*
436                      TetrahedralArray(cube.psi_tilde,i+a,j+b,k+g);
437              *psi+=sum*(*ijk++);
438              psi++;
439            }
440        interactive_neighbor++;
441      }
442      /*
443        Shift local expansion of the parent cube to the center of the
444        current leaf cube using the `Translation of a Local Expansion'
445        lemma.
446      */
447      parent=node->parent;
448      x_distance=node->mid_x-parent->mid_x;
449      y_distance=node->mid_y-parent->mid_y;
450      z_distance=node->mid_z-parent->mid_z;
451      for (i=0; i <= (long) cube.precision; i++)
452      {
453        cube.x_power[i]=pow(x_distance,(double) i);
454        cube.y_power[i]=pow(y_distance,(double) i);
455        cube.z_power[i]=pow(z_distance,(double) i);
456      }
457      psi=node->psi;
458      ijk=cube.ijk_factorial;
459      for (i=0; i <= (long) cube.precision; i++)
460        for (j=0; j <= (long) (cube.precision-i); j++)
461          for (k=0; k <= (long) (cube.precision-i-j); k++)
462          {
463            sum=0.0;
464            n=i+j+k;
465            for (a=0; a <= (long) (cube.precision-n); a++)
466              for (b=0; b <= (long) (cube.precision-n-a); b++)
467                for (g=0; g <= (long) (cube.precision-n-a-b); g++)
468                  sum+=TetrahedralArray(parent->psi,i+a,j+b,k+g)*
469                    TetrahedralArray(cube.ijk_factorial,a,b,g)*
470                    cube.x_power[a]*cube.y_power[b]*cube.z_power[g];
471            *psi+=sum*(*ijk++);
472            psi++;
473          }
474      EvaluatePotential(node);
475    }
476  else
477    if (node->level > 1)
478      {
479        /*
480          Form a local expansion by using the `Conversion of Multipole
481          into a Local Expansion' lemma to convert the multipole
482          expansion of each cube in the interaction-field of the
483          current cube to a local expansion about the center of the
484          cube.
485        */
486        interactive_neighbor=node->interactive_field;
487        while (*interactive_neighbor != (Node *) NULL)
488        {
489          /*
490            Shift multipole expansion of the interactive cube to the
491            center of the current cube.
492          */
493          LocalExpansion(*interactive_neighbor,node,cube.psi_tilde);
494          psi=node->psi;
495          phi=(*interactive_neighbor)->phi;
496          for (i=0; i <= (long) cube.precision; i++)
497            for (j=0; j <= (long) (cube.precision-i); j++)
498              for (k=0; k <= (long) (cube.precision-i-j); k++)
499              {
500                sum=0.0;
501                n=i+j+k;
502                for (a=0; a <= (long) (cube.precision-n); a++)
503                  for (b=0; b <= (long) (cube.precision-n-a); b++)
504                    for (g=0; g <= (long) (cube.precision-n-a-b); g++)
505                      sum+=TetrahedralArray(phi,a,b,g)*
506                        TetrahedralArray(cube.psi_tilde,i+a,j+b,k+g);
507                *psi+=sum;
508                psi++;
509              }
510          interactive_neighbor++;
511        }
512        /*
513          Shift local expansion of the parent cube to the center of the
514          current node using the `Translation of a Local Expansion'.
515        */
516        parent=node->parent;
517        x_distance=node->mid_x-parent->mid_x;
518        y_distance=node->mid_y-parent->mid_y;
519        z_distance=node->mid_z-parent->mid_z;
520        for (i=0; i <= (long) cube.precision; i++)
521        {
522          cube.x_power[i]=pow(x_distance,(double) i);
523          cube.y_power[i]=pow(y_distance,(double) i);
524          cube.z_power[i]=pow(z_distance,(double) i);
525        }
526        psi=node->psi;
527        for (i=0; i <= (long) cube.precision; i++)
528          for (j=0; j <= (long) (cube.precision-i); j++)
529            for (k=0; k <= (long) (cube.precision-i-j); k++)
530            {
531              sum=0.0;
532              n=i+j+k;
533              for (a=0; a <= (long) (cube.precision-n); a++)
534                for (b=0; b <= (long) (cube.precision-n-a); b++)
535                  for (g=0; g <= (long) (cube.precision-n-a-b); g++)
536                    sum+=TetrahedralArray(parent->psi,i+a,j+b,k+g)*
537                      TetrahedralArray(cube.ijk_factorial,a,b,g)*
538                      cube.x_power[a]*cube.y_power[b]*cube.z_power[g];
539              *psi+=sum;
540              psi++;
541            }
542      }
543  /*
544    Recursively descend the particle cube.
545  */
546  if (node->level < (cube.depth-1))
547    for (id=0; id < MaxChildren; id++)
548      ComputePsi(node->child[id]);
549}
550
551/*
552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553%                                                                             %
554%                                                                             %
555%                                                                             %
556%  C r e a t e C u b e                                                        %
557%                                                                             %
558%                                                                             %
559%                                                                             %
560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561%
562%  CreateCube() creates all levels of the particle cube.
563%
564%  The format of the CreateCube routine is:
565%
566%    CreateCube(register Node *node)
567%
568%  A description of each parameter follows:
569%
570%    o node: Specifies a pointer to a Node structure.
571%
572*/
573static void CreateCube(register Node *node)
574{
575  register double
576    bisect;
577
578  register unsigned long
579    id,
580    level;
581
582  /*
583    Create a child cube and recursively descend to the next level.
584  */
585  level=node->level+1;
586  bisect=cube.edge_length[level]*0.5;
587  for (id=0; id < MaxChildren; id++)
588  {
589    node->child[id]=InitializeNode(id,level,node,
590      node->mid_x+(id & 1 ? bisect : -bisect),
591      node->mid_y+(id & 2 ? bisect : -bisect),
592      node->mid_z+(id & 4 ? bisect : -bisect));
593    if (node->child[id] == (Node *) NULL)
594      Error("unable to allocate memory",(char *) NULL);
595    if (level < (cube.depth-1))
596      CreateCube(node->child[id]);
597  }
598}
599
600/*
601%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
602%                                                                             %
603%                                                                             %
604%                                                                             %
605%  D e f i n e I n t e r a c t i v e F i e l d                                %
606%                                                                             %
607%                                                                             %
608%                                                                             %
609%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
610%
611%  DefineInteractiveField() assigns the near-field of a particular cube.  The
612%  interaction-field of a node is the set of nodes which are children of the
613%  nodes in the near-field of it parent and which are not in the near-field of
614%  itself.  If all eight children of a parent node is in the interaction-field,
615%  they are replaced by their common parent node (super_node).
616%
617%  The format of the DefineInteractiveField routine is:
618%
619%    DefineInteractiveField(node)
620%
621%  A description of each parameter follows:
622%
623%    o node: Specifies a pointer to a Node structure.
624%
625*/
626static void DefineInteractiveField(register Node *node)
627{
628  register Node
629    **interactive_neighbor,
630    **near_neighbor;
631
632  register unsigned long
633    id,
634    super_node;
635
636  /*
637    Recursively descend the particle cube.
638  */
639  if (node->level < (cube.depth-1))
640    for (id=0; id < MaxChildren; id++)
641      DefineInteractiveField(node->child[id]);
642  if (node->level > 0)
643    {
644      /*
645        Nodes in the parents near-field are candidates for the interactive
646        field.
647      */
648      interactive_neighbor=node->interactive_field;
649      near_neighbor=node->parent->near_field;
650      while (*near_neighbor != (Node *) NULL)
651      {
652        super_node=True;
653        for (id=0; id < MaxChildren; id++)
654          if (InNearField((*near_neighbor)->child[id],node))
655            super_node=False;
656          else
657            {
658              *interactive_neighbor=(*near_neighbor)->child[id];
659              interactive_neighbor++;
660            }
661        super_node=False;  /* for our benchmark, turn supernode off */
662        if (super_node)
663          {
664            /*
665              Supernode:  All 8 children represented by this node.
666            */
667            interactive_neighbor-=MaxChildren;
668            *interactive_neighbor=(*near_neighbor);
669            interactive_neighbor++;
670          }
671        near_neighbor++;
672      }
673      *interactive_neighbor=(Node *) NULL;
674    }
675}
676
677/*
678%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
679%                                                                             %
680%                                                                             %
681%                                                                             %
682%  D e f i n e N e a r F i e l d                                              %
683%                                                                             %
684%                                                                             %
685%                                                                             %
686%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687%
688%  DefineNearField() assigns the near-field of a particular cube.  The
689%  near-field of a node is the set of nodes at the same level of refinement
690%  which shares a boundary point with it.
691%
692%  The format of the DefineNearField routine is:
693%
694%    DefineNearField(node)
695%
696%  A description of each parameter follows:
697%
698%    o node: Specifies a pointer to a Node structure.
699%
700*/
701static void DefineNearField(register Node *node)
702{
703  register unsigned long
704    id;
705
706  /*
707    Recursively descend the particle cube.
708  */
709  if (node->level < (cube.depth-1))
710    for (id=0; id < MaxChildren; id++)
711      DefineNearField(node->child[id]);
712  if (node->level > 0)
713    {
714      /*
715        Find the near neighbors of this particular node.
716      */
717      cube.near_neighbor=node->near_field;
718      FindNeighbors(cube.root,node);
719      *cube.near_neighbor=(Node *) NULL;
720    }
721}
722
723/*
724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725%                                                                             %
726%                                                                             %
727%                                                                             %
728%   E r r o r                                                                 %
729%                                                                             %
730%                                                                             %
731%                                                                             %
732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733%
734%  Error() displays an error message and then terminates the program.
735%
736%  The format of the Error routine is:
737%
738%      Error(char *message,char *qualifier)
739%
740%  A description of each parameter follows:
741%
742%    o message: Specifies the message to display before terminating the
743%      program.
744%
745%    o qualifier: Specifies any qualifier to the message.
746%
747*/
748static void Error(char *message,char *qualifier)
749{
750  (void) fprintf(stderr,"%s: %s",application_name,message);
751  if (qualifier != (char *) NULL)
752    (void) fprintf(stderr," (%s)",qualifier);
753  (void) fprintf(stderr,".\n");
754  exit(1);
755}
756
757/*
758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759%                                                                             %
760%                                                                             %
761%                                                                             %
762%  E v a l u a t e P o t e n t i a l                                          %
763%                                                                             %
764%                                                                             %
765%                                                                             %
766%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
767%
768%  EvaluatePotential() evaluates the local expansions at the particle position
769%  to determine the far field potential.  The direct near-field potential is
770%  summed to give the total potential.
771%
772%  The format of the EvaluatePotential routine is:
773%
774%    EvaluatePotential(register Node *node)
775%
776%  A description of each parameter follows:
777%
778%    o node: Specifies a pointer to a Node structure.
779%
780*/
781static void EvaluatePotential(register Node *node)
782{
783  double
784    distance_squared,
785    far_potential,
786    near_potential,
787    x_distance,
788    y_distance,
789    z_distance;
790
791  register float
792    *psi;
793
794  register long
795    i,
796    j,
797    k;
798
799  register Node
800    **near_neighbor;
801
802  register Particle
803    *p,
804    *q;
805
806  far_potential=0.0;
807  near_potential=0.0;
808  for (p=node->particle; p != (Particle *) NULL; p=p->next)
809  {
810    /*
811      Evaluate local expansions at particle positions.
812    */
813    x_distance=p->x-node->mid_x;
814    y_distance=p->y-node->mid_y;
815    z_distance=p->z-node->mid_z;
816    for (i=0; i <= (long) cube.precision; i++)
817    {
818      cube.x_power[i]=pow(x_distance,(double) i);
819      cube.y_power[i]=pow(y_distance,(double) i);
820      cube.z_power[i]=pow(z_distance,(double) i);
821    }
822    psi=node->psi;
823    for (i=0; i <= (long) cube.precision; i++)
824      for (j=0; j <= (long) (cube.precision-i); j++)
825        for (k=0; k <= (long) (cube.precision-i-j); k++)
826        {
827          far_potential+=(*psi)*cube.x_power[i]*cube.y_power[j]*cube.z_power[k];
828          psi++;
829        }
830    /*
831      Compute potential due to particles in current cube directly.  Use
832      Newton's third law to reduce the number of pairwise interactions.
833    */
834    for (q=p->next; q != (Particle *) NULL; q=q->next)
835    {
836      x_distance=p->x-q->x;
837      y_distance=p->y-q->y;
838      z_distance=p->z-q->z;
839      distance_squared=x_distance*x_distance+y_distance*y_distance+
840        z_distance*z_distance;
841      near_potential+=1.0/sqrt(distance_squared);
842    }
843    /*
844      Compute potential due to particles in near-field directly.
845    */
846    near_neighbor=node->near_field;
847    while (*near_neighbor != (Node *) NULL)
848    {
849      for (q=(*near_neighbor)->particle; q != (Particle *) NULL; q=q->next)
850      {
851        if (p > q)
852          continue/* canonical form */
853        x_distance=p->x-q->x;
854        y_distance=p->y-q->y;
855        z_distance=p->z-q->z;
856        distance_squared=x_distance*x_distance+y_distance*y_distance+
857          z_distance*z_distance;
858        near_potential+=1.0/sqrt(distance_squared);
859      }
860      near_neighbor++;
861    }
862  }
863  cube.near_potential+=2.0*GravitationalConstant*near_potential;
864  cube.far_potential+=GravitationalConstant*far_potential;
865}
866
867/*
868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869%                                                                             %
870%                                                                             %
871%                                                                             %
872%  F i n d N e i g h b o r s                                                  %
873%                                                                             %
874%                                                                             %
875%                                                                             %
876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877%
878%  FindNeighbors() determines which nodes in the particle cube are in the near-
879%  field of the reference node.  The near-field of a cube is defined as
880%  consisiting of those cubes that are on the same level as the cube, and have
881%  a distance to the center of the cube less than sqrt(3) of the cube edge
882%  length.
883%
884%  The format of the FindNeighbors routine is:
885%
886%    FindNeighbors(register Node *node,register Node *reference_node)
887%
888%  A description of each parameter follows:
889%
890%    o node: Specifies a pointer to a Node structure.
891%
892%    o reference_node: Specifies a pointer to a Node structure.
893%
894*/
895static void FindNeighbors(register Node *node,register Node *reference_node)
896{
897  register double
898    distance_squared;
899
900  register double
901    x_distance,
902    y_distance,
903    z_distance;
904
905  unsigned long
906    id;
907
908  /*
909    Recursively descend the particle cube.
910  */
911  if (node->level < reference_node->level)
912    for (id=0; id < MaxChildren; id++)
913      FindNeighbors(node->child[id],reference_node);
914  if (node->level == reference_node->level)
915    if (node != reference_node)
916      {
917        /*
918          Determine if this node is within the specified distance of the
919          reference node.
920        */
921        x_distance=reference_node->mid_x-node->mid_x;
922        y_distance=reference_node->mid_y-node->mid_y;
923        z_distance=reference_node->mid_z-node->mid_z;
924        distance_squared=x_distance*x_distance+y_distance*y_distance+
925          z_distance*z_distance;
926        if (distance_squared <= cube.diagonal_length[node->level])
927          {
928            *cube.near_neighbor=node;
929            cube.near_neighbor++;
930          }
931      }
932}
933
934/*
935%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
936%                                                                             %
937%                                                                             %
938%                                                                             %
939%  I n i t i a l i z e N o d e                                                %
940%                                                                             %
941%                                                                             %
942%                                                                             %
943%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
944%
945%  InitializeNode() allocates memory for a new node in the color cube treee and
946%  presets all fields to zero.
947%
948%  The format of the InitializeNode routine is:
949%
950%      node=InitializeNode(unsigned long id,unsigned long level,Node *parent,
951%        double mid_x,double mid_y,double mid_z)
952%
953%  A description of each parameter follows.
954%
955%    o node: The InitializeNode routine returns this integer address.
956%
957%    o id: Specifies the child number of the node.
958%
959%    o level: Specifies the level in the particle cube the node resides.
960%
961%    o parent: Specifies the parent of the initialized node.
962%
963%    o mid_x: Specifies the mid point of the x extent for this node.
964%
965%    o mid_y: Specifies the mid point of the y extent for this node.
966%
967%    o mid_z: Specifies the mid point of the z extent for this node.
968%
969*/
970static Node *InitializeNode(unsigned long id,unsigned long level,Node *parent,
971  double mid_x,double mid_y,double mid_z)
972{
973  register float
974    *phi,
975    *psi;
976
977  register long
978    i;
979
980  register Node
981    *node;
982
983  if (cube.free_nodes == 0)
984    {
985      Nodes
986        *nodes;
987
988      /*
989        Allocate a queue of nodes.
990      */
991      nodes=(Nodes *) malloc(sizeof(Nodes));
992      if (nodes == (Nodes *) NULL)
993        return((Node *) NULL);
994      nodes->phi=(float *)
995        malloc(NodesInAQueue*cube.coefficients*sizeof(float));
996      if (nodes->phi == (float *) NULL)
997        return((Node *) NULL);
998      nodes->psi=(float *)
999        malloc(NodesInAQueue*cube.coefficients*sizeof(float));
1000      if (nodes->phi == (float *) NULL)
1001        return((Node *) NULL);
1002      nodes->next=cube.node_queue;
1003      cube.node_queue=nodes;
1004      cube.next_node=nodes->nodes;
1005      cube.free_nodes=NodesInAQueue;
1006      cube.phi=nodes->phi;
1007      cube.psi=nodes->psi;
1008    }
1009  /*
1010    Initialize node fields.
1011  */
1012  node=cube.next_node;
1013  node->parent=parent;
1014  for (i=0; i < MaxChildren; i++)
1015    node->child[i]=(Node *) NULL;
1016  node->near_field[0]=(Node *) NULL;
1017  node->interactive_field[0]=(Node *) NULL;
1018  node->id=id;
1019  node->level=level;
1020  node->mid_x=mid_x;
1021  node->mid_y=mid_y;
1022  node->mid_z=mid_z;
1023  node->phi=cube.phi;
1024  node->psi=cube.psi;
1025  /*
1026    Set multipole and local expansions to zero.
1027  */
1028  phi=node->phi;
1029  psi=node->psi;
1030  for (i=0; i < (long) cube.coefficients; i++)
1031  {
1032    *phi++=0.0;
1033    *psi++=0.0;
1034  }
1035  node->particle=(Particle *) NULL;
1036  /*
1037    Prepare for next node.
1038  */
1039  cube.nodes++;
1040  cube.free_nodes--;
1041  cube.next_node++;
1042  cube.phi+=cube.coefficients;
1043  cube.psi+=cube.coefficients;
1044  return(node);
1045}
1046
1047/*
1048%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1049%                                                                             %
1050%                                                                             %
1051%                                                                             %
1052%  I n N e a r F i e l d                                                      %
1053%                                                                             %
1054%                                                                             %
1055%                                                                             %
1056%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1057%
1058%  InNearField() returns True if the specified node is a member of the
1059%  near-field.
1060%
1061%  The format of the InNearField routine is:
1062%
1063%    InNearField(register Node *reference_node,register Node *node)
1064%
1065%  A description of each parameter follows:
1066%
1067%    o reference_node: Specifies a pointer to a Node structure.
1068%
1069%    o node: Specifies a pointer to a Node structure.
1070%
1071*/
1072static unsigned long InNearField(register Node *reference_node,
1073  register Node *node)
1074{
1075  register Node
1076    **near_neighbor;
1077
1078  if (reference_node == node)
1079    return(True);
1080  /*
1081    Search the near-field for the specified node.
1082  */
1083  near_neighbor=node->near_field;
1084  while (*near_neighbor != (Node *) NULL)
1085  {
1086    if (*near_neighbor == reference_node)
1087      return(True);
1088    near_neighbor++;
1089  }
1090  return(False);
1091}
1092
1093/*
1094%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1095%                                                                             %
1096%                                                                             %
1097%                                                                             %
1098%  L o c a l E x p a n s i o n                                                %
1099%                                                                             %
1100%                                                                             %
1101%                                                                             %
1102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103%
1104%  LocalExpansion() forms a local expansion which describes the potential field
1105%  due to all particles in the system that are not contained in the current
1106%  cube or its near-field.
1107%
1108%  The format of the LocalExpansion routine is:
1109%
1110%    LocalExpansion(Node *interactive_neighbor,Node *node,float *psi_tilde)
1111%
1112%  A description of each parameter follows:
1113%
1114%    o interactive_neighbor: Specifies a pointer to a Node structure.
1115%
1116%    o node: Specifies a pointer to a Node structure.
1117%
1118%    o psi_tilde: Specifies a pointer to an array of floats representing the
1119%      local expansion for this node.
1120%
1121*/
1122static void LocalExpansion(Node *interactive_neighbor,Node *node,
1123 float *psi_tilde)
1124{
1125  double
1126    distance,
1127    tau_x,
1128    tau_y,
1129    tau_z,
1130    x_distance,
1131    y_distance,
1132    z_distance;
1133
1134  long
1135    i,
1136    j,
1137    k,
1138    n;
1139
1140  register double
1141    sum;
1142
1143  register float
1144    *ijk;
1145
1146  register long
1147    a,
1148    b,
1149    g;
1150
1151  static float
1152    r_zero[HighestDegreeHarmonic+1];
1153
1154  /*
1155    Initialize Psi' as specified by Theorem 3.2.2.
1156  */
1157  x_distance=interactive_neighbor->mid_x-node->mid_x;
1158  y_distance=interactive_neighbor->mid_y-node->mid_y;
1159  z_distance=interactive_neighbor->mid_z-node->mid_z;
1160  distance=sqrt(x_distance*x_distance+y_distance*y_distance+
1161    z_distance*z_distance);
1162  tau_x=2.0*(x_distance/distance);
1163  tau_y=2.0*(y_distance/distance);
1164  tau_z=2.0*(z_distance/distance);
1165  for (i=0; i <= (long) cube.precision; i++)
1166  {
1167    cube.x_power[i]=pow(tau_x,(double) i);
1168    cube.y_power[i]=pow(tau_y,(double) i);
1169    cube.z_power[i]=pow(tau_z,(double) i);
1170    r_zero[i]=1.0/pow(distance,(double) i+1);
1171  }
1172  ijk=cube.ijk_factorial;
1173  for (i=0; i <= (long) cube.precision; i++)
1174    for (j=0; j <= (long) (cube.precision-i); j++)
1175      for (k=0; k <= (long) (cube.precision-i-j); k++)
1176      {
1177        sum=0.0;
1178        for (a=0; a <= (i/2); a++)
1179          for (b=0; b <= (j/2); b++)
1180            for (g=0; g <= (k/2); g++)
1181              sum+=TetrahedralArray(cube.ijk_binomial,i-a,j-b,k-g)*
1182                cube.binomial[i-a][a]*cube.binomial[j-b][b]*
1183                cube.binomial[k-g][g]*cube.x_power[i-2*a]*cube.y_power[j-2*b]*
1184                cube.z_power[k-2*g];
1185        n=i+j+k;
1186        sum*=cube.one_power[n]*r_zero[n]/(*ijk++);
1187        *psi_tilde++=sum;
1188      }
1189}
1190
1191/*
1192%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1193%                                                                             %
1194%                                                                             %
1195%                                                                             %
1196%  M u l t i p o l e E x p a n s i o n                                        %
1197%                                                                             %
1198%                                                                             %
1199%                                                                             %
1200%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1201%
1202%  MultipoleExpansion() forms the multipole expansions of the potential field
1203%  due to particle in each node.
1204%
1205%  The format of the MultipoleExpansion routine is:
1206%
1207%    MultipoleExpansion(Node *node,Node *parent,register float *phi_tilde)
1208%
1209%  A description of each parameter follows:
1210%
1211%    o node: Specifies a pointer to a Node structure.
1212%
1213%    o parent: Specifies a pointer to a Node structure.
1214%
1215%    o phi_tilde: Specifies a pointer to an array of floats representing the
1216%      multipole expansion for this node.
1217%
1218*/
1219static void MultipoleExpansion(Node *node,Node *parent,
1220  register float *phi_tilde)
1221{
1222  double
1223    x_distance,
1224    y_distance,
1225    z_distance;
1226
1227  register float
1228    *ijk;
1229
1230  register long
1231    i,
1232    j,
1233    k;
1234
1235  /*
1236    Initialize Phi' as specified by Theorem 3.2.1.
1237  */
1238  x_distance=node->mid_x-parent->mid_x;
1239  y_distance=node->mid_y-parent->mid_y;
1240  z_distance=node->mid_z-parent->mid_z;
1241  for (i=0; i <= (long) cube.precision; i++)
1242  {
1243    cube.x_power[i]=pow(-x_distance,(double) i);
1244    cube.y_power[i]=pow(-y_distance,(double) i);
1245    cube.z_power[i]=pow(-z_distance,(double) i);
1246  }
1247  ijk=cube.ijk_factorial;
1248  for (i=0; i <= (long) cube.precision; i++)
1249    for (j=0; j <= (long) (cube.precision-i); j++)
1250      for (k=0; k <= (long) (cube.precision-i-j); k++)
1251        *phi_tilde++=cube.x_power[i]*cube.y_power[j]*cube.z_power[k]*(*ijk++);
1252}
1253
1254/*
1255%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1256%                                                                             %
1257%                                                                             %
1258%                                                                             %
1259%  M u l t i p o l e P o t e n t i a l                                        %
1260%                                                                             %
1261%                                                                             %
1262%                                                                             %
1263%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1264%
1265%  MultipolePotential() calculates the interaction potentials of a number of
1266%  particles in O(N).
1267%
1268%  The format of the MultipolePotential routine is:
1269%
1270%    potential=MultipolePotential(Particle *particles,
1271%      unsigned long number_particles,unsigned long precision,
1272%      unsigned long tree_depth,double minimum_extent,double maximum_extent)
1273%
1274%  A description of each parameter follows:
1275%
1276%    o particles: Specifies a pointer to an array of Particles structures.
1277%
1278%    o number_particles: Specifies the number of particles in the
1279%      particles array.
1280%
1281%    o precision: Specifies the number of terms to use in the multipole
1282%      and local expansions.
1283%
1284%    o tree_depth: Normally, this integer value is zero or one.  A zero
1285%      tells MultipolePotential to choose a optimal tree depth of
1286%      Log8(number_particles).
1287%
1288%    o minimum_extent: Specifies the minumum of the cube extent.
1289%
1290%    o maximum_extent: Specifies the maximum of the cube extent.
1291%
1292*/
1293static double MultipolePotential(Particle *particles,
1294  unsigned long number_particles,unsigned long precision,
1295  unsigned long tree_depth,double minimum_extent,double maximum_extent)
1296{
1297  double
1298    factorial[HighestDegreeHarmonic+1];
1299
1300  long
1301    count,
1302    start_time;
1303
1304  Nodes
1305    *nodes;
1306
1307  register double
1308    sum;
1309
1310  register float
1311    *ijk,
1312    *ijk_binomial;
1313
1314  register long
1315    i,
1316    j,
1317    k,
1318    n;
1319
1320  unsigned long
1321    level;
1322
1323  (void) fprintf(stderr,"Particles: %lu\n",number_particles);
1324  cube.precision=precision;
1325  if (cube.precision > HighestDegreeHarmonic)
1326    cube.precision=HighestDegreeHarmonic;
1327  (void) fprintf(stderr,"Precision: %lu\n",cube.precision);
1328  cube.coefficients=Binomial((int) cube.precision+3,3);
1329  (void) fprintf(stderr,"Coefficients: %lu\n",cube.coefficients);
1330  /*
1331    Initialize ijk factorial table.
1332  */
1333  factorial[0]=1.0;
1334  sum=1.0;
1335  for (i=1; i <= (long) cube.precision; i++)
1336  {
1337    sum*=(double) i;
1338    factorial[i]=sum;
1339  }
1340  cube.ijk_factorial=(float *) malloc(cube.coefficients*sizeof(float));
1341  if (cube.ijk_factorial == (float *) NULL)
1342    Error("unable to allocate memory",(char *) NULL);
1343  ijk=cube.ijk_factorial;
1344  for (i=0; i <= (long) cube.precision; i++)
1345    for (j=0; j <= (long) (cube.precision-i); j++)
1346      for (k=0; k <= (long) (cube.precision-i-j); k++)
1347        *ijk++=1.0/(factorial[i]*factorial[j]*factorial[k]);
1348  /*
1349    Initialize ijk binomial table: [(-1/2)*(-1/2-1)*(-1/2-2)*...].
1350  */
1351  factorial[0]=1.0;
1352  sum=1.0;
1353  for (i=1; i <= (long) cube.precision; i++)
1354  {
1355    sum*=(-0.5-(double) i+1.0);
1356    factorial[i]=sum;
1357  }
1358  cube.ijk_binomial=(float *) malloc(cube.coefficients*sizeof(float));
1359  if (cube.ijk_binomial == (float *) NULL)
1360    Error("unable to allocate memory",(char *) NULL);
1361  ijk_binomial=cube.ijk_binomial;
1362  ijk=cube.ijk_factorial;
1363  for (i=0; i <= (long) cube.precision; i++)
1364    for (j=0; j <= (long) (cube.precision-i); j++)
1365      for (k=0; k <= (long) (cube.precision-i-j); k++)
1366        *ijk_binomial++=factorial[i+j+k]*(*ijk++);
1367  /*
1368    Initialize binomial coefficient table.
1369  */
1370  for (n=0; n <= (long) cube.precision; n++)
1371    for (k=0; k <= (long) cube.precision; k++)
1372      cube.binomial[n][k]=(float) Binomial(n,k);
1373  /*
1374    Initialize power of one lookup table.
1375  */
1376  for (i=0; i <= (long) cube.precision; i++)
1377    cube.one_power[i]=pow((double) -1.0,(double) i);
1378  /*
1379    Initialize Tetrahedral cubic array lookup table.
1380  */
1381  for (i=0; i <= (long) (cube.precision+2); i++)
1382    cube.tetra_three[i]=cube.coefficients-Binomial((int) cube.precision-i+2,3);
1383  for (i=0; i <= (long) (cube.precision+2); i++)
1384    cube.tetra_two[i]=Binomial((int) cube.precision-i+2,2);
1385  /*
1386    Allocate Phi' & Psi'.
1387  */
1388  cube.phi_tilde=(float *) malloc(cube.coefficients*sizeof(float));
1389  cube.psi_tilde=(float *) malloc(cube.coefficients*sizeof(float));
1390  if ((cube.phi_tilde == (float *) NULL) ||
1391      (cube.psi_tilde == (float *) NULL))
1392    Error("unable to allocate memory",(char *) NULL);
1393  cube.near_potential=0.0;
1394  cube.far_potential=0.0;
1395  /*
1396    Choose a level of refinement:  depth is log8(number_particles)-1.
1397  */
1398  if (tree_depth > 1)
1399    cube.depth=Min(tree_depth,8);
1400  else
1401    {
1402      cube.depth=0;
1403      count=(long) (number_particles >> 3);
1404      do
1405      {
1406        count>>=3;
1407        cube.depth++;
1408      }
1409      while (count > 0);
1410      if (cube.depth < 1)
1411        cube.depth=1;
1412    }
1413  (void) fprintf(stderr,"Cube depth: %lu\n",cube.depth);
1414  /*
1415    Initialize edge length table.
1416  */
1417  (void) fprintf(stderr,"Cube lower left front vertex: %.8g %.8g %.8g\n",
1418    minimum_extent,minimum_extent,minimum_extent);
1419  (void) fprintf(stderr,"Cube upper right behind vertex: %.8g %.8g %.8g\n",
1420    maximum_extent,maximum_extent,maximum_extent);
1421  cube.edge_length[0]=maximum_extent-minimum_extent;
1422  cube.diagonal_length[0]=2.0*CircumscribedSphereRadius*cube.edge_length[0]*
1423    cube.edge_length[0];
1424  for (level=1; level < cube.depth; level++)
1425  {
1426    cube.edge_length[level]=cube.edge_length[level-1]*0.5;
1427    cube.diagonal_length[level]=2.0*CircumscribedSphereRadius*
1428      cube.edge_length[level]*cube.edge_length[level];
1429  }
1430  /*
1431    Allocate particle description tree.
1432  */
1433  cube.node_queue=(Nodes *) NULL;
1434  cube.nodes=0;
1435  cube.free_nodes=0;
1436  cube.root=InitializeNode(0,0,(Node *) NULL,
1437    minimum_extent+cube.edge_length[0]*0.5,
1438    minimum_extent+cube.edge_length[0]*0.5,
1439    minimum_extent+cube.edge_length[0]*0.5);
1440  if (cube.root == (Node *) NULL)
1441    Error("unable to allocate memory",(char *) NULL);
1442  cube.root->parent=cube.root;
1443  /*
1444    Multibody steps:
1445  */
1446  (void) fprintf(stderr,"CreateCube: ");
1447  start_time=ProcessTime();
1448  CreateCube(cube.root);
1449  (void) fprintf(stderr,"%lds\n",ProcessTime()-start_time);
1450  (void) fprintf(stderr,"Classification: ");
1451  start_time=ProcessTime();
1452  Classification(particles,number_particles);
1453  (void) fprintf(stderr,"%lds\n",ProcessTime()-start_time);
1454  (void) fprintf(stderr,"DefineNearField: ");
1455  start_time=ProcessTime();
1456  DefineNearField(cube.root);
1457  (void) fprintf(stderr,"%lds\n",ProcessTime()-start_time);
1458  (void) fprintf(stderr,"DefineInteractiveField: ");
1459  start_time=ProcessTime();
1460  DefineInteractiveField(cube.root);
1461  (void) fprintf(stderr,"%lds\n",ProcessTime()-start_time);
1462  (void) fprintf(stderr,"ComputePhi: ");
1463  start_time=ProcessTime();
1464  ComputePhi(cube.root);
1465  (void) fprintf(stderr,"%lds\n",ProcessTime()-start_time);
1466  (void) fprintf(stderr,"ComputePsi: ");
1467  start_time=ProcessTime();
1468  ComputePsi(cube.root);
1469  (void) fprintf(stderr,"%lds\n",ProcessTime()-start_time);
1470  (void) fprintf(stderr,"Near: %.8g\nFar: %.8g\n",cube.near_potential,
1471    cube.far_potential);
1472  /*
1473    Release N-body cube tree storage.
1474  */
1475  do
1476  {
1477    nodes=cube.node_queue->next;
1478    (void) free((char *) cube.node_queue->phi);
1479    (void) free((char *) cube.node_queue->psi);
1480    (void) free((char *) cube.node_queue);
1481    cube.node_queue=nodes;
1482  }
1483  while (cube.node_queue != (Nodes *) NULL);
1484  (void) free((char *) cube.ijk_factorial);
1485  (void) free((char *) cube.ijk_binomial);
1486  (void) free((char *) cube.phi_tilde);
1487  (void) free((char *) cube.psi_tilde);
1488  return(cube.near_potential+cube.far_potential);
1489}
1490
1491/*
1492%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493%                                                                             %
1494%                                                                             %
1495%                                                                             %
1496%  N a i v e P o t e n t i a l                                                %
1497%                                                                             %
1498%                                                                             %
1499%                                                                             %
1500%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501%
1502%  NaivePotential() calculates the interaction potentials of a number of
1503%  particles using the naive direct method.
1504%
1505%  The format of the NaivePotential routine is:
1506%
1507%    potential=NaivePotential(Particle *particles,
1508%      long number_particles)
1509%
1510%  A description of each parameter follows:
1511%
1512%    o particles: Specifies a pointer to an array of Particles structures.
1513%
1514%    o number_particles: Specifies the number of particles in the
1515%      particles array.
1516%
1517*/
1518static double NaivePotential(Particle *particles,unsigned long number_particles)
1519{
1520  double
1521    distance_squared,
1522    total_potential,
1523    x_distance,
1524    y_distance,
1525    z_distance;
1526
1527  register int
1528    i,
1529    j;
1530
1531  register Particle
1532    *p,
1533    *q;
1534
1535  /*
1536    Calculate gravitational total potential.  Use Newton's third law to
1537    reduce the number of pairwise interactions.
1538  */
1539  total_potential=0.0;
1540  p=particles;
1541  for (i=0; i < (long) number_particles; i++)
1542  {
1543    q=p;
1544    for (j=(i+1); j < (long) number_particles; j++)
1545    {
1546      q++;
1547      x_distance=p->x-q->x;
1548      y_distance=p->y-q->y;
1549      z_distance=p->z-q->z;
1550      distance_squared=x_distance*x_distance+y_distance*y_distance+
1551        z_distance*z_distance;
1552      total_potential+=1.0/sqrt(distance_squared);
1553    }
1554    p++;
1555  }
1556  return(2.0*GravitationalConstant*total_potential);
1557}
1558
1559/*
1560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561%                                                                             %
1562%                                                                             %
1563%                                                                             %
1564%   P r o c e s s T i m e                                                     %
1565%                                                                             %
1566%                                                                             %
1567%                                                                             %
1568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1569%
1570%  ProcessTime() returns the number of processor seconds our program has
1571%  consumed.
1572%
1573%  The format of the ProcessTime routine is:
1574%
1575%      seconds=ProcessTime(void)
1576%
1577%  A description of each parameter follows:
1578%
1579%    o seconds: return the number of processor seconds our program has consumed.
1580%
1581*/
1582static long ProcessTime(void)
1583{
1584#if defined(mac)
1585   return((long) time(0));
1586#elif defined(WIN32)
1587   return(GetTickCount()/1000);
1588#else
1589#include <sys/times.h>
1590#include <limits.h>
1591
1592#ifndef CLK_TCK
1593#define CLK_TCK sysconf(_SC_CLK_TCK)
1594#endif
1595
1596  struct tms
1597    usage;
1598
1599  (void) times(&usage);
1600  return((long) (usage.tms_utime/CLK_TCK));
1601#endif
1602}
1603
1604/*
1605%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1606%                                                                             %
1607%                                                                             %
1608%                                                                             %
1609%   M a i n                                                                   %
1610%                                                                             %
1611%                                                                             %
1612%                                                                             %
1613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1614%
1615%
1616*/
1617int main(argc,argv)
1618int
1619  argc;
1620
1621char
1622  **argv;
1623{
1624#define MinRange  0.0
1625#define MaxRange  255.9999999
1626
1627  double
1628    multipole_potential,
1629    maximum_extent,
1630    minimum_extent,
1631    naive_potential;
1632
1633  long
1634    start_time;
1635
1636  Particle
1637    *particles;
1638
1639  register long
1640    i;
1641
1642  unsigned long
1643    number_particles,
1644    precision;
1645
1646  /*
1647    Allocate particle array.
1648  */
1649  application_name=argv[0];
1650  number_particles=4095;
1651  if (argc > 1)
1652    number_particles=(unsigned long) atol(argv[1]);
1653  precision=3;
1654  if (argc > 2)
1655    precision=(unsigned long) atol(argv[2]);
1656  particles=(Particle *) malloc(number_particles*sizeof(Particle));
1657  if (particles == (Particle *) NULL)
1658    Error("unable to allocate memory",(char *) NULL);
1659  /*
1660    Read particles positions.
1661  */
1662  srand(30428877);  /* must be fixed to obtain proper benchmarked results */
1663  minimum_extent=MaxRange;
1664  maximum_extent=MinRange;
1665  for (i=0; i < (long) number_particles; i++)
1666  {
1667    /*
1668      Define vertexes of the particle cube.
1669    */
1670    particles[i].x=(rand()*((MaxRange-MinRange)+MinRange))/RAND_MAX;
1671    particles[i].y=(rand()*((MaxRange-MinRange)+MinRange))/RAND_MAX;
1672    particles[i].z=(rand()*((MaxRange-MinRange)+MinRange))/RAND_MAX;
1673    if (particles[i].x < minimum_extent)
1674      minimum_extent=particles[i].x;
1675    if (particles[i].y < minimum_extent)
1676      minimum_extent=particles[i].y;
1677    if (particles[i].z < minimum_extent)
1678      minimum_extent=particles[i].z;
1679    if (particles[i].x > maximum_extent)
1680      maximum_extent=particles[i].x;
1681    if (particles[i].y > maximum_extent)
1682      maximum_extent=particles[i].y;
1683    if (particles[i].z > maximum_extent)
1684      maximum_extent=particles[i].z;
1685  }
1686  minimum_extent=floor(minimum_extent);
1687  maximum_extent=ceil(maximum_extent);
1688  start_time=ProcessTime();
1689  multipole_potential=MultipolePotential(particles,number_particles,precision,
1690    0,minimum_extent,maximum_extent);
1691  (void) fprintf(stderr,"The multipole-expansion potential is %.8g (%lds).\n",
1692    multipole_potential,ProcessTime()-start_time);
1693  start_time=ProcessTime();
1694  naive_potential=NaivePotential(particles,number_particles);
1695  (void) fprintf(stderr,"The naive potential is %.8g (%lds).\n",
1696    naive_potential,ProcessTime()-start_time);
1697  (void) fprintf(stderr,"The expansion error: %.8g\n\n",
1698    AbsoluteValue(multipole_potential-naive_potential));
1699  (void) free((char *) particles);
1700  return(False);
1701}
Note: See TracBrowser for help on using the browser.