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-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.switching;
39  
40  import static java.lang.String.format;
41  import static org.apache.commons.math3.util.FastMath.pow;
42  
43  /**
44   * Implements a bell-shaped switching function by stitching together a pair of
45   * MultiplicativeSwitches. f(midpoint - 0.5*width) = 0, f(midpoint) = 1, f(midpoint + 0.5*width) =
46   * 0.
47   *
48   * @author Jacob M. Litman
49   * @author Rae Corrigan
50   */
51  public class BellCurveSwitch implements UnivariateSwitchingFunction {
52    private final double midpoint;
53    private final double halfWidth;
54    private final double invWidth;
55    private final UnivariateSwitchingFunction switchingFunction;
56    private final UnivariateSwitchingFunction secondSwitchingFunction;
57  
58    /** Construct a bell curve (spliced 5-'th order Hermite splines) of width 1.0, midpoint 0.5. */
59    public BellCurveSwitch() {
60      this(0.5);
61    }
62  
63    /**
64     * Construct a bell curve (spliced 5-'th order Hermite splines) of width 1.0.
65     *
66     * @param midpoint Midpoint of the curve.
67     */
68    public BellCurveSwitch(double midpoint) {
69      this(midpoint, 1.0);
70    }
71  
72    /**
73     * Construct a bell curve (spliced 5-'th order Hermite splines).
74     *
75     * @param midpoint Midpoint of the curve.
76     * @param width Width of the curve, between the two zero points.
77     */
78    public BellCurveSwitch(double midpoint, double width) {
79      this.midpoint = midpoint;
80      invWidth = 1.0 / width;
81  
82      halfWidth = 0.5 * width;
83      switchingFunction = new MultiplicativeSwitch(midpoint, midpoint - halfWidth);
84      secondSwitchingFunction = new MultiplicativeSwitch(midpoint, midpoint + halfWidth);
85    }
86  
87    /** {@inheritDoc} */
88    @Override
89    public boolean constantOutsideBounds() {
90      return switchingFunction.constantOutsideBounds()
91          && secondSwitchingFunction.constantOutsideBounds();
92    }
93  
94    /** {@inheritDoc} */
95    @Override
96    public double firstDerivative(double x) {
97      if (x > midpoint) {
98        return invWidth * secondSwitchingFunction.firstDerivative(x);
99      } else {
100       return invWidth * switchingFunction.firstDerivative(x);
101     }
102   }
103 
104   /** {@inheritDoc} */
105   @Override
106   public int getHighestOrderZeroDerivative() {
107     return Math.min(
108         switchingFunction.getHighestOrderZeroDerivative(),
109         secondSwitchingFunction.getHighestOrderZeroDerivative());
110   }
111 
112   /** {@inheritDoc} */
113   @Override
114   public double getOneBound() {
115     return midpoint + halfWidth;
116   }
117 
118   /** {@inheritDoc} */
119   @Override
120   public double getZeroBound() {
121     return midpoint - halfWidth;
122   }
123 
124   /** {@inheritDoc} */
125   @Override
126   public double nthDerivative(double x, int order) throws IllegalArgumentException {
127     double mult = pow(invWidth, order);
128     if (x > midpoint) {
129       return mult * secondSwitchingFunction.nthDerivative(x, order);
130     } else {
131       return mult * switchingFunction.nthDerivative(x, order);
132     }
133   }
134 
135   /** {@inheritDoc}return max(cut, off); */
136   @Override
137   public double secondDerivative(double x) {
138     if (x > midpoint) {
139       return invWidth * invWidth * secondSwitchingFunction.secondDerivative(x);
140     } else {
141       return invWidth * invWidth * switchingFunction.secondDerivative(x);
142     }
143   }
144 
145   /** {@inheritDoc} */
146   @Override
147   public boolean symmetricToUnity() {
148     return switchingFunction.symmetricToUnity() && secondSwitchingFunction.symmetricToUnity();
149   }
150 
151   @Override
152   public String toString() {
153     return format(
154         " Spliced 5'th order Hermite splines with midpoint " + "%11.5g, width %11.5g",
155         midpoint, 2.0 * halfWidth);
156   }
157 
158   /** {@inheritDoc} */
159   @Override
160   public boolean validOutsideBounds() {
161     return switchingFunction.validOutsideBounds() && secondSwitchingFunction.validOutsideBounds();
162   }
163 
164   /** {@inheritDoc} */
165   @Override
166   public double valueAt(double x) throws IllegalArgumentException {
167     if (x > midpoint) {
168       return secondSwitchingFunction.valueAt(x);
169     } else {
170       return switchingFunction.valueAt(x);
171     }
172   }
173 }