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.integrate; 39 40 import static ffx.numerics.integrate.FunctionDataCurve.approxEquals; 41 import static java.lang.String.format; 42 import static java.lang.System.arraycopy; 43 44 /** 45 * Describes a set of x, f(x) obtained by some mechanism; intended for numerical integration. 46 * 47 * @author Jacob M. Litman 48 */ 49 public class DoublesDataSet implements DataSet { 50 51 /** An array of input points. */ 52 private final double[] x; 53 /** An array of results f(x). */ 54 private final double[] fX; 55 /** Lower bound. */ 56 private final double lb; 57 /** Upper bound. */ 58 private final double ub; 59 /** Number of data points. */ 60 private final int nX; 61 /** Bin size. */ 62 private final double sep; 63 /** If ends should have 1/2 regular separation. */ 64 private final boolean halfWidthEnd; 65 66 /** 67 * Constructs a DataSet from actual data, with no known underlying function (or at least none with 68 * an analytically solved integral). Assumes no half-width end bins (such as found in OST). 69 * 70 * @param x Points where f(x) is known 71 * @param fX Values/estimates of f(x) 72 */ 73 public DoublesDataSet(double[] x, double[] fX) { 74 this(x, fX, false); 75 } 76 77 /** 78 * Constructs a DataSet from actual data, with no known underlying function (or at least none with 79 * an analytically solved integral). 80 * 81 * @param x Points where f(x) is known 82 * @param fX Values/estimates of f(x) 83 * @param halvedEnds Whether the first and last bins are half-width (such as OST) 84 */ 85 public DoublesDataSet(double[] x, double[] fX, boolean halvedEnds) { 86 nX = x.length; 87 assert nX == fX.length; 88 89 this.x = new double[nX]; 90 arraycopy(x, 0, this.x, 0, nX); 91 92 this.fX = new double[nX]; 93 arraycopy(fX, 0, this.fX, 0, nX); 94 95 lb = x[0]; 96 ub = x[nX - 1]; 97 // sep = ((ub - lb) / ((double) nX)); 98 halfWidthEnd = halvedEnds; 99 double sepDist = ub - lb; 100 sep = halfWidthEnd ? (sepDist / ((double) nX - 2)) : (sepDist / ((double) nX - 1)); 101 assertXIntegrity(this.x); 102 } 103 104 /** 105 * Constructs a DataSet from another DataSet, effectively masquerading a test set such as a sine 106 * wave as data from an "unknown" function. Used primarily for testing purposes. 107 * 108 * @param set DataSet to cast 109 */ 110 public DoublesDataSet(DataSet set) { 111 nX = set.numPoints(); 112 113 this.x = new double[nX]; 114 arraycopy(set.getX(), 0, this.x, 0, nX); 115 116 this.fX = new double[nX]; 117 arraycopy(set.getAllFxPoints(), 0, this.fX, 0, nX); 118 119 lb = x[0]; 120 ub = x[nX - 1]; 121 this.halfWidthEnd = set.halfWidthEnds(); 122 sep = set.binWidth(); 123 // Possibly add assertion for separation distance 124 assertXIntegrity(x); 125 } 126 127 /** {@inheritDoc} */ 128 @Override 129 public double binWidth() { 130 return sep; 131 } 132 133 /** {@inheritDoc} */ 134 @Override 135 public double[] getAllFxPoints() { 136 double[] pts = new double[nX]; 137 arraycopy(fX, 0, pts, 0, nX); 138 return pts; 139 } 140 141 /** {@inheritDoc} */ 142 @Override 143 public double getFxPoint(int index) { 144 return fX[index]; 145 } 146 147 /** {@inheritDoc} */ 148 @Override 149 public double[] getX() { 150 double[] copyX = new double[x.length]; 151 arraycopy(x, 0, copyX, 0, x.length); 152 return copyX; 153 } 154 155 /** {@inheritDoc} */ 156 @Override 157 public boolean halfWidthEnds() { 158 return halfWidthEnd; 159 } 160 161 /** {@inheritDoc} */ 162 @Override 163 public double lowerBound() { 164 return lb; 165 } 166 167 /** {@inheritDoc} */ 168 @Override 169 public int numPoints() { 170 return x.length; 171 } 172 173 /** {@inheritDoc} */ 174 @Override 175 public String toString() { 176 StringBuilder sb = 177 new StringBuilder( 178 format( 179 "Data set with %d points from lower bound %9.3g and upper bound %9.3g", 180 nX, lb, ub)); 181 if (halfWidthEnd) { 182 sb.append(" and half-width start/end bins"); 183 } 184 sb.append("."); 185 return sb.toString(); 186 } 187 188 /** {@inheritDoc} */ 189 @Override 190 public double upperBound() { 191 return ub; 192 } 193 194 /** 195 * Used to check that the passed-in x array is composed of equally-spaced points from lb to ub. 196 * 197 * @param x The array to check. 198 */ 199 private void assertXIntegrity(double[] x) { 200 assert ub > lb; 201 if (halfWidthEnd) { 202 assert x.length == nX; 203 assert lb == x[0]; 204 assert ub == x[nX - 1]; 205 206 assert approxEquals(x[1], lb + 0.5 * sep); 207 assert approxEquals(x[nX - 2], (ub - 0.5 * sep)); 208 209 for (int i = 2; i < (nX - 2); i++) { 210 double target = lb + 0.5 * sep; 211 target += ((i - 1) * sep); 212 assert approxEquals(x[i], target); 213 } 214 } else { 215 for (int i = 0; i < x.length; i++) { 216 assert approxEquals(x[i], x[0] + i * sep); 217 } 218 } 219 } 220 }