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 		}
1501 	    } catch (NoSuchMatPropException e) {
1502 		//Can't happen but...
1503 		throw new SlownessModelException("Caught NoSuchMatPropException: "+
1504 						 e.getMessage());
1505 	    }
1506 
1507 
1508 	    // We don't need to check for S waves in a fluid or 
1509 	    // in inner core if allowInnerCoreS==false.
1510 	    if (! isPWave) {
1511 		if (! allowInnerCoreS && 
1512 		    sLayer.getBotDepth() > vMod.getIocbDepth()) {
1513 		    break;
1514 		} else if (topVelocity == 0.0) {
1515 		    continue;
1516 		}
1517 	    }
1518 
1519 	    if ((sLayer.getTopP() - p)*(p - sLayer.getBotP()) > 0) {
1520 		madeAChange = true;
1521 		topLayer = (SlownessLayer)sLayer.clone();
1522 		topLayer.setBotP(p);
1523 		if (sLayer.getBotDepth() != sLayer.getTopDepth()) {
1524 		    /* not a zero thickness layer, so calculate the depth for 
1525 		     * the ray parameter. */
1526 		    slope = (botVelocity - topVelocity ) / 
1527 			(sLayer.getBotDepth()-sLayer.getTopDepth());
1528 		    topLayer.setBotDepth(interpolate(p, 
1529 						    topVelocity, 
1530 						    sLayer.getTopDepth(), 
1531 						    slope));
1532 
1533 		}
1534 		botLayer = (SlownessLayer)sLayer.clone();
1535 		botLayer.setTopP(p);
1536 		botLayer.setTopDepth(topLayer.getBotDepth());
1537 		layers.removeElementAt(i);
1538 		layers.insertElementAt(botLayer, i);
1539 		layers.insertElementAt(topLayer, i);
1540 		otherIndex = otherLayers.indexOf(sLayer);
1541 		if (otherIndex != -1) {
1542 		    otherLayers.removeElementAt(otherIndex);
1543 		    otherLayers.insertElementAt(botLayer, otherIndex);
1544 		    otherLayers.insertElementAt(topLayer, otherIndex);
1545 		}
1546 			
1547 	    }
1548    	}
1549     }
1550    
1551     /*** Resets the slowness layers that correspond to critical points.
1552      */
1553     protected void fixCriticalPoints() throws NoSuchLayerException {
1554    	CriticalDepth cd;
1555    	SlownessLayer sLayer;
1556    	for (int i=0; i< criticalDepthVector.size() ; i++) {
1557 	    cd = (CriticalDepth)criticalDepthVector.elementAt(i);
1558 
1559 	    cd.PLayerNum = layerNumberBelow(cd.depth, PWAVE);
1560 	    sLayer = getSlownessLayer(cd.PLayerNum, PWAVE);
1561 	    if (cd.PLayerNum == PLayers.size()-1 && sLayer.getBotDepth() == cd.depth) {
1562 		cd.PLayerNum++;	// want the last critical point to be the bottom of the last layer
1563 	    }
1564    		
1565 	    cd.SLayerNum = layerNumberBelow(cd.depth, SWAVE);
1566 	    sLayer = getSlownessLayer(cd.SLayerNum, SWAVE);
1567 	    if (cd.SLayerNum == SLayers.size()-1 && sLayer.getBotDepth() == cd.depth) {
1568 		cd.SLayerNum++;	// want the last critical point to be the bottom of the last layer
1569 	    }
1570      	}
1571     }
1572 
1573     /*** Finds the index of the slowness layer that contains the given depth
1574      *  Note that if the depth is a layer boundary, it returns the shallower
1575      *  of the two or possibly more (since total reflections are zero 
1576      *  thickness layers) layers.
1577      *
1578      *  @return the layer number.
1579      *  @exception NoSuchLayerException occurs if no layer in the slowness
1580      *     model contains the given depth.
1581      */
1582     public int layerNumberAbove(double depth, boolean isPWave)
1583 	throws NoSuchLayerException {
1584 	SlownessLayer tempLayer;
1585 	Vector layers;
1586  
1587 	if (isPWave) {
1588 	    layers = PLayers;
1589 	} else {
1590 	    layers = SLayers;
1591 	}
1592 
1593 	// check surface first
1594 	tempLayer = (SlownessLayer)layers.elementAt(0);
1595 	if (tempLayer.getTopDepth() == depth) {
1596 	    return 0;
1597 	}
1598 	if (depth < tempLayer.getTopDepth() || 
1599 	    ((SlownessLayer)layers.elementAt(layers.size()-1)).getBotDepth() < depth) {
1600 	    throw new NoSuchLayerException(depth);
1601 	}
1602 
1603 	int tooSmallNum = 0;
1604 	int tooLargeNum = layers.size()-1;
1605 	int currentNum = 0;
1606 	boolean found = false;
1607 	while ( ! found) {
1608 	    currentNum = Math.round((tooSmallNum + tooLargeNum) / 2.0f);
1609 	    tempLayer = getSlownessLayer(currentNum, isPWave);
1610 
1611 	    if (tempLayer.getTopDepth() >= depth) {
1612 		tooLargeNum = currentNum-1;
1613 	    } else if (tempLayer.getBotDepth() < depth) {
1614 		tooSmallNum = currentNum+1;
1615 	    } else {
1616 		found = true;
1617 	    }
1618 	}
1619 	return currentNum;
1620     }
1621    
1622     /*** Finds the index of the slowness layer that contains the given depth
1623      *  Note that if the depth is a layer boundary, it returns the deeper
1624      *  of the two or possibly more (since total reflections are zero 
1625      *  thickness layers) layers.
1626      *
1627      *  @return the layer number.
1628      *  @exception NoSuchLayerException occurs if no layer in the slowness
1629      *     model contains the given depth.
1630      */
1631     public int layerNumberBelow(double depth, boolean isPWave)
1632 	throws NoSuchLayerException {
1633 	SlownessLayer tempLayer;
1634 	Vector layers;
1635  
1636 	if (isPWave) {
1637 	    layers = PLayers;
1638 	} else {
1639 	    layers = SLayers;
1640 	}
1641 
1642 	// check surface first
1643 	tempLayer = (SlownessLayer)layers.elementAt(0);
1644 	if (tempLayer.getTopDepth() == depth) {
1645 	    return 0;
1646 	}
1647       	// check bottommost layer
1648 	tempLayer = (SlownessLayer)layers.elementAt(layers.size()-1);
1649 	if (tempLayer.getBotDepth() == depth) {
1650 	    return layers.size()-1;
1651 	}
1652       	// check to make sure depth is within the range available
1653 	if (depth < ((SlownessLayer)layers.elementAt(0)).getTopDepth() || 
1654 	    ((SlownessLayer)layers.elementAt(layers.size()-1)).getBotDepth() < depth) {
1655 	    throw new NoSuchLayerException(depth);
1656 	}
1657 
1658 	int tooSmallNum = 0;
1659 	int tooLargeNum = layers.size()-1;
1660 	int currentNum = 0;
1661 	boolean found = false;
1662 	while ( ! found) {
1663 	    currentNum = Math.round((tooSmallNum + tooLargeNum) / 2.0f);
1664 	    tempLayer = getSlownessLayer(currentNum, isPWave);
1665 
1666 	    if (tempLayer.getTopDepth() > depth) {
1667 		tooLargeNum = currentNum-1;
1668 	    } else if (tempLayer.getBotDepth() <= depth) {
1669 		tooSmallNum = currentNum+1;
1670 	    } else {
1671 		found = true;
1672 	    }
1673 	}
1674 	return currentNum;
1675     }
1676 
1677 
1678     /*** Performs consistency check on the slowness model.
1679      *  @return true if successful, throws SlownessModelException otherwise.
1680      *  @exception SlownessModelException if any check fails
1681      */
1682     public boolean validate() 
1683 	throws SlownessModelException 
1684     {
1685 	boolean isOK = true;
1686 	double prevDepth;
1687 	DepthRange highSZoneDepth, fluidZone;
1688 	SlownessLayer sLayer;
1689 
1690 	/* is radiusOfEarth positive? */
1691 	if (radiusOfEarth <= 0.0) {
1692 	    throw new SlownessModelException(
1693 					     "Radius of earth is not positive. radiusOfEarth = "+radiusOfEarth);
1694 	}
1695 
1696 	/* is maxDepthInterval positive? */
1697 	if (maxDepthInterval <= 0.0) {
1698 	    throw new SlownessModelException(
1699 					     "maxDepthInterval is not positive. maxDepthInterval = "+
1700 					     maxDepthInterval);
1701 	}
1702       
1703 	/* Check for inconsistencies in high slowness zones. */
1704 	Vector highSlownessLayerDepths = highSlownessLayerDepthsP;
1705 	boolean isPWave = PWAVE;
1706 	for (int j=0;j<2; j++, isPWave = SWAVE) {
1707 	    if (isPWave) {
1708 		highSlownessLayerDepths = highSlownessLayerDepthsP;
1709 	    } else {
1710 		highSlownessLayerDepths = highSlownessLayerDepthsS;
1711 	    }
1712 
1713 	    prevDepth= -1*Double.MAX_VALUE;
1714 	    for (int i=0;i<highSlownessLayerDepths.size();i++) {
1715 		highSZoneDepth = (DepthRange)highSlownessLayerDepths.elementAt(i);
1716 		if (highSZoneDepth.topDepth >= highSZoneDepth.botDepth) {
1717 		    throw new SlownessModelException(
1718 						     "High slowness zone has zero or negative thickness. Num "+
1719 						     i+" isPWave="+isPWave+
1720 						     " top depth "+highSZoneDepth.topDepth+
1721 						     " bottom depth "+highSZoneDepth.botDepth);
1722 		}
1723 		if (highSZoneDepth.topDepth <= prevDepth) {
1724 		    throw new SlownessModelException
1725 			("High slowness zone overlaps previous zone. Num "+
1726 			 i+" isPWave="+isPWave+
1727 			 " top depth "+highSZoneDepth.topDepth+
1728 			 " bottom depth "+highSZoneDepth.botDepth);
1729 		}
1730 		prevDepth = highSZoneDepth.botDepth;
1731 	    }
1732 	}
1733 
1734 	/* Check for inconsistencies in fluid zones. */
1735 	prevDepth= -1*Double.MAX_VALUE;
1736 	for (int i=0;i<fluidLayerDepths.size();i++) {
1737 	    fluidZone = (DepthRange)fluidLayerDepths.elementAt(i);
1738 	    if (fluidZone.topDepth >= fluidZone.botDepth) {
1739 		throw new SlownessModelException(
1740 						 "Fluid zone has zero or negative thickness. Num "+
1741 						 i+" top depth "+fluidZone.topDepth+
1742 						 " bottom depth "+fluidZone.botDepth);
1743 	    }
1744 	    if (fluidZone.topDepth <= prevDepth) {
1745 		throw new SlownessModelException
1746 		    ("Fluid zone overlaps previous zone. Num "+
1747 		     i+" top depth "+fluidZone.topDepth+
1748 		     " bottom depth "+fluidZone.botDepth);
1749 	    }
1750 	    prevDepth = fluidZone.botDepth;
1751 	}
1752 
1753 	/* Check for inconsistencies in slowness layers. */
1754 	isPWave = PWAVE;
1755 	double prevBotP;
1756 	for (int j=0;j<2; j++, isPWave = SWAVE) {
1757 
1758 	    prevDepth= 0.0;
1759 	    if (getNumLayers(isPWave) > 0) {
1760 		prevBotP = getSlownessLayer(0, isPWave).getTopP();
1761 	    } else {
1762 		prevBotP = -1;
1763 	    }
1764 	    for (int i=0;i<getNumLayers(isPWave);i++) {
1765 		sLayer = getSlownessLayer(i, isPWave);
1766 		isOK &= sLayer.validate();
1767 
1768 		if (sLayer.getTopDepth() > prevDepth) {
1769 		    throw new SlownessModelException(
1770 						     "Gap of "+(sLayer.getTopDepth()-prevDepth)+
1771 						     " between slowness layers. Num "+
1772 						     i+" isPWave="+isPWave+
1773 						     " top depth "+sLayer.getTopDepth()+
1774 						     " bottom depth "+sLayer.getBotDepth());
1775 		}
1776 		if (sLayer.getTopDepth() < prevDepth) {
1777 		    throw new SlownessModelException(
1778 						     "Slowness layer overlaps previous layer by "+
1779 						     (prevDepth-sLayer.getTopDepth())+". Num "+
1780 						     i+" isPWave="+isPWave+
1781 						     " top depth "+sLayer.getTopDepth()+
1782 						     " bottom depth "+sLayer.getBotDepth());
1783 		}
1784 		if (sLayer.getTopP() != prevBotP) {
1785 		    throw new SlownessModelException(
1786 						     "Slowness layer gap/overlaps previous layer in slowness "+
1787 						     ". Num "+ i+" isPWave="+isPWave+
1788 						     " prevBotP= "+prevBotP+
1789 						     " sLayer= "+sLayer);
1790 		}
1791 		if (Double.isNaN(sLayer.getTopDepth())) {
1792 		    throw new SlownessModelException(
1793 						     "Top depth is NaN, layerNum="+i+
1794 						     " waveType="+(isPWave?'P':'S'));
1795 		}
1796 		if (Double.isNaN(sLayer.getBotDepth())) {
1797 		    throw new SlownessModelException(
1798 						     "Top depth is NaN, layerNum="+i+
1799 						     " waveType="+(isPWave?'P':'S'));
1800 		}
1801 		prevBotP = sLayer.getBotP();
1802 		prevDepth = sLayer.getBotDepth();
1803 	    }
1804 	}
1805 		
1806 	/* Everything checks out OK so return true. */
1807 	return isOK;
1808     }
1809 
1810     /*
1811    
1812       public void writeToStream(String filename) throws IOException {
1813       DataOutputStream dos = new DataOutputStream(
1814       new BufferedOutputStream( new FileOutputStream(filename)));
1815       writeToStream(dos);
1816       dos.close();
1817       }
1818    
1819       public void writeToStream(DataOutputStream dos) throws IOException{
1820       DepthRange dr;
1821       SlownessLayer sl;
1822       
1823       dos.writeInt(getClass().getName().length());
1824       dos.writeBytes(getClass().getName());
1825 
1826       dos.writeDouble(radiusOfEarth);
1827       vMod.writeToStream(dos);
1828       dos.writeInt(criticalVelLayers.size());
1829       for (int i=0;i<criticalVelLayers.size();i++) {
1830       dos.writeInt(((Integer)criticalVelLayers.elementAt(i)).intValue());
1831       }
1832       dos.writeInt(criticalLayers.size());
1833       for (int i=0;i<criticalLayers.size();i++) {
1834       dos.writeInt(((Integer)criticalLayers.elementAt(i)).intValue());
1835       }
1836       dos.writeInt(highSlownessLayerDepths.size());
1837       for (int i=0;i<highSlownessLayerDepths.size();i++) {
1838       dr = (DepthRange)highSlownessLayerDepths.elementAt(i);
1839       dos.writeDouble(dr.topDepth);
1840       dos.writeDouble(dr.botDepth);
1841       dos.writeDouble(dr.rayParam);
1842       }
1843       dos.writeInt(fluidLayerDepths.size());
1844       for (int i=0;i<fluidLayerDepths.size();i++) {
1845       dr = (DepthRange)fluidLayerDepths.elementAt(i);
1846       dos.writeDouble(dr.topDepth);
1847       dos.writeDouble(dr.botDepth);
1848       dos.writeDouble(dr.rayParam);
1849       }
1850       dos.writeInt(slowness.size());
1851       for (int i=0;i<slowness.size();i++) {
1852       sl = (SlownessLayer)slowness.elementAt(i);
1853       dos.writeDouble(sl.topDepth);
1854       dos.writeDouble(sl.botDepth);
1855       dos.writeDouble(sl.topP);
1856       dos.writeDouble(sl.botP);
1857       }
1858       dos.writeDouble(maxDeltaP);
1859       dos.writeDouble(minDeltaP);
1860       dos.writeDouble(maxDepthInterval);
1861       dos.writeDouble(maxRangeInterval);
1862       dos.writeDouble(minRangeInterval);
1863       dos.writeDouble(minRadiusCheck);
1864       dos.writeBoolean(allowInnerCoreS);
1865       dos.writeDouble(minInnerCoreDepth);
1866       dos.writeDouble(slownessTolerance);
1867       }
1868       
1869       public static SlownessModel readFromStream(String filename) 
1870       throws FileNotFoundException, IOException, ClassNotFoundException,
1871       IllegalAccessException, InstantiationException {
1872       DataInputStream dis = new DataInputStream(
1873       new BufferedInputStream( new FileInputStream(filename)));
1874       SlownessModel sMod = readFromStream(dis);
1875       dis.close();
1876       return sMod;
1877       }
1878    
1879       public static SlownessModel readFromStream(DataInputStream dis) 
1880       throws IOException, InstantiationException, IllegalAccessException,
1881       ClassNotFoundException {
1882       int length;
1883       
1884  
1885       byte[] classString = new byte[dis.readInt()];
1886       dis.read(classString);
1887       Class sModClass = Class.forName(new String(classString));
1888       SlownessModel sMod = (SlownessModel)sModClass.newInstance();
1889  
1890       sMod.vMod = VelocityModel.readFromStream(dis);
1891       length = dis.readInt();
1892       sMod.criticalVelLayers = new Vector(length);
1893       for (int i=0;i<length;i++) {
1894       sMod.criticalVelLayers.addElement(new Integer(dis.readInt()));
1895       }
1896       length = dis.readInt();
1897       sMod.criticalLayers = new Vector(length);
1898       for (int i=0;i<length;i++) {
1899       sMod.criticalLayers.addElement(new Integer(dis.readInt()));
1900       }
1901       length = dis.readInt();
1902       sMod.highSlownessLayerDepths = new Vector(length);
1903       DepthRange dr;
1904       for (int i=0;i<length;i++) {
1905       dr = new DepthRange();
1906       dr.topDepth = dis.readDouble();
1907       dr.botDepth = dis.readDouble();
1908       dr.rayParam = dis.readDouble();
1909       sMod.highSlownessLayerDepths.addElement(dr);
1910       }
1911       length = dis.readInt();
1912       sMod.fluidLayerDepths = new Vector(length);
1913       for (int i=0;i<length;i++) {
1914       dr = new DepthRange();
1915       dr.topDepth = dis.readDouble();
1916       dr.botDepth = dis.readDouble();
1917       dr.rayParam = dis.readDouble();
1918       sMod.fluidLayerDepths.addElement(dr);
1919       }
1920       
1921       length = dis.readInt();
1922       sMod.slowness = new Vector(length);
1923       SlownessLayer sl;
1924       for (int i=0;i<length;i++) {
1925       sl = new SlownessLayer();
1926       sl.topDepth = dis.readDouble();
1927       sl.botDepth = dis.readDouble();
1928       sl.topP = dis.readDouble();
1929       sl.botP = dis.readDouble();
1930       sMod.slowness.addElement(sl);
1931       }
1932       
1933       sMod.maxDeltaP = dis.readDouble();
1934       sMod.minDeltaP = dis.readDouble();
1935       sMod.maxDepthInterval = dis.readDouble();
1936       sMod.maxRangeInterval = dis.readDouble();
1937       sMod.minRangeInterval = dis.readDouble();
1938       sMod.minRadiusCheck = dis.readDouble();
1939       sMod.allowInnerCoreS = dis.readBoolean();
1940       sMod.minInnerCoreDepth = dis.readDouble();
1941       sMod.slownessTolerance = dis.readDouble();
1942       return sMod;
1943       }
1944     */
1945     
1946     /* Returns a clone of this slowness model. All fields are correctly
1947      * copied so modifications to the clone do not affect the original. */
1948     public Object clone() {
1949 	SlownessModel newObject;
1950 	try {
1951 	    newObject = (SlownessModel)super.clone();
1952 
1953 	    newObject.criticalDepthVector = new Vector(criticalDepthVector.size());
1954 	    for (int i=0;i<criticalDepthVector.size();i++) {
1955 		newObject.criticalDepthVector.addElement(
1956 							 ((CriticalDepth)criticalDepthVector.elementAt(i)).clone());
1957 	    }
1958 
1959 	    newObject.highSlownessLayerDepthsP = 
1960 		new Vector(highSlownessLayerDepthsP.size());
1961 	    for (int i=0;i<highSlownessLayerDepthsP.size();i++) {
1962 		newObject.highSlownessLayerDepthsP.addElement(
1963 							      ((DepthRange)highSlownessLayerDepthsP.elementAt(i)).clone());
1964 	    }
1965 
1966 	    newObject.highSlownessLayerDepthsS = 
1967 		new Vector(highSlownessLayerDepthsS.size());
1968 	    for (int i=0;i<highSlownessLayerDepthsS.size();i++) {
1969 		newObject.highSlownessLayerDepthsS.addElement(
1970 							      ((DepthRange)highSlownessLayerDepthsS.elementAt(i)).clone());
1971 	    }
1972 
1973 	    newObject.fluidLayerDepths = new Vector(fluidLayerDepths.size());
1974 	    for (int i=0;i<fluidLayerDepths.size();i++) {
1975 		newObject.fluidLayerDepths.addElement(
1976 						      ((DepthRange)fluidLayerDepths.elementAt(i)).clone());
1977 	    }
1978 
1979 	    newObject.PLayers = new Vector(getNumLayers(PWAVE));
1980 	    for (int i=0;i<getNumLayers(PWAVE);i++) {
1981 		newObject.PLayers.addElement( getSlownessLayerClone(i, PWAVE));
1982 	    }
1983 
1984 	    newObject.SLayers = new Vector(getNumLayers(SWAVE));
1985 	    for (int i=0;i<getNumLayers(SWAVE);i++) {
1986 		newObject.SLayers.addElement( getSlownessLayerClone(i, SWAVE));
1987 	    }
1988 	    return newObject;
1989 
1990 	} catch (CloneNotSupportedException e) {
1991 	    // Can't happen, but...
1992 	    System.err.println("Caught CloneNotSupportedException: "+
1993 			       e.getMessage());
1994 	    throw new InternalError(e.toString());
1995 	}
1996       
1997     }
1998 
1999     public String toString() {
2000 	int topCriticalLayerNum;
2001 	int botCriticalLayerNum;
2002 	String desc = "";
2003 		
2004 	desc = "radiusOfEarth="+radiusOfEarth+
2005 	    "\n maxDeltaP="+maxDeltaP+
2006 	    "\n minDeltaP="+minDeltaP+
2007 	    "\n maxDepthInterval="+maxDepthInterval+
2008 	    "\n maxRangeInterval="+maxRangeInterval+
2009 	    "\n allowInnerCoreS="+allowInnerCoreS+
2010 	    "\n slownessTolerance="+slownessTolerance+
2011 	    "\n getNumLayers('P')="+getNumLayers(PWAVE)+
2012 	    "\n getNumLayers('S')="+getNumLayers(SWAVE)+
2013 	    "\n fluidLayerDepths.size()="+fluidLayerDepths.size()+
2014 	    "\n highSlownessLayerDepthsP.size()="+
2015 	    highSlownessLayerDepthsP.size()+
2016 	    "\n highSlownessLayerDepthsS.size()="+
2017 	    highSlownessLayerDepthsS.size()+
2018 	    "\n criticalDepthVector.size()="+criticalDepthVector.size()+
2019 	    "\n";
2020 
2021 	if (criticalDepthVector.size()!=0) {
2022 	    desc += ("**** Critical Depth Layers ************************\n");
2023 	    botCriticalLayerNum = 
2024 		((CriticalDepth)criticalDepthVector.elementAt(0)).velLayerNum-1;
2025 	    for (int criticalNum=1;criticalNum<criticalDepthVector.size();
2026 		 criticalNum++) {
2027 		topCriticalLayerNum = botCriticalLayerNum+1;
2028 		botCriticalLayerNum = 
2029 		    ((CriticalDepth)criticalDepthVector.elementAt(criticalNum)).velLayerNum-1;
2030 		desc += " "+topCriticalLayerNum+","+botCriticalLayerNum;
2031 	    }
2032 	}
2033 	desc += "\n";
2034 
2035 	if (fluidLayerDepths.size()!=0) {
2036 	    desc+="\n**** Fluid Layer Depths ************************\n";
2037 	    for (int i=0;i<fluidLayerDepths.size();i++) {
2038 		desc+=((DepthRange)fluidLayerDepths.elementAt(i)).topDepth+","+
2039 		    ((DepthRange)fluidLayerDepths.elementAt(i)).botDepth+" ";
2040 	    }
2041 	}
2042 	desc += "\n";
2043 
2044 	if (highSlownessLayerDepthsP.size()!=0) {
2045 	    desc+="\n**** P High Slowness Layer Depths ****************\n";
2046 	    for (int i=0;i<highSlownessLayerDepthsP.size();i++) {
2047 		desc+=((DepthRange)highSlownessLayerDepthsP.elementAt(i)).topDepth+","+
2048 		    ((DepthRange)highSlownessLayerDepthsP.elementAt(i)).botDepth+" ";
2049 	    }
2050 	}
2051 	desc += "\n";
2052 
2053 	if (highSlownessLayerDepthsS.size()!=0) {
2054 	    desc+="\n**** S High Slowness Layer Depths ****************\n";
2055 	    for (int i=0;i<highSlownessLayerDepthsS.size();i++) {
2056 		desc+=((DepthRange)highSlownessLayerDepthsS.elementAt(i)).topDepth+","+
2057 		    ((DepthRange)highSlownessLayerDepthsS.elementAt(i)).botDepth+" ";
2058 	    }
2059 	}
2060 	desc += "\n";
2061 
2062 	return desc;
2063     }
2064 }
2065