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  
32  /***
33    * Class to hold a single slowness layer sample.
34    *
35    * @version 1.1.3 Wed Jul 18 15:00:35 GMT 2001
36  
37  
38  
39    * @author H. Philip Crotwell
40   */
41  public class SlownessLayer implements Serializable, Cloneable {
42  
43      /*** top slowness, top depth, bottom slowness, bottom depth */
44      public SlownessLayer(double topP, double topDepth, 
45                           double botP, double botDepth) {
46  	Assert.isFalse(topDepth > botDepth, 
47        "topDepth > botDepth: "+topDepth+" > "+botDepth);
48  	Assert.isFalse(topDepth < 0.0 
49                    || Double.isNaN(topDepth) 
50                    || Double.isInfinite(topDepth),
51  	               "topDepth is not a number or is negative: "+topDepth);
52  	Assert.isFalse(botDepth < 0.0 
53                    || Double.isNaN(botDepth) 
54                    || Double.isInfinite(botDepth),
55  	               "botDepth is not a number or is negative: "+botDepth);
56  	this.setTopP(topP);
57  	this.setTopDepth(topDepth);
58  	this.setBotP(botP);
59  	this.setBotDepth(botDepth);
60      }
61  
62      /*** Compute the slowness layer from a velocity layer.
63         *       */
64      public SlownessLayer(VelocityLayer vLayer, boolean spherical,
65  			 double radiusOfEarth, boolean isPWave) {
66  	Assert.isFalse(vLayer.getTopDepth() > vLayer.getBotDepth(),
67  		       "vLayer.topDepth > vLayer.botDepth :"+
68  		       vLayer.getTopDepth()+ " "+vLayer.getBotDepth());
69  	setTopDepth(vLayer.getTopDepth());
70  	setBotDepth(vLayer.getBotDepth());
71  
72  	char waveType;
73  	if (isPWave) { waveType = 'P';
74  	} else { waveType = 'S'; }
75  		
76  	try {
77  	    if (spherical) {
78  		setTopP((radiusOfEarth-getTopDepth()) /
79  		    vLayer.evaluateAtTop(waveType));
80  		setBotP((radiusOfEarth-getBotDepth()) /
81  		    vLayer.evaluateAtBottom(waveType));
82  
83  	    } else {
84  		setTopP(1.0/vLayer.evaluateAtTop(waveType));
85  		setBotP(1.0/vLayer.evaluateAtBottom(waveType));
86  	    }
87  	    Assert.isFalse(Double.isNaN(getTopP()) || Double.isNaN(getBotP()),
88  			   "Slowness sample is NaN: topP="+getTopP()+" botP="+getBotP());
89  	} catch (NoSuchMatPropException e) {
90  	    // Can't happen
91  	    e.printStackTrace();
92  	}
93      }
94  
95        /*** Compute the slowness layer from a velocity layer.
96         *  Since radiusOfEarth is given we assume a spherical model.
97         */
98     public SlownessLayer(VelocityLayer vLayer, boolean isPWave,
99           double radiusOfEarth) {
100       this(vLayer, true, radiusOfEarth, isPWave);
101    }
102 
103       /*** Compute the slowness layer from a velocity layer. Since 
104        *  radiusOfEarth is not given we assume a flat model.
105        */
106    public SlownessLayer(VelocityLayer vLayer, boolean isPWave) {
107       this(vLayer, false, 0.0, isPWave);
108    }
109 
110    public void setTopP(double topP) {
111         this.topP = topP;
112     }
113 
114     public double getTopP() {
115         return topP;
116     }
117 
118     public void setBotP(double botP) {
119         this.botP = botP;
120     }
121 
122     public double getBotP() {
123         return botP;
124     }
125 
126     public void setTopDepth(double topDepth) {
127         this.topDepth = topDepth;
128     }
129 
130     public double getTopDepth() {
131         return topDepth;
132     }
133 
134     public void setBotDepth(double botDepth) {
135         this.botDepth = botDepth;
136     }
137 
138     public double getBotDepth() {
139         return botDepth;
140     }
141 
142     /*** Is the layer a zero thickness layer, ie a total reflection? */
143    public boolean isZeroThickness() {
144       if (getTopDepth() == getBotDepth()) {
145          return true;
146       } else {
147          return false;
148       }
149    }
150 
151       /*** Finds the slowness at the given depth. radiusOfEarth is needed
152        *  as a slowness layer doesn't have access to the slowness model.
153        *  Note that this method assumes
154        *  a Bullen type of slowness interpolation, ie p(r) = a*r^b. This
155        *  will produce results consistent with a tau model that uses this
156        *  interpolant, but it may differ slightly from going directly to
157        *  the velocity model. Also, if the tau model is generated using
158        *  another interpolant, linear for instance, then the result may
159        *  not be consistent with the tau model. */
160    public double evaluateAt_bullen(double depth, double radiusOfEarth) 
161          throws SlownessModelException
162    {
163       Assert.isFalse(getBotDepth()>radiusOfEarth,
164             "SlownessLayer.evaluateAt_bullen:"+
165             " radiusOfEarth="+radiusOfEarth+
166             " is smaller than the maximum depth of this layer."+
167             " topDepth="+getTopDepth()+" botDepth="+getBotDepth());
168 
169       Assert.isFalse((getTopDepth()-depth)*(depth-getBotDepth())<0.0,
170             "SlownessLayer.evaluateAt_bullen:"+
171             " depth="+depth+" is not contained within this layer."+
172             " topDepth="+getTopDepth()+" botDepth="+getBotDepth());
173 
174       if (depth == getTopDepth()) {
175 	  return getTopP();
176       } else if (depth == getBotDepth()) {
177 	  return getBotP();
178       } else {
179 	  double B = Math.log(getTopP()/getBotP()) /
180 	      Math.log((radiusOfEarth-getTopDepth()) / 
181 		       (radiusOfEarth-getBotDepth()));
182 	  double A = getTopP()/Math.pow((radiusOfEarth-getTopDepth()), B);
183 	  double answer = A * Math.pow((radiusOfEarth-depth), B);
184 	  if (answer < 0.0 
185 	      || Double.isNaN(answer) 
186 	      || Double.isInfinite(answer)) {
187 	      // numerical instability in power law calculation???
188 	      // try a linear interpolation if the layer is small ( <2 km).
189 	      if ((getBotDepth()- getTopDepth()) < 2.0) {
190 		  double linear = 
191 		      (getBotP()-getTopP())/(getBotDepth()-getTopDepth())*(depth-getTopDepth()) 
192 		      + getTopP();
193 		  if (linear < 0.0 
194 		      || Double.isNaN(linear) 
195 		      || Double.isInfinite(linear)) {
196 		  } else {
197 		      return linear;
198 		  }
199 	      }
200 	      throw new SlownessModelException(
201 "calculated slowness is not a number or is negative: "+
202 answer+"\n"+this.toString()+"\n A="+A+"   B="+B);
203 	  }
204 	  return answer;
205       }
206    }
207 
208       /*** Calculates the time and distance (in radians) increments accumulated
209        *  by a ray of spherical ray parameter p when passing through this layer.
210        *  Note that this gives 1/2 of the true range and time increments since
211        *  there will be both an up going and a downgoing path.
212        *  Here we use the Mohorovicic or Bullen law p=A*r^B
213        *
214        *  @exception SlownessModelException occurs if the calculated
215        *     distance or time increments are negative or NaN, this indicates a
216        *     bug in the code (and hopefully will never happen).
217        */
218    public TimeDist bullenRadialSlowness(double p, double radiusOfEarth)
219       throws SlownessModelException
220    {
221          // To hold the return values.
222       TimeDist timedist = new TimeDist(p);
223  
224       if (getTopDepth() == getBotDepth()) {
225 	  timedist.dist = 0.0;
226 	  timedist.time = 0.0;
227 	  return timedist;
228       }
229       
230       // only do bullen radial slowness if the layer is not too thin
231       // here we use 1 micron = .000000001
232       // just return 0 in this case
233       if (getBotDepth() - getTopDepth() < .000000001) {
234 	  return timedist;
235       }
236 	  
237       double B = Math.log(getTopP()/getBotP()) /
238          Math.log((radiusOfEarth-getTopDepth()) /
239                   (radiusOfEarth-getBotDepth()));
240       double sqrtTopTopMpp = Math.sqrt(getTopP()*getTopP()-p*p);
241       double sqrtBotBotMpp = Math.sqrt(getBotP()*getBotP()-p*p);
242  
243       timedist.dist = 1/B*( 
244 			   Math.atan2(p, sqrtBotBotMpp)-
245 			   Math.atan2(p, sqrtTopTopMpp));
246 
247       timedist.time = 1/B*(sqrtTopTopMpp - sqrtBotBotMpp);
248 
249       if (timedist.dist<0.0 || timedist.time<0.0 ||
250              Double.isNaN(timedist.time) || Double.isNaN(timedist.dist)) {
251          throw new SlownessModelException("timedist <0.0 or NaN: "+
252             "\n RayParam= "+p+
253             "\n topDepth = "+getTopDepth()+
254             "\n botDepth = "+getBotDepth()+
255             "\n dist="+timedist.dist+
256             "\n time="+timedist.time+
257             "\n topP = "+getTopP()+
258             "\n botP = "+getBotP()+
259             "\n B = "+B+" "+toString());
260       }
261       return timedist;
262    }
263 
264     /*** Finds the depth for a ray parameter within this layer. Uses
265      *  a Bullen interpolant, Ar^B. 
266      *  Special case for botP == 0 or botDepth == radiusOfEarth as
267      *  these cause div by 0, use linear interpolation in this
268      *  case. */
269    public double bullenDepthFor(double rayParam, double radiusOfEarth) 
270    throws SlownessModelException {
271       if ((getTopP()-rayParam)*(rayParam-getBotP()) >= 0) {
272 	  double tempDepth;
273 
274 	  // easy case for 0 thickness layer
275 	  if (getTopDepth() == getBotDepth()) {
276 	      return getBotDepth();
277 	  }
278 
279 	  if (getBotP() != 0.0 && getBotDepth() != radiusOfEarth) {
280 	      double B = Math.log(getTopP()/getBotP()) /
281 		  Math.log((radiusOfEarth-getTopDepth())/(radiusOfEarth-getBotDepth()));
282 	      double A = getTopP()/Math.pow((radiusOfEarth-getTopDepth()), B);
283 	      tempDepth = radiusOfEarth - Math.exp(1.0/B * Math.log(rayParam/A) );
284 /*
285 	      tempDepth = radiusOfEarth - Math.pow(rayParam/A, 1.0/B);
286 */
287 	      if (tempDepth < 0.0 
288 		  || Double.isNaN(tempDepth) 
289 		  || Double.isInfinite(tempDepth)
290 		  || tempDepth < getTopDepth()
291 		  || tempDepth > getBotDepth()) {
292 		  // numerical instability in power law calculation???
293 		  // try a linear interpolation if the layer is small ( <5 km).
294 		  if ((getBotDepth()- getTopDepth()) < 5.0) {
295 		      double linear = 
296 			  (getBotDepth()-getTopDepth())/(getBotP()-getTopP())*(rayParam-getTopP()) 
297 			  + getTopDepth();
298 		      if (linear < 0.0 
299 			  || Double.isNaN(linear) 
300 			  || Double.isInfinite(linear)) {
301 		      } else {
302 			  return linear;
303 		      }
304 		  }
305 		  throw new SlownessModelException("claculated depth is not a number or is negative: "+tempDepth+"\n"+this+"\n"+A+"  "+B+"\n"+rayParam);
306 	      }
307 	      // check for tempDepth just above top depth
308 	      if (tempDepth < getTopDepth() && (getTopDepth() - tempDepth) < 1e-10) {
309 		  return getTopDepth();
310 	      }
311 	      // check for tempDepth just below bottom depth
312 	      if (tempDepth > getBotDepth() && (tempDepth- getBotDepth()) < 1e-10) {
313 		  return getBotDepth();
314 	      }
315 	      return tempDepth;
316 	  } else {
317 	      // a special case for the center of the earth, since ar^b 
318 	      // might blow up at r=0
319 	      if (getTopP() != getBotP()) {
320 		  return getBotDepth() + (rayParam - getBotP()) * (getTopDepth() - getBotDepth()) /
321 		      (getTopP() - getBotP());
322 	      } else {
323 		  // weird case, return botDepth???
324 		  return getBotDepth();
325 	      }
326 	  }
327       } else {
328 	  throw new SlownessModelException("Ray parameter = "+rayParam+
329                   " is not contained within this slowness layer. topP="+
330 		  getTopP()+" botP="+getBotP());
331       }
332    }
333  
334    public Object clone() {
335       SlownessLayer newObject;
336       try {
337          newObject = (SlownessLayer)super.clone();
338          return newObject;
339       } catch (CloneNotSupportedException e) {
340          // Can't happen, but...
341          System.err.println("Caught CloneNotSupportedException: "+
342             e.getMessage());
343          throw new InternalError(e.toString());
344       }
345    }
346 
347       /*** returns a String description of this SlownessLayer. */
348    public String toString() {
349 //      String desc = "top p "+ (float)topP +", topDepth " + (float)topDepth
350 //                   +", bot p "+ (float)botP +", botDepth " + (float)botDepth;
351       String desc = "top p "+ getTopP() +", topDepth " + getTopDepth()
352                    +", bot p "+ getBotP() +", botDepth " + getBotDepth();
353       return desc;
354    }
355 
356 	public boolean validate() throws SlownessModelException {
357       if (Double.isNaN(getTopP()) || Double.isNaN(getTopDepth()) ||
358 		Double.isNaN(getBotP()) || Double.isNaN(getBotDepth())) {
359          throw new SlownessModelException(
360             "Slowness layer has NaN values."+"\n "+this);
361 		}
362       if (getTopP() < 0.0 || getBotP() < 0.0) {
363          throw new SlownessModelException(
364             "Slowness layer has negative slownesses. \n "+this);
365       }
366       if (getTopDepth() > getBotDepth()) {
367          throw new SlownessModelException
368             ("Slowness layer has negative thickness. \n"+this);
369       }
370 		return true;
371 	}
372 
373     /*** Slowness at the top of the layer. */
374  private double topP;
375 
376     /*** Slowness at the bottom of the layer. */
377  private double botP;
378 
379     /*** Depth at the top of the layer. */
380  private double topDepth;
381 
382     /*** Depth at the bottom of the layer. */
383  private double botDepth;
384 }