001/*
002 *   GridSpacing  -- Factory methods for creating various grid spacings.
003 *
004 *   Copyright (C) 2009-2017, by Joseph A. Huwaldt.
005 *   All rights reserved.
006 *   
007 *   This library is free software; you can redistribute it and/or
008 *   modify it under the terms of the GNU Lesser General Public
009 *   License as published by the Free Software Foundation; either
010 *   version 2.1 of the License, or (at your option) any later version.
011 *   
012 *   This library is distributed in the hope that it will be useful,
013 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
014 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 *   Library General Public License for more details.
016 *
017 *  You should have received a copy of the GNU Lesser General Public License
018 *  along with this program; if not, write to the Free Software
019 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
020 *  Or visit:  http://www.gnu.org/licenses/lgpl.html
021 */
022package geomss.geom;
023
024import jahuwaldt.tools.math.AbstractEvaluatable1D;
025import jahuwaldt.tools.math.RootException;
026import jahuwaldt.tools.math.Roots;
027import java.lang.reflect.InvocationTargetException;
028import java.lang.reflect.Method;
029import java.text.MessageFormat;
030import java.util.Collections;
031import java.util.List;
032import static java.util.Objects.requireNonNull;
033import java.util.ResourceBundle;
034import java.util.logging.Level;
035import java.util.logging.Logger;
036import javolution.context.ObjectFactory;
037import javolution.lang.MathLib;
038import javolution.util.FastTable;
039
040/**
041 * A collection of methods for creating various grid spacing options.
042 *
043 * <p> Modified by: Joseph A. Huwaldt </p>
044 *
045 * @author Joseph A. Huwaldt, Date: June 26, 2009
046 * @version January 30, 2017
047 */
048public class GridSpacing {
049
050    /**
051     * The resource bundle for this package.
052     */
053    private static final ResourceBundle RESOURCES = AbstractGeomElement.RESOURCES;
054
055    /**
056     * An enumeration of the available grid spacing types.
057     */
058    public enum GridType {
059
060        LINEAR("linear"),
061        SQR("sqr"),
062        ONEMSQRT("oneMinusSqrt"),
063        ACOS("acos"),
064        COS("cos"),
065        SQRT("sqrt");
066
067        private static final String[] TITLES;
068        static {
069            TITLES = RESOURCES.getString("spacingTypeLabels").split(",");
070        }
071
072        //  The GridSpacing method signature for the spacing constant.
073        private Method spacingMethod;
074
075        GridType(String methodName) {
076            requireNonNull(methodName);
077
078            //  Use reflection to get the correct method for this grid type constant.
079            try {
080                this.spacingMethod = GridSpacing.class.getMethod(methodName, new Class[]{Integer.TYPE});
081            } catch (NoSuchMethodException | SecurityException ex) {
082                Logger.getLogger(GridType.class.getName()).log(Level.SEVERE, null, ex);
083            }
084        }
085
086        @Override
087        public String toString() {
088            return TITLES[ordinal()];
089        }
090
091        /**
092         * Return a list of "n" values between 0 and 1 (inclusive) using this grid spacing
093         * type.
094         *
095         * @param n The number of points to include in the spacing.
096         * @return A list of points, of size <code>n</code>, with the spacing of this
097         *         GridType.
098         */
099        public List<Double> spacing(int n) {
100            //  Use reflection to call the correct method in the GridSpacing class.
101            List<Double> output = null;
102            try {
103                output = (List<Double>)spacingMethod.invoke(GridSpacing.class, new Object[]{n});
104            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
105                Logger.getLogger(GridType.class.getName()).log(Level.SEVERE, null, ex);
106            }
107            return output;
108        }
109    }
110    
111    /**
112     * Returns a list of values evenly or linearly spaced between 0 and 1.
113     *
114     * @param n The number of points to include in the spacing.
115     * @return A list of points, of size <code>n</code>, evenly spaced between 0
116     * and 1.
117     */
118    public static List<Double> linear(int n) {
119        FastTable<Double> values = FastTable.newInstance();
120        int nm1 = n - 1;
121        for (int i = 0; i < n; ++i) {
122            double s = (double)(i) / nm1;
123            values.add(s);
124        }
125        return values;
126    }
127
128    /**
129     * Returns a list of values with a cosine spacing between 0 and 1. This
130     * distribution tends to group points near the ends of the list.
131     *
132     * @param n The number of points to include in the spacing.
133     * @return A list of points, of size <code>n</code>, with a cosine spacing
134     * between 0 and 1.
135     */
136    public static List<Double> cos(int n) {
137        FastTable<Double> values = FastTable.newInstance();
138        int nm1 = n - 1;
139        for (int i = 0; i < n; ++i) {
140            double s = (1 - MathLib.cos(MathLib.PI * i / nm1)) / 2;
141            values.add(s);
142        }
143        return values;
144    }
145
146    /**
147     * Returns a list of values with an arc-cosine spacing between 0 and 1. This
148     * distribution tends to group points near the middle of the list.
149     *
150     * @param n The number of points to include in the spacing.
151     * @return A list of points, of size <code>n</code>, with an arc-cosine
152     * spacing between 0 and 1.
153     */
154    public static List<Double> acos(int n) {
155        FastTable<Double> values = FastTable.newInstance();
156        int nm1 = n - 1;
157        for (int i = 0; i < n; ++i) {
158            double s = MathLib.acos(1 - 2. * i / nm1) / MathLib.PI;
159            values.add(s);
160        }
161        return values;
162    }
163
164    /**
165     * Returns a list of values with a square root spacing between 0 and 1. This
166     * distribution tends to group points near the end of the list.
167     *
168     * @param n The number of points to include in the spacing.
169     * @return A list of points, of size <code>n</code>, with a square root
170     * spacing between 0 and 1.
171     */
172    public static List<Double> sqrt(int n) {
173        FastTable<Double> values = FastTable.newInstance();
174        int nm1 = n - 1;
175        for (int i = 0; i < n; ++i) {
176            double s = MathLib.sqrt(((double)(i)) / nm1);
177            values.add(s);
178        }
179        return values;
180    }
181
182    /**
183     * Returns a list of values with a 1-square root spacing between 0 and 1.
184     * This distribution tends to group points near the start of the list.
185     *
186     * @param n The number of points to include in the spacing.
187     * @return A list of points, of size <code>n</code>, with a square root
188     * spacing between 0 and 1.
189     */
190    public static List<Double> oneMinusSqrt(int n) {
191        FastTable<Double> values = FastTable.newInstance();
192        int nm1 = n - 1;
193        for (int i = 0; i < n; ++i) {
194            double s = 1 - MathLib.sqrt(((double)(i)) / nm1);
195            values.add(s);
196        }
197        Collections.reverse(values);
198        return values;
199    }
200
201    /**
202     * Returns a list of values with a squared spacing between 0 and 1. This
203     * distribution tends to group points near the start of the list.
204     *
205     * @param n The number of points to include in the spacing.
206     * @return A list of points, of size <code>n</code>, with a squared spacing
207     * between 0 and 1.
208     */
209    public static List<Double> sqr(int n) {
210        FastTable<Double> values = FastTable.newInstance();
211        int nm1 = n - 1;
212        for (int i = 0; i < n; ++i) {
213            double r = ((double)(i)) / nm1;
214            double s = r * r;
215            values.add(s);
216        }
217        return values;
218    }
219
220    /**
221     * Returns a list of values with a hyperbolic tangent spacing between 0 and
222     * 1. This distribution tends to group points near the ends of the list
223     * depending on the inputs.
224     *
225     * @param n The number of points to include in the spacing.
226     * @param ds1 The fractional arc-length for the 1st segment at the start of
227     * the list.
228     * @param ds2 The fractional arc-length for the last segment at the end of
229     * the list.
230     * @return A list of points, of size <code>n</code>, with a hyperbolic
231     * tangent spacing between 0 and 1.
232     */
233    public static List<Double> tanh(int n, double ds1, double ds2) {
234        //  Reference: Thompson, J.F., Warsi, Z., Wayne Mastin, C., "Numerical Grid Generation", Chapter 8, pg 218.
235
236        FastTable<Double> values = FastTable.newInstance();
237
238        double A = MathLib.sqrt(ds2/ds1);
239        int capI = n - 1;
240        double B = 1. / (capI * MathLib.sqrt(ds1 * ds2));
241        if (B < 1) {
242            throw new IllegalArgumentException(MessageFormat.format(
243                    RESOURCES.getString("invTanhSpacing"), (int)(1. / MathLib.sqrt(ds1 * ds2))));
244        }
245
246        //  Use a root finder to solve the equation: sinh(delta)/delta = B for delta.
247        //  Initial guess by series expansion.
248        double delta = MathLib.sqrt(6. * (B - 1.));
249        TanhPEvaluatable sinhFunc = TanhPEvaluatable.newInstance(B);
250        try {
251            delta = Roots.findRoot1D(sinhFunc, delta / 2, delta * 2, 1e-10);
252        } catch (Exception err) {
253            Logger.getLogger(GridType.class.getName()).log(Level.SEVERE, null, err);
254        }
255        TanhPEvaluatable.recycle(sinhFunc);
256
257        //  Create the spacing points.
258        values.add(0.);
259        for (int i = 1; i < capI; ++i) {
260            double ratio = ((double)i) / capI;
261            double u = 0.5 * (1. + MathLib.tanh(delta * (ratio - 0.5)) / MathLib.tanh(0.5 * delta));
262            double s = u / (A + (1. - A) * u);
263            values.add(s);
264        }
265        values.add(1.);
266
267        return values;
268    }
269
270    /**
271     * An Evaulatable1D that is used to solve an equation related to a tanhp
272     * distribution.
273     */
274    private static class TanhPEvaluatable extends AbstractEvaluatable1D {
275
276        private double _B;
277
278        public static TanhPEvaluatable newInstance(double B) {
279            TanhPEvaluatable o = FACTORY.object();
280            o._B = B;
281            return o;
282        }
283
284        /**
285         * Calculates: sinh(x)/(x*B) - 1. When this is zero, then the solution
286         * has been found.
287         */
288        @Override
289        public double function(double x) throws RootException {
290            double func = MathLib.sinh(x) / (x * _B) - 1.0;
291            return func;
292        }
293
294        public static void recycle(TanhPEvaluatable instance) {
295            FACTORY.recycle(instance);
296        }
297
298        private TanhPEvaluatable() { }
299
300        @SuppressWarnings("unchecked")
301        private static final ObjectFactory<TanhPEvaluatable> FACTORY = new ObjectFactory<TanhPEvaluatable>() {
302            @Override
303            protected TanhPEvaluatable create() {
304                return new TanhPEvaluatable();
305            }
306        };
307    }
308
309}