View Javadoc
1   // ******************************************************************************
2   //
3   // Title:       Force Field X.
4   // Description: Force Field X - Software for Molecular Biophysics.
5   // Copyright:   Copyright (c) Michael J. Schnieders 2001-2024.
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 }