1 // ******************************************************************************
2 //
3 // Title: Force Field X.
4 // Description: Force Field X - Software for Molecular Biophysics.
5 // Copyright: Copyright (c) Michael J. Schnieders 2001-2025.
6 //
7 // This file is part of Force Field X.
8 //
9 // Force Field X is free software; you can redistribute it and/or modify it
10 // under the terms of the GNU General Public License version 3 as published by
11 // the Free Software Foundation.
12 //
13 // Force Field X is distributed in the hope that it will be useful, but WITHOUT
14 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 // details.
17 //
18 // You should have received a copy of the GNU General Public License along with
19 // Force Field X; if not, write to the Free Software Foundation, Inc., 59 Temple
20 // Place, Suite 330, Boston, MA 02111-1307 USA
21 //
22 // Linking this library statically or dynamically with other modules is making a
23 // combined work based on this library. Thus, the terms and conditions of the
24 // GNU General Public License cover the whole combination.
25 //
26 // As a special exception, the copyright holders of this library give you
27 // permission to link this library with independent modules to produce an
28 // executable, regardless of the license terms of these independent modules, and
29 // to copy and distribute the resulting executable under terms of your choice,
30 // provided that you also meet, for each linked independent module, the terms
31 // and conditions of the license of that module. An independent module is a
32 // module which is not derived from or based on this library. If you modify this
33 // library, you may extend this exception to your version of the library, but
34 // you are not obligated to do so. If you do not wish to do so, delete this
35 // exception statement from your version.
36 //
37 // ******************************************************************************
38 package ffx.numerics.multipole;
39
40 import jdk.incubator.vector.DoubleVector;
41
42 import static jdk.incubator.vector.DoubleVector.SPECIES_PREFERRED;
43 import static jdk.incubator.vector.DoubleVector.fromArray;
44
45 /**
46 * The PolarizableMultipole class defines a polarizable multipole.
47 *
48 * @author Michael J. Schnieders
49 * @since 1.0
50 */
51 public class PolarizableMultipoleSIMD {
52
53 private static final double oneThird = 1.0 / 3.0;
54 private static final double twoThirds = 2.0 / 3.0;
55
56 /**
57 * Partial charge.
58 */
59 protected DoubleVector q;
60 /**
61 * Dipole x-component.
62 */
63 protected DoubleVector dx;
64 /**
65 * Dipole y-component.
66 */
67 protected DoubleVector dy;
68 /**
69 * Dipole z-component.
70 */
71 protected DoubleVector dz;
72 /**
73 * Quadrupole xx-component multiplied by 1/3.
74 */
75 protected DoubleVector qxx;
76 /**
77 * Quadrupole yy-component multiplied by 1/3.
78 */
79 protected DoubleVector qyy;
80 /**
81 * Quadrupole zz-component multiplied by 1/3.
82 */
83 protected DoubleVector qzz;
84 /**
85 * Quadrupole xy-component multiplied by 2/3.
86 */
87 protected DoubleVector qxy;
88 /**
89 * Quadrupole xz-component multiplied by 2/3.
90 */
91 protected DoubleVector qxz;
92 /**
93 * Quadrupole xz-component multiplied by 2/3.
94 */
95 protected DoubleVector qyz;
96
97 /**
98 * Induced dipole x-component.
99 */
100 protected DoubleVector ux;
101 /**
102 * Induced dipole y-component.
103 */
104 protected DoubleVector uy;
105 /**
106 * Induced dipole z-component.
107 */
108 protected DoubleVector uz;
109 /**
110 * Induced dipole chain rule x-component.
111 */
112 protected DoubleVector px;
113 /**
114 * Induced dipole chain rule y-component.
115 */
116 protected DoubleVector py;
117 /**
118 * Induced dipole chain rule z-component.
119 */
120 protected DoubleVector pz;
121 /**
122 * Averaged induced dipole + induced dipole chain-rule x-component: sx = 0.5 * (ux + px).
123 */
124 protected DoubleVector sx;
125 /**
126 * Averaged induced dipole + induced dipole chain-rule y-component: sy = 0.5 * (uy + py).
127 */
128 protected DoubleVector sy;
129 /**
130 * Averaged induced dipole + induced dipole chain-rule z-component: sz = 0.5 * (uz + pz).
131 */
132 protected DoubleVector sz;
133
134 /**
135 * PolarizableMultipole constructor with zero moments.
136 */
137 public PolarizableMultipoleSIMD() {
138 }
139
140 /**
141 * PolarizableMultipole constructor.
142 *
143 * @param Q Multipoles Q[q, dx, dy, dz, qxx, qyy, qzz, qxy, qxz, qyz]
144 * @param u Induced dipoles u[ux, uy, uz]
145 * @param uCR Induced dipole chain-rules uCR[ux, uy, uz]
146 */
147 public PolarizableMultipoleSIMD(double[][] Q, double[][] u, double[][] uCR) {
148 setPermanentMultipole(Q);
149 setInducedDipole(u, uCR);
150 }
151
152 /**
153 * Set the permanent multipole.
154 * <p>
155 * Note that the quadrupole trace components are multiplied by 1/3 and the
156 * off-diagonal components are multiplied by 2/3.
157 *
158 * @param Q Multipoles Q[q, dx, dy, dz, qxx, qyy, qzz, qxy, qxz, qyz]
159 * @param u Induced dipoles u[ux, uy, uz]
160 * @param uCR Induced dipole chain-rules uCR[ux, uy, uz]
161 */
162 public void set(double[][] Q, double[][] u, double[][] uCR) {
163 setPermanentMultipole(Q);
164 setInducedDipole(u, uCR);
165 }
166
167 /**
168 * Set the permanent multipole.
169 * <p>
170 * Note that the quadrupole trace components are multiplied by 1/3 and the
171 * off-diagonal components are multiplied by 2/3.
172 *
173 * @param Q Multipole Q[q, dx, dy, dz, qxx, qyy, qzz, qxy, qxz, qyz]
174 */
175 public final void setPermanentMultipole(double[][] Q) {
176 q = fromArray(SPECIES_PREFERRED, Q[0], 0);
177 dx = fromArray(SPECIES_PREFERRED, Q[1], 0);
178 dy = fromArray(SPECIES_PREFERRED, Q[2], 0);
179 dz = fromArray(SPECIES_PREFERRED, Q[3], 0);
180 qxx = fromArray(SPECIES_PREFERRED, Q[4], 0).mul(oneThird);
181 qyy = fromArray(SPECIES_PREFERRED, Q[5], 0).mul(oneThird);
182 qzz = fromArray(SPECIES_PREFERRED, Q[6], 0).mul(oneThird);
183 qxy = fromArray(SPECIES_PREFERRED, Q[7], 0).mul(twoThirds);
184 qxz = fromArray(SPECIES_PREFERRED, Q[8], 0).mul(twoThirds);
185 qyz = fromArray(SPECIES_PREFERRED, Q[9], 0).mul(twoThirds);
186 }
187
188 /**
189 * Set the induced dipole.
190 *
191 * @param u Induced dipole u[ux, uy, uz]
192 * @param uCR Induced dipole chain-rule uCR[ux, uy, uz]
193 */
194 public final void setInducedDipole(double[][] u, double[][] uCR) {
195 ux = fromArray(SPECIES_PREFERRED, u[0], 0);
196 uy = fromArray(SPECIES_PREFERRED, u[1], 0);
197 uz = fromArray(SPECIES_PREFERRED, u[2], 0);
198 px = fromArray(SPECIES_PREFERRED, uCR[0], 0);
199 py = fromArray(SPECIES_PREFERRED, uCR[1], 0);
200 pz = fromArray(SPECIES_PREFERRED, uCR[2], 0);
201 sx = ux.add(px).mul(0.5);
202 sy = uy.add(py).mul(0.5);
203 sz = uz.add(pz).mul(0.5);
204 }
205
206 /**
207 * Compute the scaled and averaged induced dipole.
208 *
209 * @param scaleInduction Induction mask scale factor.
210 * @param scaleEnergy Energy mask scale factor.
211 */
212 public final void applyMasks(DoubleVector scaleInduction, DoubleVector scaleEnergy) {
213 // [Ux, Uy, Uz] resulted from induction masking rules, and we now apply the energy mask.
214 // [Px, Py, Pz] resulted from energy masking rules, and we now apply the induction mask.
215 sx = ux.mul(scaleEnergy).add(px.mul(scaleInduction)).mul(0.5);
216 sy = uy.mul(scaleEnergy).add(py.mul(scaleInduction)).mul(0.5);
217 sz = uz.mul(scaleEnergy).add(pz.mul(scaleInduction)).mul(0.5);
218 }
219
220 }