1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
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
188
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
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
231
232
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
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
286
287 if (tempDepth < 0.0
288 || Double.isNaN(tempDepth)
289 || Double.isInfinite(tempDepth)
290 || tempDepth < getTopDepth()
291 || tempDepth > getBotDepth()) {
292
293
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
308 if (tempDepth < getTopDepth() && (getTopDepth() - tempDepth) < 1e-10) {
309 return getTopDepth();
310 }
311
312 if (tempDepth > getBotDepth() && (tempDepth- getBotDepth()) < 1e-10) {
313 return getBotDepth();
314 }
315 return tempDepth;
316 } else {
317
318
319 if (getTopP() != getBotP()) {
320 return getBotDepth() + (rayParam - getBotP()) * (getTopDepth() - getBotDepth()) /
321 (getTopP() - getBotP());
322 } else {
323
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
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
350
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 }