View Javadoc

1   /*
2     The TauP Toolkit: Flexible Seismic Travel-Time and Raypath Utilities.
3     Copyright (C) 1998-2000 University of South Carolina
4   
5     This program is free software; you can redistribute it and/or
6     modify it under the terms of the GNU General Public License
7     as published by the Free Software Foundation; either version 2
8     of the License, or (at your option) any later version.
9   
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14  
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  
19    The current version can be found at 
20    <A HREF="www.seis.sc.edu">http://www.seis.sc.edu</A>
21  
22    Bug reports and comments should be directed to 
23    H. Philip Crotwell, crotwell@seis.sc.edu or
24    Tom Owens, owens@seis.sc.edu
25  
26  */
27  
28  package edu.sc.seis.TauP;
29  
30  import java.io.Serializable;
31  import java.util.Vector;
32  
33  /***
34    * This class provides storage and methods for generating slowness-depth
35    * pairs.
36    *
37    * @version 1.1.3 Wed Jul 18 15:00:35 GMT 2001
38  
39  
40  
41    * @author H. Philip Crotwell
42    *
43    */
44  public abstract class SlownessModel implements Serializable, Cloneable {
45  
46      /*** True to enable debugging output. */
47      transient public boolean DEBUG = false;
48  
49      /*** True to enable verbose output. */
50      transient public boolean verbose = false;
51  
52      /*** Radius of the Earth in km, usually input from the velocity model. */
53      protected double radiusOfEarth = 6371.0;
54  
55      /*** Velocity Model used to get slowness model. Usually set in 
56       *  createSlowness(). */
57      protected VelocityModel vMod;
58  
59      /*** Stores the layer number for layers in the velocity model with
60       *  a critical point at their top. These form the
61       *  "branches" of slowness sampling.
62       *  @see edu.sc.seis.TauP.CriticalDepth */
63      protected Vector criticalDepthVector = new Vector();
64   
65      /*** Stores depth ranges that contains a high
66       *  slowness zone for P. Stored as DepthRange objects, containing
67       *  the top depth and bottom depth. 
68       *  @see DepthRange */
69      protected Vector highSlownessLayerDepthsP = new Vector();
70   
71      /*** Stores depth ranges that contains a high
72       *  slowness zone for S. Stored as DepthRange objects, containing
73       *  the top depth and bottom depth. 
74       *  @see DepthRange */
75      protected Vector highSlownessLayerDepthsS = new Vector();
76   
77      /*** Stores depth ranges that are fluid, ie S velocity is zero.
78       *  Stored as DepthRange objects, containing
79       *  the top depth and bottom depth. 
80       *  @see DepthRange */
81      protected Vector fluidLayerDepths = new Vector();
82  
83      /*** Initial length of the slowness vectors. */
84      protected static int vectorLength = 256;
85  
86      /*** Stores the final slowness-depth layers for P waves. Stored as 
87       *  SlownessLayer objects.
88       *  @see edu.sc.seis.TauP.SlownessLayer */
89      protected Vector PLayers = new Vector(vectorLength);
90  
91      /*** Stores the final slowness-depth layers for S waves. Stored as 
92       *  SlownessLayer objects. Note that SLayers and PLayers share the
93       *  same SlownessLayer object within fluid layers, so changes made
94       *  to one will affect the other.
95       *  @see edu.sc.seis.TauP.SlownessLayer */
96      protected Vector SLayers = new Vector(vectorLength);
97  
98      /*** Minimum difference between successive slowness samples. The
99       *  default is 0.1 (km-sec/km or sec/rad for spherical, sec/km
100      *  for flat models). This keeps the sampling from becoming too
101      *  fine. For example, a strong negative S velocity gradient just
102      *  above the CMB will cause the totally reflected ScS too have
103      *  an extremely large range of distances, over a very small range
104      *  of ray parameters. The distance check would otherwise
105      *  force a very fine sampling of this region. However since in this
106      *  case time and distance are likely to be very close to being 
107      *  linearly related, this sort of sampling is overkill. So we
108      *  ignore the distance check if the ray parameter becomes smaller
109      *  than minDeltaP. */
110     protected double minDeltaP = 0.1;
111 
112     /*** Maximum difference between successive slowness samples. The
113      *  default is 11.0 (km-sec/km or sec/rad for spherical, sec/km
114      *  for flat models). See Buland and Chapman p1292 */
115     protected double maxDeltaP = 11.0;
116 
117     /*** Maximum difference between successive depth samples,
118      *  default is 115 km. See Buland and Chapman p1292*/
119     protected double maxDepthInterval = 115.0;
120 
121     /*** Maximum difference between successive ranges, in radians. The
122      *  default is 200 km / radiusOfEarth. See Buland and Chapman p1292.
123      *  @see radiusOfEarth  */
124     protected double maxRangeInterval = 200.0/radiusOfEarth;
125 
126     protected double maxInterpError = .5;
127 
128     /*** Should we allow J phases, S waves in the inner core? If true, then
129      *  the slowness sampling for S will use the S velocity structure for
130      *  the inner core. If false, then we will use the P velocity structure
131      *  for both the inner and outer core for S waves as well as P waves.
132      *  Disallowing inner core S phases reduces the number of slowness
133      *  samples significantly due to the large geometrical spreading
134      *  of S waves in the inner core. The default is false.
135      *  @see minInnerCoreDepth */
136     protected boolean allowInnerCoreS = true;
137 
138     /*** Tolerance for slownesses. If two slownesses are closer that this
139      *  value, then we consider them to be identical. Basically this just
140      *  provides some protection against numerical "chatter". */
141     protected double slownessTolerance = 1e-16;
142 
143     /*** Just useful for calling methods that need to know whether to use P or
144      *  S waves. */
145     public static final boolean PWAVE = true;
146 
147     /*** Just useful for calling methods that need to know whether to use P or
148      *  S waves. */
149     public static final boolean SWAVE = false;
150 
151     // METHODS ----------------------------------------------------------------
152 
153     // Accessor methods
154 
155     public void setRadiusOfEarth(double radiusOfEarth) {
156 	this.radiusOfEarth = radiusOfEarth;
157     }
158 
159     public void setMinDeltaP(double minDeltaP) {
160 	this.minDeltaP = minDeltaP;
161     }
162 
163     public void setMaxDeltaP(double maxDeltaP) {
164 	this.maxDeltaP = maxDeltaP;
165     }
166 
167     public void setMaxDepthInterval(double maxDepthInterval) {
168 	this.maxDepthInterval = maxDepthInterval;
169     }
170 
171     /*** sets the maximum range interval for surface focus turning
172      *  waves between slowness samples, input in degrees.
173      *  */
174     public void setMaxRangeInterval(double maxRangeInterval) {
175 	this.maxRangeInterval = maxRangeInterval * Math.PI / 180.0 ;
176     }
177 
178     /*** sets the maximum value of the estimated error due to linear
179      *  interpolation. Care should be taken not to set this too small
180      *  as a very large number of samples may be required. Note also
181      *  that this is only an estimate of the error, and thus the bound
182      *  is by no means assured. */
183     public void setMaxInterpError(double maxInterpError) {
184 	this.maxInterpError = maxInterpError;
185     }
186 
187     public void setAllowInnerCoreS(boolean allowInnerCoreS) {
188 	this.allowInnerCoreS = allowInnerCoreS;
189     }
190 
191     public void setSlownessTolerance(double slownessTolerance) {
192 	this.slownessTolerance = slownessTolerance;
193     }
194 
195     // get accessor methods
196 
197     public final double getRadiusOfEarth() {
198 	return radiusOfEarth;
199     }
200 
201     public final double getMinDeltaP() {
202 	return minDeltaP;
203     }
204 
205     public final double getMaxDeltaP() {
206 	return maxDeltaP;
207     }
208 
209     public final double getMaxDepthInterval() {
210 	return maxDepthInterval;
211     }
212 
213     /*** @returns the maximum range interval for surface focus turning
214      *  waves between slowness samples output in degrees. */
215     public final double getMaxRangeInterval() {
216 	return 180.0 * maxRangeInterval /Math.PI;
217     }
218 
219     /*** gets the maximum value of the estimated error due to linear
220      *  interpolation. Care should be taken not to set this too small
221      *  as a very large number of samples may be required. Note also
222      *  that this is only an estimate of the error, and thus the bound
223      *  is by no means assured. */
224     public final double getMaxInterpError() {
225 	return maxInterpError;
226     }
227 
228     public final boolean isAllowInnerCoreS() {
229 	return allowInnerCoreS;
230     }
231 
232     public final double getSlownessTolerance() {
233 	return slownessTolerance;
234     }
235 
236     public final int getNumCriticalDepths() {
237 	return criticalDepthVector.size();
238     }
239 
240     public final CriticalDepth getCriticalDepth(int i) {
241 	return (CriticalDepth)criticalDepthVector.elementAt(i);
242     }
243 
244     public final int getNumLayers(boolean isPWave) {
245 	if (isPWave) {
246 	    return PLayers.size();
247 	} else {
248 	    return SLayers.size();
249 	}
250     }
251    
252     /*** @returns the minimum ray parameter that turns, but is not reflected,
253      *  at or above the given depth. Normally this is the slowness sample
254      *  at the given depth, but if the depth is within a high slowness zone,
255      *  then it may be smaller.
256      */
257     public double getMinTurnRayParam(double depth, boolean isPWave) 
258 	throws NoSuchLayerException, SlownessModelException {
259 	double minPSoFar = Double.MAX_VALUE;
260 	SlownessLayer sLayer;
261 	Vector layers;
262 
263 	if (isPWave) {
264 	    layers = PLayers;
265 	} else {
266 	    layers = SLayers;
267 	}
268 
269 	if (depthInHighSlowness(depth, Double.MAX_VALUE, isPWave)) {
270 	    for (int i=0;i<layers.size();i++) {
271 		sLayer = getSlownessLayer(i, isPWave);
272 		if (sLayer.getBotDepth() == depth) {
273 		    minPSoFar = Math.min(minPSoFar, sLayer.getBotP());
274 		    return minPSoFar;
275 		} else if (sLayer.getBotDepth() > depth) {
276 		    minPSoFar = Math.min(minPSoFar, 
277 					 sLayer.evaluateAt_bullen(depth,getRadiusOfEarth()));
278 		    return minPSoFar;
279 		} else {
280 		    minPSoFar = Math.min(minPSoFar, sLayer.getBotP());
281 		}
282 	    }
283 	} else {
284 	    sLayer = getSlownessLayer(layerNumberAbove(depth, isPWave), isPWave);
285 	    if (depth == sLayer.getBotDepth()) {
286 		minPSoFar = sLayer.getBotP();
287 	    } else {
288 		minPSoFar =  sLayer.evaluateAt_bullen(depth,getRadiusOfEarth());
289 	    }
290 	}
291 	return minPSoFar;
292     }
293 
294     /*** @returns the minimum ray parameter that turns or is reflected
295      *  at or above the given depth. Normally this is the slowness sample
296      *  at the given depth, but if the depth is within a high slowness zone,
297      *  then it may be smaller. Also, at first order discontinuities, there
298      *  may be many slowness samples at the same depth.
299      */
300     public double getMinRayParam(double depth, boolean isPWave) 
301 	throws NoSuchLayerException, SlownessModelException {
302 	double minPSoFar = getMinTurnRayParam(depth, isPWave); 
303 	int i=layerNumberAbove(depth, isPWave);
304 	int j=layerNumberBelow(depth, isPWave);
305 	SlownessLayer sLayerAbove = getSlownessLayer(i, isPWave);
306 	SlownessLayer sLayerBelow = getSlownessLayer(j, isPWave);
307 
308 	if (sLayerAbove.getBotDepth() == depth) {
309 	    minPSoFar = Math.min(Math.min(minPSoFar, sLayerAbove.getBotP()), sLayerBelow.getTopP());
310 	}
311 	return minPSoFar;
312     }
313 
314     /*** @returns the DepthRange objects for all high slowness zones
315      *  within the slowness model. */
316     public DepthRange[] getHighSlowness(boolean isPWave) {
317 	Vector highSlownessLayerDepths;
318 	if (isPWave) {
319 	    highSlownessLayerDepths = highSlownessLayerDepthsP;
320 	} else {
321 	    highSlownessLayerDepths = highSlownessLayerDepthsS;
322 	}
323 
324 	DepthRange[] hsz = new DepthRange[highSlownessLayerDepths.size()];
325 	for (int i=0; i< highSlownessLayerDepths.size();i++) {
326 	    hsz[i] = (DepthRange)(
327 				  ((DepthRange)highSlownessLayerDepths.elementAt(i)).clone());
328 	}
329 	return hsz;
330     }
331 
332     /*** @returns a clone of the requested waveType slowness layer. Note that 
333      *  as this is a clone, no changes made to the layer will be
334      *  incorporated into the slowness model. */
335     public SlownessLayer getSlownessLayerClone(int layerNum, boolean isPWave) {
336 	if (isPWave) {
337 	    return (SlownessLayer)((SlownessLayer)PLayers.elementAt(layerNum)).clone();
338 	} else {
339 	    return (SlownessLayer)((SlownessLayer)SLayers.elementAt(layerNum)).clone();
340 	}
341     }
342 
343     /*** Returns the SlownessLayer of the requested waveType. This is NOT a 
344      * clone and any changes will possibly corrupt the SlownessModel.
345      */
346     protected SlownessLayer getSlownessLayer(int layerNum, boolean isPWave) {
347 	if (isPWave) {
348 	    return (SlownessLayer)PLayers.elementAt(layerNum);
349 	} else {
350 	    return (SlownessLayer)SLayers.elementAt(layerNum);
351 	}
352     }
353    
354     // Abstract methods
355 
356     public abstract double toSlowness(double velocity, double depth) throws SlownessModelException;
357    
358     public abstract double toVelocity(double slowness, double depth) throws SlownessModelException;
359    
360     public abstract TimeDist layerTimeDist(double rayParam, int layerNum, 
361 					   boolean isPWave) throws SlownessModelException;
362 
363     public abstract SlownessLayer toSlownessLayer(VelocityLayer vLayer, boolean isPWave) throws SlownessModelException;
364 
365     public abstract double interpolate(double p, double topVelocity,
366 				       double topDepth, double slope) throws SlownessModelException;
367 
368     // Defined methods
369 
370     /*** generate approximate distance, in radians, for a ray from a surface
371      * source that turns at the bottom of the given slowness layer. 
372      *
373      * @exception NoSuchLayerException occurs if no layer in the 
374      *                            velocity model contains the given depth.
375      * @exception SlownessModelException occurs if getNumLayers() == 0 as
376      *                            we cannot compute a distance without a
377      *                            layer.
378      */
379     public TimeDist approxDistance(int slownessTurnLayer, double p, boolean isPWave)
380 	throws NoSuchLayerException,
381 	SlownessModelException
382     {
383 	/*
384 	 * First, if slowness contains less than slownessTurnLayer elements
385 	 * then we can't calculate a distance, otherwise we must signal
386 	 * an exception.
387 	 */
388 	if (slownessTurnLayer>=getNumLayers(isPWave)) {
389 	    throw new SlownessModelException(
390 					     "Can't calculate a distance when "+
391 					     "slownessTurnLayer >= getNumLayers("+isPWave+")\n"+
392 					     " slownessTurnLayer="+slownessTurnLayer+
393 					     " getNumLayers()="+getNumLayers(isPWave));
394 	}
395 	if (p < 0.0) {
396 	    throw new SlownessModelException(
397 					     "approxDistance: Ray parameter is negative!!!"+p+
398 					     " slownessTurnLayer="+slownessTurnLayer);
399 	}
400 
401 	/*
402 	 * OK, now we are able to do the calculations for the approximate
403 	 * distance, hopefully without errors.
404 	 */
405 
406 	TimeDist td = new TimeDist(p);
407 	TimeDist layerTD;
408 
409 	for (int layerNum=0;layerNum<=slownessTurnLayer;layerNum++) {
410 	    td.add( layerTimeDist(p, layerNum, isPWave));
411 	}
412 	/* Return 2.0*distance and time because there is a downgoing as well as
413 	 * up going leg, which are equal because this is for a surface source.
414 	 */
415 	td.dist *= 2.0;
416 	td.time *= 2.0;
417 	return td;
418     }
419 
420     /*** Determines if the given depth and corresponding slowness
421      *  is contained within a high
422      *  slowness zone. Whether the high slowness zone includes its upper
423      *  boundary and its lower boundaries depends upon the ray parameter.
424      *  The slowness at the depth is needed because if depth happens to
425      *  correspond to a discontinuity that marks the bottom of the
426      *  high slowness zone but the ray is actually a total reflection
427      *  then it is not part of the high slowness zone.
428      *  Calls depthInHighSlowness(double, double, DepthRange). 
429      *  @see depthInHighSlowness. */
430     public boolean depthInHighSlowness(double depth, double rayParam, boolean isPWave) {
431 	DepthRange highSZoneDepth = new DepthRange();
432 	return depthInHighSlowness(depth, rayParam, highSZoneDepth, isPWave);
433     }
434 
435     /*** Determines if the given depth and corresponding slowness
436      * is contained within a high
437      *  slowness zone. Whether the high slowness zone includes its upper
438      *  boundary and its lower boundaries depends upon the ray parameter.
439      *  The slowness at the depth is needed because if depth happens to
440      *  correspond to a discontinuity that marks the bottom of the
441      *  high slowness zone but the ray is actually a total reflection
442      *  then it is not part of the high slowness zone.
443      *  The ray parameter that delimits the zone, ie it can turn at the
444      *  top and the bottom, is in the zone at the top, but out of the
445      *  zone at the bottom.
446      *  */
447     public boolean depthInHighSlowness(double depth, double rayParam,
448 				       DepthRange highSZoneDepth, boolean isPWave) {
449 	DepthRange tempRange;
450 	Vector highSlownessLayerDepths;
451 	if (isPWave) {
452 	    highSlownessLayerDepths = highSlownessLayerDepthsP;
453 	} else {
454 	    highSlownessLayerDepths = highSlownessLayerDepthsS;
455 	}
456 
457 	for (int i=0;i<highSlownessLayerDepths.size();i++) {
458 	    tempRange = (DepthRange)highSlownessLayerDepths.elementAt(i);
459 	    if (tempRange.topDepth <= depth && depth <= tempRange.botDepth) {
460 		highSZoneDepth.topDepth = tempRange.topDepth;
461 		highSZoneDepth.botDepth = tempRange.botDepth;
462 		highSZoneDepth.rayParam = tempRange.rayParam;
463 		if (rayParam > tempRange.rayParam || (
464 						      rayParam == tempRange.rayParam &&
465 						      depth == tempRange.topDepth)) {
466 		    return true;
467 		}
468 	    }
469 	}
470 	return false;
471     }
472 
473     /*** Determines if the given depth is contained within a fluid
474      *  zone. The fluid zone includes its upper
475      *  boundary but not its lower boundary. Calls
476      *  depthInFluid(double, DepthRange).
477      *  @see depthInFluid(double, DepthRange). */
478     public boolean depthInFluid(double depth) {
479 	DepthRange fluidZoneDepth = new DepthRange();
480 	return depthInFluid(depth, fluidZoneDepth);
481     }
482  
483     /*** Determines if the given depth is contained within a 
484      *  fluid zone. The fluid zone includes its upper
485      *  boundary but not its lower boundary. The top and bottom
486      *  of the fluid zone are returned in DepthRange.
487      */
488     public boolean depthInFluid(double depth, DepthRange fluidZoneDepth) {
489 	DepthRange tempRange;
490  
491 	for (int i=0;i<fluidLayerDepths.size();i++) {
492 	    tempRange = (DepthRange)fluidLayerDepths.elementAt(i);
493 	    if (tempRange.topDepth <= depth && depth < tempRange.botDepth) {
494 		fluidZoneDepth.topDepth = tempRange.topDepth;
495 		fluidZoneDepth.botDepth = tempRange.botDepth;
496 		return true;
497 	    }
498 	}
499 	return false;
500     }
501 
502     /* Splits a slowness layer into two slowness layers. 
503        returns a SplitLayerInfo object with 
504        neededSplit=true if a layer was actually split, false otherwise,
505        movedSample=true if a layer was very close, and so moving the layers
506        depth is better than making a very thin layer,
507        rayParam= the new ray parameter, if the layer was split.
508        The interpolation for splitting a layer
509        is a Bullen p=Ar^B and so does not directly use information from the
510        VelocityModel. */
511     public SplitLayerInfo splitLayer(double depth, boolean isPWave) 
512 	throws SlownessModelException, NoSuchLayerException {
513 	Vector layers, otherLayers;
514 	if (isPWave) {
515 	    layers = PLayers;
516 	    otherLayers = SLayers;
517 	} else {
518 	    layers = SLayers;
519 	    otherLayers = PLayers;
520 	}
521 	boolean otherWaveType = ! isPWave;
522 	boolean changeMade = false;
523 
524 	int layerNum = layerNumberAbove(depth, isPWave);
525 	SlownessLayer sLayer = getSlownessLayer(layerNum, isPWave);
526 
527 	if (sLayer.getTopDepth()==depth || sLayer.getBotDepth()==depth) {
528             /* depth is already on a slowness layer boundary so we don't
529              * need to split any slowness layers. */
530 	    return new SplitLayerInfo(false, false, 0.0);
531 	} else if (Math.abs(sLayer.getTopDepth() - depth) < 0.000001) {
532 	    /* check for very thin layers, just move the layer to hit the boundary */
533 	    sLayer.setTopDepth(depth);
534 	    sLayer = getSlownessLayer(layerNum-1, isPWave);
535 	    sLayer.setBotDepth(depth);
536 	    return new SplitLayerInfo(false, true, sLayer.getBotP());
537 	} else if (Math.abs(depth - sLayer.getBotDepth()) < 0.000001) {
538 	    /* check for very thin layers, just move the layer to hit the boundary */
539 	    sLayer.setBotDepth(depth);
540 	    sLayer = getSlownessLayer(layerNum+1, isPWave);
541 	    sLayer.setTopDepth(depth);
542 	    return new SplitLayerInfo(false, true, sLayer.getTopP());
543 	} else {
544 	    double p = sLayer.evaluateAt_bullen(depth, radiusOfEarth);
545 	    int index = -1;
546 	    int otherIndex = -1;
547 	    SlownessLayer topLayer, botLayer;
548 	  
549 	    topLayer = (SlownessLayer)sLayer.clone();
550 	    topLayer.setBotP(p);
551 	    topLayer.setBotDepth(depth);
552 
553 	    botLayer = (SlownessLayer)sLayer.clone();
554 	    botLayer.setTopP(p);
555 	    botLayer.setTopDepth(topLayer.getBotDepth());
556 	    layers.removeElementAt(layerNum);
557 	    layers.insertElementAt(botLayer, layerNum);
558 	    layers.insertElementAt(topLayer, layerNum);
559 	    // fix critical layers since we have added a slowness layer
560 	    for (int i=0; i<getNumCriticalDepths(); i++) {
561 		CriticalDepth cd = getCriticalDepth(i);
562 		if (cd.getLayerNum(isPWave) > layerNum) {
563 		    cd.setLayerNum(cd.getLayerNum(isPWave)+1, isPWave);
564 		}
565 	    }
566 	    // now make sure we keep the sampling consistant
567 	    // if in a fluid, then both wavetypes will share a single 
568 	    // slowness layer object. Otherwise indexOf returns -1
569 	    otherIndex = otherLayers.indexOf(sLayer);
570 	    if (otherIndex != -1) {
571 		otherLayers.removeElementAt(otherIndex);
572 		otherLayers.insertElementAt(botLayer, otherIndex);
573 		otherLayers.insertElementAt(topLayer, otherIndex);
574 	    }
575 	    for (int otherLayerNum=0; otherLayerNum< otherLayers.size() ; otherLayerNum++) {
576 		sLayer = (SlownessLayer)otherLayers.elementAt(otherLayerNum);
577 		if ((sLayer.getTopP() - p)*(p-sLayer.getBotP()) > 0.0) {
578 		    // found the slowness layer with the other wave type that
579 		    // contains the new slowness sample
580 		    topLayer = (SlownessLayer)sLayer.clone();
581 		    topLayer.setBotP(p);
582 		    topLayer.setBotDepth(sLayer.bullenDepthFor(p, radiusOfEarth));
583 		  
584 		    botLayer = (SlownessLayer)sLayer.clone();
585 		    botLayer.setTopP(p);
586 		    botLayer.setTopDepth(topLayer.getBotDepth());
587 		  
588 		    otherLayers.removeElementAt(otherLayerNum);
589 		    otherLayers.insertElementAt(botLayer, otherLayerNum);
590 		    otherLayers.insertElementAt(topLayer, otherLayerNum);
591 		    // fix critical layers since we have added a slowness layer
592 		    for (int critNum=0; critNum<getNumCriticalDepths(); critNum++) {
593 			CriticalDepth cd = getCriticalDepth(critNum);
594 			if (cd.getLayerNum(otherWaveType) > otherLayerNum) {
595 			    cd.setLayerNum(cd.getLayerNum(otherWaveType)+1, otherWaveType);
596 			}
597 		    }
598 		}
599 	    }
600 	    return new SplitLayerInfo(true, false, p);
601 	}
602     }
603 
604     /***
605      * Finds all critical points within a velocity model. Critical points
606      * are first order discontinuities in velocity/slowness, local extrema
607      * in slowness. A high
608      * slowness zone is a low velocity zone, but it is possible to have
609      * a slight low velocity zone within a spherical earth that is
610      * not a high slowness zone and thus does not exhibit 
611      * any of the pathological behavior of a low velocity zone.
612      *
613      * @exception NoSuchMatPropException occurs if wavetype is not
614      *    recognized.
615      * @exception SlownessModelException occurs if validate() returns
616      *    false, this indicates a bug in the code.
617      */
618     protected void findCriticalPoints()
619 	throws SlownessModelException {
620 	
621 	double topP, botP;
622 	double topDepth, botDepth = getRadiusOfEarth();
623 	double topVelocity, botVelocity;
624 	
625 	double minPSoFar = Double.MAX_VALUE;
626 	double minSSoFar = Double.MAX_VALUE;
627 	DepthRange highSlownessZoneP = new DepthRange();
628 	DepthRange highSlownessZoneS = new DepthRange();
629 	boolean inHighSlownessZoneP = false;
630 	boolean inHighSlownessZoneS = false;
631 	
632 	DepthRange fluidZone = new DepthRange();
633 	boolean inFluidZone = false;
634 	
635 	boolean belowOuterCore = false;  /* are we in the inner core,
636 					  * see allowInnerCoreS. */
637 	
638 	VelocityLayer prevVLayer, currVLayer;
639 	
640 	SlownessLayer prevSLayer, currSLayer, prevPLayer, currPLayer;
641 	
642 	// First remove any critical points previously stored
643 	highSlownessLayerDepthsP.removeAllElements();
644 	highSlownessLayerDepthsS.removeAllElements();
645 	criticalDepthVector.removeAllElements();
646 	fluidLayerDepths.removeAllElements();
647 	
648 	// Initialize the current velocity layer
649 	// to be zero thickness layer with values at the surface
650     currVLayer = vMod.getVelocityLayer(0);
651     currVLayer = new VelocityLayer(0,
652                                    currVLayer.getTopDepth(), currVLayer.getTopDepth(),
653                                    currVLayer.getTopPVelocity(), currVLayer.getTopPVelocity(),
654                                    currVLayer.getTopSVelocity(), currVLayer.getTopSVelocity(),
655                                    currVLayer.getTopDensity(), currVLayer.getTopDensity(),
656                                    currVLayer.getTopQp(), currVLayer.getTopQp(),
657                                    currVLayer.getTopQs(),currVLayer.getTopQs());
658 	
659 	currSLayer =  toSlownessLayer(currVLayer, SWAVE);
660 	currPLayer =  toSlownessLayer(currVLayer, PWAVE);
661 	
662 	
663 	// We know that the top is always a critical slowness so add 0
664 	criticalDepthVector.addElement(new CriticalDepth(0.0, 0, 0, 0));
665 	
666 	/* Check to see if we start in a fluid zone. */
667 	if (!inFluidZone && currVLayer.getTopSVelocity()==0.0 )  {
668 	    inFluidZone = true;
669 	    fluidZone = new DepthRange();
670 	    fluidZone.topDepth = currVLayer.getTopDepth();
671 	    currSLayer = currPLayer;
672 	}
673 	if (minSSoFar > currSLayer.getTopP()) {
674 	    minSSoFar = currSLayer.getTopP();
675 	}
676 	if (minPSoFar > currPLayer.getTopP()) {
677 	    minPSoFar = currPLayer.getTopP();
678 	}
679 	
680 	for (int layerNum=0;layerNum<vMod.getNumLayers();layerNum++) {
681 	    
682 	    prevVLayer = currVLayer;
683 	    prevSLayer = currSLayer;
684 	    prevPLayer = currPLayer;
685 	    currVLayer = vMod.getVelocityLayerClone(layerNum);
686 	    currSLayer = toSlownessLayer(currVLayer, SWAVE);
687 	    currPLayer = toSlownessLayer(currVLayer, PWAVE);
688 
689             /* If we are not already in a fluid check to see if we have 
690              * just entered a fluid zone. */
691 	    if (!inFluidZone && currVLayer.getTopSVelocity()==0.0 )  {
692 		inFluidZone = true;
693 		fluidZone = new DepthRange();
694 		fluidZone.topDepth = currVLayer.getTopDepth();
695 	    }
696 
697             /* If we are already in a fluid check to see if we have 
698              * just exited it. */
699 	    if (inFluidZone && currVLayer.getTopSVelocity()!=0.0)  {
700 		if (prevVLayer.getBotDepth() > vMod.getIocbDepth() ) {
701 		    belowOuterCore = true;
702 		}
703 		inFluidZone = false;
704 		fluidZone.botDepth = prevVLayer.getBotDepth();
705 		fluidLayerDepths.addElement(fluidZone);
706 	    }
707 
708             /* If we are in a fluid zone ( S velocity = 0.0 ) or if
709              * we are below the outer core and allowInnerCoreS=false
710              * then use the P velocity structure to look for critical points. */
711 	    if (inFluidZone || ( belowOuterCore && ! allowInnerCoreS )) {
712 		currSLayer = currPLayer;
713 	    }
714 
715 	    if (prevSLayer.getBotP() != currSLayer.getTopP() || prevPLayer.getBotP() != currPLayer.getTopP()) {
716 		// first order discontinuity
717 		criticalDepthVector.addElement(new CriticalDepth(currSLayer.getTopDepth(), layerNum, -1, -1));
718 
719 		if (DEBUG) {
720 		    System.out.println("first order discontinuity, depth="+
721 				       currSLayer.getTopDepth());
722 		    System.out.println(prevSLayer+"\n"+currSLayer);
723 		    System.out.println(prevPLayer+"\n"+currPLayer);
724 		}
725 
726 		if (inHighSlownessZoneS && (currSLayer.getTopP() < minSSoFar)) {
727 		    // top of current layer is the bottom of a high slowness zone.
728 		    if (DEBUG) {
729 			System.out.println("top of current layer is the bottom"+
730 					   " of a high slowness zone.");
731 		    }
732 		    highSlownessZoneS.botDepth = currSLayer.getTopDepth();
733 		    highSlownessLayerDepthsS.addElement(highSlownessZoneS);
734 		    inHighSlownessZoneS = false;
735 		}
736 		if (inHighSlownessZoneP && (currPLayer.getTopP() < minPSoFar)) {
737 		    // top of current layer is the bottom of a high slowness zone.
738 		    if (DEBUG) {
739 			System.out.println("top of current layer is the bottom"+
740 					   " of a high slowness zone.");
741 		    }
742 		    highSlownessZoneP.botDepth = currSLayer.getTopDepth();
743 		    highSlownessLayerDepthsP.addElement(highSlownessZoneP);
744 		    inHighSlownessZoneP = false;
745 		}
746 
747 		/* Update minPSoFar and minSSoFar as all total reflections off of the top
748 		 * of the discontinuity are ok even though below the 
749 		 * discontinuity could be the start of a high slowness zone. */
750 		if (minPSoFar > currPLayer.getTopP()) {
751 		    minPSoFar = currPLayer.getTopP();
752 		}
753 		if (minSSoFar > currSLayer.getTopP()) {
754 		    minSSoFar = currSLayer.getTopP();
755 		}
756             
757 		if (!inHighSlownessZoneS && (
758 					     prevSLayer.getBotP() < currSLayer.getTopP() ||
759 					     currSLayer.getTopP() < currSLayer.getBotP())) {
760 		    // start of a high slowness zone
761 		    if (DEBUG) {
762 			System.out.println("Found S high slowness at first order "+
763 					   "discontinuity, layer = "+layerNum);
764 		    }
765 		    inHighSlownessZoneS = true;
766 		    highSlownessZoneS = new DepthRange();
767 		    highSlownessZoneS.topDepth = currSLayer.getTopDepth();
768 		    highSlownessZoneS.rayParam = minSSoFar;
769 		}
770 		if (!inHighSlownessZoneP && (
771 					     prevPLayer.getBotP() < currPLayer.getTopP() ||
772 					     currPLayer.getTopP() < currPLayer.getBotP())) {
773 		    // start of a high slowness zone
774 		    if (DEBUG) {
775 			System.out.println("Found P high slowness at first order "+
776 					   "discontinuity, layer = "+layerNum);
777 		    }
778 		    inHighSlownessZoneP = true;
779 		    highSlownessZoneP = new DepthRange();
780 		    highSlownessZoneP.topDepth = currPLayer.getTopDepth();
781 		    highSlownessZoneP.rayParam = minPSoFar;
782 		}
783 
784 	    } else {
785 		if ((prevSLayer.getTopP()-prevSLayer.getBotP())*
786 		    (prevSLayer.getBotP()-currSLayer.getBotP()) < 0.0 ||
787 		    (prevPLayer.getTopP()-prevPLayer.getBotP())*
788 		    (prevPLayer.getBotP()-currPLayer.getBotP()) < 0.0) {
789 		         	// local slowness extrema
790 		    criticalDepthVector.addElement(new CriticalDepth(currSLayer.getTopDepth(),layerNum,-1,-1));
791 
792 		    if (DEBUG) {
793 			System.out.println("local slowness extrema, depth="+
794 					   currSLayer.getTopDepth());
795 		    }
796 
797 		    if (!inHighSlownessZoneP && (currPLayer.getTopP() < currPLayer.getBotP())) {
798 			// start of a high slowness zone
799 			if (DEBUG) {
800 			    System.out.println("start of a P high slowness zone,"+
801 					       " local slowness extrema, minPSoFar="+minPSoFar);
802 			}
803 			inHighSlownessZoneP = true;
804 			highSlownessZoneP = new DepthRange();
805 			highSlownessZoneP.topDepth = currPLayer.getTopDepth();
806 			highSlownessZoneP.rayParam = minPSoFar;
807 		    }
808 		    if (!inHighSlownessZoneS && (currSLayer.getTopP() < currSLayer.getBotP())) {
809 			// start of a high slowness zone
810 			if (DEBUG) {
811 			    System.out.println("start of a S high slowness zone,"+
812 					       " local slowness extrema, minSSoFar="+minSSoFar);
813 			}
814 			inHighSlownessZoneS = true;
815 			highSlownessZoneS = new DepthRange();
816 			highSlownessZoneS.topDepth = currSLayer.getTopDepth();
817 			highSlownessZoneS.rayParam = minSSoFar;
818 		    }
819 		}
820 	    }
821 
822 	    if (inHighSlownessZoneP && (currPLayer.getBotP() < minPSoFar)) {
823 		// layer contains the bottom of a high slowness zone.
824 		if (DEBUG) {
825 		    System.out.println("layer contains the bottom of a P "+
826 				       "high slowness zone. minPSoFar="+minPSoFar+" "+currPLayer);
827 		}
828 		highSlownessZoneP.botDepth = findDepth(minPSoFar,
829 						       layerNum, layerNum, PWAVE);
830 		highSlownessLayerDepthsP.addElement(highSlownessZoneP);
831 		inHighSlownessZoneP = false;
832 	    }
833 	    if (inHighSlownessZoneS && (currSLayer.getBotP() < minSSoFar)) {
834 		// layer contains the bottom of a high slowness zone.
835 		if (DEBUG) {
836 		    System.out.println("layer contains the bottom of a S "+
837 				       "high slowness zone. minSSoFar="+minSSoFar+" "+currSLayer);
838 		}
839 		highSlownessZoneS.botDepth = findDepth(minSSoFar,
840 						       layerNum, layerNum, SWAVE);
841 		highSlownessLayerDepthsS.addElement(highSlownessZoneS);
842 		inHighSlownessZoneS = false;
843 	    }
844 
845 	    if (minPSoFar > currPLayer.getBotP()) {
846 		minPSoFar = currPLayer.getBotP();
847 	    }
848 	    if (minPSoFar > currPLayer.getTopP()) {
849 		minPSoFar = currPLayer.getTopP();
850 	    }
851 	    if (minSSoFar > currSLayer.getBotP()) {
852 		minSSoFar = currSLayer.getBotP();
853 	    }
854 	    if (minSSoFar > currSLayer.getTopP()) {
855 		minSSoFar = currSLayer.getTopP();
856 	    }
857 
858 	    if (DEBUG && inHighSlownessZoneS) {
859 		System.out.println("In S high slowness zone, layerNum = "+layerNum+
860 				   " minSSoFar="+minSSoFar);
861 	    }
862 	    if (DEBUG && inHighSlownessZoneP) {
863 		System.out.println("In P high slowness zone, layerNum = "+layerNum+
864 				   " minPSoFar="+minPSoFar);
865 	    }
866 	}
867 
868 	// We know that the bottommost depth is always a critical slowness,
869 	// so we add vMod.getNumLayers()
870 	criticalDepthVector.addElement(new CriticalDepth(getRadiusOfEarth(),vMod.getNumLayers(),-1,-1));
871 
872 	// Check if the bottommost depth is contained within a high slowness
873 	// zone, might happen in a flat non-whole-earth model
874 	if (inHighSlownessZoneS) {
875 	    highSlownessZoneS.botDepth = currVLayer.getBotDepth();
876 	    highSlownessLayerDepthsS.addElement(highSlownessZoneS);
877 	}
878 	if (inHighSlownessZoneP) {
879 	    highSlownessZoneP.botDepth = currVLayer.getBotDepth();
880 	    highSlownessLayerDepthsP.addElement(highSlownessZoneP);
881 	}
882 
883 	/* Check if the bottommost depth is contained within a fluid zone,
884 	 * this would be the case if we have a non whole earth model with
885 	 * the bottom in the outer core or if allowInnerCoreS == false
886 	 * and we want to use the P velocity structure in the inner core. */
887 	if (inFluidZone ) {
888 	    fluidZone.botDepth = currVLayer.getBotDepth();
889 	    fluidLayerDepths.addElement(fluidZone);
890 	}
891 
892 	if (DEBUG && criticalDepthVector.size()!=0) {
893 	    int botCriticalLayerNum, topCriticalLayerNum;
894 	    String desc = 
895 		"**** Critical Velocity Layers ************************\n";
896 	    botCriticalLayerNum = 
897 		((CriticalDepth)criticalDepthVector.elementAt(0)).velLayerNum-1;
898 	    for (int criticalNum=1; criticalNum<criticalDepthVector.size(); criticalNum++) {
899 		topCriticalLayerNum = botCriticalLayerNum+1;
900 		botCriticalLayerNum = 
901 		    ((CriticalDepth)criticalDepthVector.elementAt(criticalNum)).velLayerNum-1;
902 		desc += " "+topCriticalLayerNum+","+botCriticalLayerNum;
903 	    }
904 	    System.out.println(desc);
905 	}
906 
907 	if (DEBUG && highSlownessLayerDepthsP.size()!=0) {
908 	    for (int layerNum=0;layerNum<highSlownessLayerDepthsP.size();layerNum++) {
909 		System.out.println((DepthRange)highSlownessLayerDepthsP.elementAt(layerNum));
910 	    }
911 	}
912 	if (DEBUG && highSlownessLayerDepthsS.size()!=0) {
913 	    for (int layerNum=0;layerNum<highSlownessLayerDepthsS.size();layerNum++) {
914 		System.out.println((DepthRange)highSlownessLayerDepthsS.elementAt(layerNum));
915 	    }
916 	}
917       
918 	if (!validate()) {
919 	    throw new SlownessModelException("Validation Failed!");
920 	}
921     }
922    
923     /*** Finds a depth corresponding to a slowness over the whole 
924      *  VelocityModel. Calls findDepth(double, int, int, char).
925      */
926     public double findDepth(double rayParam, boolean isPWave) 
927 	throws SlownessModelException {
928 	return findDepth(rayParam, 0, vMod.getNumLayers()-1, isPWave);
929     }
930 
931     /*** Finds a depth corresponding to a slowness between two given
932      *  depths in the Velocity Model. Calls findDepth(double, int, int, char).
933      */
934     public double findDepth(double rayParam, double topDepth, double botDepth,
935 			    boolean isPWave) throws SlownessModelException {
936 	try {
937 	    int topLayerNum = vMod.layerNumberBelow(topDepth);
938 	    if (vMod.getVelocityLayer(topLayerNum).getBotDepth() == topDepth) {
939 		topLayerNum++;
940 	    }
941 	    int botLayerNum = vMod.layerNumberAbove(botDepth);
942 	    return findDepth(rayParam, topLayerNum, botLayerNum, isPWave);
943 	} catch (NoSuchLayerException e) {
944 	    throw new SlownessModelException(e.getMessage());
945 	}
946     }
947 
948     /*** Finds a depth corresponding to a slowness between two given 
949      *  velocity layers, including the top and the bottom.
950      *  We also check to see if the slowness is less than the bottom
951      *  slowness of these layers but greater than the top slowness
952      *  of the next deeper layer. This corresponds to a total reflection.
953      *  In this case a check needs to be made to see if this is an S wave
954      *  reflecting off of a fluid layer, use P velocity below in this case.
955      *  We assume that slowness is monotonic within these layers and
956      *  therefore there is only one depth with the given slowness.
957      *  This means we return the first depth that we find.
958      *
959      *  @exception SlownessModelException occurs if
960      *     topCriticalLayer > botCriticalLayer because there are no layers 
961      *     to search, or if there is an increase in slowness, ie a negative
962      *     velocity gradient, that just balances the decrease in slowness due
963      *     to the spherical earth, or if the ray parameter p is not contained
964      *     within the specified layer range.
965      */
966     public double findDepth(double p,
967 			    int topCriticalLayer, int botCriticalLayer, boolean isPWave)
968 	throws SlownessModelException {
969 
970 	VelocityLayer velLayer = new VelocityLayer();
971 	double topP=Double.MAX_VALUE, botP=Double.MAX_VALUE;
972 	double topVelocity, botVelocity;
973 	double depth;
974 	double denominator;
975 	double slope;
976 
977 	char waveType;
978 	if (isPWave) {
979 	    waveType = 'P';
980 	} else {
981 	    waveType = 'S';
982 	}
983 		
984 	try {
985 	    if (topCriticalLayer > botCriticalLayer) {
986 		throw new SlownessModelException("findDepth: no layers to search!: "+
987 						 "topCriticalLayer = "+topCriticalLayer+
988 						 "botCriticalLayer = "+botCriticalLayer);
989 	    }
990 
991 	    for (int layerNum=topCriticalLayer;layerNum<=botCriticalLayer;layerNum++){
992 		velLayer = (VelocityLayer)vMod.getVelocityLayer(layerNum);
993 		topVelocity = velLayer.evaluateAtTop(waveType);
994 		botVelocity = velLayer.evaluateAtBottom(waveType);
995 
996 		topP = toSlowness(topVelocity, velLayer.getTopDepth());
997 		botP = toSlowness(botVelocity, velLayer.getBotDepth());
998 	 
999 		/* check to see if we are within chatter level of the top or bottom
1000 		 * and if so then return that depth. */
1001 		if (Math.abs(topP - p) < slownessTolerance) {
1002 	            return velLayer.getTopDepth();
1003 		} 
1004 		if (Math.abs(p - botP) < slownessTolerance) {
1005 	            return velLayer.getBotDepth();
1006 		} 
1007 
1008 		if ((topP - p)*(p - botP) >= 0.0) {  // found the layer containing p
1009 		    /*
1010 		     * We interpolate assuming that velocity is
1011 		     * linear within this interval. So slope is the slope
1012 		     * for velocity versus depth.
1013 		     */
1014 	            slope = (botVelocity-topVelocity)/
1015 			(velLayer.getBotDepth()-velLayer.getTopDepth());
1016 	            depth = interpolate(p, topVelocity, velLayer.getTopDepth(), slope);
1017 	            return depth;
1018 
1019 		} else if (layerNum==topCriticalLayer && 
1020 			   Math.abs(p-topP)<slownessTolerance) {
1021 		    /* Check to see if p is just outside the topmost layer.
1022 		     * If so than return the top depth.  */
1023 	            return velLayer.getTopDepth();
1024 		}
1025 
1026 		/* Is p a total reflection? 
1027 		 * botP is the slowness at the bottom of the last velocity
1028 		 * layer from the previous loop, set topP to be the slowness
1029 		 * at the top of the next layer. */
1030 		if (layerNum < vMod.getNumLayers()-1) {
1031 	            velLayer = (VelocityLayer)vMod.getVelocityLayerClone(
1032 									 layerNum+1);
1033 	            topVelocity = velLayer.evaluateAtTop(waveType);
1034 	   
1035 	            if (! isPWave &&
1036 			depthInFluid(velLayer.getTopDepth())){
1037 			/* Special case for S waves above a fluid. If 
1038 			 * top next layer is in a fluid then we should set topVelocity
1039 			 * to be the P velocity at the top of the layer. */
1040 			topVelocity = velLayer.evaluateAtTop('P');
1041 	            } 
1042 	            topP = toSlowness(topVelocity, velLayer.getTopDepth());
1043 	            if (botP >= p && p >= topP) {
1044 	                return velLayer.getTopDepth();
1045 	            }
1046 		}
1047 	    }
1048 
1049 	    if (Math.abs(p-botP)<slownessTolerance) {
1050 		/* Check to see if p is just outside the bottommost layer.
1051 		 * If so than return the bottom depth.  */
1052 		System.out.println(" p is just outside the bottommost layer."+
1053 				   " This probably shouldn't be allowed to happen!\n");
1054 		return velLayer.getBotDepth();
1055 	    }
1056 	} catch (NoSuchMatPropException e) {
1057 	    // can't happen...
1058 	    e.printStackTrace();
1059 	}
1060 		
1061 	throw new SlownessModelException("slowness p="+p+
1062 					 " is not contained within the specified layers."+
1063 					 "\np="+p+" topCriticalLayer="+topCriticalLayer+
1064 					 " botCriticalLayer="+ botCriticalLayer+" isPWave="+ isPWave+
1065 					 " topP="+topP+" botP="+botP);
1066     }
1067 
1068     /***
1069      * This method takes a velocity model and creates a vector containing
1070      * slowness-depth layers that, hopefully, adequately sample both
1071      * slowness and depth so that the travel time as a function of
1072      * distance can be reconstructed from the theta function.
1073      * It catches NoSuchLayerException which might be generated in the 
1074      * velocity model. This shouldn't happen though.
1075      * @see VelocityModel
1076      * @exception SlownessModelException occurs if the validation on the
1077      *    velocity model fails, or if the velocity model
1078      *    has no layers.
1079      * @exception NoSuchMatPropException occurs if wavetype is not
1080      *    recognized.
1081      */
1082     public void createSample(VelocityModel velModel) 
1083 	throws SlownessModelException, NoSuchMatPropException, NoSuchLayerException
1084     {
1085 
1086 	VelocityLayer velLayer;
1087 	double maxVelSoFar = 0.0;
1088 	double previousBotVel;
1089 	int botCriticalLayerNum, topCriticalLayerNum;
1090 	DepthRange highSZoneDepth = new DepthRange();
1091 
1092       
1093 	// First check to make sure velocity model is ok.
1094 	if (velModel.validate()== false) {
1095 	    throw new SlownessModelException("Error in velocity model!");
1096 	}
1097 	if (velModel.getNumLayers()==0) {
1098 	    throw new SlownessModelException("velModel.getNumLayers()==0");
1099 	}
1100 	      
1101 	if (DEBUG) {System.out.println("start createSample");}
1102 	vMod = velModel;
1103 	setRadiusOfEarth(velModel.getRadiusOfEarth());
1104 
1105  
1106 	if (DEBUG) {System.out.println("findCriticalPoints");}
1107 	findCriticalPoints();
1108 	if (DEBUG) {System.out.println("coarseSample");}
1109 	coarseSample();
1110 
1111 	boolean isOK = false;
1112 	if (DEBUG) {
1113 	    isOK = validate();
1114 	    System.out.println("rayParamCheck");}
1115 	rayParamIncCheck();
1116 
1117 	if (DEBUG) {
1118 	    isOK &= validate();
1119 	    System.out.println("depthIncCheck");}
1120 	depthIncCheck();
1121 
1122 	if (DEBUG) {
1123 	    isOK &= validate();
1124 	    System.out.println("distanceCheck");}
1125 	distanceCheck();
1126 
1127 	if (DEBUG) {
1128 	    isOK &= validate();
1129 	    System.out.println("fixCriticalPoints");}
1130 	fixCriticalPoints();
1131 
1132 	if (DEBUG) {
1133 	    System.out.println("done createSample");
1134 	}
1135     }
1136    
1137     /*** Creates a coarse slowness sampling of the velocity model (vMod). The
1138      *  resultant slowness layers will satisfy the maximum depth increments
1139      *  as well as sampling each point specified within the VelocityModel. 
1140      * The P and S sampling will also be compatible.
1141      */
1142     protected void coarseSample() 
1143 	throws SlownessModelException, NoSuchLayerException {
1144 	VelocityLayer prevVLayer;
1145 	VelocityLayer origVLayer;
1146 	VelocityLayer currVLayer = new VelocityLayer();
1147 	SlownessLayer currPLayer, currSLayer;
1148 
1149 	PLayers.removeAllElements();
1150 	SLayers.removeAllElements();
1151       
1152       	// to initialize prevVLayer
1153     origVLayer = vMod.getVelocityLayer(0);
1154     origVLayer = new VelocityLayer(0, 
1155                                    origVLayer.getTopDepth(), origVLayer.getTopDepth(),
1156                                    origVLayer.getTopPVelocity(), origVLayer.getTopPVelocity(),
1157                                    origVLayer.getTopSVelocity(), origVLayer.getTopSVelocity(),
1158                                    origVLayer.getTopDensity(), origVLayer.getTopDensity(),
1159                                    origVLayer.getTopQp(), origVLayer.getTopQp(),
1160                                    origVLayer.getTopQs(),origVLayer.getTopQs());
1161 
1162 	try {
1163 	    for (int layerNum=0; layerNum < vMod.getNumLayers(); layerNum++) {
1164 	      	prevVLayer = origVLayer;
1165 		origVLayer = vMod.getVelocityLayer(layerNum);
1166 	         
1167 		/* Check for first order discontinuity. However, we only 
1168 		 * consider S discontinuities in the inner core if 
1169 		 * allowInnerCoreS is true. */
1170 		if (prevVLayer.getBotPVelocity() != origVLayer.getTopPVelocity() ||
1171 		    (prevVLayer.getBotSVelocity() != origVLayer.getTopSVelocity() &&
1172 		     (allowInnerCoreS || 
1173 		      origVLayer.getTopDepth() < vMod.getIocbDepth()))) {
1174 		    currVLayer.setTopDepth(prevVLayer.getBotDepth());
1175 		    currVLayer.setBotDepth(prevVLayer.getBotDepth());
1176 		    currVLayer.setTopPVelocity(prevVLayer.evaluateAtBottom('P'));
1177 		    currVLayer.setBotPVelocity(origVLayer.evaluateAtTop('P'));
1178 	         	
1179 		    /* if we are going from a fluid to a solid or
1180                        solid to fluid, ex core mantle or outer core to
1181                        inner core then we need to use the P velocity
1182                        for determining the S discontinuity. */
1183 		    if (prevVLayer.getBotSVelocity() == 0.0) {
1184 			currVLayer.setTopSVelocity(prevVLayer.evaluateAtBottom('P'));
1185 		    } else {
1186 			currVLayer.setTopSVelocity(prevVLayer.evaluateAtBottom('S'));
1187 		    }
1188 		    if (origVLayer.getTopSVelocity() == 0.0) {
1189 			currVLayer.setBotSVelocity(origVLayer.evaluateAtTop('P'));
1190 		    } else {
1191 			currVLayer.setBotSVelocity(origVLayer.evaluateAtTop('S'));
1192 		    }
1193 	         	
1194 		    /* Add the zero thickness, but with nonzero
1195                        slowness step, layer corresponding to the
1196                        discontinuity. */
1197 		    currPLayer = toSlownessLayer(currVLayer, PWAVE);
1198 		    PLayers.addElement(currPLayer);
1199 		    if ((prevVLayer.getBotSVelocity() == 0.0 && 
1200 			 origVLayer.getTopSVelocity() == 0.0) || 
1201 			(! allowInnerCoreS && 
1202 			 currVLayer.getTopDepth() >= vMod.getIocbDepth())) {
1203 			currSLayer = currPLayer;
1204 		    } else {
1205 			currSLayer = toSlownessLayer(currVLayer, SWAVE);
1206 		    }
1207 		    SLayers.addElement(currSLayer);
1208 		}
1209 	         
1210 		currPLayer = toSlownessLayer(origVLayer, PWAVE);
1211 		PLayers.addElement(currPLayer);
1212 	         
1213 		if (depthInFluid(origVLayer.getTopDepth()) || 
1214 		    (! allowInnerCoreS && origVLayer.getTopDepth() >= vMod.getIocbDepth())) {
1215 		    currSLayer = currPLayer;
1216 		} else {
1217 		    currSLayer = toSlownessLayer(origVLayer, SWAVE);
1218 		}
1219 		SLayers.addElement(currSLayer);
1220 	    }
1221 			
1222 			
1223 	    // make sure that all high slowness layers are sampled exactly
1224 	    // at their bottom
1225 	    int highZoneNum, SLayerNum;
1226 	    SlownessLayer highSLayer;
1227 	    DepthRange highZone;
1228 	    for (highZoneNum = 0; highZoneNum < highSlownessLayerDepthsS.size(); highZoneNum++) {
1229 		highZone = (DepthRange)highSlownessLayerDepthsS.elementAt(highZoneNum);
1230 		SLayerNum = layerNumberAbove(highZone.botDepth, SWAVE);
1231 		highSLayer = getSlownessLayer(SLayerNum, SWAVE);
1232 		while (highSLayer.getTopDepth() == highSLayer.getBotDepth() && 
1233 		       (highSLayer.getTopP() - highZone.rayParam)*(highZone.rayParam - highSLayer.getBotP()) < 0) {
1234 		    SLayerNum++;
1235 		    highSLayer = getSlownessLayer(SLayerNum, SWAVE);
1236 		}
1237 		if (highZone.rayParam != highSLayer.getBotP()) {
1238 		    addSlowness(highZone.rayParam, SWAVE);
1239 		}
1240 	    }
1241 	    for (highZoneNum = 0; highZoneNum < highSlownessLayerDepthsP.size(); highZoneNum++) {
1242 		highZone = (DepthRange)highSlownessLayerDepthsP.elementAt(highZoneNum);
1243 		SLayerNum = layerNumberAbove(highZone.botDepth, PWAVE);
1244 		highSLayer = getSlownessLayer(SLayerNum, PWAVE);
1245 		while (highSLayer.getTopDepth() == highSLayer.getBotDepth() && 
1246 		       (highSLayer.getTopP() - highZone.rayParam)*(highZone.rayParam - highSLayer.getBotP()) < 0) {
1247 		    SLayerNum++;
1248 		    highSLayer = getSlownessLayer(SLayerNum, PWAVE);
1249 		}
1250 		if (highZone.rayParam != highSLayer.getBotP()) {
1251 		    addSlowness(highZone.rayParam, PWAVE);
1252 		}
1253 	    }
1254 	    // make sure P and S sampling are consistant
1255 	    double botP = -1;
1256 	    double topP = -1;
1257 	    for (int j=0; j<PLayers.size(); j++) {
1258 		topP = ((SlownessLayer)PLayers.elementAt(j)).getTopP();
1259 		if (topP != botP) {
1260 		    addSlowness(topP, SWAVE);
1261 		}
1262 		botP = ((SlownessLayer)PLayers.elementAt(j)).getBotP();
1263 		addSlowness(botP, SWAVE);
1264 	    }
1265 	    botP = -1;
1266 	    for (int j=0; j<SLayers.size(); j++) {
1267 		topP = ((SlownessLayer)SLayers.elementAt(j)).getTopP();
1268 		if (topP != botP) {
1269 		    addSlowness(topP, PWAVE);
1270 		}
1271 		botP = ((SlownessLayer)SLayers.elementAt(j)).getBotP();
1272 		addSlowness(botP, PWAVE);
1273 	    }
1274 	} catch (NoSuchMatPropException e) {
1275 	    // can't happen...
1276 	    e.printStackTrace();
1277 	}
1278     }
1279    
1280     /*** Checks to make sure that no slowness layer spans more than maxDeltaP. 
1281      */
1282     protected void rayParamIncCheck() 
1283 	throws SlownessModelException, NoSuchLayerException 
1284     {
1285    	SlownessLayer sLayer;
1286    	double numNewP;
1287    	double deltaP;
1288 
1289    	for (int j=0; j<SLayers.size(); j++) {
1290 	    sLayer = (SlownessLayer)SLayers.elementAt(j);
1291 	    if (Math.abs(sLayer.getTopP() - sLayer.getBotP()) > maxDeltaP) {
1292 		numNewP = Math.ceil(Math.abs(sLayer.getTopP() - sLayer.getBotP()) 
1293 				    / maxDeltaP);
1294 		deltaP = (sLayer.getTopP() - sLayer.getBotP()) / numNewP;
1295 
1296 		for (int rayNum=1; rayNum < numNewP; rayNum++) {
1297 		    addSlowness(sLayer.getTopP()+rayNum*deltaP, PWAVE);
1298 		    addSlowness(sLayer.getTopP()+rayNum*deltaP, SWAVE);
1299 		}
1300 	    }
1301 	}
1302    	for (int j=0; j<PLayers.size(); j++) {
1303 	    sLayer = (SlownessLayer)PLayers.elementAt(j);
1304 	    if (Math.abs(sLayer.getTopP() - sLayer.getBotP()) > maxDeltaP) {
1305 		numNewP = Math.ceil(Math.abs(sLayer.getTopP() - sLayer.getBotP()) 
1306 				    / maxDeltaP);
1307 		deltaP = (sLayer.getTopP() - sLayer.getBotP()) / numNewP;
1308 				
1309 		for (int rayNum=1; rayNum < numNewP; rayNum++) {
1310 		    addSlowness(sLayer.getTopP()+rayNum*deltaP, PWAVE);
1311 		    addSlowness(sLayer.getTopP()+rayNum*deltaP, SWAVE);
1312 		}
1313 	    }
1314 	}
1315     }
1316    
1317     /*** Checks to make sure no slowness layer spans more than 
1318      *  maxDepthInterval. */
1319     protected void depthIncCheck() 
1320 	throws SlownessModelException, NoSuchLayerException 
1321     {
1322    	SlownessLayer sLayer;
1323    	int numNewDepths;
1324    	double deltaDepth;
1325    	double velocity;
1326    	double p;
1327    	
1328 	try {
1329 	    for (int j=0; j<SLayers.size(); j++) {
1330 		sLayer = (SlownessLayer)SLayers.elementAt(j);
1331 		if ((sLayer.getBotDepth() - sLayer.getTopDepth()) > maxDepthInterval) {
1332 		    numNewDepths = (int)Math.ceil((sLayer.getBotDepth() - sLayer.getTopDepth()) / maxDepthInterval);
1333 		    deltaDepth = (sLayer.getBotDepth() - sLayer.getTopDepth()) / numNewDepths;
1334 		    for (int depthNum=1; depthNum < numNewDepths; depthNum++) {
1335 			velocity = vMod.evaluateAbove(sLayer.getTopDepth() + depthNum*deltaDepth, 'S');
1336 			if (velocity == 0.0 || (! allowInnerCoreS && 
1337 						sLayer.getTopDepth()+ depthNum*deltaDepth >= vMod.getIocbDepth()) ) {
1338 			    velocity = vMod.evaluateAbove(sLayer.getTopDepth() + depthNum*deltaDepth, 'P');
1339 			}
1340 			p = toSlowness(velocity, sLayer.getTopDepth() + depthNum*deltaDepth);
1341 			addSlowness(p, PWAVE);
1342 			addSlowness(p, SWAVE);
1343 		    }
1344 		}
1345 	    }
1346 	    for (int j=0; j<PLayers.size(); j++) {
1347 		sLayer = (SlownessLayer)PLayers.elementAt(j);
1348 		if ((sLayer.getBotDepth() - sLayer.getTopDepth()) > maxDepthInterval) {
1349 		    numNewDepths = (int)Math.ceil((sLayer.getBotDepth() - sLayer.getTopDepth()) / maxDepthInterval);
1350 		    deltaDepth = (sLayer.getBotDepth() - sLayer.getTopDepth()) / numNewDepths;
1351 		    for (int depthNum=1; depthNum < numNewDepths; depthNum++) {
1352 			p = toSlowness(vMod.evaluateAbove(sLayer.getTopDepth() + depthNum*deltaDepth, 'P'), 
1353 				       sLayer.getTopDepth() + depthNum*deltaDepth);
1354 			addSlowness(p, PWAVE);
1355 			addSlowness(p, SWAVE);
1356 		    }
1357 		}
1358 	    }
1359 	} catch (NoSuchMatPropException e) {
1360 	    // can't happen
1361 	    e.printStackTrace();
1362 	}
1363     }
1364    
1365     /*** Checks to make sure no slowness layer spans more than maxRangeInterval
1366      *  and that the (estimated) error due to linear interpolation is less than
1367      *  maxInterpError. */
1368     protected void distanceCheck() throws SlownessModelException, NoSuchMatPropException, NoSuchLayerException {
1369    	SlownessLayer sLayer, prevSLayer;
1370    	int j;
1371 	TimeDist prevTD;
1372 	TimeDist currTD;
1373 	TimeDist prevPrevTD;
1374 	boolean isCurrOK;
1375 	boolean isPrevOK;
1376 	boolean currWaveType, otherWaveType;  // TRUE=P and FALSE=S
1377 
1378 	/* do SWAVE and then PWAVE, waveN is ONLY used on the next 2 lines */
1379 	for (int waveN=0; waveN < 2; waveN++) {
1380 	    currWaveType = waveN==0 ? SWAVE : PWAVE;
1381 	    otherWaveType = ! currWaveType;
1382 	    prevPrevTD = null;
1383 	    prevTD = null;
1384 	    currTD = null;
1385 	    isCurrOK = false;
1386 	    isPrevOK = false;
1387 	    j=0;
1388 	    sLayer = getSlownessLayer(0, currWaveType);  // preset sLayer so prevSLayer is ok
1389 	    while ( j < getNumLayers(currWaveType)) {
1390 		prevSLayer = sLayer;
1391 		sLayer = getSlownessLayer(j, currWaveType);
1392 		if ( ! depthInHighSlowness(sLayer.getBotDepth(), sLayer.getBotP(), currWaveType) && 
1393 		     ! depthInHighSlowness(sLayer.getTopDepth(), sLayer.getTopP(), currWaveType)) {
1394 	
1395 		    // Don't calculate prevTD if we can avoid it
1396 		    if (isCurrOK ) {
1397 			if (isPrevOK) {
1398 			    prevPrevTD = prevTD;
1399 			} else {
1400 			    prevPrevTD = null;
1401 			}
1402 			prevTD = currTD;
1403 			isPrevOK = true;
1404 		    } else {
1405 			prevTD = approxDistance(j-1, sLayer.getTopP(), currWaveType);
1406 			isPrevOK = true;
1407 		    }
1408 		    currTD = approxDistance(j, sLayer.getBotP(), currWaveType);
1409 		    isCurrOK = true;
1410 	
1411 		    // check for too great of distance jump
1412 		    if ( Math.abs(prevTD.dist - currTD.dist) > maxRangeInterval &&
1413 			 Math.abs(sLayer.getTopP() - sLayer.getBotP()) > 2*minDeltaP) {
1414 			addSlowness((sLayer.getTopP()+sLayer.getBotP())/2.0, PWAVE);
1415 			addSlowness((sLayer.getTopP()+sLayer.getBotP())/2.0, SWAVE);
1416 			currTD = prevTD;
1417 			prevTD = prevPrevTD;
1418 		    } else {
1419 			// make guess as to error estimate due to linear interpolation
1420 			// if it is not ok, then we split both the previous and current
1421 			// slowness layers, this has the nice, if unintended, consequense
1422 			// of adding extra samples in the neighborhood of poorly sampled
1423 			// caustics
1424 			if (prevPrevTD != null && Math.abs(
1425 							   prevTD.time - ((currTD.time-prevPrevTD.time)*
1426 									  (prevTD.dist-prevPrevTD.dist)/
1427 									  (currTD.dist - prevPrevTD.dist) + prevPrevTD.time)) > maxInterpError) {
1428 	
1429 			    addSlowness((prevSLayer.getTopP()+prevSLayer.getBotP())/2.0, PWAVE);
1430 			    addSlowness((prevSLayer.getTopP()+prevSLayer.getBotP())/2.0, SWAVE);
1431 			    addSlowness((sLayer.getTopP()+sLayer.getBotP())/2.0, PWAVE);
1432 			    addSlowness((sLayer.getTopP()+sLayer.getBotP())/2.0, SWAVE);
1433 			    currTD = prevPrevTD;
1434 			    isPrevOK = false;
1435 			    j--;
1436 			    sLayer = getSlownessLayer( ((j-1>=0) ? j-1 : 0),currWaveType);
1437 			    //     ^^^ make sure j != 0
1438 			} else {
1439 			    j++;
1440 			    if (DEBUG && (j % 100 == 0)) { System.out.print(" "+j); }
1441 			}
1442 		    }
1443 		} else {
1444 		    prevPrevTD = null;
1445 		    prevTD = null;
1446 		    currTD = null;
1447 		    isCurrOK = false;
1448 		    isPrevOK = false;
1449 		    j++;
1450 		    if (DEBUG && (j % 100 == 0)) { System.out.print(" "+j); }
1451 		}
1452 	
1453 	    }
1454 	    if (DEBUG) {
1455 		System.out.println("\nNumber of "+(currWaveType ? 'P' : 'S')+" slowness layers: "+j);
1456 	    }
1457 	}
1458     }
1459    
1460     /*** Adds the given ray parameter, p, to the slowness sampling for the
1461      *  given waveType. It splits slowness layers as needed and keeps 
1462      *  P and S sampling consistant within fluid layers. Note, this makes 
1463      *  use of the velocity model, so all interpolation is linear in 
1464      *  velocity, not in slowness!
1465      *  @returns true if a change was made, false otherwise.
1466      */
1467     protected void addSlowness(double p, boolean isPWave) 
1468 	throws SlownessModelException, NoSuchLayerException 
1469     {
1470    	boolean madeAChange = false;
1471    	Vector layers, otherLayers;
1472    	SlownessLayer sLayer, topLayer, botLayer;
1473    	double slope;
1474    	double topVelocity, botVelocity;
1475    	int otherIndex;
1476    	
1477    	if (isPWave) {
1478 	    layers = PLayers;
1479 	    otherLayers = SLayers;
1480    	} else {
1481 	    layers = SLayers;
1482 	    otherLayers = PLayers;
1483    	}
1484    	
1485    	for (int i=0; i< layers.size(); i++) {
1486 	    sLayer = (SlownessLayer)layers.elementAt(i);
1487 	    try {
1488 		if (sLayer.getTopDepth() != sLayer.getBotDepth()) {
1489 		    topVelocity = vMod.evaluateBelow(sLayer.getTopDepth(), 
1490 						     (isPWave ? 'P' : 'S'));
1491 		    botVelocity = vMod.evaluateAbove(sLayer.getBotDepth(), 
1492 						     (isPWave ? 'P' : 'S'));
1493 		} else {
1494 		    // if depths are same we really only need topVelocity, 
1495 		    // and just to verify that we are not in a fluid.
1496 		    topVelocity = vMod.evaluateAbove(sLayer.getBotDepth(), 
1497 						     (isPWave ? 'P' : 'S'));
1498 		    botVelocity = vMod.evaluateBelow(sLayer.getTopDepth(), 
1499 						     (isPWave ? 'P' : 'S'));
1500