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}