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 /***
29 * package for storage and manipulation of seismic earth models.
30 *
31 */
32 package edu.sc.seis.TauP;
33
34
35 import java.io.BufferedInputStream;
36 import java.io.BufferedOutputStream;
37 import java.io.DataInputStream;
38 import java.io.DataOutputStream;
39 import java.io.FileInputStream;
40 import java.io.FileNotFoundException;
41 import java.io.FileOutputStream;
42 import java.io.FileReader;
43 import java.io.IOException;
44 import java.io.Serializable;
45 import java.io.StreamTokenizer;
46 import java.util.Vector;
47
48 /***
49 * This class defines basic classes to store and manipulate
50 * a velocity model.
51 *
52 * @version 1.1.3 Wed Jul 18 15:00:35 GMT 2001
53
54
55
56 * @author H. Philip Crotwell
57 */
58 public class VelocityModel
59 implements Cloneable, Serializable
60 {
61 /*** name of the velocity model. */
62 protected String modelName = "unknown";
63
64 /*** type of velocity file to be read in, either "nd" for named
65 * discontinuities or "tvel" for ttimes style files. */
66 protected String fileType = "nd";
67
68 /*** reference radius (km), usually radius of the earth,
69 * by default 6371 kilometers. */
70 protected double radiusOfEarth = 6371.0;
71
72 /*** Depth (km) of the moho. It can be input from velocity model (*.nd) or
73 * should be explicitly set.
74 * By default it is 35 kilometers (from Iasp91). For phase naming, the
75 * tau model will choose the closest 1st order discontinuity. Thus
76 * for most simple earth models these values are satisfactory. Take
77 * proper care if your model has a thicker crust and a discontinuity
78 * near 35 km depth. */
79 protected double mohoDepth = 35.0;
80
81 /*** Depth (km) of the cmb (core mantle boundary). It can be input
82 * from velocity model (*.nd) or should be explicitly set.
83 * By default it is 2889 kilometers (from Iasp91). For phase naming, the
84 * tau model will choose the closest 1st order discontinuity. Thus
85 * for most simple earth models these values are satisfactory. */
86 protected double cmbDepth = 2889.0;
87
88 /*** Depth (km) of the iocb (inner core outer core boundary).
89 * It can be input from velocity model (*.nd) or
90 * should be explicitly set.
91 * By default it is 5153.9 kilometers (from Iasp91). For phase naming,
92 * the tau model will choose the closest 1st order discontinuity. Thus
93 * for most simple earth models these values are satisfactory. */
94 protected double iocbDepth = 5153.9;
95
96 /*** Not used, mean Density (kg/m^3), default 5517.0 */
97 protected double meanDensity = 5517.0;
98
99 /*** Not used, gravitational constant, default 6.67e-11 m^3 / kg s^2 */
100 protected double G = 6.67e-11;
101
102 /*** minimum radius of the model (km), default 0.0 */
103 protected double minRadius = 0.0;
104
105 /*** maximum radius of the model (km), default 6371.0 */
106 protected double maxRadius = 6371.0;
107
108 /*** is this a spherical model? Default is true. */
109 protected boolean spherical = true;
110
111 /*** the initial length of the layer vector. */
112 protected static int vectorLength = 16;
113
114 /*** expandable array to hold the layers
115 * @see java.util.Vector
116 * @see edu.sc.seis.TauP.VelocityLayer */
117 protected Vector layer = new Vector(vectorLength);
118
119
120
121
122
123
124
125 /*** get the model name. */
126 public String getModelName() {
127 return modelName;
128 }
129 /*** set the model name. */
130 public void setModelName(String modelName) {
131 if (modelName.length() > 0) {
132 this.modelName = modelName;
133 } else {
134 this.modelName = "unknown";
135 }
136 }
137
138 /*** sets file type, either "nd" for named discontinuities or "tvel"
139 * for ttimes tvel models. */
140 public void setFileType(String fileType) {
141 this.fileType = fileType;
142 }
143
144 /*** sets radius of the earth (km),
145 * by default 6371 kilometers. */
146 public void setRadiusOfEarth(double radiusOfEarth) {
147 this.radiusOfEarth = radiusOfEarth;
148 }
149
150 /*** gets radius of the earth (km),
151 * by default 6371 kilometers. */
152 public double getRadiusOfEarth() {
153 return radiusOfEarth;
154 }
155
156 /*** @returns the depths of discontinuities within the velocity model */
157 public double[] getDisconDepths() {
158 double[] disconDepths = new double[getNumLayers()+2];
159 int numFound = 0;
160 VelocityLayer aboveLayer, belowLayer;
161 disconDepths[numFound++] = getVelocityLayer(0).getTopDepth();
162 for (int layerNum = 0; layerNum < getNumLayers()-1; layerNum++) {
163 aboveLayer = getVelocityLayer(layerNum);
164 belowLayer = getVelocityLayer(layerNum+1);
165 if (aboveLayer.getBotPVelocity() != belowLayer.getTopPVelocity() ||
166 aboveLayer.getBotSVelocity() != belowLayer.getTopSVelocity()) {
167
168 disconDepths[numFound++] = aboveLayer.getBotDepth();
169 }
170 }
171 disconDepths[numFound++] = getVelocityLayer(getNumLayers()-1).getBotDepth();
172 double[] temp = new double[numFound];
173 System.arraycopy(disconDepths, 0, temp, 0, numFound);
174 return temp;
175 }
176
177 /*** @returns depth (km) of the moho. It can be input from velocity model
178 * (*.nd) or should be explicitly set.
179 * By default it is 35 kilometers (from Iasp91). For phase naming, the
180 * tau model will choose the closest 1st order discontinuity. Thus
181 * for most simple earth models these values are satisfactory. Take
182 * proper care if your model has a thicker crust and a discontinuity
183 * near 35 km depth. */
184 public double getMohoDepth() {
185 return mohoDepth;
186 }
187
188 public void setMohoDepth(double mohoDepth) {
189 this.mohoDepth = mohoDepth;
190 }
191
192 /*** @returns depth (km) of the cmb (core mantle boundary). It can be input
193 * from velocity model (*.nd) or should be explicitly set.
194 * By default it is 2889 kilometers (from Iasp91). For phase naming, the
195 * tau model will choose the closest 1st order discontinuity. Thus
196 * for most simple earth models these values are satisfactory. */
197 public double getCmbDepth() {
198 return cmbDepth;
199 }
200
201 public void setCmbDepth(double cmbDepth) {
202 this.cmbDepth = cmbDepth;
203 }
204
205 /*** @returns the depth (km) of the iocb (inner core outer core boundary).
206 * It can be input from velocity model (*.nd) or
207 * should be explicitly set.
208 * By default it is 5153.9 kilometers (from Iasp91). For phase naming,
209 * the tau model will choose the closest 1st order discontinuity. Thus
210 * for most simple earth models these values are satisfactory. */
211 public double getIocbDepth() {
212 return iocbDepth;
213 }
214
215 public void setIocbDepth(double iocbDepth) {
216 this.iocbDepth = iocbDepth;
217 }
218
219 public double getMeanDensity() {
220 return meanDensity;
221 }
222
223 public void setMeanDensity(double meanDensity) {
224 this.meanDensity = meanDensity;
225 }
226
227 public double getMinRadius() {
228 return minRadius;
229 }
230
231 public void setMinRadius(double minRadius) {
232 this.minRadius = minRadius;
233 }
234
235 public double getMaxRadius() {
236 return maxRadius;
237 }
238
239 public void setMaxRadius(double maxRadius) {
240 this.maxRadius = maxRadius;
241 }
242
243 public double getG() {
244 return G;
245 }
246
247 public void setG(double G) {
248 this.G = G;
249 }
250
251 public boolean getSpherical() {
252 return spherical;
253 }
254
255 public void setSpherical(boolean spherical) {
256 this.spherical = spherical;
257 }
258
259 public VelocityLayer getVelocityLayerClone(int layerNum) {
260 return (VelocityLayer)((VelocityLayer)layer.elementAt(layerNum)).clone();
261 }
262
263 public VelocityLayer getVelocityLayer(int layerNum) {
264 return (VelocityLayer)layer.elementAt(layerNum);
265 }
266
267 /*** Returns the number of layers in this velocity model. */
268 public int getNumLayers() {
269 return layer.size();
270 }
271
272
273
274 /***
275 * Finds the layer containing the given depth. Note this returns the
276 * upper layer if the depth happens to be at a layer boundary.
277 *
278 * @return the layer number
279 * @exception NoSuchLayerException occurs if no layer contains the
280 * given depth.
281 */
282 public int layerNumberAbove(double depth)
283 throws NoSuchLayerException
284 {
285 VelocityLayer tempLayer;
286
287
288 tempLayer = (VelocityLayer)getVelocityLayer(0);
289 if (depth == tempLayer.getTopDepth()) {
290 return 0;
291 } else {
292 int tooSmallNum = 0;
293 int tooLargeNum = getNumLayers()-1;
294 int currentNum = 0;
295 boolean found = false;
296
297 if (depth < tempLayer.getTopDepth() ||
298 getVelocityLayer(tooLargeNum).getBotDepth() < depth) {
299 throw new NoSuchLayerException(depth);
300 }
301
302 while ( ! found) {
303 currentNum = Math.round((tooSmallNum + tooLargeNum) / 2.0f);
304 tempLayer = getVelocityLayer(currentNum);
305
306 if (tempLayer.getTopDepth() >= depth) {
307 tooLargeNum = currentNum-1;
308 } else if (tempLayer.getBotDepth() < depth) {
309 tooSmallNum = currentNum+1;
310 } else {
311 found = true;
312 }
313 }
314 return currentNum;
315 }
316 }
317
318
319 /***
320 * Finds the layer containing the given depth. Note this returns the
321 * lower layer if the depth happens to be at a layer boundary.
322 *
323 * @return the layer number
324 * @exception NoSuchLayerException occurs if no layer contains the
325 * given depth.
326 */
327 public int layerNumberBelow(double depth)
328 throws NoSuchLayerException
329 {
330 VelocityLayer tempLayer = getVelocityLayer(0);
331 int tooSmallNum = 0;
332 int tooLargeNum = getNumLayers()-1;
333 int currentNum = 0;
334 boolean found = false;
335
336
337 if (depth == tempLayer.getTopDepth()) {
338 return 0;
339 } else if (getVelocityLayer(tooLargeNum).getBotDepth() == depth) {
340
341 return tooLargeNum;
342 } else {
343
344 if (depth < tempLayer.getTopDepth() ||
345 getVelocityLayer(tooLargeNum).getBotDepth() < depth) {
346 throw new NoSuchLayerException(depth);
347 }
348
349 while ( ! found) {
350 currentNum = Math.round((tooSmallNum + tooLargeNum) / 2.0f);
351 tempLayer = getVelocityLayer(currentNum);
352
353 if (tempLayer.getTopDepth() > depth) {
354 tooLargeNum = currentNum-1;
355 } else if (tempLayer.getBotDepth() <= depth) {
356 tooSmallNum = currentNum+1;
357 } else {
358 found = true;
359 }
360 }
361 return currentNum;
362 }
363 }
364
365 /***
366 * returns the value of the given material property, usually
367 * P or S velocity, at the given depth. Note this returns the
368 * value at the bottom of the upper layer if the depth happens
369 * to be at a layer boundary.
370 *
371 * @return the value of the given material property
372 * @exception NoSuchLayerException occurs if no layer contains the given
373 * depth.
374 * @exception NoSuchMatPropException occurs if the material
375 * property is not recognized.
376 */
377 public double evaluateAbove(double depth, char materialProperty)
378 throws NoSuchLayerException, NoSuchMatPropException
379 {
380 VelocityLayer tempLayer;
381 tempLayer = (VelocityLayer)getVelocityLayer(layerNumberAbove(depth));
382 return tempLayer.evaluateAt(depth, materialProperty);
383 }
384
385 /***
386 * returns the value of the given material property, usually
387 * P or S velocity, at the given depth. Note this returns the
388 * value at the top of the lower layer if the depth happens
389 * to be at a layer boundary.
390 *
391 * @return the value of the given material property
392 * @exception NoSuchLayerException occurs if no layer contains the given
393 * depth.
394 * @exception NoSuchMatPropException occurs if the material
395 * property is not recognized.
396 */
397 public double evaluateBelow(double depth, char materialProperty)
398 throws NoSuchLayerException, NoSuchMatPropException
399 {
400 VelocityLayer tempLayer;
401 tempLayer = (VelocityLayer)getVelocityLayer(layerNumberBelow(depth));
402 return tempLayer.evaluateAt(depth, materialProperty);
403 }
404
405 /***
406 * returns the value of the given material property, usually
407 * P or S velocity, at the top of the given layer.
408 * @return the value of the given material property
409 * @exception NoSuchMatPropException occurs if the material
410 * property is not recognized.
411 */
412 public double evaluateAtTop(int layerNumber, char materialProperty)
413 throws NoSuchMatPropException
414 {
415 VelocityLayer tempLayer;
416
417 tempLayer = (VelocityLayer)getVelocityLayer(layerNumber);
418 return tempLayer.evaluateAtTop(materialProperty);
419 }
420
421 /***
422 * returns the value of the given material property, usually
423 * P or S velocity, at the bottom of the given layer.
424 * @return the value of the given material property
425 * @exception NoSuchMatPropException occurs if the material
426 * property is not recognized.
427 */
428 public double evaluateAtBottom(int layerNumber, char materialProperty)
429 throws NoSuchMatPropException
430 {
431 VelocityLayer tempLayer;
432
433 tempLayer = (VelocityLayer)getVelocityLayer(layerNumber);
434 return tempLayer.evaluateAtBottom(materialProperty);
435 }
436
437 /***
438 * returns the depth at the top of the given layer.
439 * @return the depth.
440 */
441 public double depthAtTop(int layerNumber)
442 {
443 VelocityLayer tempLayer;
444
445 tempLayer = (VelocityLayer)getVelocityLayer(layerNumber);
446 return tempLayer.getTopDepth();
447 }
448
449 /***
450 * returns the depth at the bottom of the given layer.
451 * @return the depth.
452 * @exception NoSuchMatPropException occurs if the material
453 * property is not recognized.
454 */
455 public double depthAtBottom(int layerNumber)
456 throws NoSuchMatPropException
457 {
458 VelocityLayer tempLayer;
459
460 tempLayer = (VelocityLayer)getVelocityLayer(layerNumber);
461 return tempLayer.getBotDepth();
462 }
463
464
465
466
467
468
469
470
471 public void replaceLayers(VelocityLayer[] newLayers, boolean matchTop,
472 boolean matchBot) throws NoSuchLayerException
473 {
474
475 int topLayerNum = layerNumberBelow(newLayers[0].getTopDepth());
476 VelocityLayer topLayer = getVelocityLayer(topLayerNum);
477 int botLayerNum = layerNumberAbove(newLayers[newLayers.length-1].getBotDepth());
478 VelocityLayer botLayer = getVelocityLayer(botLayerNum);
479
480 if (matchTop) {
481 try {
482 newLayers[0].setTopPVelocity(topLayer.evaluateAt(
483 newLayers[0].getTopDepth(), 'P'));
484 newLayers[0].setTopSVelocity(topLayer.evaluateAt(
485 newLayers[0].getTopDepth(), 'S'));
486 } catch (NoSuchMatPropException e) {
487
488 System.err.println("Caught NoSuchMatPropException: "+
489 e.getMessage());
490 e.printStackTrace();
491 }
492 }
493
494 if (matchBot) {
495 try {
496 newLayers[newLayers.length-1].setBotPVelocity(botLayer.evaluateAt(newLayers[newLayers.length-1].getBotDepth(), 'P'));
497 newLayers[newLayers.length-1].setBotSVelocity(botLayer.evaluateAt(newLayers[newLayers.length-1].getBotDepth(), 'S'));
498 } catch (NoSuchMatPropException e) {
499
500 System.err.println("Caught NoSuchMatPropException: "+
501 e.getMessage());
502 e.printStackTrace();
503 }
504 }
505
506 if (topLayer.getBotDepth() > newLayers[0].getTopDepth()) {
507
508 VelocityLayer newVLayer = (VelocityLayer)topLayer.clone();
509
510 try {
511 topLayer.setBotPVelocity(topLayer.evaluateAt(
512 newLayers[0].getTopDepth(), 'P'));
513 topLayer.setBotSVelocity(topLayer.evaluateAt(
514 newLayers[0].getTopDepth(), 'S'));
515 topLayer.setBotDepth(newLayers[0].getTopDepth());
516 } catch (NoSuchMatPropException e) {
517
518 System.err.println("Caught NoSuchMatPropException: "+
519 e.getMessage());
520 e.printStackTrace();
521 }
522
523 newVLayer.setTopPVelocity(topLayer.getBotPVelocity());
524 newVLayer.setTopSVelocity(topLayer.getBotSVelocity());
525 newVLayer.setTopDepth(topLayer.getBotDepth());
526 layer.insertElementAt(newVLayer, topLayerNum+1);
527 botLayerNum++;
528 topLayerNum++;
529 }
530 if (botLayer.getBotDepth() > newLayers[newLayers.length-1].getBotDepth()) {
531
532 VelocityLayer newVLayer = (VelocityLayer)botLayer.clone();
533
534 try {
535 botLayer.setBotPVelocity(botLayer.evaluateAt(
536 newLayers[newLayers.length-1].getBotDepth(), 'P'));
537 botLayer.setBotSVelocity(botLayer.evaluateAt(
538 newLayers[newLayers.length-1].getBotDepth(), 'S'));
539 botLayer.setBotDepth(newLayers[newLayers.length-1].getBotDepth());
540 } catch (NoSuchMatPropException e) {
541
542 System.err.println("Caught NoSuchMatPropException: "+
543 e.getMessage());
544 e.printStackTrace();
545 }
546
547 newVLayer.setTopPVelocity(botLayer.getBotPVelocity());
548 newVLayer.setTopSVelocity(botLayer.getBotSVelocity());
549 newVLayer.setTopDepth(botLayer.getBotDepth());
550 layer.insertElementAt(newVLayer, botLayerNum+1);
551 botLayerNum++;
552 }
553
554 for (int i=topLayerNum;i<=botLayerNum; i++) {
555 layer.removeElementAt(topLayerNum);
556 }
557 for (int i=0;i<newLayers.length;i++) {
558 layer.insertElementAt(newLayers[i], topLayerNum+i);
559 }
560 validate();
561 }
562
563 /*** prints out the velocity model into a file in a form suitable for
564 * plotting with GMT. */
565 public void printGMT(String filename) throws IOException {
566 DataOutputStream dos = new DataOutputStream(
567 new BufferedOutputStream(
568 new FileOutputStream(filename)));
569 printGMT(dos);
570 dos.close();
571 }
572
573 /*** prints out the velocity model into a file in a for suitable for
574 * plotting with GMT. */
575 public void printGMT(DataOutputStream dos) throws IOException {
576
577 double depth = 0.0;
578 double pVel = -1.0;
579 double sVel = -1.0;
580 VelocityLayer currVelocityLayer;
581
582 dos.writeBytes("> P velocity for "+modelName+" below\n");
583 for (int layerNum=0;layerNum<getNumLayers();layerNum++) {
584 currVelocityLayer = getVelocityLayer(layerNum);
585 if (currVelocityLayer.getTopPVelocity() != pVel) {
586 dos.writeBytes((float)currVelocityLayer.getTopDepth()+" "+
587 (float)currVelocityLayer.getTopPVelocity()+"\n");
588 }
589 dos.writeBytes((float)currVelocityLayer.getBotDepth()+" "+
590 (float)currVelocityLayer.getBotPVelocity()+"\n");
591 pVel = currVelocityLayer.getBotPVelocity();
592 }
593
594 dos.writeBytes("> S velocity for "+modelName+" below\n");
595 for (int layerNum=0;layerNum<getNumLayers();layerNum++) {
596 currVelocityLayer = getVelocityLayer(layerNum);
597 if (currVelocityLayer.getTopSVelocity() != sVel) {
598 dos.writeBytes((float)currVelocityLayer.getTopDepth()+" "+
599 (float)currVelocityLayer.getTopSVelocity()+"\n");
600 }
601 dos.writeBytes((float)currVelocityLayer.getBotDepth()+" "+
602 (float)currVelocityLayer.getBotSVelocity()+"\n");
603 sVel = currVelocityLayer.getBotSVelocity();
604 }
605 }
606
607 /***
608 * Performs internal consistency checks on the velocity model.
609 */
610 public boolean validate() {
611 VelocityLayer currVelocityLayer, prevVelocityLayer;
612
613
614 if (radiusOfEarth <= 0.0) {
615 System.err.println(
616 "Radius of earth is not positive. radiusOfEarth = "+radiusOfEarth);
617 return false;
618 }
619
620 if (mohoDepth < 0.0) {
621 System.err.println(
622 "mohoDepth is not non-negative. mohoDepth = "+mohoDepth);
623 return false;
624 }
625
626 if (cmbDepth < mohoDepth) {
627 System.err.println(
628 "cmbDepth < mohoDepth. cmbDepth = "+cmbDepth+
629 " mohoDepth = "+mohoDepth);
630 return false;
631 }
632
633 if (cmbDepth <= 0.0) {
634 System.err.println(
635 "cmbDepth is not positive. cmbDepth = "+cmbDepth);
636 return false;
637 }
638
639 if (iocbDepth < cmbDepth) {
640 System.err.println(
641 "iocbDepth < cmbDepth. iocbDepth = "+iocbDepth+
642 " cmbDepth = "+cmbDepth);
643 return false;
644 }
645
646 if (iocbDepth <= 0.0) {
647 System.err.println(
648 "iocbDepth is not positive. iocbDepth = "+iocbDepth);
649 return false;
650 }
651
652 if (minRadius < 0.0) {
653 System.err.println(
654 "minRadius is not non-negative. minRadius = "+minRadius);
655 return false;
656 }
657
658 if (maxRadius <= 0.0) {
659 System.err.println(
660 "maxRadius is not positive. maxRadius = "+maxRadius);
661 return false;
662 }
663
664 if (maxRadius <= minRadius) {
665 System.err.println(
666 "maxRadius <= minRadius. maxRadius = "+maxRadius+
667 " minRadius = "+minRadius);
668 return false;
669 }
670
671 currVelocityLayer = getVelocityLayer(0);
672 prevVelocityLayer = new VelocityLayer();
673 prevVelocityLayer.setBotDepth(currVelocityLayer.getTopDepth());
674 prevVelocityLayer.setBotPVelocity(currVelocityLayer.getTopPVelocity());
675 prevVelocityLayer.setBotSVelocity(currVelocityLayer.getTopSVelocity());
676 prevVelocityLayer.setBotDensity(currVelocityLayer.getTopDensity());
677
678 for (int layerNum=0;layerNum<getNumLayers();layerNum++) {
679 currVelocityLayer = getVelocityLayer(layerNum);
680
681 if (prevVelocityLayer.getBotDepth() != currVelocityLayer.getTopDepth()) {
682
683
684
685 System.err.println("There is a gap in the velocity model "+
686 "between layers "+(layerNum-1)+" and "+layerNum);
687 System.err.println("prevVelocityLayer="+prevVelocityLayer);
688 System.err.println("currVelocityLayer="+currVelocityLayer);
689 return false;
690 }
691 if (currVelocityLayer.getBotDepth() == currVelocityLayer.getTopDepth()) {
692
693
694
695 System.err.println("There is a zero thickness layer in the "+
696 "velocity model at layer "+layerNum);
697 System.err.println("prevVelocityLayer="+prevVelocityLayer);
698 System.err.println("currVelocityLayer="+currVelocityLayer);
699 return false;
700 }
701 if (currVelocityLayer.getTopPVelocity() <= 0.0 ||
702 currVelocityLayer.getBotPVelocity() <= 0.0) {
703
704
705
706 System.err.println("There is a negative P velocity layer in the "+
707 "velocity model at layer "+layerNum);
708 return false;
709 }
710 if (currVelocityLayer.getTopSVelocity() < 0.0 ||
711 currVelocityLayer.getBotSVelocity() < 0.0) {
712
713
714
715 System.err.println("There is a negative S velocity layer in the "+
716 "velocity model at layer "+layerNum);
717 return false;
718 }
719 if ((currVelocityLayer.getTopPVelocity() != 0.0 &&
720 currVelocityLayer.getBotPVelocity() == 0.0) || (
721 currVelocityLayer.getTopPVelocity() == 0.0 &&
722 currVelocityLayer.getBotPVelocity() != 0.0)) {
723
724
725
726 System.err.println("There is a layer that goes to zero P velocity "+
727 "without a discontinuity in the "+
728 "velocity model at layer "+layerNum +
729 "\nThis would cause a divide by zero within this " +
730 "depth range. Try making the velocity small, followed by a " +
731 "discontinuity to zero velocity.");
732 return false;
733 }
734 if ((currVelocityLayer.getTopSVelocity() != 0.0 &&
735 currVelocityLayer.getBotSVelocity() == 0.0) || (
736 currVelocityLayer.getTopSVelocity() == 0.0 &&
737 currVelocityLayer.getBotSVelocity() != 0.0)) {
738
739
740
741 System.err.println("There is a layer that goes to zero S velocity "+
742 "without a discontinuity in the "+
743 "velocity model at layer "+layerNum +
744 "\nThis would cause a divide by zero within this " +
745 "depth range. Try making the velocity small, followed by a " +
746 "discontinuity to zero velocity.");
747 return false;
748 }
749 prevVelocityLayer = currVelocityLayer;
750 }
751 return true;
752 }
753
754 public String toString() {
755 String desc = "modelName="+modelName +"\n"+
756 "\n radiusOfEarth="+radiusOfEarth+
757 "\n mohoDepth="+mohoDepth+
758 "\n cmbDepth="+cmbDepth+
759 "\n iocbDepth="+iocbDepth+
760 "\n meanDensity="+meanDensity+
761 "\n G="+G+
762 "\n minRadius="+minRadius+
763 "\n maxRadius="+maxRadius+
764 "\n spherical="+spherical;
765
766 desc += "\ngetNumLayers()="+getNumLayers() + "\n";
767 return desc;
768 }
769
770 public Object clone() {
771 VelocityModel newObject;
772 try {
773 newObject = (VelocityModel)super.clone();
774 newObject.layer = new Vector(getNumLayers());
775 for (int i=0;i<getNumLayers();i++) {
776 newObject.layer.addElement(getVelocityLayerClone(i));
777 }
778 return newObject;
779
780 } catch (CloneNotSupportedException e) {
781
782
783 throw new InternalError(e.toString());
784 }
785 }
786
787 public void print() {
788 for (int i=0;i<getNumLayers();i++) {
789 System.out.println(getVelocityLayer(i));
790 }
791 }
792
793 /***
794 * Reads in a velocity file. The type of file is determined by the
795 * fileType var.
796 * Calls readTVelFile or readNDFile.
797 * @exception VelocityModelException if the type of file cannot
798 * be determined.
799 */
800 public void readVelocityFile(String filename)
801 throws IOException, VelocityModelException
802 {
803
804
805 int j = filename.lastIndexOf(System.getProperty("file.separator"));
806 String modelFilename = filename.substring(j+1);
807
808 if (modelFilename.endsWith("tvel")) {
809 modelName = modelFilename.substring(0,modelFilename.length() - 5);
810 } else if (modelFilename.endsWith(".nd")) {
811 modelName = modelFilename.substring(0,modelFilename.length() - 3);
812 } else if (modelFilename.startsWith("GB.")) {
813 modelName = modelFilename.substring(3,modelFilename.length());
814 } else {
815 modelName = modelFilename;
816 }
817 if (fileType.equalsIgnoreCase("nd")) {
818 readNDFile(filename);
819 } else if (fileType.equalsIgnoreCase("tvel")) {
820 readTVelFile(filename);
821 } else {
822 throw new VelocityModelException(
823 "What type of velocity file, .tvel or .nd?");
824 }
825 boolean changeMade = fixDisconDepths();
826 }
827
828
829 /***
830 * Reads in a cubic spline file, the original format of the ttimes
831 * code. <em>not yet implemented since linear interpolation (.tvel)
832 * files are conceptually simpler. */
833 public void readCubicSplineFile(String filename, String lookForModelName)
834 throws IOException {
835 System.err.println("readCubicSplineFile not yet implemented.");
836 }
837
838 /***
839 This method reads in a velocity model from a "tvel" ASCII text file.
840 The name of the model file for model "modelname" should be
841 "modelname.tvel". The format of the file is:
842 comment line - generally info about the P velocity model
843 comment line - generally info about the S velocity model
844 depth pVel sVel Density
845 depth pVel sVel Density
846 .
847 .
848 .
849
850 The velocities are assumed to be linear between sample points.
851 Because this type of model file doesn't give complete information
852 we make the following assumptions:
853 modelname - from the filename, with ".tvel" dropped if present
854 radiusOfEarth - the largest depth in the model
855 meanDensity - 5517.0
856 G - 6.67e-11
857
858 Also, because this method makes use of the string tokenizer, comments
859 are allowed. # as well as // signify that the rest of the line is a
860 comment. C style slash-star comments are also allowed.
861 *
862 * @exception VelocityModelException occurs if an EOL should have been
863 * read but wasn't. This may indicate a poorly formatted tvel file.
864 */
865 public void readTVelFile(String filename)
866 throws IOException, VelocityModelException
867 {
868 FileReader fileIn = new FileReader(filename);
869 StreamTokenizer tokenIn = new StreamTokenizer(fileIn);
870
871 tokenIn.commentChar('#');
872 tokenIn.slashStarComments(true);
873 tokenIn.slashSlashComments(true);
874 tokenIn.eolIsSignificant(true);
875 tokenIn.parseNumbers();
876
877
878
879
880
881 while (tokenIn.nextToken() != StreamTokenizer.TT_EOL) {}
882 while (tokenIn.nextToken() != StreamTokenizer.TT_EOL) {}
883
884
885
886
887
888
889
890
891 int myLayerNumber = 0;
892 VelocityLayer tempLayer = new VelocityLayer(myLayerNumber);
893 double depth, pVel, sVel, density;
894
895
896 tokenIn.nextToken();
897 depth = tokenIn.nval;
898 tokenIn.nextToken();
899 pVel = tokenIn.nval;
900 tokenIn.nextToken();
901 sVel = tokenIn.nval;
902 tokenIn.nextToken();
903 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
904
905 density = tokenIn.nval;
906 tokenIn.nextToken();
907 } else { density = 5571.0;}
908 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
909
910 throw new VelocityModelException(
911 "Should have found an EOL but didn't"+
912 " Layer="+myLayerNumber+
913 " tokenIn="+tokenIn);
914 } else {tokenIn.nextToken();}
915
916 while (tokenIn.ttype != StreamTokenizer.TT_EOF) {
917
918
919 tempLayer.setTopDepth(depth);
920 tempLayer.setTopPVelocity(pVel);
921 tempLayer.setTopSVelocity(sVel);
922 tempLayer.setTopDensity(density);
923
924 tempLayer.setBotDepth(depth = tokenIn.nval);
925 tokenIn.nextToken();
926 tempLayer.setBotPVelocity(pVel = tokenIn.nval);
927 tokenIn.nextToken();
928 tempLayer.setBotSVelocity(sVel = tokenIn.nval);
929 tokenIn.nextToken();
930 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
931
932 tempLayer.setBotDensity(density = tokenIn.nval);
933 tokenIn.nextToken();
934 }
935 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
936
937 throw new VelocityModelException(
938 "Should have found an EOL but didn't"+
939 " Layer="+myLayerNumber+
940 " tokenIn="+tokenIn);
941 } else {tokenIn.nextToken();}
942 if (tempLayer.getTopDepth() != tempLayer.getBotDepth()) {
943
944
945
946
947 layer.addElement(tempLayer);
948 myLayerNumber++;
949 tempLayer = new VelocityLayer(myLayerNumber);
950 }
951 }
952 radiusOfEarth = depth;
953 maxRadius = depth;
954
955
956 }
957
958
959 /***
960 * This method reads in a velocity model from a "nd" ASCII text file,
961 * the format used by Xgbm.
962 * The name of the model file for model "modelname" should be
963 * "modelname.nd". The format of the file is:
964 * depth pVel sVel Density Qp Qs
965 * depth pVel sVel Density Qp Qs
966 * .
967 * .
968 * .
969 * with each major boundary separated with a line with "mantle",
970 * "outer-core" or "inner-core". This feature makes phase interpretation
971 * much easier to code. Also, as they are not needed for travel time
972 * calculations, the density, Qp and Qs may be omitted.
973 *
974 * The velocities are assumed to be linear between sample points.
975 * Because this type of model file doesn't give complete information
976 * we make the following assumptions:
977 * modelname - from the filename, with ".nd" dropped, if present
978 * radiusOfEarth - the largest depth in the model
979 *
980 * Also, because this method makes use of the string tokenizer, comments
981 * are allowed. # as well as // signify that the rest of the line is a
982 * comment. C style slash-star comments are also allowed.
983 *
984 * @exception VelocityModelException occurs if an EOL should have been
985 * read but wasn't. This may indicate a poorly formatted model file.
986 */
987 public void readNDFile(String filename)
988 throws IOException, VelocityModelException
989 {
990 FileReader fileIn = new FileReader(filename);
991 StreamTokenizer tokenIn = new StreamTokenizer(fileIn);
992
993 tokenIn.commentChar('#');
994 tokenIn.slashStarComments(true);
995 tokenIn.slashSlashComments(true);
996 tokenIn.eolIsSignificant(true);
997 tokenIn.parseNumbers();
998
999
1000
1001
1002
1003
1004
1005 int myLayerNumber = 0;
1006 VelocityLayer tempLayer = new VelocityLayer(myLayerNumber);
1007 double depth, pVel, sVel, density=2.6, qp=1000, qs=2000;
1008
1009
1010 tokenIn.nextToken();
1011 depth = tokenIn.nval;
1012 tokenIn.nextToken();
1013 pVel = tokenIn.nval;
1014 tokenIn.nextToken();
1015 sVel = tokenIn.nval;
1016 tokenIn.nextToken();
1017 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1018
1019 density = tokenIn.nval;
1020 tokenIn.nextToken();
1021 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1022
1023 qp = tokenIn.nval;
1024 tokenIn.nextToken();
1025 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1026
1027 qs = tokenIn.nval;
1028 tokenIn.nextToken();
1029 }
1030 }
1031 }
1032 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1033
1034 throw new VelocityModelException(
1035 "Should have found an EOL but didn't"+
1036 " Layer="+myLayerNumber+
1037 " tokenIn="+tokenIn);
1038 } else {tokenIn.nextToken();}
1039
1040 while (tokenIn.ttype != StreamTokenizer.TT_EOF) {
1041
1042
1043 if (tokenIn.ttype == StreamTokenizer.TT_WORD) {
1044 if (tokenIn.sval.equalsIgnoreCase("mantle")) {
1045 mohoDepth = depth;
1046 }
1047 if (tokenIn.sval.equalsIgnoreCase("outer-core")) {
1048 cmbDepth = depth;
1049 }
1050 if (tokenIn.sval.equalsIgnoreCase("inner-core")) {
1051 iocbDepth = depth;
1052 }
1053 while (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1054 tokenIn.nextToken();
1055 }
1056 tokenIn.nextToken();
1057 }
1058 tempLayer.setTopDepth(depth);
1059 tempLayer.setTopPVelocity(pVel);
1060 tempLayer.setTopSVelocity(sVel);
1061 tempLayer.setTopDensity(density);
1062 tempLayer.setTopQp(qp);
1063 tempLayer.setTopQs(qs);
1064
1065 tempLayer.setBotDepth(depth = tokenIn.nval);
1066 tokenIn.nextToken();
1067 tempLayer.setBotPVelocity(pVel = tokenIn.nval);
1068 tokenIn.nextToken();
1069 tempLayer.setBotSVelocity(sVel = tokenIn.nval);
1070 tokenIn.nextToken();
1071
1072 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1073
1074 tempLayer.setBotDensity(density = tokenIn.nval);
1075 tokenIn.nextToken();
1076 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1077
1078 tempLayer.setBotQp(qp = tokenIn.nval);
1079 tokenIn.nextToken();
1080 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1081
1082 tempLayer.setBotQs(qs = tokenIn.nval);
1083 tokenIn.nextToken();
1084 }
1085 }
1086 }
1087 if (tokenIn.ttype != StreamTokenizer.TT_EOL) {
1088
1089 throw new VelocityModelException(
1090 "Should have found an EOL but didn't"+
1091 " Layer="+myLayerNumber+
1092 " tokenIn="+tokenIn);
1093 } else {tokenIn.nextToken();}
1094 if (tempLayer.getTopDepth() != tempLayer.getBotDepth()) {
1095
1096
1097
1098
1099 layer.addElement(tempLayer);
1100 myLayerNumber++;
1101 tempLayer = new VelocityLayer(myLayerNumber);
1102 }
1103 }
1104 radiusOfEarth = depth;
1105 maxRadius = depth;
1106
1107
1108 }
1109
1110 /*** resets depths of major discontinuities to match those existing in the
1111 * input velocity model. The initial values are set such that if there
1112 * is no discontinuity within the top 100 km then the moho is set to 0.0.
1113 * Similarly, if there are no discontinuities at al then the cmb is set
1114 * to the radius of the earth. Similarly for the iocb, except it must
1115 * be a fluid to solid boundary and deeper than 100km to avoid problems
1116 * with shallower fluid layers, eg oceans.
1117 */
1118 public boolean fixDisconDepths() {
1119 boolean changeMade = false;
1120 VelocityLayer aboveLayer, belowLayer;
1121 double mohoMin = 65.0, cmbMin=radiusOfEarth, iocbMin=radiusOfEarth-100.0;
1122 double tempMohoDepth=0.0, tempCmbDepth=radiusOfEarth,
1123 tempIocbDepth=radiusOfEarth;
1124
1125 for (int layerNum = 0; layerNum < getNumLayers()-1; layerNum++) {
1126 aboveLayer = getVelocityLayer(layerNum);
1127 belowLayer = getVelocityLayer(layerNum+1);
1128 if (aboveLayer.getBotPVelocity() != belowLayer.getTopPVelocity() ||
1129 aboveLayer.getBotSVelocity() != belowLayer.getTopSVelocity()) {
1130
1131 if (Math.abs(mohoDepth - aboveLayer.getBotDepth()) < mohoMin) {
1132 tempMohoDepth = aboveLayer.getBotDepth();
1133 mohoMin = Math.abs(mohoDepth - aboveLayer.getBotDepth());
1134 }
1135 if (Math.abs(cmbDepth - aboveLayer.getBotDepth()) < cmbMin) {
1136 tempCmbDepth = aboveLayer.getBotDepth();
1137 cmbMin = Math.abs(cmbDepth - aboveLayer.getBotDepth());
1138 }
1139 if (aboveLayer.getBotSVelocity() == 0.0 &&
1140 belowLayer.getTopSVelocity() > 0.0 &&
1141 Math.abs(iocbDepth - aboveLayer.getBotDepth()) < iocbMin) {
1142 tempIocbDepth = aboveLayer.getBotDepth();
1143 iocbMin = Math.abs(iocbDepth - aboveLayer.getBotDepth());
1144 }
1145 }
1146 }
1147 if (mohoDepth != tempMohoDepth || cmbDepth != tempCmbDepth ||
1148 iocbDepth != tempIocbDepth) {
1149 changeMade = true;
1150 }
1151 mohoDepth = tempMohoDepth;
1152 cmbDepth = tempCmbDepth;
1153 iocbDepth = (tempCmbDepth !=tempIocbDepth ? tempIocbDepth: radiusOfEarth);
1154 return changeMade;
1155 }
1156
1157 /***
1158 * Returns a flat velocity model object equivalent to the spherical velocity
1159 * model via the earth flattening transform.
1160 *
1161 * @return the flattened VelocityModel object.
1162 * @exception VelocityModelException
1163 * occurs ???.
1164 */
1165 public VelocityModel earthFlattenTransform() throws VelocityModelException {
1166 VelocityModel flatModel;
1167 VelocityLayer newLayer, oldLayer;
1168 flatModel = (VelocityModel)this.clone();
1169 flatModel.spherical = false;
1170 flatModel.layer = new Vector(vectorLength);
1171 for(int i = 0; i < getNumLayers(); i++) {
1172 oldLayer = getVelocityLayer(i);
1173 newLayer = new VelocityLayer(i,
1174 radiusOfEarth
1175 * Math.log(oldLayer.getTopDepth()
1176 / radiusOfEarth),
1177 radiusOfEarth
1178 * Math.log(oldLayer.getBotDepth()
1179 / radiusOfEarth),
1180 radiusOfEarth
1181 * oldLayer.getTopPVelocity()
1182 / oldLayer.getTopDepth(),
1183 radiusOfEarth
1184 * oldLayer.getBotPVelocity()
1185 / oldLayer.getBotDepth(),
1186 radiusOfEarth
1187 * oldLayer.getTopSVelocity()
1188 / oldLayer.getTopDepth(),
1189 radiusOfEarth
1190 * oldLayer.getBotSVelocity()
1191 / oldLayer.getBotDepth());
1192 flatModel.layer.addElement(newLayer);
1193 }
1194 return flatModel;
1195 }
1196
1197 /*** Just for debugging purposes. */
1198 public static void main(String[] args) {
1199 VelocityModel vMod = new VelocityModel();
1200 String modelFilename;
1201 if (args.length >= 1) {
1202 modelFilename = args[0];
1203 } else {
1204 modelFilename = "iasp91.tvel";
1205 }
1206 boolean DEBUG = true;
1207
1208 try {
1209 vMod.readVelocityFile(modelFilename);
1210
1211 System.out.println("Done reading.");
1212 if (!vMod.validate()) {
1213 System.out.println("FAILED VELOCITY MODEL VALIDATION!");
1214 }
1215 vMod.printGMT(vMod.modelName+".gmt");
1216 } catch (IOException e) {
1217 System.out.println("Tried to read!\n Caught IOException "
1218 + e.getMessage());
1219 } catch (VelocityModelException e) {
1220 System.out.println("Tried to read!\n Caught VelocityModelException "
1221 + e.getMessage());
1222 e.printStackTrace();
1223 } finally {
1224 System.out.println("Done!\n");
1225 }
1226 }
1227 }