View Javadoc
1   package ffx.numerics.multipole;
2   
3   import java.util.HashMap;
4   
5   import static ffx.numerics.math.ScalarMath.binomial;
6   import static java.lang.String.format;
7   
8   public class MultipoleUtilities {
9   
10  
11    /**
12     * Returns the number of tensors for derivatives to the given order.
13     *
14     * @param order maximum number of derivatives.
15     * @return the number of tensors.
16     * @since 1.0
17     */
18    public static int tensorCount(int order) {
19      long ret = binomial(order + 3, 3);
20      assert (ret < Integer.MAX_VALUE);
21      return (int) ret;
22    }
23  
24    /**
25     * Convenience method for writing out tensor indices.
26     *
27     * @param l number of d/dx partial derivatives.
28     * @param m number of d/dx partial derivatives.
29     * @param n number of d/dx partial derivatives.
30     * @return a String of the form <code>Rlmn</code>.
31     */
32    protected static String rlmn(int l, int m, int n) {
33      return format("R%d%d%d", l, m, n);
34    }
35  
36    /**
37     * Convenience method for writing out intermediate terms in the recursion.
38     *
39     * @param l number of d/dx partial derivatives.
40     * @param m number of d/dx partial derivatives.
41     * @param n number of d/dx partial derivatives.
42     * @return a String of the form <code>termLMN</code>.
43     */
44    protected static String term(int l, int m, int n) {
45      return format("term%d%d%d", l, m, n);
46    }
47  
48    /**
49     * Convenience method for writing out intermediate terms in the recursion.
50     *
51     * @param l number of d/dx partial derivatives.
52     * @param m number of d/dx partial derivatives.
53     * @param n number of d/dx partial derivatives.
54     * @param j the jth intermediate term.
55     * @return a String of the form <code>termLMNJ</code>.
56     */
57    protected static String term(int l, int m, int n, int j) {
58      return format("term%d%d%d%d", l, m, n, j);
59    }
60  
61    /**
62     * The index is based on the idea of filling tetrahedron.
63     * <p>
64     * 1/r has an index of 0.
65     * <br>
66     * derivatives of x are first; indices from 1..o for d/dx..(d/dx)^o
67     * <br>
68     * derivatives of x and y are second; base triangle of size (o+1)(o+2)/2
69     * <br>
70     * derivatives of x, y and z are last; total size (o+1)*(o+2)*(o+3)/6
71     * <br>
72     * <p>
73     * This function is useful to set up masking constants:
74     * <br>
75     * static int Tlmn = ti(l,m,n,order)
76     * <br>
77     * For example the (d/dy)^2 (1/R) storage location:
78     * <br>
79     * static int T020 = ti(0,2,0,order)
80     *
81     * @param dx    int The number of d/dx operations.
82     * @param dy    int The number of d/dy operations.
83     * @param dz    int The number of d/dz operations.
84     * @param order int The maximum tensor order (0 .LE. dx + dy + dz .LE. order).
85     * @return int in the range (0..binomial(order + 3, 3) - 1)
86     */
87    protected static int ti(int dx, int dy, int dz, int order) {
88      if (dx < 0 || dy < 0 || dz < 0 || dx + dy + dz > order) {
89        return -1;
90      }
91  
92      int size = (order + 1) * (order + 2) * (order + 3) / 6;
93      /*
94       We only get to the top of the tetrahedron if dz = order, otherwise
95       subtract off the top, including the level of the requested tensor
96       index.
97      */
98      int top = order + 1 - dz;
99      top = top * (top + 1) * (top + 2) / 6;
100     int zIndex = size - top;
101     /*
102      Given the "dz level", dy can range from (0 .. order - dz).
103      To get to the row for a specific value of dy, dy*(order + 1) - dy*(dy-1)/2 indices are skipped.
104      This is an operation that looks like the area of rectangle, minus the area of an empty triangle.
105     */
106     int yIndex = dy * (order - dz) - (dy - 1) * (dy - 2) / 2 + 1;
107     /*
108      Given the dz level and dy row, dx can range from (0..order - dz - dy)
109      The dx index is just walking down the dy row for "dx" steps.
110     */
111     return dx + yIndex + zIndex;
112   }
113 
114   /**
115    * Convenience method for writing out tensor indices.
116    *
117    * @param l number of d/dx partial derivatives.
118    * @param m number of d/dx partial derivatives.
119    * @param n number of d/dx partial derivatives.
120    * @return a String of the form <code>lmn</code>.
121    */
122   protected static String lmn(int l, int m, int n) {
123     return format("%d%d%d", l, m, n);
124   }
125 
126   /**
127    * Load a tensor element into a SIMD register.
128    * <p>
129    * For l=m=n=0, the returned String is:
130    * DoubleVector t000 = fromArray(SPECIES, t, T000);
131    *
132    * @param l The number of d/dx operations.
133    * @param n The number of d/dy operations.
134    * @param m The number of d/dz operations.
135    * @return a String of the form <code>DoubleVector t000 = fromArray(SPECIES, t, T000);</code>
136    */
137   protected static String loadTensor(int l, int n, int m, HashMap<Integer, String> tensorMap) {
138     String s = lmn(l, n, m);
139     String offset = "T" + lmn(l, n, m);
140     String name = "t" + s;
141     if (tensorMap.containsValue(name)) {
142       return "";
143     }
144     int size = tensorMap.size();
145     tensorMap.put(size, name);
146     return format("\tDoubleVector %s = fromArray(SPECIES, t, %s);\n", name, offset);
147   }
148 
149   /**
150    * Code to store an electrostatic potential element into an array.
151    * <p>
152    * For l=m=n=0, the returned String is (including a preceding tab):
153    * term000.intoArray(to, T000);
154    *
155    * @param to variable name of the array to store into.
156    * @param l  number of d/dx partial derivatives.
157    * @param n  number of d/dy partial derivatives.
158    * @param m  number of d/dz partial derivatives.
159    * @return a String of the form <code>term000.intoArray(to, T000);</code>
160    */
161   protected static String storePotential(String to, int l, int n, int m) {
162     String from = term(l, n, m);
163     String offset = "T" + lmn(l, n, m);
164     return format("\t%s.intoArray(%s, %s);\n", from, to, offset);
165   }
166 
167   /**
168    * Code to store a negated electrostatic potential element into an array.
169    * <p>
170    * For l=m=n=0, the returned String is (including a preceding tab):
171    * term000.neg().intoArray(to, T000);
172    *
173    * @param to variable name of the array to store into.
174    * @param l  number of d/dx partial derivatives.
175    * @param n  number of d/dy partial derivatives.
176    * @param m  number of d/dz partial derivatives.
177    * @return a String of the form <code>term000.intoArray(to, T000);</code>
178    */
179   protected static String storePotentialNeg(String to, int l, int n, int m) {
180     String from = term(l, n, m);
181     String offset = "T" + lmn(l, n, m);
182     return format("\t%s.neg().intoArray(%s, %s);\n", from, to, offset);
183   }
184 }