View Javadoc

1   /*
2    * The TauP Toolkit: Flexible Seismic Travel-Time and Raypath Utilities.
3    * Copyright (C) 1998-2000 University of South Carolina This program is free
4    * software; you can redistribute it and/or modify it under the terms of the GNU
5    * General Public License as published by the Free Software Foundation; either
6    * version 2 of the License, or (at your option) any later version. This program
7    * is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
8    * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
9    * PARTICULAR PURPOSE. See the GNU General Public License for more details. You
10   * should have received a copy of the GNU General Public License along with this
11   * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
12   * Suite 330, Boston, MA 02111-1307, USA. The current version can be found at <A
13   * HREF="www.seis.sc.edu">http://www.seis.sc.edu </A> Bug reports and comments
14   * should be directed to H. Philip Crotwell, crotwell@seis.sc.edu or Tom Owens,
15   * owens@seis.sc.edu
16   */
17  package edu.sc.seis.TauP;
18  
19  import java.io.BufferedInputStream;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.InputStreamReader;
26  import java.io.InvalidClassException;
27  import java.io.ObjectInputStream;
28  import java.io.ObjectOutputStream;
29  import java.io.OptionalDataException;
30  import java.io.OutputStream;
31  import java.io.Serializable;
32  import java.io.StreamCorruptedException;
33  import java.io.StreamTokenizer;
34  
35  /***
36   * provides storage all of the TauBranch's comprising a model.
37   * 
38   * @version 1.1.3 Wed Jul 18 15:00:35 GMT 2001
39   * @author H. Philip Crotwell
40   */
41  public class TauModel implements Serializable, Cloneable {
42  
43      /*** True to enable debugging output. */
44      transient public boolean DEBUG = false;
45  
46      /*** True if this is a spherical slowness model. False if flat. */
47      public boolean spherical = true;
48  
49      /*** Depth for which tau model was constructed. */
50      protected double sourceDepth = 0.0;
51  
52      /*** Branch with the source at its top. */
53      protected int sourceBranch = 0;
54  
55      /***
56       * Depths that should not have reflections or phase conversions. For
57       * instance, if the source is not at a branch boundary then noDisconDepths
58       * contains source depth and reflections and phase conversions are not
59       * allowed at this branch boundary. If the source happens to fall on a real
60       * discontinuity then then it is not included.
61       */
62      protected double[] noDisconDepths = new double[0];
63  
64      /*** Depth of the moho. */
65      protected double mohoDepth;
66  
67      /*** Branch with the moho at its top. */
68      protected int mohoBranch;
69  
70      /*** Depth of the cmb. */
71      protected double cmbDepth;
72  
73      /*** Branch with the cmb at its top. */
74      protected int cmbBranch;
75  
76      /*** Depth of the iocb. */
77      protected double iocbDepth;
78  
79      /*** Branch with the iocb at its top. */
80      protected int iocbBranch;
81  
82      /*** Radius of the Earth in km, usually input from the velocity model. */
83      protected double radiusOfEarth = 6371.0;
84  
85      /***
86       * The slowness model that was used to generate the tau model. This in
87       * needed in order to modify the tau branches from a surface focus event to
88       * an event at depth. This is normally be set when the tau model is
89       * generated to be a clone of the slowness model.
90       */
91      public SlownessModel sMod;
92  
93      /***
94       * ray parameters used to construct the tau branches. This may only be a
95       * subset of the slownesses/ray parameters saved in the slowness model due
96       * to high slowness zones (low velocity zones).
97       */
98      protected double[] rayParams;
99  
100     /***
101      * 2D Array containing a TauBranch object corresponding to each "branch" of
102      * the tau model, 0 is P and 1 is S. Branches correspond to depth regions
103      * between discontinuities or reversals in slowness gradient for a wave
104      * type. Each branch contains time, distance, and tau increments for each
105      * ray parameter in rayParams for the layer. Rays that turn above the branch
106      * layer are assigned 0.0 time, distance, and tau increments.
107      */
108     public TauBranch[][] tauBranches = new TauBranch[2][];
109 
110     // Methods -----------------------------------------------------------
111     // accessor methods
112     /*** @returns the name of the earth model used to construct the tau model. */
113     public String getModelName() {
114         return sMod.vMod.getModelName();
115     }
116 
117     public SlownessModel getSlownessModel() {
118         return sMod;
119     }
120 
121     public VelocityModel getVelocityModel() {
122         return sMod.vMod;
123     }
124 
125     /*** @returns depth for which tau model was constructed. */
126     public double getSourceDepth() {
127         return sourceDepth;
128     }
129 
130     /*** @returns branch number with the source at its top. */
131     public int getSourceBranch() {
132         return sourceBranch;
133     }
134 
135     /***
136      * Branches, such as the branch with the source at its top, that are not
137      * allowed to have reflections and phase conversions at their tops.
138      */
139     public double[] getNoDisconDepths() {
140         return noDisconDepths;
141     }
142 
143     /***
144      * Does the given branch number have a noDisconDepth at its top? We test
145      * against PWave Tau branches (ie true) since S is the same.
146      */
147     public boolean isNoDisconBranch(int branchNum) {
148         for(int i = 0; i < noDisconDepths.length; i++) {
149             if(noDisconDepths[i] == getTauBranch(branchNum, true).getTopDepth()) { return true; }
150         }
151         return false;
152     }
153 
154     /***
155      * Is the given depth a "noDisconDepth"?
156      */
157     public boolean isNoDisconDepth(double noDisconDepth) {
158         for(int i = 0; i < noDisconDepths.length; i++) {
159             if(noDisconDepths[i] == noDisconDepth) { return true; }
160         }
161         return false;
162     }
163 
164     public synchronized void setNoDisconDepths(double[] noDisconDepths) {
165         this.noDisconDepths = noDisconDepths;
166     }
167 
168     public synchronized void appendNoDisconDepth(double noDisconDepth) {
169         double[] temp = new double[noDisconDepths.length + 1];
170         System.arraycopy(noDisconDepths, 0, temp, 0, noDisconDepths.length);
171         noDisconDepths = temp;
172         noDisconDepths[noDisconDepths.length - 1] = noDisconDepth;
173     }
174 
175     /*** @returns depth of the moho. */
176     public double getMohoDepth() {
177         return mohoDepth;
178     }
179 
180     /*** @returns branch number with the moho at its top. */
181     public int getMohoBranch() {
182         return mohoBranch;
183     }
184 
185     /*** @returns depth of the cmb. */
186     public double getCmbDepth() {
187         return cmbDepth;
188     }
189 
190     /*** @returns branch number with the cmb at its top. */
191     public int getCmbBranch() {
192         return cmbBranch;
193     }
194 
195     /*** @returns depth of the iocb. */
196     public double getIocbDepth() {
197         return iocbDepth;
198     }
199 
200     /*** @returns branch number with the iocb at its top. */
201     public int getIocbBranch() {
202         return iocbBranch;
203     }
204 
205     /***
206      * @returns the radius of the Earth in km, usually input from the velocity
207      *          model.
208      */
209     public double getRadiusOfEarth() {
210         return radiusOfEarth;
211     }
212 
213     /***
214      * @returns an array, cloned, of the ray parameters used to construct the
215      *          tau branches. This may only be a subset of the slownesses/ray
216      *          parameters saved in the slowness model due to high slowness
217      *          zones (low velocity zones).
218      */
219     public double[] getRayParams() {
220         return (double[])rayParams.clone();
221     }
222 
223     public double getOneRayParam(int i) {
224         return rayParams[i];
225     }
226 
227     public int getNumBranches() {
228         return tauBranches[0].length;
229     }
230 
231     public TauBranch getTauBranch(int branchNum, boolean isPWave) {
232         if(isPWave) {
233             return tauBranches[0][branchNum];
234         } else {
235             return tauBranches[1][branchNum];
236         }
237     }
238 
239     /***
240      * returns an array of the depths that are boundaries between branches
241      */
242     public double[] getBranchDepths() {
243         double[] branchDepths = new double[getNumBranches()];
244         // true means use p wave, but S wave should be the same
245         branchDepths[0] = getTauBranch(0, true).getTopDepth();
246         for(int i = 1; i < branchDepths.length; i++) {
247             branchDepths[i] = getTauBranch(i - 1, true).getBotDepth();
248         }
249         return branchDepths;
250     }
251 
252     /***
253      * returns the turning depth for a ray of given ray parameter. Note this is
254      * for a surface source, and so converted phases my give incorrect results,
255      * e.g. SKS for certain ray parameters turns within the upper part of the
256      * outer core that is a low velocity zone for P so no P wave of that ray
257      * parameter could reach the core. For layer specific turning points, see
258      * the other SlownessModel.findDepth.
259      */
260     public double findDepth(double rayParam, boolean isPWave)
261             throws TauModelException {
262         try {
263             return sMod.findDepth(rayParam, isPWave);
264         } catch(SlownessModelException e) {
265             throw new TauModelException("findDepth: caught SlownessModelException:"
266                     + e.getMessage());
267         }
268     }
269 
270     // normal methods
271     /***
272      * Calculates tau for each branch within a slowness model.
273      * 
274      * @exception SlownessModelException
275      *                occurs if getNumLayers() < 1 as we cannot compute a
276      *                distance without a layer.
277      */
278     public void calcTauIncFrom(SlownessModel sMod)
279             throws SlownessModelException, NoSuchLayerException,
280             TauModelException, NoSuchMatPropException {
281         SlownessLayer topSLayer, botSLayer, currSLayer;
282         TimeDist timedist = new TimeDist();
283         int topCritLayerNum, botCritLayerNum;
284         /*
285          * First, we must have at least 1 slowness layer to calculate a
286          * distance. Otherwise we must signal an exception.
287          */
288         if(DEBUG) {
289             System.out.println("Size of slowness model:"
290                     + " sMod.getNumLayers('P') = " + sMod.getNumLayers(true)
291                     + ", sMod.getNumLayers('S') = " + sMod.getNumLayers(false));
292         }
293         if(sMod.getNumLayers(true) == 0 || sMod.getNumLayers(false) == 0) { throw new SlownessModelException("Can't calculate tauInc when getNumLayers() = 0. "
294                 + "I need more slowness samples."); }
295         if(!sMod.validate()) { throw new SlownessModelException("Validation failed: "
296                 + "Something is wrong with the slowness model."); }
297         this.sMod = (SlownessModel)sMod.clone();
298         radiusOfEarth = sMod.getRadiusOfEarth();
299         sourceDepth = 0.0;
300         sourceBranch = 0;
301         /*
302          * Create a array holding the ray parameter that we will use for
303          * constructing the tau splines. Only store ray parameters that are not
304          * in a high slowness zone, ie they are smaller than the minimum ray
305          * parameter seen so far.
306          */
307         int numBranches = sMod.getNumCriticalDepths() - 1;
308         tauBranches[0] = new TauBranch[numBranches];
309         tauBranches[1] = new TauBranch[numBranches];
310         /*
311          * Here we find the list of ray parameters to be used for the tau model.
312          * We only need to find ray parameters for S waves since P waves have
313          * been constructed to be a subset of the S samples.
314          */
315         int rayNum = 0;
316         double minPSoFar = sMod.getSlownessLayerClone(0, false).getTopP();
317         double[] tempRayParams = new double[2 * sMod.getNumLayers(false)
318                 + sMod.getNumCriticalDepths()];
319         // make sure we get the top slowness of the very top layer
320         tempRayParams[rayNum] = minPSoFar;
321         rayNum++;
322         for(int layerNum = 0; layerNum < sMod.getNumLayers(false); layerNum++) {
323             currSLayer = sMod.getSlownessLayer(layerNum, false);
324             /*
325              * Add the top if it is strictly less than the last sample added.
326              * Note that this will not be added if the slowness is continuous
327              * across the layer boundary.
328              */
329             if(currSLayer.getTopP() < minPSoFar) {
330                 tempRayParams[rayNum] = currSLayer.getTopP();
331                 rayNum++;
332                 minPSoFar = currSLayer.getTopP();
333             }
334             /*
335              * Add the bottom if it is strictly less than the last sample added.
336              * This will always happen unless we are within a high slowness
337              * zone.
338              */
339             if(currSLayer.getBotP() < minPSoFar) {
340                 tempRayParams[rayNum] = currSLayer.getBotP();
341                 rayNum++;
342                 minPSoFar = currSLayer.getBotP();
343             }
344         }
345         /* Copy tempRayParams to rayParams so the the size is exactly right. */
346         rayParams = new double[rayNum];
347         System.arraycopy(tempRayParams, 0, rayParams, 0, rayNum);
348         tempRayParams = null;
349         if(DEBUG) {
350             System.out.println("Number of slowness samples for tau =" + rayNum);
351         }
352         CriticalDepth topCritDepth, botCritDepth;
353         int waveNum;
354         boolean isPWave;
355         for(waveNum = 0, isPWave = true; waveNum < 2; waveNum++, isPWave = false) {
356             // The minimum slowness seen so far
357             minPSoFar = sMod.getSlownessLayerClone(0, isPWave).getTopP();
358             // loop over critical slowness layers since they form the branches
359             for(int critNum = 0; critNum < sMod.getNumCriticalDepths() - 1; critNum++) {
360                 topCritDepth = sMod.getCriticalDepth(critNum);
361                 topCritLayerNum = topCritDepth.getLayerNum(isPWave);
362                 botCritDepth = sMod.getCriticalDepth(critNum + 1);
363                 botCritLayerNum = botCritDepth.getLayerNum(isPWave) - 1;
364                 if(DEBUG) {
365                     System.out.println("Calculating " + (isPWave ? "P" : "S")
366                             + " tau branch for branch " + critNum
367                             + " topCritLayerNum=" + topCritLayerNum
368                             + " botCritLayerNum=" + botCritLayerNum
369                             + "\nminPSoFar=" + minPSoFar);
370                 }
371                 tauBranches[waveNum][critNum] = new TauBranch(topCritDepth.depth,
372                                                               botCritDepth.depth,
373                                                               isPWave);
374                 tauBranches[waveNum][critNum].DEBUG = DEBUG;
375                 tauBranches[waveNum][critNum].createBranch(sMod,
376                                                            minPSoFar,
377                                                            rayParams);
378                 /*
379                  * update minPSoFar. Note that the new minPSoFar could be at the
380                  * start of a discontinuty over a high slowness zone, so we need
381                  * to check the top, bottom and the layer just above the
382                  * discontinuity.
383                  */
384                 topSLayer = sMod.getSlownessLayer(topCritLayerNum, isPWave);
385                 botSLayer = sMod.getSlownessLayer(botCritLayerNum, isPWave);
386                 minPSoFar = Math.min(minPSoFar, Math.min(topSLayer.getTopP(),
387                                                          botSLayer.getBotP()));
388                 botSLayer = sMod.getSlownessLayer(sMod.layerNumberAbove(botCritDepth.depth,
389                                                                         isPWave),
390                                                   isPWave);
391                 minPSoFar = Math.min(minPSoFar, botSLayer.getBotP());
392             }
393         }
394         /*
395          * Here we decide which branches are the closest to the moho, cmb, and
396          * iocb by comparing the depth of the top of the branch with the depths
397          * in the Velocity Model.
398          */
399         double bestMoho = Double.MAX_VALUE;
400         double bestCmb = Double.MAX_VALUE;
401         double bestIocb = Double.MAX_VALUE;
402         for(int branchNum = 0; branchNum < tauBranches[0].length; branchNum++) {
403             TauBranch tBranch = (TauBranch)tauBranches[0][branchNum];
404             if(Math.abs(tBranch.getTopDepth() - sMod.vMod.getMohoDepth()) <= bestMoho) {
405                 mohoBranch = branchNum;
406                 bestMoho = Math.abs(tBranch.getTopDepth()
407                         - sMod.vMod.getMohoDepth());
408             }
409             if(Math.abs(tBranch.getTopDepth() - sMod.vMod.getCmbDepth()) < bestCmb) {
410                 cmbBranch = branchNum;
411                 bestCmb = Math.abs(tBranch.getTopDepth()
412                         - sMod.vMod.getCmbDepth());
413             }
414             if(Math.abs(tBranch.getTopDepth() - sMod.vMod.getIocbDepth()) < bestIocb) {
415                 iocbBranch = branchNum;
416                 bestIocb = Math.abs(tBranch.getTopDepth()
417                         - sMod.vMod.getIocbDepth());
418             }
419         }
420         /*
421          * Now set mohoDepth, etc to the top of the branches we have decided on.
422          */
423         mohoDepth = tauBranches[0][mohoBranch].getTopDepth();
424         cmbDepth = tauBranches[0][cmbBranch].getTopDepth();
425         iocbDepth = tauBranches[0][iocbBranch].getTopDepth();
426         if(!validate()) { throw new TauModelException("calcTauIncFrom: Validation failed!"); }
427     }
428 
429     /***
430      * Finds the branch that either has the depth as its top boundary, or
431      * strictly contains the depth. Also, we allow the bottommost branch to
432      * contain its bottom depth, so that the center if the earth is contained
433      * within the bottom branch.
434      */
435     public int findBranch(double depth) throws TauModelException {
436         for(int i = 0; i < tauBranches[0].length; i++) {
437             if(tauBranches[0][i].getTopDepth() <= depth
438                     && tauBranches[0][i].getBotDepth() > depth) { return i; }
439         }
440         /* Check to see if depth is center of earth. */
441         if(tauBranches[0][tauBranches[0].length - 1].getBotDepth() == depth) {
442             return tauBranches[0].length - 1;
443         } else {
444             throw new TauModelException("No TauBranch contains depth=" + depth);
445         }
446     }
447 
448     /***
449      * Computes a new tau model for a source at depth using the previously
450      * computed branches for a surface source. No change is needed to the
451      * branches above and below the branch containing the depth, except for the
452      * addition of a slowness sample. The branch containing the source depth is
453      * split into 2 branches, and up going branch and a downgoing branch.
454      * Additionally, the slowness at the source depth must be sampled exactly as
455      * it is an extremal point for each of these branches. See Buland and
456      * Chapman p 1290.
457      */
458     public TauModel depthCorrect(double depth) throws TauModelException {
459         if(sourceDepth != 0.0) { throw new TauModelException("depthCorrect: Can't depth correct "
460                 + "a tau model that is not for a surface source."); }
461         if(depth > getCmbDepth()) { throw new TauModelException("depthCorrect: Can't depth correct "
462                 + "for a depth in the core."); }
463         TauModel tMod = splitBranch(depth);
464         tMod.sourceDepth = depth;
465         tMod.sourceBranch = tMod.findBranch(depth);
466         validate();
467         return tMod;
468     }
469 
470     /***
471      * returns a new TauModel with the branches containing depth split at depth.
472      * Used for putting a source at depth since a source can only be located on
473      * a branch boundary.
474      */
475     public TauModel splitBranch(double depth) throws TauModelException {
476         TauModel tMod;
477         int topCritLayerNum, botCritLayerNum;
478         SlownessLayer topSLayer, botSLayer;
479         try {
480             tMod = (TauModel)clone();
481             /*
482              * first check to see if depth is already a branch boundary. If so
483              * then we need only return the clone.
484              */
485             for(int branchNum = 0; branchNum < tMod.tauBranches[0].length; branchNum++) {
486                 if(tMod.tauBranches[0][branchNum].getTopDepth() == depth
487                         || tMod.tauBranches[0][branchNum].getBotDepth() == depth) { return tMod; }
488             }
489             /*
490              * depth is not a branch boundary, so we must modify the tau model.
491              */
492             int indexP = -1;
493             double PWaveRayParam = -1.0;
494             int indexS = -1;
495             double SWaveRayParam = -1.0;
496             int waveNum;
497             boolean isPWave;
498             SplitLayerInfo splitInfo;
499             /*
500              * do S wave first (isPWave=false) since the S wave ray parameter is >
501              * P wave ray parameter and thus comes before it in the rayParams
502              * array
503              */
504             for(waveNum = 1, isPWave = false; waveNum >= 0; waveNum--, isPWave = true) {
505                 splitInfo = tMod.sMod.splitLayer(depth, isPWave);
506                 if(splitInfo.movedSample) {} else if(splitInfo.neededSplit) {
507                     /*
508                      * We split the slowness layers containing depth into 2
509                      * layers each.
510                      */
511                     int layerNum = tMod.sMod.layerNumberAbove(depth, isPWave);
512                     SlownessLayer sLayer = tMod.sMod.getSlownessLayer(layerNum,
513                                                                       isPWave);
514                     double newRayParam = splitInfo.getRayParam();
515                     int index = -1;
516                     // add the new ray parameters to the rayParams array
517                     // Only loop to length-1 as last sample is always 0
518                     // and negative is not allowed
519                     for(int i = 0; i < tMod.rayParams.length - 1; i++) {
520                         if(tMod.rayParams[i] < newRayParam
521                                 && tMod.rayParams[i + 1] > newRayParam) {
522                             index = i;
523                             double[] oldRayParams = tMod.rayParams;
524                             tMod.rayParams = new double[oldRayParams.length + 1];
525                             System.arraycopy(oldRayParams,
526                                              0,
527                                              tMod.rayParams,
528                                              0,
529                                              index);
530                             tMod.rayParams[index] = newRayParam;
531                             System.arraycopy(oldRayParams,
532                                              index,
533                                              tMod.rayParams,
534                                              index + 1,
535                                              oldRayParams.length - index);
536                             if(isPWave) {
537                                 indexP = index;
538                                 PWaveRayParam = newRayParam;
539                             } else {
540                                 indexS = index;
541                                 SWaveRayParam = newRayParam;
542                             }
543                             break;
544                         }
545                     }
546                 }
547             }
548             /*
549              * Now we add a sample to each branch above the depth, split the
550              * branch containing the depth, and add a sample to each deeper
551              * branch.
552              */
553             int branchToSplit = tMod.findBranch(depth);
554             int topCritLayerNumP, botCritLayerNumP;
555             int topCritLayerNumS, botCritLayerNumS;
556             int splitLayerNumP, splitLayerNumS;
557             TauBranch[][] newtauBranches = new TauBranch[2][tMod.getNumBranches() + 1];
558             for(int i = 0; i < branchToSplit; i++) {
559                 newtauBranches[0][i] = (TauBranch)tMod.tauBranches[0][i].clone();
560                 newtauBranches[1][i] = (TauBranch)tMod.tauBranches[1][i].clone();
561                 topCritLayerNumP = tMod.sMod.layerNumberBelow(newtauBranches[0][i].getTopDepth(),
562                                                               true);
563                 botCritLayerNumP = tMod.sMod.layerNumberAbove(newtauBranches[0][i].getBotDepth(),
564                                                               true);
565                 topCritLayerNumS = tMod.sMod.layerNumberBelow(newtauBranches[1][i].getTopDepth(),
566                                                               false);
567                 botCritLayerNumS = tMod.sMod.layerNumberAbove(newtauBranches[1][i].getBotDepth(),
568                                                               false);
569                 if(indexS != -1) {
570                     // add the new ray parameter from splitting the S Wave
571                     // slowness layer to both the P and S wave Tau branches
572                     newtauBranches[0][i].insert(SWaveRayParam,
573                                                 tMod.sMod,
574                                                 indexS);
575                     newtauBranches[1][i].insert(SWaveRayParam,
576                                                 tMod.sMod,
577                                                 indexS);
578                 }
579                 if(indexP != -1) {
580                     // add the new ray parameter from splitting the P Wave
581                     // slowness layer to both the P and S wave Tau branches
582                     newtauBranches[0][i].insert(PWaveRayParam,
583                                                 tMod.sMod,
584                                                 indexP);
585                     newtauBranches[1][i].insert(PWaveRayParam,
586                                                 tMod.sMod,
587                                                 indexP);
588                 }
589             }
590             tMod.appendNoDisconDepth(depth);
591             topCritLayerNumS = tMod.sMod.layerNumberBelow(tMod.tauBranches[1][branchToSplit].getTopDepth(),
592                                                           false);
593             splitLayerNumS = tMod.sMod.layerNumberAbove(depth, false);
594             newtauBranches[1][branchToSplit] = new TauBranch(tMod.tauBranches[1][branchToSplit].getTopDepth(),
595                                                              depth,
596                                                              false);
597             newtauBranches[1][branchToSplit].createBranch(tMod.sMod,
598                                                           tMod.tauBranches[1][branchToSplit].getMaxRayParam(),
599                                                           tMod.rayParams);
600             newtauBranches[1][branchToSplit + 1] = tMod.tauBranches[1][branchToSplit].difference(newtauBranches[1][branchToSplit],
601                                                                                                  indexP,
602                                                                                                  indexS,
603                                                                                                  tMod.sMod,
604                                                                                                  newtauBranches[1][branchToSplit].getMinRayParam(),
605                                                                                                  tMod.rayParams);
606             topCritLayerNumP = tMod.sMod.layerNumberBelow(tMod.tauBranches[0][branchToSplit].getTopDepth(),
607                                                           true);
608             splitLayerNumP = tMod.sMod.layerNumberAbove(depth, true);
609             newtauBranches[0][branchToSplit] = new TauBranch(tMod.tauBranches[0][branchToSplit].getTopDepth(),
610                                                              depth,
611                                                              true);
612             newtauBranches[0][branchToSplit].createBranch(tMod.sMod,
613                                                           tMod.tauBranches[0][branchToSplit].getMaxRayParam(),
614                                                           tMod.rayParams);
615             newtauBranches[0][branchToSplit + 1] = tMod.tauBranches[0][branchToSplit].difference(newtauBranches[0][branchToSplit],
616                                                                                                  indexP,
617                                                                                                  indexS,
618                                                                                                  tMod.sMod,
619                                                                                                  newtauBranches[0][branchToSplit].getMinRayParam(),
620                                                                                                  tMod.rayParams);
621             for(int i = branchToSplit + 1; i < tMod.tauBranches[0].length; i++) {
622                 newtauBranches[1][i + 1] = tMod.tauBranches[1][i];
623                 newtauBranches[0][i + 1] = tMod.tauBranches[0][i];
624                 if(indexS != -1) {
625                     // add the new ray parameter from splitting the S Wave
626                     // slowness layer to both the P and S wave Tau branches
627                     newtauBranches[0][i + 1].insert(SWaveRayParam,
628                                                     tMod.sMod,
629                                                     indexS);
630                     newtauBranches[1][i + 1].insert(SWaveRayParam,
631                                                     tMod.sMod,
632                                                     indexS);
633                 }
634                 if(indexP != -1) {
635                     // add the new ray parameter from splitting the P Wave
636                     // slowness layer to both the P and S wave Tau branches
637                     newtauBranches[0][i + 1].insert(PWaveRayParam,
638                                                     tMod.sMod,
639                                                     indexP);
640                     newtauBranches[1][i + 1].insert(PWaveRayParam,
641                                                     tMod.sMod,
642                                                     indexP);
643                 }
644             }
645             tMod.tauBranches = newtauBranches;
646             /*
647              * We have split a branch so possibly sourceBranch, mohoBranch,
648              * cmbBranch, and iocbBranch are off by 1.
649              */
650             if(tMod.sourceDepth > depth) tMod.sourceBranch++;
651             if(tMod.mohoDepth > depth) tMod.mohoBranch++;
652             if(tMod.cmbDepth > depth) tMod.cmbBranch++;
653             if(tMod.iocbDepth > depth) tMod.iocbBranch++;
654             if(!tMod.validate()) { throw new TauModelException("splitBranch("
655                     + depth + "): Validation failed!"); }
656         } catch(NoSuchLayerException e) {
657             throw new TauModelException("TauModel.depthCorrect - "
658                     + "NoSuchLayerException", e);
659         } catch(SlownessModelException e) {
660             e.printStackTrace();
661             throw new TauModelException("TauModel.depthCorrect - "
662                     + "SlownessModelException", e);
663         } finally {}
664         return tMod;
665     }
666 
667     public void writeModel(String filename) throws IOException {
668         FileOutputStream fOut = new FileOutputStream(filename);
669         ObjectOutputStream out = new ObjectOutputStream(fOut);
670         try {
671             out.writeObject(this);
672         } finally {
673             out.close();
674             fOut.close();
675         }
676     }
677 
678     public void writeModelToStream(OutputStream outStream) throws IOException {
679         ObjectOutputStream out = new ObjectOutputStream(outStream);
680         out.writeObject(this);
681     }
682 
683     public static TauModel readModel(String filename)
684             throws FileNotFoundException, IOException,
685             StreamCorruptedException, ClassNotFoundException,
686             OptionalDataException {
687         TauModel tMod;
688         BufferedInputStream in = new BufferedInputStream(new FileInputStream(filename));
689         try {
690             tMod = readModelFromStream(in);
691         } finally {
692             in.close();
693         }
694         return tMod;
695     }
696 
697     public static TauModel readModelFromStream(InputStream inStream)
698             throws InvalidClassException, IOException,
699             StreamCorruptedException, ClassNotFoundException,
700             OptionalDataException {
701         ObjectInputStream in = new ObjectInputStream(inStream);
702         TauModel tMod = (TauModel)in.readObject();
703         return tMod;
704     }
705 
706     /*
707      * public void writeToStream(String filename) throws IOException {
708      * DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(
709      * new FileOutputStream(filename))); writeToStream(dos); dos.close(); }
710      * public void writeToStream(DataOutputStream dos) throws IOException {
711      * dos.writeInt(getClass().getName().length());
712      * dos.writeBytes(getClass().getName()); dos.writeBoolean(spherical);
713      * dos.writeDouble(sourceDepth); dos.writeInt(sourceBranch);
714      * dos.writeInt(noDisconBranch); dos.writeDouble(mohoDepth);
715      * dos.writeInt(mohoBranch); dos.writeDouble(cmbDepth);
716      * dos.writeInt(cmbBranch); dos.writeDouble(iocbDepth);
717      * dos.writeInt(iocbBranch); dos.writeDouble(radiusOfEarth);
718      * sMod.writeToStream(dos); dos.writeInt(rayParams.length); for (int i=0;i
719      * <rayParams.length;i++) { dos.writeDouble(rayParams[i]); }
720      * dos.writeInt(getNumBranches()); for (int i=0;i <getNumBranches();i++) {
721      * tauBranches[0][i].writeToStream(dos);
722      * tauBranches[1][i].writeToStream(dos); } } public static TauModel
723      * readFromStream(String filename) throws FileNotFoundException,
724      * IOException, InstantiationException, IllegalAccessException,
725      * ClassNotFoundException { DataInputStream dis = new DataInputStream( new
726      * BufferedInputStream( new FileInputStream(filename))); TauModel tMod =
727      * readFromStream(dis); dis.close(); return tMod; } public static TauModel
728      * readFromStream(DataInputStream dis) throws IOException,
729      * ClassNotFoundException, IllegalAccessException, InstantiationException {
730      * int length; byte[] classString = new byte[dis.readInt()];
731      * dis.read(classString); Class tModClass = Class.forName(new
732      * String(classString)); TauModel tMod = (TauModel)tModClass.newInstance();
733      * tMod.spherical = dis.readBoolean(); tMod.sourceDepth = dis.readDouble();
734      * tMod.sourceBranch = dis.readInt(); tMod.noDisconBranch = dis.readInt();
735      * tMod.mohoDepth = dis.readDouble(); tMod.mohoBranch = dis.readInt();
736      * tMod.cmbDepth = dis.readDouble(); tMod.cmbBranch = dis.readInt();
737      * tMod.iocbDepth = dis.readDouble(); tMod.iocbBranch = dis.readInt();
738      * tMod.radiusOfEarth = dis.readDouble(); tMod.sMod =
739      * SlownessModel.readFromStream(dis); length = dis.readInt(); tMod.rayParams =
740      * new double[length]; for (int i=0;i <tMod.rayParams.length;i++) {
741      * tMod.rayParams[i] = dis.readDouble(); } length = dis.readInt();
742      * tMod.tauBranches = new TauBranch[2][length]; for (int i=0;i <length;i++) {
743      * tMod.tauBranches[0][i] = TauBranch.readFromStream(dis);
744      * tMod.tauBranches[1][i] = TauBranch.readFromStream(dis); } return tMod; }
745      */
746     public boolean validate() {
747         for(int i = 0; i < rayParams.length - 1; i++) {
748             if(rayParams[i + 1] >= rayParams[i]) {
749                 System.err.println("RayParams are not monotonically decreasing. "
750                         + "rayParams["
751                         + i
752                         + "]="
753                         + rayParams[i]
754                         + " rayParams[" + (i + 1) + "]=" + rayParams[(i + 1)]);
755                 return false;
756             }
757         }
758         if(tauBranches[0].length != tauBranches[1].length) {
759             System.err.println("TauBranches for P and S are not equal. "
760                     + tauBranches[0].length + " " + tauBranches[1].length);
761             return false;
762         }
763         if(tauBranches[0][0].getTopDepth() != 0 || tauBranches[1][0].getTopDepth() != 0) {
764             System.err.println("branch 0 topDepth != 0");
765             return false;
766         }
767         // this is only for S wave (tauBranches[1][])
768         if(tauBranches[1][0].getMaxRayParam() != rayParams[0]) {
769             System.err.println("branch 0 maxRayParam != rayParams[0]");
770             return false;
771         }
772         for(int i = 1; i < getNumBranches(); i++) {
773             if(tauBranches[0][i].getTopDepth() != tauBranches[1][i].getTopDepth()) {
774                 System.err.println("branch " + i + " P topDepth != S topDepth");
775                 return false;
776             }
777             if(tauBranches[0][i].getBotDepth() != tauBranches[1][i].getBotDepth()) {
778                 System.err.println("branch " + i + " P botDepth != S botDepth");
779                 return false;
780             }
781             if(tauBranches[0][i].getTopDepth() != tauBranches[0][i - 1].getBotDepth()) {
782                 System.err.println("branch " + i + " topDepth != botDepth of "
783                         + (i - 1));
784                 return false;
785             }
786             if(tauBranches[0][i].getMaxRayParam() != tauBranches[0][i - 1].getMinRayParam()) {
787                 System.err.println("branch " + i
788                         + " P maxRayParam != minRayParam of " + (i - 1)
789                         + "\nmaxRayParam=" + tauBranches[0][i].getMaxRayParam()
790                         + "\nminRayParam=" + tauBranches[0][i - 1].getMinRayParam());
791                 return false;
792             }
793             if(tauBranches[1][i].getMaxRayParam() != tauBranches[1][i - 1].getMinRayParam()) {
794                 System.err.println("branch " + i
795                         + " S maxRayParam != minRayParam of " + (i - 1)
796                         + "\nmaxRayParam=" + tauBranches[1][i].getMaxRayParam()
797                         + "\nminRayParam=" + tauBranches[1][i - 1].getMinRayParam()
798                         + "\ndepth = " + tauBranches[1][i].getTopDepth());
799                 return false;
800             }
801         }
802         if(tauBranches[0][getNumBranches() - 1].getMinRayParam() != 0) {
803             System.err.println("branch tauBranches[0].length-1 minRayParam != 0");
804             return false;
805         }
806         if(tauBranches[1][getNumBranches() - 1].getMinRayParam() != 0) {
807             System.err.println("branch tauBranches[1].length-1 minRayParam != 0");
808             return false;
809         }
810         return true;
811     }
812 
813     public void print() {
814         double deg, time;
815         if(DEBUG) System.out.println("Starting print() in TauModel");
816         System.out.println("Delta tau for each slowness sample and layer.");
817         for(int j = 0; j < rayParams.length; j++) {
818             deg = 0;
819             time = 0;
820             for(int i = 0; i < getNumBranches(); i++) {
821                 deg += tauBranches[0][i].getDist(j) * 180 / Math.PI;
822                 time += tauBranches[0][i].time[j];
823                 System.out.println(" i " + i + " j " + j + " rayParam "
824                         + rayParams[j] + " tau " + tauBranches[0][i].tau[j]
825                         + " time " + tauBranches[0][i].time[j] + " dist "
826                         + tauBranches[0][i].getDist(j) + " degrees "
827                         + (tauBranches[0][i].getDist(j) * 180 / Math.PI));
828             }
829             System.out.println();
830             System.out.println("deg= " + deg + "  time=" + time);
831         }
832     }
833 
834     /***
835      * Returns a clone of the tau model so that changes to the clone do not
836      * affect the original.
837      */
838     public Object clone() {
839         TauModel newObject;
840         try {
841             newObject = (TauModel)super.clone();
842             newObject.rayParams = (double[])rayParams.clone();
843             newObject.sMod = (SlownessModel)sMod.clone();
844             newObject.tauBranches = new TauBranch[2][getNumBranches()];
845             for(int i = 0; i < getNumBranches(); i++) {
846                 newObject.tauBranches[0][i] = (TauBranch)tauBranches[0][i].clone();
847                 newObject.tauBranches[1][i] = (TauBranch)tauBranches[1][i].clone();
848             }
849             return newObject;
850         } catch(CloneNotSupportedException e) {
851             // Can't happen, but...
852             System.err.println("Caught CloneNotSupportedException: "
853                     + e.getMessage());
854             throw new InternalError(e.toString());
855         }
856     }
857 
858     public String toString() {
859         if(DEBUG) System.out.println("Starting toString() in TauModel");
860         String desc = "Delta tau for each slowness sample and layer.\n";
861         for(int j = 0; j < rayParams.length; j++) {
862             for(int i = 0; i < tauBranches[0].length; i++) {
863                 desc += " i " + i + " j " + j + " rayParam " + rayParams[j]
864                         + " tau " + tauBranches[0][i].tau[j] + " time "
865                         + tauBranches[0][i].time[j] + " dist "
866                         + tauBranches[0][i].getDist(j) + " degrees "
867                         + (tauBranches[0][i].getDist(j) * 180 / Math.PI) + "\n";
868             }
869             desc += "\n";
870         }
871         return desc;
872     }
873 
874     public static void main(String[] args) {
875         VelocityModel vMod = new VelocityModel();
876         SphericalSModel sMod = new SphericalSModel();
877         TauModel tMod = new TauModel();
878         int branch, rayNum;
879         String modelFilename;
880         if(args.length == 1) {
881             modelFilename = args[0];
882         } else {
883             modelFilename = "iasp91.tvel";
884         }
885         boolean DEBUG = false;
886         try {
887             vMod.setFileType("tvel");
888             vMod.readVelocityFile(modelFilename);
889             System.out.println("Done reading.");
890             sMod.createSample(vMod);
891             tMod.calcTauIncFrom(sMod);
892             StreamTokenizer tokenIn = new StreamTokenizer(new InputStreamReader(System.in));
893             tokenIn.parseNumbers();
894             System.out.println("Enter branch rayNum");
895             tokenIn.nextToken();
896             while(tokenIn.ttype == StreamTokenizer.TT_NUMBER) {
897                 branch = (int)tokenIn.nval;
898                 tokenIn.nextToken();
899                 rayNum = (int)tokenIn.nval;
900                 System.out.println("ray parameter=" + tMod.rayParams[rayNum]
901                         + " distance="
902                         + tMod.tauBranches[0][branch].getDist(rayNum) + " time="
903                         + tMod.tauBranches[0][branch].time[rayNum] + " tau="
904                         + tMod.tauBranches[0][branch].tau[rayNum]);
905                 System.out.println("Enter branch rayNum");
906                 tokenIn.nextToken();
907             }
908         } catch(IOException e) {
909             System.out.println("Tried to read!\n Caught IOException "
910                     + e.getMessage());
911         } catch(VelocityModelException e) {
912             System.out.println("Tried to read!\n Caught VelocityModelException "
913                     + e.getMessage());
914         } catch(SlownessModelException e) {
915             System.out.println("Caught SlownessModelException "
916                     + e.getMessage());
917             e.printStackTrace();
918         } catch(TauModelException e) {
919             System.out.println("Caught TauModelException " + e.getMessage());
920             e.printStackTrace();
921         } finally {
922             System.out.println("Done!\n");
923         }
924     }
925 }