Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
The TauP Toolkit: Flexible Seismic Travel-Time and Raypath Utilities.
* Copyright (C) 1998-2000 University of South Carolina This program is free
* software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version. This program
* is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details. You
* should have received a copy of the GNU General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place -
* Suite 330, Boston, MA 02111-1307, USA. The current version can be found at http://www.seis.sc.edu Bug reports and comments
* should be directed to H. Philip Crotwell, [email protected] or Tom Owens,
* [email protected]
*/
package edu.sc.seis.TauP;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.io.StreamCorruptedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Stores and transforms seismic phase names to and from their corresponding
* sequence of branches.
*
* @version 1.1.3 Wed Jul 18 15:00:35 GMT 2001
* @author H. Philip Crotwell
*
* Modified to add "expert" mode wherein paths may start in the core. Principal
* use is to calculate leg contributions for scattered phases. Nomenclature: "K" -
* downgoing wave from source in core; "k" - upgoing wave from source in core.
*
* G. Helffrich/U. Bristol 24 Feb. 2007
*/
public class SeismicPhase implements Serializable, Cloneable {
/** Enables debugging output. */
public transient boolean DEBUG = ToolRun.DEBUG;
/** Enables verbose output. */
public transient boolean verbose = false;
/** Enables phases originating in core. */
public static transient boolean expert = TauP_Time.expert;
/** TauModel to generate phase for. */
protected TauModel tMod;
/**
* Used by addToBranch when the path turns within a segment. We assume that
* no ray will turn downward so turning implies turning from downward to
* upward, ie U.
*/
public static final int TURN = 0;
/**
* Used by addToBranch when the path reflects off the top of the end of a
* segment, ie ^.
*/
public static final int REFLECT_UNDERSIDE = 1;
/**
* Used by addToBranch when the path reflects off the bottom of the end of a
* segment, ie v.
*/
public static final int REFLECT_TOPSIDE = 2;
/**
* Used by addToBranch when the path transmits up through the end of a
* segment.
*/
public static final int TRANSUP = 3;
/**
* Used by addToBranch when the path transmits down through the end of a
* segment.
*/
public static final int TRANSDOWN = 4;
/**
* Used by addToBranch when the path transmits down through the end of a
* segment.
*/
public static final int DIFFRACT = 5;
/**
* Used by addToBranch for the last segment of a phase.
*/
public static final int END = 6;
/**
* Used by addToBranch for the last segment of a phase if downgoing to
* receiver at depth..
*/
public static final int END_DOWN = 7;
/**
* Used by addToBranch when the path critically reflects off the top of the end of a
* segment, ie "^x". Note this is disabled as it is hard to create a model where this
* phase interaction is physically possible, delay implement this feature for now.
*/
public static final int REFLECT_UNDERSIDE_CRITICAL = 8;
/**
* Used by addToBranch when the path reflects off the bottom of the end of a
* segment, ie "V".
*/
public static final int REFLECT_TOPSIDE_CRITICAL = 9;
/**
* The maximum degrees that a Pn or Sn can refract along the moho. Note this
* is not the total distance, only the segment along the moho. The default
* is 20 degrees.
*/
protected static double maxRefraction = 20;
/**
* The maximum degrees that a Pdiff or Sdiff can diffract along the CMB.
* Note this is not the total distance, only the segment along the CMB. The
* default is 60 degrees.
*/
protected static double maxDiffraction = 60;
/**
* The source depth within the TauModel that was used to generate this
* phase.
*/
protected double sourceDepth;
/**
* The receiver depth within the TauModel that was used to generate this
* phase. Normally this is 0.0 for a surface stations, but can be different
* for borehole or scattering calculations.
*/
protected double receiverDepth = 0.0;
/**
* Array of distances corresponding to the ray parameters stored in
* rayParams.
*/
protected double[] dist = new double[0];
/**
* Array of times corresponding to the ray parameters stored in rayParams.
*/
protected double[] time = new double[0];
/** Array of possible ray parameters for this phase. */
protected double[] rayParams = new double[0];
/** Minimum ray parameter that exists for this phase. */
protected double minRayParam;
/** Maximum ray parameter that exists for this phase. */
protected double maxRayParam;
/**
* Index within TauModel.rayParams that corresponds to maxRayParam. Note
* that maxRayParamIndex < minRayParamIndex as ray parameter decreases with
* increasing index.
*/
protected int maxRayParamIndex = -1;
/**
* Index within TauModel.rayParams that corresponds to minRayParam. Note
* that maxRayParamIndex < minRayParamIndex as ray parameter decreases with
* increasing index.
*/
protected int minRayParamIndex = -1;
/** The minimum distance that this phase can be theoretically observed. */
protected double minDistance = 0.0;
/** The maximum distance that this phase can be theoretically observed. */
protected double maxDistance = Double.MAX_VALUE;
/**
* Array of branch numbers for the given phase. Note that this depends upon
* both the earth model and the source depth.
*/
protected List branchSeq = new ArrayList();
/**
* Array of branchSeq positions where a head or diffracted segment occurs.
*/
protected List headOrDiffractSeq = new ArrayList();
/** The phase name, ie PKiKP. */
protected String name;
/**
* name with depths corrected to be actuall discontinuities in the model.
*/
protected String puristName;
/** ArrayList containing Strings for each leg. */
protected ArrayList legs = new ArrayList();
/** Description of segments of the phase. */
protected List segmentList = new ArrayList();
/**
* temporary branch number so we know where to start add to the branch
* sequence. Used in addToBranch() and parseName().
*/
protected transient int currBranch;
/**
* records the end action for the current leg. Will be one of
* SeismicPhase.TURN, SeismicPhase.TRANSDOWN, SeismicPhase.TRANSUP,
* SeismicPhase.REFLECTBOT, or SeismicPhase.REFLECTTOP. This allows a check
* to make sure the path is correct. Used in addToBranch() and parseName().
*/
protected ArrayList legAction = new ArrayList();
/**
* true if the current leg of the phase is down going. This allows a check
* to make sure the path is correct. Used in addToBranch() and parseName().
*/
protected ArrayList downGoing = new ArrayList();
/**
* ArrayList of wave types corresponding to each leg of the phase.
*
*/
protected ArrayList waveType = new ArrayList();
protected double refineDistToleranceRadian = 0.0049*Math.PI/180;
protected int maxRecursion = 5;
public static final boolean PWAVE = true;
public static final boolean SWAVE = false;
public SeismicPhase(String name, String modelName, double depth) throws TauModelException {
this(name, TauModelLoader.load(modelName).depthCorrect(depth));
}
/**
* @param name
* String containing a name of the phase.
* @param tMod
* Tau model to be used to construct the phase. This should be corrected for the source
* depth.
* @throws TauModelException
*/
public SeismicPhase(String name, TauModel tMod) throws TauModelException {
this(name, tMod, 0.0); //surface receiver
}
public SeismicPhase(String name, TauModel tMod, double receiverDepth) throws TauModelException {
this(name, tMod, receiverDepth, ToolRun.DEBUG);
}
public SeismicPhase(String name, TauModel tMod, double receiverDepth, boolean debug) throws TauModelException {
this.DEBUG = debug ;
if (name == null || name.length() == 0) {
throw new TauModelException("Phase name cannot be empty to null: "+name);
}
this.name = name;
this.sourceDepth = tMod.getSourceDepth();
this.receiverDepth = receiverDepth;
// make sure we have layer boundary at receiver
// this does nothing if already split at receiver
this.tMod = tMod.splitBranch(receiverDepth);
legs = legPuller(name);
createPuristName(tMod);
parseName(tMod);
sumBranches(tMod);
}
public boolean phasesExistsInModel() {
return getMaxRayParam() >= 0;
}
public Arrival getEarliestArrival(double degrees) {
double soonest = Double.MAX_VALUE;
Arrival soonestArrival = null;
List arrivals = calcTime(degrees);
for (Arrival a : arrivals) {
if (a.getTime() < soonest) {
soonestArrival = a;
soonest = a.getTime();
}
}
return soonestArrival;
}
public TauModel getTauModel() {
return tMod;
}
public double getMinDistanceDeg() {
return getMinDistance() * 180.0 / Math.PI;
}
public double getMinDistance() {
return minDistance;
}
public double getMaxDistanceDeg() {
return getMaxDistance() * 180.0 / Math.PI;
}
public double getMaxDistance() {
return maxDistance;
}
public double getMaxRayParam() {
return maxRayParam;
}
public double getMinRayParam() {
return minRayParam;
}
public int getMaxRayParamIndex() {
return maxRayParamIndex;
}
public int getMinRayParamIndex() {
return minRayParamIndex;
}
public static double getMaxRefraction() {
return maxRefraction;
}
public static void setMaxRefraction(double max) {
maxRefraction = max;
}
public static double getMaxDiffraction() {
return maxDiffraction;
}
public static void setMaxDiffraction(double max) {
maxDiffraction = max;
}
public String getName() {
return name;
}
public String getPuristName() {
return name;
}
public List getLegs() {
return Collections.unmodifiableList(legs);
}
public List getPhaseSegments() {
return Collections.unmodifiableList(segmentList);
}
public double getRayParams(int i) {
return rayParams[i];
}
public double[] getRayParams() {
return (double[])rayParams.clone();
}
public double getDist(int i) {
return dist[i];
}
public double[] getDist() {
return (double[])dist.clone();
}
public double getTime(int i) {
return time[i];
}
public double[] getTime() {
return (double[])time.clone();
}
public double getTau(int i) {
return time[i] - rayParams[i] * dist[i];
}
public double[] getTau() {
double[] tau = new double[dist.length];
for(int i = 0; i < dist.length; i++) {
tau[i] = time[i] - rayParams[i] * dist[i];
}
return tau;
}
/**
* Direction of the leg between pierce point i and i+1, true is downgoing,
* false if upgoing
*/
public boolean[] getDownGoing() {
Boolean[] b = (Boolean[])downGoing.toArray(new Boolean[0]);
boolean[] out = new boolean[b.length];
for(int i = 0; i < b.length; i++) {
out[i] = b[i].booleanValue();
}
return out;
}
/**
* Wave type of the leg between pierce point i and i+1, true is P, false if
* S
*/
public boolean[] getWaveType() {
Boolean[] b = (Boolean[])waveType.toArray(new Boolean[0]);
boolean[] out = new boolean[b.length];
for(int i = 0; i < b.length; i++) {
out[i] = b[i].booleanValue();
}
return out;
}
/**
* Leg type i layer interaction, one of TURN, REFLECTTOP, REFLECTBOT,
* TRANSUP, TRANSDOWN
*/
public int[] getLegAction() {
Integer[] b = (Integer[])legAction.toArray(new Integer[0]);
int[] out = new int[b.length];
for(int i = 0; i < b.length; i++) {
out[i] = b[i].intValue();
}
return out;
}
public boolean hasArrivals() {
return dist != null && dist.length != 0;
}
// Normal methods
/** calculates arrival times for this phase, sorted by time.
* */
public List calcTime(double deg) {
double tempDeg = deg;
if(tempDeg < 0.0) {
tempDeg *= -1.0;
} // make sure deg is positive
while(tempDeg > 360.0) {
tempDeg -= 360.0;
} // make sure it is less than 360
if(tempDeg > 180.0) {
tempDeg = 360.0 - tempDeg;
} // make sure less than or equal to 180
// now we have 0.0 <= deg <= 180
double radDist = tempDeg * Math.PI / 180.0;
List arrivals = new ArrayList();
/*
* Search all distances 2n*PI+radDist and 2(n+1)*PI-radDist that are
* less than the maximum distance for this phase. This insures that we
* get the time for phases that accumulate more than 180 degrees of
* distance, for instance PKKKKP might wrap all of the way around. A
* special case exists at 180, so we skip the second case if
* tempDeg==180.
*/
int n = 0;
double searchDist;
while(n * 2.0 * Math.PI + radDist <= maxDistance) {
/*
* Look for arrivals that are radDist + 2nPi, ie rays that have done
* more than n laps.
*/
searchDist = n * 2.0 * Math.PI + radDist;
for(int rayNum = 0; rayNum < (dist.length - 1); rayNum++) {
if(searchDist == dist[rayNum + 1]
&& rayNum + 1 != dist.length - 1) {
/* So we don't get 2 arrivals for the same ray. */
continue;
} else if((dist[rayNum] - searchDist)
* (searchDist - dist[rayNum + 1]) >= 0.0) {
/* look for distances that bracket the search distance */
if((rayParams[rayNum] == rayParams[rayNum + 1])
&& rayParams.length > 2) {
/*
* Here we have a shadow zone, so it is not really an
* arrival.
*/
continue;
}
if(DEBUG) {
System.err.println("SeismicPhase " + name
+ ", found arrival:\n" + "dist "
+ (float)(180 / Math.PI * dist[rayNum]) + " "
+ (float)(180 / Math.PI * searchDist) + " "
+ (float)(180 / Math.PI * dist[rayNum + 1]));
}
arrivals.add(refineArrival(rayNum, searchDist, refineDistToleranceRadian, maxRecursion));
}
}
/*
* Look for arrivals that are 2(n+1)Pi-radDist, ie rays that have
* done more than one half lap plus some number of whole laps.
*/
searchDist = (n + 1) * 2.0 * Math.PI - radDist;
if(tempDeg != 180) {
for(int rayNum = 0; rayNum < (dist.length - 1); rayNum++) {
if(searchDist == dist[rayNum + 1]
&& rayNum + 1 != dist.length - 1) {
/* So we don't get 2 arrivals for the same ray. */
continue;
} else if((dist[rayNum] - searchDist)
* (searchDist - dist[rayNum + 1]) >= 0.0) {
if((rayParams[rayNum] == rayParams[rayNum + 1])
&& rayParams.length > 2) {
/*
* Here we have a shadow zone, so it is not really
* an arrival.
*/
continue;
}
if(DEBUG) {
System.err.println("SeismicPhase " + name
+ ", found arrival:\n" + "dist "
+ (float)(180 / Math.PI * dist[rayNum])
+ " " + (float)(180 / Math.PI * searchDist)
+ " "
+ (float)(180 / Math.PI * dist[rayNum + 1]));
}
arrivals.add(refineArrival(rayNum, searchDist, refineDistToleranceRadian, maxRecursion));
}
}
}
n++;
}
Collections.sort(arrivals, new Comparator() {
public int compare(Arrival o1, Arrival o2) {
return Double.compare(o1.getTime(), o2.getTime());
}});
return arrivals;
}
public Arrival refineArrival(int rayNum, double distRadian, double distTolRadian, int maxRecursion) {
Arrival left = new Arrival(this,
getTime(rayNum),
getDist(rayNum),
getRayParams(rayNum),
rayNum,
name,
puristName,
sourceDepth);
Arrival right = new Arrival(this,
getTime(rayNum+1),
getDist(rayNum+1),
getRayParams(rayNum+1),
rayNum, // use rayNum as know dist is between rayNum and rayNum+1
name,
puristName,
sourceDepth);
return refineArrival(left, right, distRadian, distTolRadian, maxRecursion);
}
public Arrival refineArrival(Arrival leftEstimate, Arrival rightEstimate, double searchDist, double distTolRadian, int maxRecursion) {
Arrival linInterp = linearInterpArrival(searchDist, leftEstimate, rightEstimate);
if(maxRecursion <= 0 || name.indexOf("Sdiff") != -1
|| name.indexOf("Pdiff") != -1
|| name.indexOf("Pn") != -1
|| name.indexOf("Sn") != -1
|| name.endsWith("kmps")) {
// can't shoot/refine for diffracted, head and non-body waves
return linInterp;
}
if (linInterp.getRayParam() == leftEstimate.getRayParam()) { return leftEstimate;}
if (linInterp.getRayParam() == rightEstimate.getRayParam()) { return rightEstimate;}
if(DEBUG) {
System.err.println("Phase: "+this);
System.err.println("Refine: "+maxRecursion+"\nleft: "+leftEstimate+"\nright: "+rightEstimate+"\nlinInterp: "+linInterp);
}
if (leftEstimate.getRayParam() < minRayParam || maxRayParam < leftEstimate.getRayParam()) {
throw new RuntimeException("Left Ray param "+leftEstimate.getRayParam()+" is outside range for this phase: "+getName()+" min="+minRayParam+" max="+maxRayParam);
}
if (rightEstimate.getRayParam() < minRayParam || maxRayParam < rightEstimate.getRayParam()) {
throw new RuntimeException("Right Ray param "+rightEstimate.getRayParam()+" is outside range for this phase: "+getName()+" min="+minRayParam+" max="+maxRayParam);
}
try {
Arrival shoot = shootRay(linInterp.getRayParam());
if ((leftEstimate.getDist() - searchDist)
* (searchDist - shoot.getDist()) > 0) {
// search between left and shoot
if (Math.abs(shoot.getDist()-linInterp.getDist()) < distTolRadian) {
return linearInterpArrival(searchDist, leftEstimate, shoot);
} else {
return refineArrival(leftEstimate, shoot, searchDist, distTolRadian, maxRecursion-1);
}
} else {
// search between shoot and right
if (Math.abs(shoot.getDist()-linInterp.getDist()) < distTolRadian) {
return linearInterpArrival(searchDist, shoot, rightEstimate);
} else {
return refineArrival(shoot, rightEstimate, searchDist, distTolRadian, maxRecursion-1);
}
}
} catch(NoSuchLayerException e) {
throw new RuntimeException("Should not happen: "+getName(), e);
} catch(SlownessModelException e) {
throw new RuntimeException("Should not happen: "+getName(), e);
}
}
public Arrival shootRay(double rayParam) throws SlownessModelException, NoSuchLayerException {
if(name.indexOf("Sdiff") != -1
|| name.indexOf("Pdiff") != -1
|| name.indexOf("Pn") != -1
|| name.indexOf("Sn") != -1
|| name.endsWith("kmps")) {
throw new SlownessModelException("Unable to shoot ray in non-body waves");
}
if (rayParam < minRayParam || maxRayParam < rayParam) {
throw new SlownessModelException("Ray param "+rayParam+" is outside range for this phase: "+getName()+" min="+minRayParam+" max="+maxRayParam);
}
// looks like a body wave and can ray param can propagate
int rayParamIndex = -1;
for (rayParamIndex = 0; rayParamIndex < rayParams.length-1 && rayParams[rayParamIndex+1] >= rayParam; rayParamIndex++) {}
/* counter for passes through each branch. 0 is P and 1 is S. */
int[][] timesBranches = calcBranchMultiplier();
TimeDist sum = new TimeDist(rayParam);
/* Sum the branches with the appropriate multiplier. */
for(int j = 0; j < tMod.getNumBranches(); j++) {
if(timesBranches[0][j] != 0) {
int topLayerNum = tMod.getSlownessModel().layerNumberBelow(tMod.getTauBranch(j, PWAVE).getTopDepth(), PWAVE);
int botLayerNum = tMod.getSlownessModel().layerNumberAbove(tMod.getTauBranch(j, PWAVE).getBotDepth(), PWAVE);
TimeDist td = tMod.getTauBranch(j, PWAVE).calcTimeDist(tMod.getSlownessModel(),
topLayerNum,
botLayerNum,
rayParam,
true);
td = new TimeDist(rayParam,
timesBranches[0][j]*td.getTime(),
timesBranches[0][j]*td.getDistRadian(),
td.getDepth());
sum = sum.add(td);
}
if(timesBranches[1][j] != 0) {
int topLayerNum = tMod.getSlownessModel().layerNumberBelow(tMod.getTauBranch(j, SWAVE).getTopDepth(), SWAVE);
int botLayerNum = tMod.getSlownessModel().layerNumberAbove(tMod.getTauBranch(j, SWAVE).getBotDepth(), SWAVE);
TimeDist td = tMod.getTauBranch(j, SWAVE).calcTimeDist(tMod.getSlownessModel(),
topLayerNum,
botLayerNum,
rayParam,
true);
td = new TimeDist(rayParam,
timesBranches[1][j]*td.getTime(),
timesBranches[1][j]*td.getDistRadian(),
td.getDepth());
sum = sum.add(td);
}
}
return new Arrival(this,
sum.getTime(),
sum.getDistRadian(),
rayParam,
rayParamIndex,
name,
puristName,
sourceDepth);
}
private Arrival linearInterpArrival(double searchDist,
Arrival left,
Arrival right) {
if (left.getDist() == searchDist) {
return left;
}
if (right.getDist() == searchDist) {
return right;
}
double arrivalTime = (searchDist - left.getDist())
/ (right.getDist() - left.getDist())
* (right.getTime() - left.getTime()) + left.getTime();
if (Double.isNaN(arrivalTime)) {
throw new RuntimeException("Time is NaN, search "+searchDist +" leftDist "+ left.getDist()+ " leftTime "+left.getTime()
+" rightDist "+right.getDist()+" rightTime "+right.getTime());
}
double arrivalRayParam = (searchDist - right.getDist())
* (left.getRayParam() - right.getRayParam())
/ (left.getDist() - right.getDist())
+ right.getRayParam();
return new Arrival(this,
arrivalTime,
searchDist,
arrivalRayParam,
left.getRayParamIndex(),
name,
puristName,
sourceDepth);
}
public double calcRayParamForTakeoffAngle(double takeoffDegree) {
double takeoffVelocity;
VelocityModel vMod = getTauModel().getVelocityModel();
try {
if (getDownGoing()[0]) {
takeoffVelocity = vMod.evaluateBelow(sourceDepth, name.charAt(0));
} else {
takeoffVelocity = vMod.evaluateAbove(sourceDepth, name.charAt(0));
}
double rayParam = (getTauModel().getRadiusOfEarth()-sourceDepth)*Math.sin(takeoffDegree*Math.PI/180)/takeoffVelocity;
return rayParam;
} catch(NoSuchLayerException e) {
throw new RuntimeException("Should not happen", e);
} catch(NoSuchMatPropException e) {
throw new RuntimeException("Should not happen", e);
}
}
public double calcTakeoffAngle(double arrivalRayParam) {
double takeoffVelocity;
if (name.endsWith("kmps")) {
return 0;
}
VelocityModel vMod = getTauModel().getVelocityModel();
try {
char firstLeg = name.charAt(0);
if (firstLeg == 'P' || firstLeg == 'p' || firstLeg == 'K' || firstLeg == 'k' || firstLeg == 'I') {
firstLeg = 'P';
} else if (firstLeg == 'S' || firstLeg == 's' || firstLeg == 'J' ) {
firstLeg = 'S';
}
if (getDownGoing()[0]) {
takeoffVelocity = vMod.evaluateBelow(sourceDepth, firstLeg);
} else {
takeoffVelocity = vMod.evaluateAbove(sourceDepth, firstLeg);
}
double takeoffAngle = 180/Math.PI*Math.asin(takeoffVelocity*arrivalRayParam/(getTauModel().getRadiusOfEarth()-sourceDepth));
if ( ! getDownGoing()[0]) {
// upgoing, so angle is in 90-180 range
takeoffAngle = 180-takeoffAngle;
}
return takeoffAngle;
} catch(NoSuchLayerException e) {
throw new RuntimeException("Should not happen", e);
} catch(NoSuchMatPropException e) {
throw new RuntimeException("Should not happen", e);
}
}
public double calcIncidentAngle(double arrivalRayParam) {
if (name.endsWith("kmps")) {
return 0;
}
double incidentVelocity;
VelocityModel vMod = getTauModel().getVelocityModel();
try {
char lastLeg = getLegs().get(getLegs().size()-2).charAt(0); // last item is "END", assume first char is P or S
if (lastLeg == 'P' || lastLeg == 'p' || lastLeg == 'K' || lastLeg == 'k' || lastLeg == 'I') {
lastLeg = 'P';
} else if (lastLeg == 'S' || lastLeg == 's' || lastLeg == 'J' ) {
lastLeg = 'S';
}
if (getDownGoing()[getDownGoing().length-1]) {
incidentVelocity = vMod.evaluateAbove(receiverDepth, lastLeg);
} else {
incidentVelocity = vMod.evaluateBelow(receiverDepth, lastLeg);
}
double incidentAngle = 180/Math.PI*Math.asin(incidentVelocity*arrivalRayParam/(getTauModel().getRadiusOfEarth()-receiverDepth));
if (getDownGoing()[getDownGoing().length-1]) {
incidentAngle = 180 - incidentAngle;
}
return incidentAngle;
} catch(NoSuchLayerException e) {
throw new RuntimeException("Should not happen", e);
} catch(NoSuchMatPropException e) {
throw new RuntimeException("Should not happen", e);
}
}
/**
* changes maxRayParam and minRayParam whenever there is a phase conversion.
* For instance, SKP needs to change the maxRayParam because there are SKS
* ray parameters that cannot propagate from the cmb into the mantle as a p
* wave.
*/
protected void phaseConversion(TauModel tMod,
int fromBranch,
int endAction,
boolean isPtoS) throws TauModelException {
if(endAction == TURN) {
// can't phase convert for just a turn point
throw new TauModelException("Illegal endAction: endAction="
+ endAction
+ "\nphase conversion are not allowed at turn points.");
} else if(endAction == REFLECT_UNDERSIDE || endAction == REFLECT_UNDERSIDE_CRITICAL) {
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(fromBranch,
isPtoS)
.getMaxRayParam());
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(fromBranch,
!isPtoS)
.getMaxRayParam());
} else if(endAction == REFLECT_TOPSIDE || endAction == REFLECT_TOPSIDE_CRITICAL) {
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(fromBranch,
isPtoS)
.getMinTurnRayParam());
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(fromBranch,
!isPtoS)
.getMinTurnRayParam());
} else if(endAction == TRANSUP) {
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(fromBranch,
isPtoS)
.getMaxRayParam());
maxRayParam = Math.min(maxRayParam,
tMod.getTauBranch(fromBranch - 1, !isPtoS)
.getMinTurnRayParam());
} else if(endAction == TRANSDOWN) {
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(fromBranch,
isPtoS)
.getMinRayParam());
maxRayParam = Math.min(maxRayParam,
tMod.getTauBranch(fromBranch + 1, !isPtoS)
.getMaxRayParam());
} else {
throw new TauModelException("Illegal endAction: endAction="
+ endAction);
}
}
public static final String endActionString(int endAction) {
if(endAction == TURN) {
return "TURN";
} else if(endAction == REFLECT_UNDERSIDE) {
return "REFLECT_UNDERSIDE";
} else if(endAction == REFLECT_UNDERSIDE_CRITICAL) {
return "REFLECT_UNDERSIDE_CRITICAL";
} else if(endAction == END ) {
return "END";
} else if(endAction == END_DOWN) {
return "END_DOWN";
} else if(endAction == REFLECT_TOPSIDE) {
return "REFLECT_TOPSIDE";
} else if(endAction == REFLECT_TOPSIDE_CRITICAL) {
return "REFLECT_TOPSIDE_CRITICAL";
} else if(endAction == TRANSUP) {
return "TRANSUP";
} else if(endAction == TRANSDOWN) {
return "TRANSDOWN";
} else if(endAction == DIFFRACT) {
return "DIFFRACT";
} else {
throw new RuntimeException("UNKNOWN Action: "+endAction);
}
}
/*
* Adds the branch numbers from startBranch to endBranch, inclusive, to
* branchSeq, in order. Also, currBranch is set correctly based on the value
* of endAction. endAction can be one of TRANSUP, TRANSDOWN, REFLECTTOP,
* REFLECTBOT, or TURN.
*/
protected void addToBranch(TauModel tMod,
int startBranch,
int endBranch,
boolean isPWave,
int endAction,
String currLeg) throws TauModelException {
if (startBranch < 0 || startBranch > tMod.getNumBranches()) {
throw new IllegalArgumentException(getName()+": start branch outside range: (0-"+tMod.getNumBranches()+") "+startBranch);
}
if (endBranch < 0 || endBranch > tMod.getNumBranches()) {
throw new IllegalArgumentException(getName()+": end branch outside range: "+endBranch);
}
if(endAction == TRANSUP && endBranch == 0) {
throw new IllegalArgumentException(getName()+": cannot TRANSUP with end branch zero: "+endBranch);
}
int endOffset;
boolean isDownGoing;
if(DEBUG) {
System.out.println("before addToBranch: minRP="+minRayParam+" maxRP="+maxRayParam);
System.out.println("addToBranch start=" + startBranch + " end=" + endBranch
+ " endAction="+endActionString(endAction)+" ");
}
if(endAction == TURN) {
endOffset = 0;
isDownGoing = true;
minRayParam = Math.max(minRayParam, tMod.getTauBranch(endBranch,
isPWave)
.getMinTurnRayParam());
// careful if the ray param cannot turn due to high slowness at bottom. Do not use these
// layers if their top in in high slowness for the given ray parameter
int bNum = endBranch;
while (bNum >= startBranch) {
if (tMod.getSlownessModel().depthInHighSlowness(tMod.getTauBranch(bNum, isPWave).getTopDepth(),
minRayParam, isPWave)) {
// tau branch is in high slowness, so turn is not possible, only
// non-critical reflect, so do not add these branches
endBranch = bNum-1;
}
bNum--;
}
} else if(endAction == REFLECT_UNDERSIDE || endAction == REFLECT_UNDERSIDE_CRITICAL) {
endOffset = 0;
isDownGoing = false;
maxRayParam = Math.min(maxRayParam,
tMod.getTauBranch(endBranch, isPWave).getMaxRayParam());
if (endAction == REFLECT_UNDERSIDE_CRITICAL) {
try {
TauBranch endTauBranch = tMod.getTauBranch(endBranch, isPWave);
int slayAbove = tMod.getSlownessModel().layerNumberAbove(endTauBranch.getTopDepth(), isPWave);
SlownessLayer sLayer = tMod.getSlownessModel().getSlownessLayer(slayAbove, isPWave);
minRayParam = Math.max(minRayParam, sLayer.getBotP());
} catch (NoSuchLayerException e) {
throw new TauModelException(e);
}
}
} else if(endAction == END) {
endOffset = 0;
isDownGoing = false;
maxRayParam = Math.min(maxRayParam,
tMod.getTauBranch(endBranch, isPWave).getMaxRayParam());
} else if (endAction == END_DOWN) {
endOffset = 0;
isDownGoing = true;
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(endBranch,
isPWave)
.getMinTurnRayParam());
} else if(endAction == REFLECT_TOPSIDE || endAction == REFLECT_TOPSIDE_CRITICAL) {
endOffset = 0;
isDownGoing = true;
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(endBranch,
isPWave)
.getMinTurnRayParam());
if (endAction == REFLECT_TOPSIDE_CRITICAL) {
try {
TauBranch endTauBranch = tMod.getTauBranch(endBranch, isPWave);
int slayBelow = tMod.getSlownessModel().layerNumberBelow(endTauBranch.getBotDepth(), isPWave);
SlownessLayer sLayer = tMod.getSlownessModel().getSlownessLayer(slayBelow,isPWave);
minRayParam = Math.max(minRayParam,
sLayer.getTopP());
} catch (NoSuchLayerException e) {
throw new TauModelException(e);
}
}
} else if(endAction == TRANSUP) {
endOffset = -1;
isDownGoing = false;
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(endBranch,
isPWave)
.getMaxRayParam());
} else if(endAction == TRANSDOWN) {
endOffset = 1;
isDownGoing = true;
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(endBranch,
isPWave)
.getMinRayParam());
} else if(endAction == DIFFRACT) {
endOffset = 0;
isDownGoing = true;
maxRayParam = Math.min(maxRayParam, tMod.getTauBranch(endBranch,
isPWave)
.getMinTurnRayParam());
} else {
throw new TauModelException(getName()+": Illegal endAction: endAction="
+ endAction);
}
SeismicPhaseSegment segment = new SeismicPhaseSegment(tMod, startBranch, endBranch, isPWave, endAction, isDownGoing, currLeg);
if (segmentList.size() > 0) {
SeismicPhaseSegment prevSegment = segmentList.get(segmentList.size()-1);
if (isDownGoing) {
if (prevSegment.endBranch > startBranch) {
throw new TauModelException(getName()+": Segment is downgoing, but we are already below the start: "+currLeg);
}
if (prevSegment.endAction == REFLECT_TOPSIDE || prevSegment.endAction == REFLECT_TOPSIDE_CRITICAL) {
throw new TauModelException(getName()+": Segment is downgoing, but previous action was to reflect up: "+currLeg);
}
if (prevSegment.endAction == TURN) {
throw new TauModelException(getName()+": Segment is downgoing, but previous action was to turn: "+currLeg);
}
if (prevSegment.endAction == TRANSUP) {
throw new TauModelException(getName()+": Segment is downgoing, but previous action was to transmit up: "+currLeg);
}
if (prevSegment.endBranch == startBranch && prevSegment.isDownGoing == false &&
! (prevSegment.endAction == REFLECT_UNDERSIDE || prevSegment.endAction == REFLECT_UNDERSIDE_CRITICAL)) {
throw new TauModelException(getName()+": Segment "+currLeg+" is downgoing, but previous action was not to reflect underside: "+currLeg+" "+endActionString(prevSegment.endAction));
}
} else {
if (prevSegment.endBranch < startBranch) {
throw new TauModelException(getName()+": Segment is upgoing, but we are already above the start: "+currLeg);
}
if (prevSegment.endAction == REFLECT_UNDERSIDE || prevSegment.endAction == REFLECT_UNDERSIDE_CRITICAL) {
throw new TauModelException(getName()+": Segment is upgoing, but previous action was to underside reflect down: "+currLeg);
}
if (prevSegment.endAction == TRANSDOWN) {
throw new TauModelException(getName()+": Segment is upgoing, but previous action was to trans down: "+currLeg);
}
if (prevSegment.endBranch == startBranch && prevSegment.isDownGoing == true
&& ! ( prevSegment.endAction == TURN || prevSegment.endAction == DIFFRACT || prevSegment.endAction == REFLECT_TOPSIDE || prevSegment.endAction == REFLECT_TOPSIDE_CRITICAL)) {
throw new TauModelException(getName()+": Segment is upgoing, but previous action was not to reflect topside: "+currLeg+" "+endActionString(prevSegment.endAction));
}
}
}
segmentList.add(segment);
if(isDownGoing) {
if (startBranch > endBranch) {
// can't be downgoing as we are already below
minRayParam = -1;
maxRayParam = -1;
throw new TauModelException("can't be downgoing as we are already below: "+startBranch+" "+endBranch);
} else {
/* Must be downgoing, so use i++. */
for(int i = startBranch; i <= endBranch; i++) {
branchSeq.add(i);
downGoing.add(isDownGoing);
waveType.add(isPWave);
legAction.add(endAction);
}
if(DEBUG) {
for(int i = startBranch; i <= endBranch; i++) {
System.out.println("i=" + i + " isDownGoing=" + isDownGoing
+ " isPWave=" + isPWave + " startBranch="
+ startBranch + " endBranch=" + endBranch + " "
+ endActionString(endAction));
}
}
}
} else {
if (startBranch < endBranch) {
// can't be upgoing as we are already above
minRayParam = -1;
maxRayParam = -1;
throw new TauModelException("can't be upgoing as we are already above: "+startBranch+" "+endBranch);
} else {
/* Must be up going so use i--. */
for(int i = startBranch; i >= endBranch; i--) {
branchSeq.add(i);
downGoing.add(isDownGoing);
waveType.add(isPWave);
legAction.add(endAction);
}
if(DEBUG) {
for(int i = startBranch; i >= endBranch; i--) {
System.out.println("i=" + i + " isDownGoing=" + isDownGoing
+ " isPWave=" + isPWave + " startBranch="
+ startBranch + " endBranch=" + endBranch + " "
+ endActionString(endAction));
}
}
}
}
currBranch = endBranch + endOffset;
if(DEBUG) {
System.out.println("after addToBranch: minRP="+minRayParam+" maxRP="+maxRayParam);
}
}
/**
* Finds the closest discontinuity to the given depth that can have
* refletions and phase transformations.
*
* @return the branch number with the closest top depth.
*/
public int closestBranchToDepth(TauModel tMod, String depthString) {
if(depthString.equals("m")) {
return tMod.getMohoBranch();
} else if(depthString.equals("c")) {
return tMod.getCmbBranch();
} else if(depthString.equals("i")) {
return tMod.getIocbBranch();
}
// nonstandard boundary, given by a number, so we must look for it
int disconBranch = -1;
double disconMax = Double.MAX_VALUE;
double disconDepth = (Double.valueOf(depthString)).doubleValue();
TauBranch tBranch;
for(int i = 0; i < tMod.getNumBranches(); i++) {
tBranch = tMod.getTauBranch(i, PWAVE);
if(Math.abs(disconDepth - tBranch.getTopDepth()) < disconMax
&& !tMod.isNoDisconDepth(tBranch.getTopDepth())) {
disconBranch = i;
disconMax = Math.abs(disconDepth - tBranch.getTopDepth());
}
}
return disconBranch;
}
/**
* Constructs a branch sequence from the given phase name and tau model.
*/
protected void parseName(TauModel tMod) throws TauModelException {
String prevLeg;
String currLeg = (String)legs.get(0);
String nextLeg = currLeg;
branchSeq.clear();
boolean isPWave = PWAVE;
boolean isPWavePrev = isPWave;
int endAction = TRANSDOWN;
/*
* Deal with surface wave velocities first, since they are a special
* case.
*/
if(legs.size() == 2 && currLeg.endsWith("kmps")) {
return;
}
/* Make a check for J legs if the model doesn not allow J */
if(name.indexOf('J') != -1
&& !tMod.getSlownessModel().isAllowInnerCoreS()) {
throw new TauModelException("'J' phases were not created for this model: "
+ name);
}
/* set currWave to be the wave type for this leg, 'P' or 'S'. */
if(currLeg.equals("p") || currLeg.startsWith("P")
|| currLeg.equals("K") || currLeg.equals("k")
|| currLeg.equals("I")) {
isPWave = PWAVE;
isPWavePrev = isPWave;
} else if(currLeg.equals("s") || currLeg.startsWith("S")
|| currLeg.equals("J")) {
isPWave = SWAVE;
isPWavePrev = isPWave;
} else {
throw new TauModelException("Unknown starting phase: "+currLeg);
}
// where we end up, depending on if we end going down or up
int upgoingRecBranch = tMod.findBranch(receiverDepth);
int downgoingRecBranch = upgoingRecBranch-1; // one branch shallower
/*
* First, decide whether the ray is up going or downgoing from the
* source. If it is up going then the first branch number would be
* tMod.getSourceBranch()-1 and downgoing would be
* tMod.getSourceBranch().
*/
if(currLeg.startsWith("s") || currLeg.startsWith("S")) {
// Exclude S sources in fluids
double sdep = tMod.getSourceDepth();
if(tMod.getSlownessModel().depthInFluid(sdep, new DepthRange())) {
maxRayParam = minRayParam = -1;
if(DEBUG) {
System.out.println("Cannot have S wave with starting depth in fluid layer"
+ currLeg + " within phase " + name);
}
return;
}
}
/*
* Set maxRayParam to be a horizontal ray leaving the source and set
* minRayParam to be a vertical (p=0) ray.
*/
if(currLeg.startsWith("P")
|| currLeg.startsWith("S")
|| (expert && (currLeg.startsWith("K") || currLeg.startsWith("I") || currLeg.startsWith("J")))) {
// Downgoing from source
currBranch = tMod.getSourceBranch();
endAction = REFLECT_UNDERSIDE; // treat initial downgoing as if it were a
// underside reflection
try {
int sLayerNum = tMod.getSlownessModel().layerNumberBelow(tMod.getSourceDepth(), isPWavePrev);
maxRayParam = tMod.getSlownessModel().getSlownessLayer(sLayerNum, isPWavePrev).getTopP();
} catch(NoSuchLayerException e) {
throw new RuntimeException("Should not happen", e);
}
maxRayParam = tMod.getTauBranch(tMod.getSourceBranch(),
isPWave).getMaxRayParam();
} else if(currLeg.equals("p") || currLeg.equals("s")
|| (expert && currLeg.startsWith("k"))) {
// Up going from source
endAction = REFLECT_TOPSIDE; // treat initial upgoing as if it were a topside reflection
try {
int sLayerNum = tMod.getSlownessModel().layerNumberAbove(tMod.getSourceDepth(), isPWavePrev);
maxRayParam = tMod.getSlownessModel().getSlownessLayer(sLayerNum, isPWavePrev).getBotP();
// check if source is in high slowness zone
DepthRange highSZoneDepth = new DepthRange();
if (tMod.getSlownessModel().depthInHighSlowness(tMod.getSourceDepth(), maxRayParam, highSZoneDepth, isPWavePrev)) {
// need to reduce maxRayParam until it can propagate out of high slowness zone
maxRayParam = Math.min(maxRayParam, highSZoneDepth.rayParam);
}
} catch(NoSuchLayerException e) {
throw new RuntimeException("Should not happen", e);
}
if(tMod.getSourceBranch() != 0) {
currBranch = tMod.getSourceBranch() - 1;
} else {
/*
* p and s for zero source depth are only at zero distance and
* then can be called P or S.
*/
maxRayParam = -1;
minRayParam = -1;
return;
}
} else {
throw new TauModelException("First phase not recognized: "
+ currLeg
+ " must be one of P, Pg, Pn, Pdiff, p, Ped or the S equivalents");
}
if (receiverDepth != 0) {
if (legs.get(legs.size()-2).equals("Ped") || legs.get(legs.size()-2).equals("Sed")) {
// downgoing at receiver
maxRayParam = Math.min(tMod.getTauBranch(downgoingRecBranch,
isPWave)
.getMinTurnRayParam(),
maxRayParam);
} else {
// upgoing at receiver
maxRayParam = Math.min(tMod.getTauBranch(upgoingRecBranch,
isPWave)
.getMaxRayParam(),
maxRayParam);
}
}
minRayParam = 0.0;
int disconBranch = 0;
double nextLegDepth = 0.0;
boolean isLegDepth, isNextLegDepth = false;
/*
* Now loop over all of the phase legs and construct the proper branch
* sequence.
*/
currLeg = "START"; // So the prevLeg isn't wrong on the first pass
for(int legNum = 0; legNum < legs.size(); legNum++) {
prevLeg = currLeg;
currLeg = nextLeg;
if (legNum < legs.size() - 1) {
nextLeg = legs.get(legNum + 1);
} else {
nextLeg = "END";
}
if(DEBUG) {
System.out.println(legNum + " " + prevLeg + " cur=" + currLeg
+ " " + nextLeg);
}
if (currLeg.contentEquals("END")) {
if (segmentList.size() > 0) {
segmentList.get(segmentList.size()-1).endAction = END;
continue;
}
}
isLegDepth = isNextLegDepth;
// find out if the next leg represents a phase conversion depth
try {
nextLegDepth = Double.parseDouble(nextLeg);
isNextLegDepth = true;
} catch(NumberFormatException e) {
nextLegDepth = -1;
isNextLegDepth = false;
}
/* set currWave to be the wave type for this leg, 'P' or 'S'. */
isPWavePrev = isPWave;
if(currLeg.equals("p") || currLeg.startsWith("P")
|| currLeg.equals("k") || currLeg.equals("I")) {
isPWave = PWAVE;
} else if(currLeg.equals("s") || currLeg.startsWith("S")
|| currLeg.equals("J")) {
isPWave = SWAVE;
} else if(currLeg.equals("K")) {
/*
* here we want to use whatever isPWave was on the last leg so
* do nothing. This makes sure we us the correct maxRayParam
* from the correct TauBranch within the outer core. In other
* words K has a high slowness zone if it entered the outer core
* as a mantle P wave, but doesn't if it entered as a mantle S
* wave. It shouldn't matter for inner core to outer core type
* legs.
*/
}
// check to see if there has been a phase conversion
if(branchSeq.size() > 0 && isPWavePrev != isPWave) {
phaseConversion(tMod,
((Integer)branchSeq.get(branchSeq.size() - 1)).intValue(),
endAction,
isPWavePrev);
}
if (currLeg.equals("Ped") || currLeg.equals("Sed")) {
if(nextLeg.equals("END")) {
if (receiverDepth > 0) {
endAction = END_DOWN;
addToBranch(tMod, currBranch, downgoingRecBranch, isPWave, endAction, currLeg);
} else {
//this should be impossible except for 0 dist 0 source depth which can be called p or P
maxRayParam = -1;
minRayParam = -1;
return;
}
} else if(nextLeg.equals("Pdiff") || nextLeg.equals("Sdiff")) {
endAction = DIFFRACT;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
if (currLeg.charAt(0) != nextLeg.charAt(0)) {
// like Sed Pdiff conversion
isPWave = ! isPWave;
}
} else if(nextLeg.equals("K")) {
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("m")) {
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
tMod.getMohoBranch() - 1,
isPWave,
endAction,
currLeg);
} else if( isLegDepth) {
disconBranch = closestBranchToDepth(tMod, nextLeg);
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
disconBranch - 1,
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("c") || nextLeg.equals("i")) {
disconBranch = closestBranchToDepth(tMod, nextLeg);
endAction = REFLECT_TOPSIDE;
addToBranch(tMod,
currBranch,
disconBranch - 1,
isPWave,
endAction,
currLeg);
} else if(nextLeg.startsWith("v") || nextLeg.startsWith("V") ) {
if (nextLeg.startsWith("V")) {
endAction = REFLECT_TOPSIDE_CRITICAL;
} else {
endAction = REFLECT_TOPSIDE;
}
disconBranch = closestBranchToDepth(tMod,
nextLeg.substring(1));
if(currBranch <= disconBranch - 1) {
addToBranch(tMod,
currBranch,
disconBranch - 1,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (4): "
+ currLeg + " followed by " + nextLeg
+ " when currBranch=" + currBranch
+ " < disconBranch=" + disconBranch);
}
} else if( nextLeg.equals("I") || nextLeg.equals("J")) {
if(tMod.getCmbDepth() == tMod.getIocbDepth()) {
// degenerate case of no fluid outer core, so allow phases like PIP or SJS
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
} else {
maxRayParam = -1;
if(DEBUG) {System.out.println("P or S followed by I or J can only exist if model has no outer core");}
return;
}
} else {
throw new TauModelException(getName()+" Phase not recognized (1): "
+ currLeg + " followed by " + nextLeg);
}
/* Deal with p and s case . */
} else if(currLeg.equals("p") || currLeg.equals("s")
|| currLeg.equals("k")) {
if(nextLeg.startsWith("v") || nextLeg.startsWith("V")) {
throw new TauModelException(getName()+" p and s and k must always be up going "
+ " and cannot come immediately before a top-side reflection."
+ " currLeg=" + currLeg + " nextLeg=" + nextLeg);
} else if(nextLeg.startsWith("^")) {
String depthString;
depthString = currLeg.substring(1);
endAction = REFLECT_UNDERSIDE;
disconBranch = closestBranchToDepth(tMod, depthString);
if(currBranch >= disconBranch) {
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (2): "
+ currLeg + " followed by " + nextLeg
+ " when currBranch=" + currBranch
+ " > disconBranch=" + disconBranch);
}
} else if(nextLeg.equals("m")
&& currBranch >= tMod.getMohoBranch()) {
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
tMod.getMohoBranch(),
isPWave,
endAction,
currLeg);
} else if(nextLeg.startsWith("P") || nextLeg.startsWith("S")
|| nextLeg.equals("K") || nextLeg.equals("END")) {
if (nextLeg.equals("END")) {
disconBranch = upgoingRecBranch;
if (currBranch < upgoingRecBranch) {
maxRayParam = -1;
if (DEBUG) {
System.out.println(name+" (currBranch >= receiverBranch() "
+ currBranch
+ " "
+ upgoingRecBranch
+ " so there cannot be a "
+ currLeg
+ " phase for this sourceDepth, receiverDepth and/or path.");
}
return;
}
} else if (nextLeg.equals("K")) {
disconBranch = tMod.getCmbBranch();
} else {
disconBranch = 0;
}
if (currLeg.equals("k") && ! nextLeg.equals("K")) {
endAction = TRANSUP;
} else if (nextLeg.equals("END")) {
endAction = END;
} else {
endAction = REFLECT_UNDERSIDE;
}
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else if(isNextLegDepth) {
disconBranch = closestBranchToDepth(tMod, nextLeg);
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (3): "
+ currLeg + " followed by " + nextLeg);
}
/* Now deal with P and S case. */
} else if(currLeg.equals("P") || currLeg.equals("S")) {
if(nextLeg.equals("P") || nextLeg.equals("S")
|| nextLeg.equals("Pn") || nextLeg.equals("Sn")
|| nextLeg.equals("END")) {
if(endAction == TRANSDOWN || endAction == REFLECT_UNDERSIDE|| endAction == REFLECT_UNDERSIDE_CRITICAL) {
// was downgoing, so must first turn in mantle
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
TURN,
currLeg);
}
if (nextLeg.equals("END")) {
endAction = END;
addToBranch(tMod, currBranch, upgoingRecBranch, isPWave, END, currLeg);
} else {
endAction = REFLECT_UNDERSIDE;
addToBranch(tMod, currBranch, 0, isPWave, endAction, currLeg);
}
} else if(nextLeg.startsWith("v") || nextLeg.startsWith("V") ) {
if (nextLeg.startsWith("V")) {
endAction = REFLECT_TOPSIDE_CRITICAL;
} else {
endAction = REFLECT_TOPSIDE;
}
disconBranch = closestBranchToDepth(tMod,
nextLeg.substring(1));
if(currBranch <= disconBranch - 1) {
addToBranch(tMod,
currBranch,
disconBranch - 1,
isPWave,
endAction,
currLeg);
} else {
// can't topside reflect if already below, setting maxRayParam forces no arrivals
maxRayParam = -1;
}
} else if(nextLeg.startsWith("^")) {
String depthString;
depthString = nextLeg.substring(1);
endAction = REFLECT_UNDERSIDE;
disconBranch = closestBranchToDepth(tMod, depthString);
if (disconBranch == tMod.getNumBranches()) {
maxRayParam = -1;
if(DEBUG) {System.out.println("Attempt to underside reflect from center of earth: "+nextLeg);}
return;
}
if(prevLeg.equals("K")) {
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else if(prevLeg.startsWith("^") || prevLeg.equals("P")
|| prevLeg.equals("S") || prevLeg.equals("p")
|| prevLeg.equals("s") || prevLeg.equals("m")
|| prevLeg.equals("START")) {
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
TURN,
currLeg);
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else if(((prevLeg.startsWith("v") || prevLeg.startsWith("V"))
&& disconBranch < closestBranchToDepth(tMod, prevLeg.substring(1)))
|| (prevLeg.equals("m") && disconBranch < tMod.getMohoBranch())
|| (prevLeg.equals("c") && disconBranch < tMod.getCmbBranch())) {
if (disconBranch == tMod.getNumBranches()) {
maxRayParam = -1;
if(DEBUG) {System.out.println("Attempt to reflect from center of earth: "+nextLeg);}
return;
}
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (5): "
+ currLeg + " followed by " + nextLeg
+ " when currBranch=" + currBranch
+ " > disconBranch=" + disconBranch+" , prev="+prevLeg);
}
} else if(nextLeg.equals("c")) {
if (tMod.getCmbBranch() == tMod.getNumBranches()) {
maxRayParam = -1;
if(DEBUG) {System.out.println("Attempt to reflect from center of earth: "+nextLeg);}
return;
}
endAction = REFLECT_TOPSIDE;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("K") && prevLeg.equals("K")) {
throw new TauModelException(getName()+" Phase not recognized (5.5): "
+ currLeg + " followed by " + nextLeg
+ " and preceeded by "+prevLeg
+ " when currBranch=" + currBranch
+ " > disconBranch=" + disconBranch);
} else if(nextLeg.equals("K")) {
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
} else if( nextLeg.equals("I") || nextLeg.equals("J")) {
if(tMod.getCmbDepth() == tMod.getIocbDepth()) {
// degenerate case of no fluid outer core, so allow phases like PIP or SJS
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" P or S followed by I or J can only exist if model has no outer core: "
+ currLeg
+ " followed by "
+ nextLeg);
}
} else if(nextLeg.equals("m")
|| (isNextLegDepth && nextLegDepth < tMod.getCmbDepth())) {
// treat the moho in the same wasy as 410 type
// discontinuities
disconBranch = closestBranchToDepth(tMod, nextLeg);
if(DEBUG) {
System.out.println("DisconBranch=" + disconBranch
+ " for " + nextLeg);
System.out.println(tMod.getTauBranch(disconBranch,
isPWave)
.getTopDepth());
}
if(endAction == TURN || endAction == REFLECT_TOPSIDE
|| endAction == REFLECT_TOPSIDE_CRITICAL || endAction == TRANSUP) {
// upgoing section
if(disconBranch > currBranch) {
// check for discontinuity below the current branch
// when the ray should be upgoing
throw new TauModelException(getName()+" Phase not recognized (6): "
+ currLeg
+ " followed by "
+ nextLeg
+ " when currBranch="
+ currBranch
+ " > disconBranch=" + disconBranch);
}
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else {
// downgoing section, must look at the leg after the
// next
// leg to determine whether to convert on the downgoing
// or
// upgoing part of the path
String nextNextLeg = (String)legs.get(legNum + 2);
if(nextNextLeg.equals("p") || nextNextLeg.equals("s")) {
// convert on upgoing section
endAction = TURN;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else if(nextNextLeg.equals("P")
|| nextNextLeg.equals("S")) {
if(disconBranch > currBranch) {
// discon is below current loc
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
disconBranch - 1,
isPWave,
endAction,
currLeg);
} else {
// discon is above current loc, but we have a
// downgoing ray, so this is an illegal ray for
// this source depth
maxRayParam = -1;
if(DEBUG) {
System.out.println("Cannot phase convert on the "
+ "downgoing side if the discontinuity is above "
+ "the phase leg starting point, "
+ currLeg
+ " "
+ nextLeg
+ " "
+ nextNextLeg
+ ", so this phase, "
+ getName()
+ " is illegal for this sourceDepth.");
}
return;
}
} else {
throw new TauModelException(getName()+" Phase not recognized (7): "
+ currLeg
+ " followed by "
+ nextLeg
+ " followed by " + nextNextLeg);
}
}
} else {
throw new TauModelException(getName()+" Phase not recognized (8): "
+ currLeg + " followed by " + nextLeg);
}
} else if(currLeg.startsWith("P") || currLeg.startsWith("S")) {
if(currLeg.equals("Pdiff") || currLeg.equals("Sdiff")) {
/*
* in the diffracted case we trick addToBranch into thinking
* we are turning, but then make the maxRayParam equal to
* minRayParam, which is the deepest turning ray.
*/
if(maxRayParam >= tMod.getTauBranch(tMod.getCmbBranch() - 1,
isPWave)
.getMinTurnRayParam()
&& minRayParam <= tMod.getTauBranch(tMod.getCmbBranch() - 1,
isPWave)
.getMinTurnRayParam()) {
if (currBranch < tMod.getCmbBranch() - 1
|| (currBranch == tMod.getCmbBranch() - 1 && endAction != DIFFRACT)
|| (currBranch == tMod.getCmbBranch() && endAction != TRANSUP)) {
endAction = DIFFRACT;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch() - 1,
isPWave,
endAction,
currLeg);
} // otherwise we are already at the right branch to diffract
// remember where the diff or head happened (one less than size)
headOrDiffractSeq.add(branchSeq.size() - 1);
maxRayParam = tMod.getTauBranch(tMod.getCmbBranch() - 1,
isPWave)
.getMinTurnRayParam();
minRayParam = maxRayParam;
if(nextLeg.equals("END")) {
endAction = END;
addToBranch(tMod,
currBranch,
upgoingRecBranch,
isPWave,
endAction,
currLeg);
} else if (nextLeg.equals("K")) {
endAction = TRANSDOWN;
currBranch++;
} else if (nextLeg.startsWith("P") || nextLeg.startsWith("S")) {
endAction = REFLECT_UNDERSIDE;
addToBranch(tMod,
currBranch,
0,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (12): "
+ currLeg + " followed by " + nextLeg
+ " when currBranch=" + currBranch);
}
} else {
// can't have head wave as ray param is not within range
if(DEBUG) {
System.out.println("Cannot have the head wave "
+ currLeg + " within phase " + name
+ " for this sourceDepth and/or path.");
System.out.println(maxRayParam+" >= "+tMod.getTauBranch(tMod.getCmbBranch() - 1,
isPWave)
.getMinTurnRayParam()+" "+
"&& "+minRayParam+" <= "+tMod.getTauBranch(tMod.getCmbBranch() - 1,
isPWave)
.getMinTurnRayParam());
}
maxRayParam = -1;
return;
}
} else if(currLeg.equals("Pg") || currLeg.equals("Sg")
|| currLeg.equals("Pn") || currLeg.equals("Sn")) {
if(currBranch >= tMod.getMohoBranch()) {
/*
* Pg, Pn, Sg and Sn must be above the moho and so is
* not valid for rays coming upwards from below,
* possibly due to the source depth. Setting maxRayParam =
* -1 effectively disallows this phase.
*/
maxRayParam = -1;
if(DEBUG) {
System.out.println("(currBranch >= tMod.getMohoBranch() "
+ currBranch
+ " "
+ tMod.getMohoBranch()
+ " so there cannot be a "
+ currLeg
+ " phase for this sourceDepth and/or path.");
}
return;
}
if(currLeg.equals("Pg") || currLeg.equals("Sg")) {
endAction = TURN;
addToBranch(tMod,
currBranch,
tMod.getMohoBranch() - 1,
isPWave,
endAction,
currLeg);
if(nextLeg.equals("END")) {
endAction = END;
} else {
endAction = REFLECT_UNDERSIDE;
}
addToBranch(tMod, currBranch, upgoingRecBranch, isPWave, endAction, currLeg);
} else if(currLeg.equals("Pn") || currLeg.equals("Sn")) {
/*
* in the refracted case we trick addToBranch into
* thinking we are turning below the moho, but then make
* the minRayParam equal to maxRayParam, which is the
* head wave ray.
*/
if(maxRayParam >= tMod.getTauBranch(tMod.getMohoBranch(),
isPWave)
.getMaxRayParam()
&& minRayParam <= tMod.getTauBranch(tMod.getMohoBranch(),
isPWave)
.getMaxRayParam()) {
endAction = TURN;
addToBranch(tMod,
currBranch,
tMod.getMohoBranch(),
isPWave,
endAction,
currLeg);
// remember where the diff or head happened (one less than size)
headOrDiffractSeq.add(branchSeq.size() - 1);
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
tMod.getMohoBranch(),
isPWave,
endAction,
currLeg);
minRayParam = maxRayParam;
if(nextLeg.equals("END")) {
endAction = END;
if (currBranch >= upgoingRecBranch) {
addToBranch(tMod,
currBranch,
upgoingRecBranch,
isPWave,
endAction,
currLeg);
} else {
maxRayParam = -1;
if(DEBUG) {
System.out.println("Cannot have the head wave "
+ currLeg + " within phase " + name
+ " for this sourceDepth, receiverDepth and/or path.");
}
return;
}
} else if ( nextLeg.startsWith("P") || nextLeg.startsWith("S")) {
endAction = REFLECT_UNDERSIDE;
addToBranch(tMod,
currBranch,
0,
isPWave,
endAction,
currLeg);
}
} else {
// can't have head wave as ray param is not within
// range
maxRayParam = -1;
if(DEBUG) {
System.out.println("Cannot have the head wave "
+ currLeg + " within phase " + name
+ " for this sourceDepth and/or path.");
}
return;
}
}
} else {
throw new TauModelException(getName()+" Phase not recognized for P,S: "
+ currLeg + " followed by " + nextLeg);
}
} else if(currLeg.equals("K")) {
/* Now deal with K. */
if (tMod.getCmbDepth() == tMod.getRadiusOfEarth()) {
// degenerate case, CMB is at center, so model without a core
maxRayParam = -1;
if(DEBUG) {
System.out.println("Cannot have K phase "
+ currLeg + " within phase " + name
+ " for this model as it has no core, cmb depth = radius of Earth.");
}
return;
}
if (tMod.getCmbDepth() == tMod.getIocbDepth()) {
// degenerate case, CMB is same as IOCB, so model without an outer core
maxRayParam = -1;
if(DEBUG) {
System.out.println("Cannot have K phase "
+ currLeg + " within phase " + name
+ " for this model as it has no outer core, cmb depth = iocb depth, "+tMod.getCmbDepth());
}
return;
}
if(nextLeg.equals("P") || nextLeg.equals("S")
|| nextLeg.equals("Pdiff") || nextLeg.equals("Sdiff")) {
if(prevLeg.equals("P") || prevLeg.equals("S")
|| prevLeg.equals("Ped") || prevLeg.equals("Sed")
|| prevLeg.equals("Pdiff") || prevLeg.equals("Sdiff")
|| prevLeg.equals("K") || prevLeg.equals("k")
|| prevLeg.startsWith("^")
|| prevLeg.equals("START")) {
endAction = TURN;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch() - 1,
isPWave,
endAction,
currLeg);
}
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch(),
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("K")) {
if(prevLeg.equals("P") || prevLeg.equals("S")
|| prevLeg.equals("K")) {
endAction = TURN;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch() - 1,
isPWave,
endAction,
currLeg);
}
endAction = REFLECT_UNDERSIDE;
addToBranch(tMod,
currBranch,
tMod.getCmbBranch(),
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("I") || nextLeg.equals("J")) {
endAction = TRANSDOWN;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch() - 1,
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("i")) {
if (tMod.getIocbBranch() == tMod.getNumBranches()) {
maxRayParam = -1;
if (DEBUG) {
System.out.println("Attempt to reflect from center of earth: " + nextLeg);
}
return;
}
endAction = REFLECT_TOPSIDE;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch() - 1,
isPWave,
endAction,
currLeg);
} else if(nextLeg.startsWith("v") || nextLeg.startsWith("V") ) {
if (nextLeg.startsWith("V")) {
endAction = REFLECT_TOPSIDE_CRITICAL;
} else {
endAction = REFLECT_TOPSIDE;
}
disconBranch = closestBranchToDepth(tMod,
nextLeg.substring(1));
if(currBranch <= disconBranch - 1) {
addToBranch(tMod,
currBranch,
disconBranch - 1,
isPWave,
endAction,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (4): "
+ currLeg + " followed by " + nextLeg
+ " when currBranch=" + currBranch
+ " < disconBranch=" + disconBranch);
}
} else if(nextLeg.startsWith("^")) {
String depthString;
depthString = nextLeg.substring(1);
endAction = REFLECT_UNDERSIDE;
disconBranch = closestBranchToDepth(tMod, depthString);
if (disconBranch < tMod.getCmbBranch()) {
throw new TauModelException(getName()+" Phase not recognized (5a): "
+ currLeg + " followed by " + nextLeg
+ " when disconBranch=" + disconBranch
+" < cmbBranch="+tMod.getCmbBranch()+", likely need P or S leg , prev="+prevLeg);
}
if (disconBranch >= tMod.getIocbBranch()) {
throw new TauModelException(getName()+" Phase not recognized (5b): "
+ currLeg + " followed by " + nextLeg
+ " when disconBranch=" + disconBranch
+" > iocbBranch="+tMod.getIocbBranch()+", likely need Ior J leg , prev="+prevLeg);
}
if (disconBranch == tMod.getNumBranches()) {
maxRayParam = -1;
if(DEBUG) {System.out.println("Attempt to underside reflect from center of earth: "+nextLeg);}
return;
}
if(prevLeg.equals("I") || prevLeg.equals("J")
|| prevLeg.equals("i") || prevLeg.equals("j")
|| prevLeg.equals("k")) {
// upgoind K leg
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else if(prevLeg.equals("P") || prevLeg.equals("S") ||
prevLeg.startsWith("^") ||
prevLeg.equals("K") || prevLeg.equals("START")) {
addToBranch(tMod,
currBranch,
tMod.getIocbBranch() - 1,
isPWave,
TURN,
currLeg);
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else if(prevLeg.startsWith("v") || prevLeg.startsWith("V") ) {
String prevDepthString = prevLeg.substring(1);
int prevdisconBranch = closestBranchToDepth(tMod, prevDepthString);
if (disconBranch < prevdisconBranch) {
// upgoind K leg
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
} else {
// down-turn-up
addToBranch(tMod,
currBranch,
tMod.getIocbBranch() - 1,
isPWave,
TURN,
currLeg);
addToBranch(tMod,
currBranch,
disconBranch,
isPWave,
endAction,
currLeg);
}
} else {
throw new TauModelException(getName()+" Phase not recognized (5): "
+ currLeg + " followed by " + nextLeg
+ " when currBranch=" + currBranch
+ " > disconBranch=" + disconBranch+" , prev="+prevLeg);
}
} else {
throw new TauModelException(getName()+" Phase not recognized (9): "
+ currLeg + " followed by " + nextLeg);
}
} else if(currLeg.equals("I") || currLeg.equals("J")) {
/* And now consider inner core, I and J. */
if (tMod.getIocbDepth() == tMod.getRadiusOfEarth()) {
// degenerate case, IOCB is at center, so model without a inner core
maxRayParam = -1;
if(DEBUG) {
System.out.println("Cannot have I or J phase "
+ currLeg + " within phase " + name
+ " for this model as it has no inner core, iocb depth = radius of Earth.");
}
return;
}
endAction = TURN;
addToBranch(tMod,
currBranch,
tMod.getNumBranches() - 1,
isPWave,
endAction,
currLeg);
if(nextLeg.equals("I") || nextLeg.equals("J")) {
endAction = REFLECT_UNDERSIDE;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch(),
isPWave,
endAction,
currLeg);
} else if(nextLeg.equals("K")) {
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch(),
isPWave,
endAction,
currLeg);
} else if(nextLeg.startsWith("^")) {
String depthString;
depthString = nextLeg.substring(1);
endAction = REFLECT_UNDERSIDE;
disconBranch = closestBranchToDepth(tMod, depthString);
if (disconBranch < tMod.getIocbBranch()) {
throw new TauModelException(getName()+" Phase not recognized (6a): "
+ currLeg + " followed by " + nextLeg
+ " when disconBranch=" + disconBranch
+" < iocbBranch="+tMod.getIocbBranch()+", likely need K leg , prev="+prevLeg);
}
if (disconBranch == tMod.getNumBranches()) {
maxRayParam = -1;
if(DEBUG) {System.out.println("Attempt to underside reflect from center of earth: "+nextLeg);}
return;
}
if(prevLeg.equals("K") || prevLeg.equals("I") || prevLeg.equals("J")) {
addToBranch(tMod,
currBranch,
tMod.getNumBranches() - 1,
isPWave,
TURN,
currLeg);
} else {
throw new TauModelException(getName()+" Phase not recognized (5c): "
+ currLeg + " followed by " + nextLeg
+ " when disconBranch=" + disconBranch+" , prev="+prevLeg);
}
} else if(nextLeg.equalsIgnoreCase("P") || nextLeg.equalsIgnoreCase("S")) {
if (tMod.getCmbDepth() == tMod.getIocbDepth()) {
// degenerate case of no fluid outer core, so allow phases like PIP or SJS
endAction = TRANSUP;
addToBranch(tMod,
currBranch,
tMod.getIocbBranch(),
isPWave,
endAction,
currLeg);
} else {
maxRayParam = -1;
throw new TauModelException(getName()+" Cannot have I or J phase "
+ currLeg
+ " followed by "+nextLeg
+ " within phase " + name
+ " for this model as it has an outer core so need K in between.");
}
}
} else if(currLeg.equals("m")) {
} else if(currLeg.equals("c") || currLeg.equals("cx")) {
} else if(currLeg.equals("i") || currLeg.equals("ix")) {
} else if(currLeg.startsWith("^")) {
} else if(currLeg.startsWith("v") || currLeg.startsWith("V")) {
String depthString;
depthString = currLeg.substring(1);
int b = closestBranchToDepth(tMod, depthString);
if (b == 0) {
throw new TauModelException(getName()+" Phase not recognized: "+currLeg+" looks like a top side reflection at the free surface.");
}
} else if(isLegDepth) {
// check for phase like P0s, but could also be P2s if first discon is deeper
int b = closestBranchToDepth(tMod, currLeg);
if (b == 0 && (nextLeg.equals("p") || nextLeg.equals("s"))) {
throw new TauModelException(getName()+" Phase not recognized: "+currLeg
+ " followed by " + nextLeg+" looks like a upgoing wave from the free surface as closest discontinuity to "+currLeg+" is zero depth.");
}
} else {
throw new TauModelException(getName()+" Phase not recognized (10): " + currLeg
+ " followed by " + nextLeg);
}
}
if (maxRayParam != -1) {
if ((endAction == REFLECT_UNDERSIDE || endAction == REFLECT_UNDERSIDE) && downgoingRecBranch == branchSeq.get(branchSeq.size()-1) ) {
// last action was upgoing, so last branch should be upgoingRecBranch
if (DEBUG) {
System.out.println("Phase ends upgoing, but receiver is not on upgoing end of last branch");
}
minRayParam = -1;
maxRayParam = -1;
} else if ((endAction == REFLECT_TOPSIDE || endAction == REFLECT_TOPSIDE_CRITICAL)
&& upgoingRecBranch == branchSeq.get(branchSeq.size()-1) ) {
// last action was downgoing, so last branch should be downgoingRecBranch
if (DEBUG) {
System.out.println("Phase ends downgoing, but receiver is not on downgoing end of last branch");
System.out.println(endActionString(endAction)+" upgoingRecBranch="+upgoingRecBranch+" bs="+branchSeq.get(branchSeq.size()-1));
}
minRayParam = -1;
maxRayParam = -1;
} else {
if (DEBUG) {
System.out.println("Last action is: "+endActionString(endAction)+" upR="+upgoingRecBranch+" downR="+downgoingRecBranch+" last="+branchSeq.get(branchSeq.size()-1));
}
}
}
}
/**
* Tokenizes a phase name into legs, ie PcS becomes 'P'+'c'+'S' while p^410P
* would become 'p'+'^410'+'P'. Once a phase name has been broken into
* tokens we can begin to construct the sequence of branches to which it
* corresponds. Only minor error checking is done at this point, for
* instance pIP generates an exception but ^410 doesn't. It also appends
* "END" as the last leg.
*
* @throws TauModelException
* if the phase name cannot be tokenized.
*/
protected static ArrayList legPuller(String name) throws TauModelException {
int offset = 0;
ArrayList legs = new ArrayList();
/* Special case for surface wave velocity. */
if(name.endsWith("kmps")) {
try {
legs.add(name);
} catch(NumberFormatException e) {
throw new TauModelException("Invalid phase name:\n" + name);
}
} else
while(offset < name.length()) {
if (offset + 2 < name.length() && (name.charAt(offset+1) == 'e')&& (name.charAt(offset+2) == 'd')
&& (name.charAt(offset) == 'p'
|| name.charAt(offset) == 's'
|| name.charAt(offset) == 'k'
|| name.charAt(offset) == 'm'
|| name.charAt(offset) == 'c'
|| name.charAt(offset) == 'i'
|| name.charAt(offset) == '^'
|| name.charAt(offset) == 'v'
|| name.charAt(offset) == 'V') ) {
throw new TauModelException("Invalid phase name:\n"
+ name.charAt(offset)
+ " cannot be followed by "
+ "'ed' in " + name);
} else if(name.charAt(offset) == 'K'
|| name.charAt(offset) == 'I'
|| name.charAt(offset) == 'k'
|| name.charAt(offset) == 'J'
|| name.charAt(offset) == 'p'
|| name.charAt(offset) == 's'
|| name.charAt(offset) == 'm') {
// Do the easy ones, ie K,k,I,J,p,s,m
legs.add(name.substring(offset, offset + 1));
offset = offset + 1;
} else if(name.charAt(offset) == 'c'
|| name.charAt(offset) == 'i') {
// note c and i are different from m as they must be reflection
// check m,c,i for critical refl with x
if (name.charAt(offset+1) == 'x') {
legs.add(name.substring(offset, offset + 2));
offset = offset + 2;
} else {
legs.add(name.substring(offset, offset + 1));
offset = offset + 1;
}
} else if(name.charAt(offset) == 'P'
|| name.charAt(offset) == 'S') {
/*
* Now it gets complicated, first see if the next char is
* part of a different leg or we are at the end.
*/
if(offset + 1 == name.length()
|| name.charAt(offset + 1) == 'P'
|| name.charAt(offset + 1) == 'S'
|| name.charAt(offset + 1) == 'K'
|| name.charAt(offset + 1) == 'I' // I,J might be allowed if no outer core
|| name.charAt(offset + 1) == 'J'
|| name.charAt(offset + 1) == 'm'
|| name.charAt(offset + 1) == 'c'
|| name.charAt(offset + 1) == '^'
|| name.charAt(offset + 1) == 'v'
|| name.charAt(offset + 1) == 'V'
|| Character.isDigit(name.charAt(offset + 1))) {
legs.add(name.substring(offset, offset + 1));
offset++;
} else if(name.charAt(offset + 1) == 'p'
|| name.charAt(offset + 1) == 's') {
throw new TauModelException("Invalid phase name:\n"
+ name.charAt(offset)
+ " cannot be followed by "
+ name.charAt(offset + 1) + " in " + name);
} else if(name.charAt(offset + 1) == 'g'
|| name.charAt(offset + 1) == 'b'
|| name.charAt(offset + 1) == 'n') {
/* The leg is not described by one letter, check for 2. */
legs.add(name.substring(offset, offset + 2));
offset = offset + 2;
} else if(name.length() >= offset + 3
&& (name.substring(offset, offset + 3)
.equals("Ped") || name.substring(offset,
offset + 3)
.equals("Sed"))) {
legs.add(name.substring(offset, offset + 3));
offset = offset + 3;
} else if(name.length() >= offset + 5
&& (name.substring(offset, offset + 5)
.equals("Sdiff") || name.substring(offset,
offset + 5)
.equals("Pdiff"))) {
legs.add(name.substring(offset, offset + 5));
offset = offset + 5;
} else {
throw new TauModelException("Invalid phase name:\n"
+ name.substring(offset) + " in " + name);
}
} else if(name.charAt(offset) == '^'
|| name.charAt(offset) == 'v'
|| name.charAt(offset) == 'V') {
// check m,c,i for critical refl with x
int criticalOffset = 0;
if (name.charAt(offset+1) == 'x') {
criticalOffset = 1;
}
/*
* Top side or bottom side reflections, check for standard
* boundaries and then check for numerical ones.
*/
if(name.charAt(offset + criticalOffset + 1) == 'm'
|| name.charAt(offset + criticalOffset + 1) == 'c'
|| name.charAt(offset + criticalOffset + 1) == 'i') {
legs.add(name.substring(offset, offset + criticalOffset + 2));
offset = offset + criticalOffset + 2;
} else if(Character.isDigit(name.charAt(offset + criticalOffset + 1))
|| name.charAt(offset + criticalOffset + 1) == '.') {
String numString = name.substring(offset, offset + criticalOffset + 1);
offset = offset + criticalOffset +1;
while(Character.isDigit(name.charAt(offset))
|| name.charAt(offset) == '.') {
numString += name.substring(offset, offset + 1);
offset++;
}
try {
legs.add(numString);
} catch(NumberFormatException e) {
throw new TauModelException("Invalid phase name: "
+ numString + "\n" + e.getMessage()
+ " in " + name);
}
} else {
throw new TauModelException("Invalid phase name:\n"
+ name.substring(offset) + " in " + name);
}
} else if(Character.isDigit(name.charAt(offset))
|| name.charAt(offset) == '.') {
String numString = name.substring(offset, offset + 1);
offset++;
while(Character.isDigit(name.charAt(offset))
|| name.charAt(offset) == '.') {
numString += name.substring(offset, offset + 1);
offset++;
}
try {
legs.add(numString);
} catch(NumberFormatException e) {
throw new TauModelException("Invalid phase name: "
+ numString + "\n" + e.getMessage() + " in "
+ name);
}
} else {
throw new TauModelException("Invalid phase name:\n"
+ name.substring(offset) + " in " + name);
}
}
legs.add(new String("END"));
String validationMsg = phaseValidate(legs);
if(validationMsg != null) {
throw new TauModelException("Phase failed validation: " + name
+ " " + validationMsg);
}
return legs;
}
protected void createPuristName(TauModel tMod) {
String currLeg = (String)legs.get(0);
/*
* Deal with surface wave velocities first, since they are a special
* case.
*/
if(legs.size() == 2 && currLeg.endsWith("kmps")) {
puristName = name;
return;
}
puristName = "";
double legDepth;
int intLegDepth;
int disconBranch;
Pattern reflectDepthPattern = Pattern.compile("[Vv^][0-9\\.]+");
// only loop to size()-1 as last leg is always "END"
for(int legNum = 0; legNum < legs.size() - 1; legNum++) {
currLeg = (String)legs.get(legNum);
// find out if the next leg represents a
// phase conversion or reflection depth
Matcher m = reflectDepthPattern.matcher(currLeg);
if(m.matches()) {
puristName += currLeg.substring(0, 1);
disconBranch = closestBranchToDepth(tMod, currLeg.substring(1));
if (disconBranch == tMod.getMohoBranch()) {
puristName += "m";
} else if (disconBranch == tMod.getCmbBranch()) {
puristName += "c";
} else if (disconBranch == tMod.getIocbBranch()) {
puristName += "i";
} else {
legDepth = tMod.getTauBranch(disconBranch, true).getTopDepth();
if (legDepth == Math.rint(legDepth)) {
intLegDepth = (int) legDepth;
puristName += intLegDepth;
} else {
puristName += legDepth;
}
}
} else {
try {
legDepth = Double.parseDouble(currLeg);
// only get this far if the currLeg is a number,
// otherwise exception
disconBranch = closestBranchToDepth(tMod, currLeg);
legDepth = tMod.getTauBranch(disconBranch, true)
.getTopDepth();
if(legDepth == Math.rint(legDepth)) {
intLegDepth = (int)legDepth;
puristName += intLegDepth;
} else {
puristName += legDepth;
}
} catch(NumberFormatException e) {
puristName += currLeg;
}
}
}
}
/**
* Calculates how many times the phase passes through a branch, up or down,
* so that we can just multiply instead of doing the ray calc for each time.
* @return
*/
protected int[][] calcBranchMultiplier() {
/* initialize the counter for each branch to 0. 0 is P and 1 is S. */
int[][] timesBranches = new int[2][tMod.getNumBranches()];
for(int i = 0; i < timesBranches[0].length; i++) {
timesBranches[0][i] = 0;
timesBranches[1][i] = 0;
}
/* Count how many times each branch appears in the path. */
for(int i = 0; i < branchSeq.size(); i++) {
if(((Boolean)waveType.get(i)).booleanValue()) {
timesBranches[0][((Integer)branchSeq.get(i)).intValue()]++;
} else {
timesBranches[1][((Integer)branchSeq.get(i)).intValue()]++;
}
}
return timesBranches;
}
/**
* Sums the appropriate branches for this phase.
*
* @throws TauModelException
* if the topDepth of the high slowness zone is not contained
* within the TauModel. This should never happen and would
* indicate an invalid TauModel.
*/
protected void sumBranches(TauModel tMod) throws TauModelException {
if(maxRayParam < 0.0 || minRayParam > maxRayParam) {
/* Phase has no arrivals, possibly due to source depth. */
rayParams = new double[0];
minRayParam = -1;
maxRayParam = -1;
dist = new double[0];
time = new double[0];
maxDistance = -1;
return;
}
/* Special case for surface waves. */
if(name.endsWith("kmps")) {
dist = new double[2];
time = new double[2];
rayParams = new double[2];
dist[0] = 0.0;
time[0] = 0.0;
rayParams[0] = tMod.radiusOfEarth
/ Double.valueOf(name.substring(0, name.length() - 4))
.doubleValue();
dist[1] = 2 * Math.PI;
time[1] = 2
* Math.PI
* tMod.radiusOfEarth
/ Double.valueOf(name.substring(0, name.length() - 4))
.doubleValue();
rayParams[1] = rayParams[0];
minDistance = 0.0;
maxDistance = 2 * Math.PI;
downGoing.add(true);
return;
}
/*
* Find the ray parameter index that corresponds to the minRayParam and
* maxRayParam.
*/
for(int i = 0; i < tMod.rayParams.length; i++) {
if(tMod.rayParams[i] >= minRayParam) {
minRayParamIndex = i;
}
if(tMod.rayParams[i] >= maxRayParam) {
maxRayParamIndex = i;
}
}
if(maxRayParamIndex < 0) {
throw new RuntimeException(getName()+" Should not happen, did not find max ray param"+maxRayParam);
}
if(minRayParamIndex < 0) {
throw new RuntimeException(getName()+" Should not happen, did not find min ray param"+minRayParam);
}
if(maxRayParamIndex == 0
&& minRayParamIndex == tMod.rayParams.length - 1) {
// all ray parameters are valid so just copy
rayParams = new double[tMod.rayParams.length];
System.arraycopy(tMod.rayParams,
0,
rayParams,
0,
tMod.rayParams.length);
} else if(maxRayParamIndex == minRayParamIndex) {
if(name.indexOf("Sdiff") != -1 || name.indexOf("Pdiff") != -1) {
rayParams = new double[2];
rayParams[0] = minRayParam;
rayParams[1] = minRayParam;
} else if(name.indexOf("Pn") != -1 || name.indexOf("Sn") != -1) {
rayParams = new double[2];
rayParams[0] = minRayParam;
rayParams[1] = minRayParam;
} else if(name.endsWith("kmps")) {
rayParams = new double[2];
rayParams[0] = 0;
rayParams[1] = maxRayParam;
} else {
rayParams = new double[2];
rayParams[0] = minRayParam;
rayParams[1] = minRayParam;
}
} else {
if(DEBUG) {
System.out.println("SumBranches() maxRayParamIndex=" + maxRayParamIndex
+ " minRayParamIndex=" + minRayParamIndex
+ " tMod.rayParams.length=" + tMod.rayParams.length
+ " tMod.rayParams[0]=" + tMod.rayParams[0]
+"\n"
+ " tMod.rayParams["+minRayParamIndex+"]=" + tMod.rayParams[minRayParamIndex]
+"\n"
+ " tMod.rayParams["+maxRayParamIndex+"]=" + tMod.rayParams[maxRayParamIndex]
+ " maxRayParam=" + maxRayParam);
}
// only a subset of ray parameters are valid so only use those
rayParams = new double[minRayParamIndex - maxRayParamIndex + 1];
System.arraycopy(tMod.rayParams,
maxRayParamIndex,
rayParams,
0,
minRayParamIndex - maxRayParamIndex + 1);
}
dist = new double[rayParams.length];
time = new double[rayParams.length];
/* counter for passes through each branch. 0 is P and 1 is S. */
int[][] timesBranches = calcBranchMultiplier();
/* Sum the branches with the appropriate multiplier. */
for(int j = 0; j < tMod.getNumBranches(); j++) {
if(timesBranches[0][j] != 0) {
for(int i = maxRayParamIndex; i < minRayParamIndex + 1; i++) {
dist[i - maxRayParamIndex] += timesBranches[0][j]
* tMod.getTauBranch(j, PWAVE).getDist(i);
time[i - maxRayParamIndex] += timesBranches[0][j]
* tMod.getTauBranch(j, PWAVE).time[i];
}
}
if(timesBranches[1][j] != 0) {
for(int i = maxRayParamIndex; i < minRayParamIndex + 1; i++) {
dist[i - maxRayParamIndex] += timesBranches[1][j]
* tMod.getTauBranch(j, SWAVE).getDist(i);
time[i - maxRayParamIndex] += timesBranches[1][j]
* tMod.getTauBranch(j, SWAVE).time[i];
}
}
}
if(name.indexOf("Sdiff") != -1 || name.indexOf("Pdiff") != -1 ) {
if(tMod.cmbDepth == tMod.radiusOfEarth || tMod.getSlownessModel()
.depthInHighSlowness(tMod.cmbDepth - 1e-10,
minRayParam,
(name.charAt(0) == 'P'))) {
/*
* No diffraction if cmb is zero radius or there is a high slowness zone at the CMB.
*/
minRayParam = -1;
maxRayParam = -1;
maxDistance = -1;
dist = new double[0];
time = new double[0];
rayParams = new double[0];
return;
} else {
dist[1] = dist[0] + getMaxDiffraction() * Math.PI / 180.0;
time[1] = time[0] + getMaxDiffraction() * Math.PI / 180.0
* minRayParam;
}
} else if(name.indexOf("Pn") != -1 || name.indexOf("Sn") != -1) {
dist[1] = dist[0] + maxRefraction * Math.PI / 180.0;
time[1] = time[0] + maxRefraction * Math.PI / 180.0 * minRayParam;
} else if(maxRayParamIndex == minRayParamIndex) {
dist[1] = dist[0];
time[1] = time[0];
}
minDistance = Double.MAX_VALUE;
maxDistance = 0.0;
for(int j = 0; j < dist.length; j++) {
if(dist[j] < minDistance) {
minDistance = dist[j];
}
if(dist[j] > maxDistance) {
maxDistance = dist[j];
}
}
/*
* Now check to see if our ray parameter range includes any ray
* parameters that are associated with high slowness zones. If so, then
* we will need to insert a "shadow zone" into our time and distance
* arrays. It is represented by a repeated ray parameter.
*/
DepthRange[] hsz;
int hSZIndex;
int indexOffset;
boolean foundOverlap = false;
boolean isPWave;
int branchNum;
int dummy;
for(dummy = 0, isPWave = true; dummy < 2; dummy++, isPWave = false) {
hsz = tMod.getSlownessModel().getHighSlowness(isPWave);
hSZIndex = 0;
indexOffset = 0;
for(int i = 0; i < hsz.length; i++) {
if(maxRayParam > hsz[i].rayParam
&& hsz[i].rayParam > minRayParam) {
/*
* There is a high slowness zone within our ray parameter
* range so we might need to add a shadow zone. We need to
* check to see if this wave type, P or S, is part of the
* phase at this depth/ray parameter.
*/
branchNum = tMod.findBranch(hsz[i].topDepth);
foundOverlap = false;
for(int legNum = 0; legNum < branchSeq.size(); legNum++) {
// check for downgoing legs that cross the high slowness
// zone
// with the same wave type
if(((Integer)branchSeq.get(legNum)).intValue() == branchNum
&& ((Boolean)waveType.get(legNum)).booleanValue() == isPWave
&& ((Boolean)downGoing.get(legNum)).booleanValue() == true
&& ((Integer)branchSeq.get(legNum - 1)).intValue() == branchNum - 1
&& ((Boolean)waveType.get(legNum - 1)).booleanValue() == isPWave
&& ((Boolean)downGoing.get(legNum - 1)).booleanValue() == true) {
foundOverlap = true;
break;
}
}
if(foundOverlap) {
double[] newdist = new double[dist.length + 1];
double[] newtime = new double[time.length + 1];
double[] newrayParams = new double[rayParams.length + 1];
for(int j = 0; j < rayParams.length; j++) {
if(rayParams[j] == hsz[i].rayParam) {
hSZIndex = j;
break;
}
}
System.arraycopy(dist, 0, newdist, 0, hSZIndex);
System.arraycopy(time, 0, newtime, 0, hSZIndex);
System.arraycopy(rayParams,
0,
newrayParams,
0,
hSZIndex);
newrayParams[hSZIndex] = hsz[i].rayParam;
/* Sum the branches with the appropriate multiplier. */
newdist[hSZIndex] = 0.0;
newtime[hSZIndex] = 0.0;
for(int j = 0; j < tMod.getNumBranches(); j++) {
if(timesBranches[0][j] != 0
&& tMod.getTauBranch(j, PWAVE)
.getTopDepth() < hsz[i].topDepth) {
newdist[hSZIndex] += timesBranches[0][j]
* tMod.getTauBranch(j, PWAVE).dist[maxRayParamIndex
+ hSZIndex - indexOffset];
newtime[hSZIndex] += timesBranches[0][j]
* tMod.getTauBranch(j, PWAVE).time[maxRayParamIndex
+ hSZIndex - indexOffset];
}
if(timesBranches[1][j] != 0
&& tMod.getTauBranch(j, SWAVE)
.getTopDepth() < hsz[i].topDepth) {
newdist[hSZIndex] += timesBranches[1][j]
* tMod.getTauBranch(j, SWAVE).dist[maxRayParamIndex
+ hSZIndex - indexOffset];
newtime[hSZIndex] += timesBranches[1][j]
* tMod.getTauBranch(j, SWAVE).time[maxRayParamIndex
+ hSZIndex - indexOffset];
}
}
System.arraycopy(dist,
hSZIndex,
newdist,
hSZIndex + 1,
dist.length - hSZIndex);
System.arraycopy(time,
hSZIndex,
newtime,
hSZIndex + 1,
time.length - hSZIndex);
System.arraycopy(rayParams,
hSZIndex,
newrayParams,
hSZIndex + 1,
rayParams.length - hSZIndex);
indexOffset++;
dist = newdist;
time = newtime;
rayParams = newrayParams;
}
}
}
}
}
/**
* Calculates the "pierce points" for the arrivals stored in arrivals. The
* pierce points are stored within each arrival object.
* @deprecated Use the getPierce() method on each Arrival from calcTime()
*/
@Deprecated
public List calcPierce(double deg) throws TauModelException {
List arrivals = calcTime(deg);
for (Arrival a : arrivals) {
a.getPierce(); // side effect calc pierce
}
return arrivals;
}
/** Calculates the pierce points for a particular arrival. The returned arrival is the same
* as the input arguement but now has the pierce points filled in.
* @param currArrival
* @return same arrival with pierce points
* @deprecated Use the getPierce() method on each Arrival from calcTime()
*/
@Deprecated
public Arrival calcPierce(Arrival currArrival) {
currArrival.getPierce();
return currArrival;
}
protected List calcPierceTimeDist(Arrival currArrival) {
double branchDist = 0.0;
double branchTime = 0.0;
double prevBranchTime = 0.0;
List pierce = new ArrayList();
/*
* Find the ray parameter index that corresponds to the arrival ray
* parameter in the TauModel, ie it is between rayNum and rayNum+1,
* We know that it must be = currArrival.getRayParam()) {
rayNum = i;
} else {
break;
}
}
// here we use ray parameter and dist info stored within the
// SeismicPhase so we can use currArrival.rayParamIndex, which
// may not correspond to rayNum (for tMod.rayParams).
double distRayParam;
double distA;
double distB;
double distRatio;
if (currArrival.getRayParamIndex() == rayParams.length-1) {
// special case for exactly matching last ray param (often rayparam==0)
distRayParam = rayParams[currArrival.getRayParamIndex()];
distA = dist[currArrival.getRayParamIndex()];
distB = dist[currArrival.getRayParamIndex()];
distRatio = 1;
} else {
// normal case, in middle of ray param space
double rayParamA = rayParams[currArrival.getRayParamIndex()];
double rayParamB = rayParams[currArrival.getRayParamIndex() + 1];
distA = dist[currArrival.getRayParamIndex()];
distB = dist[currArrival.getRayParamIndex() + 1];
distRatio = (currArrival.getDist() - distA) / (distB - distA);
distRayParam = distRatio * (rayParamB - rayParamA) + rayParamA;
}
/* First pierce point is always 0 distance at the source depth. */
pierce.add(new TimeDist(distRayParam,
0.0,
0.0,
tMod.getSourceDepth()));
/*
* Loop from 0 but already done 0, so the pierce point when the ray
* leaves branch i is stored in i+1. Use linear interpolation
* between rays that we know.
*/
for(int i = 0; i < branchSeq.size(); i++) {
int branchNum = ((Integer)branchSeq.get(i)).intValue();
boolean isPWave = ((Boolean)waveType.get(i)).booleanValue();
if(DEBUG) {
System.out.println(i + " branchNum =" + branchNum
+ " downGoing=" + (Boolean)downGoing.get(i)
+ " isPWave=" + isPWave);
}
/*
* Save the turning depths for the ray parameter for both P and
* S waves. This way we get the depth correct for any rays that
* turn within a layer. We have to do this on a per branch basis
* because of converted phases, e.g. SKS.
*/
double turnDepth;
try {
if(distRayParam > tMod.getTauBranch(branchNum, isPWave)
.getMaxRayParam()) {
turnDepth = tMod.getTauBranch(branchNum, isPWave)
.getTopDepth();
} else if(distRayParam <= tMod.getTauBranch(branchNum,
isPWave)
.getMinRayParam()) {
turnDepth = tMod.getTauBranch(branchNum, isPWave)
.getBotDepth();
} else {
if(isPWave
|| tMod.getSlownessModel()
.depthInFluid((tMod.getTauBranch(branchNum,
isPWave)
.getTopDepth() + tMod.getTauBranch(branchNum,
isPWave)
.getBotDepth()) / 2.0)) {
turnDepth = tMod.getSlownessModel()
.findDepth(distRayParam,
tMod.getTauBranch(branchNum,
isPWave)
.getTopDepth(),
tMod.getTauBranch(branchNum,
isPWave)
.getBotDepth(),
PWAVE);
} else {
turnDepth = tMod.getSlownessModel()
.findDepth(distRayParam,
tMod.getTauBranch(branchNum,
isPWave)
.getTopDepth(),
tMod.getTauBranch(branchNum,
isPWave)
.getBotDepth(),
isPWave);
}
}
} catch(SlownessModelException e) {
// shouldn't happen but...
throw new RuntimeException("SeismicPhase.calcPierce: Caught SlownessModelException. "
, e);
}
double timeA, timeB;
if(name.indexOf("Pdiff") != -1 || name.indexOf("Pn") != -1
|| name.indexOf("Sdiff") != -1
|| name.indexOf("Sn") != -1) {
/* head waves and diffracted waves are a special case. */
distA = tMod.getTauBranch(branchNum, isPWave)
.getDist(rayNum);
timeA = tMod.getTauBranch(branchNum, isPWave).time[rayNum];
distB = tMod.getTauBranch(branchNum, isPWave)
.getDist(rayNum);
timeB = tMod.getTauBranch(branchNum, isPWave).time[rayNum];
} else {
distA = tMod.getTauBranch(branchNum, isPWave)
.getDist(rayNum);
timeA = tMod.getTauBranch(branchNum, isPWave).time[rayNum];
distB = tMod.getTauBranch(branchNum, isPWave)
.getDist(rayNum + 1);
timeB = tMod.getTauBranch(branchNum, isPWave).time[rayNum + 1];
}
branchDist += distRatio * (distB - distA) + distA;
prevBranchTime = branchTime;
branchTime += distRatio * (timeB - timeA) + timeA;
double branchDepth;
if(((Boolean)downGoing.get(i)).booleanValue()) {
branchDepth = Math.min(tMod.getTauBranch(branchNum, isPWave)
.getBotDepth(),
turnDepth);
} else {
branchDepth = Math.min(tMod.getTauBranch(branchNum, isPWave)
.getTopDepth(),
turnDepth);
}
// make sure ray actually propagates in this branch, leave
// a little room for numerical "chatter"
if(Math.abs(prevBranchTime - branchTime) > 1e-10) {
pierce.add(new TimeDist(distRayParam,
branchTime,
branchDist,
branchDepth));
if(DEBUG) {
System.out.println(" branchTime=" + branchTime
+ " branchDist=" + branchDist + " branchDepth="
+ branchDepth);
System.out.println("incrementTime = "
+ (distRatio * (timeB - timeA)) + " timeB="
+ timeB + " timeA=" + timeA);
}
}
for(int diffBranchIdx = 0; diffBranchIdx < headOrDiffractSeq.size(); diffBranchIdx++) {
int diffBranchNum = headOrDiffractSeq.get(diffBranchIdx);
if (DEBUG) {
System.out.println("diff check: "+diffBranchNum+" "+i + " branchNum =" + branchNum
+ " downGoing=" + (Boolean)downGoing.get(i)
+ " isPWave=" + isPWave);
}
if (i == diffBranchNum) {
double refractDist = (currArrival.getDist() - dist[0]) / headOrDiffractSeq.size();
double refractTime = (refractDist*currArrival.getRayParam()) / headOrDiffractSeq.size();
pierce.add(new TimeDist(distRayParam,
branchTime + refractTime,
branchDist + refractDist,
branchDepth));
branchDist += refractDist;
prevBranchTime = branchTime;
branchTime += refractTime;
}
}
}
if(name.indexOf("kmps") != -1) {
pierce.add(new TimeDist(distRayParam,
currArrival.getTime(),
currArrival.getDist(),
0));
}
return pierce;
}
/** calculates the paths this phase takes through the earth model.
* @deprecated Use the getPath() method on each Arrival from calcTime()
*/
@Deprecated
public List calcPath(double deg) {
List arrivals = calcTime(deg);
for (Arrival a : arrivals) {
a.getPath(); // side effect calculates path
}
return arrivals;
}
/**
*
* @param currArrival
* @return
* @deprecated use the getPath() method on the arrival.
*/
@Deprecated
public Arrival calcPath(Arrival currArrival) {
currArrival.getPath(); // side effect calculates path
return currArrival;
}
protected List calcPathTimeDist(Arrival currArrival) {
ArrayList pathList = new ArrayList();
/*
* Find the ray parameter index that corresponds to the arrival ray
* parameter in the TauModel, ie it is between rayNum and rayNum+1.
*/
TimeDist[] tempTimeDist = new TimeDist[1];
tempTimeDist[0] = new TimeDist(currArrival.getRayParam(),
0.0,
0.0,
tMod.getSourceDepth());
pathList.add(tempTimeDist);
for(int i = 0; i < branchSeq.size(); i++) {
int branchNum = ((Integer)branchSeq.get(i)).intValue();
boolean isPWave = ((Boolean)waveType.get(i)).booleanValue();
if(DEBUG) {
System.out.println("i=" + i + " branchNum=" + branchNum
+ " isPWave=" + isPWave + " downgoing="
+ ((Boolean)downGoing.get(i)).booleanValue());
}
try {
tempTimeDist = tMod.getTauBranch(branchNum, isPWave)
.path(currArrival.getRayParam(),
((Boolean)downGoing.get(i)).booleanValue(),
tMod.getSlownessModel());
} catch(SlownessModelException e) {
// shouldn't happen but...
throw new RuntimeException("SeismicPhase.calcPath: Caught SlownessModelException. "
, e);
}
if(tempTimeDist != null) {
pathList.add(tempTimeDist);
for (int j = 0; j < tempTimeDist.length; j++) {
if (tempTimeDist[j].getDistDeg() < 0) {
throw new RuntimeException("Path is backtracking, no possible: "+j+" ("+tempTimeDist[j]+")");
}
}
}
/*
* Here we worry about the special case for head and
* diffracted waves.
*/
for(int diffBranchIdx = 0; diffBranchIdx < headOrDiffractSeq.size(); diffBranchIdx++) {
int diffBranchNum = headOrDiffractSeq.get(diffBranchIdx);
if (DEBUG) {
System.out.println("diff check: "+diffBranchNum+" "+i + " branchNum =" + branchNum
+ " downGoing=" + (Boolean)downGoing.get(i)
+ " isPWave=" + isPWave);
}
if (i == diffBranchNum) {
double refractDist = (currArrival.getDist() - dist[0]) / headOrDiffractSeq.size();
double refractTime = (refractDist*currArrival.getRayParam()) / headOrDiffractSeq.size();
TimeDist[] diffTD = new TimeDist[1];
if (name.indexOf("Pdiff") != -1 || name.indexOf("Sdiff") != -1) {
diffTD[0] = new TimeDist(currArrival.getRayParam(),
refractTime,
refractDist,
tMod.cmbDepth);
} else if (name.indexOf("Pn") != -1 || name.indexOf("Sn") != -1) {
diffTD[0] = new TimeDist(currArrival.getRayParam(),
refractTime,
refractDist,
tMod.mohoDepth);
} else {
throw new RuntimeException("Path adding head and diffracted wave, but did not find P/Sdiff or P/Sn, expected: "+headOrDiffractSeq.size());
}
pathList.add(diffTD);
}
}
}
if (name.indexOf("kmps") != -1) {
// kmps phases have no branches, so need to end them at the arrival distance
TimeDist[] headTD = new TimeDist[1];
headTD[0] = new TimeDist(currArrival.getRayParam(),
currArrival.getDist()
* currArrival.getRayParam(),
currArrival.getDist(),
0);
pathList.add(headTD);
}
List outPath = new ArrayList();
TimeDist cummulative = new TimeDist(currArrival.getRayParam(),
0.0,
0.0,
currArrival.getSourceDepth());
TimeDist prev = cummulative;
TimeDist[] branchPath;
int numAdded = 0;
for(int i = 0; i < pathList.size(); i++) {
branchPath = (TimeDist[])pathList.get(i);
for(int j = 0; j < branchPath.length; j++) {
prev = cummulative;
cummulative = new TimeDist(cummulative.getP(),
cummulative.getTime()+branchPath[j].getTime(),
cummulative.getDistRadian()+branchPath[j].getDistRadian(),
branchPath[j].getDepth());
outPath.add(cummulative);
if (numAdded > 0 && cummulative.getDistRadian() < prev.getDistRadian()) {
throw new RuntimeException("Backtracking ray, not possible: "+numAdded+" "+cummulative+") < ("+prev+")");
}
numAdded++;
}
}
return outPath;
}
private String validationFailMessage = "";
public String getValidationFailMessage() {
return validationFailMessage;
}
/**
* Performs consistency checks on the previously tokenized phase name stored
* in legs. Returns null if all is ok, a message if there is a problem.
*/
public static String phaseValidate(ArrayList legs) {
String currToken = (String)legs.get(0);
String prevToken;
String nextToken = "";
boolean prevIsReflect = false;
/* Special cases for diffracted waves. */
if(legs.size() == 2
&& (currToken.equals("Pdiff") || currToken.equals("Sdiff") || currToken.endsWith("kmps"))
&& ((String)legs.get(1)).equals("END")) {
return null;
}
/* Check first leg. */
if(!(currToken.equals("Pg") || currToken.equals("Pb")
|| currToken.equals("Pn") || currToken.equals("Pdiff")
|| currToken.equals("Sg") || currToken.equals("Sb")
|| currToken.equals("Sn") || currToken.equals("Sdiff")
|| currToken.equals("Ped") || currToken.equals("Sed")
|| currToken.equals("P") || currToken.equals("S")
|| currToken.equals("p") || currToken.equals("s") || (expert && (currToken.equals("K")
|| currToken.equals("k") || currToken.equals("I"))))) {
String validationFailMessage = "First leg ("
+ currToken
+ ") must be one of Pg, Pb, Pn, Pdiff, Sg, Sb, Sn, Sdiff, P, S, p, s";
if(expert) {
validationFailMessage += ", K, k, I";
}
return validationFailMessage;
}
for(int i = 1; i < legs.size(); i++) {
prevToken = currToken;
currToken = legs.get(i);
if (i < legs.size()-1) {
nextToken = legs.get(i+1);
} else {
nextToken = "";
}
/* Check for 2 reflections/depths with no leg between them. */
if(currToken.startsWith("^") || currToken.startsWith("v")
|| currToken.equals("m") || currToken.equals("c")
|| currToken.equals("i")) {
if(prevIsReflect) {
return "Two reflections or depths with no leg in between: "
+ prevToken + ", " + currToken;
} else {
prevIsReflect = true;
}
} else {
prevIsReflect = false;
}
/* Check for "END" before the end. */
if(prevToken.equals("END")) {
return "Legs ended but more tokens exist: " + currToken;
}
/* Check for ! not second to last token */
if ((prevToken.equals("Ped") || prevToken.equals("Sed"))
&& ! ( currToken.equals("END")
|| currToken.equals("Pdiff") || currToken.equals("Sdiff")
|| currToken.equals("P") || currToken.equals("S")
|| currToken.equals("K") || currToken.startsWith("v") || currToken.startsWith("V")
|| currToken.equals("c") || currToken.equals("m") )) {
return "'Ped' or 'Sed' can only be before Pdiff,P,S,Sdiff,K,c,v,V,m or second to last token immediately before END or ";
}
// Cannot have K before P,S and followed by another K as P,S leg must turn to get back to CMB
if((prevToken.startsWith("k") || prevToken.startsWith("K"))
&& (currToken.startsWith("P") || currToken.startsWith("S") || currToken.startsWith("p") || currToken.startsWith("s"))
&& (nextToken.startsWith("k") || nextToken.startsWith("K"))) {
return "Cannot have P,S,p,s preceeded and followed by K,k: "
+ prevToken + ", " + currToken +", "+nextToken;
}
// Cannot have I,J before K and followed by another I,J as K leg must turn to get back to IOCB
if((prevToken.startsWith("I") || prevToken.startsWith("J") )
&& (currToken.startsWith("K") || currToken.startsWith("k"))
&& (nextToken.startsWith("I") || nextToken.startsWith("J"))) {
return "Cannot have K,k preceeded and followed by I,J: "
+ prevToken + ", " + currToken +", "+nextToken;
}
// Cannot have p,s before I, i, or J
if((prevToken.startsWith("p") || prevToken.startsWith("s")
|| prevToken.equals("m") || prevToken.equals("c"))
&& (currToken.equals("I") || currToken.equals("J") || currToken.equals("i"))) {
return "Cannot have P,S,p,s,m,c followed by I,J,i: "
+ prevToken + ", " + currToken;
}
// Cannot have m,c after I, i, or J
if((prevToken.equals("I") || prevToken.equals("J") || prevToken.equals("i"))
&& (currToken.equals("m") || currToken.equals("c"))) {
return "Cannot have I,J,i followed by m,c: "
+ prevToken + ", " + currToken;
}
/* Check for m, c before K. */
if((prevToken.equals("m") || prevToken.equals("c") )
&& (currToken.equals("K") || currToken.equals("I") || currToken.equals("J") || currToken.equals("i"))) {
return "Cannot have m,c followed by K,I,i,J";
}
if((currToken.equals("m") || currToken.equals("c"))
&& (prevToken.equals("K") || prevToken.equals("I") || prevToken.equals("J") || prevToken.equals("i"))) {
return "Cannot have K,I,i,J followed by m,c";
}
}
/* Make sure legs end in "END". */
if(!currToken.equals("END")) {
return "Last token must be END";
}
return null;
}
public static Arrival getEarliestArrival(List phases, double degrees) {
Arrival minArrival = null;
for (SeismicPhase seismicPhase : phases) {
seismicPhase.calcTime(degrees);
Arrival currArrival = seismicPhase.getEarliestArrival(degrees);
if (currArrival != null && ( minArrival == null || minArrival.getTime() > currArrival.getTime())) {
minArrival = currArrival;
}
}
return minArrival;
}
public String describe( ) {
String desc = name + ":\n";
if (phasesExistsInModel()) {
desc += " exists from "+Outputs.formatDistance(getMinDistanceDeg())+" to "+Outputs.formatDistance(getMaxDistanceDeg())+" degrees.\n";
if (getMaxRayParam() > getMinRayParam()) {
desc += " with ray parameter from "+Outputs.formatRayParam(getMaxRayParam())+" down to "+Outputs.formatRayParam(getMinRayParam())+" sec/rad.\n";
} else {
desc += " with degenerate ray parameter of "+Outputs.formatRayParam(getMaxRayParam())+"\n";
}
desc += " travel times from "+Outputs.formatTime(time[0])+" to "+Outputs.formatTime(time[time.length-1])+" sec.\n";
} else {
desc += " FAILS to exist, because no ray parameters satisfy the path.\n";
}
for(SeismicPhaseSegment segment : getPhaseSegments()) {
desc += segment.toString()+"\n";
}
return desc;
}
public String toString() {
String desc = name + ": ";
for(int i = 0; i < legs.size(); i++) {
desc += legs.get(i) + " ";
}
desc += "\n";
for(int i = 0; i < branchSeq.size(); i++) {
desc += (Integer)branchSeq.get(i) + " ";
}
desc += "\n";
desc += "minRayParam=" + minRayParam + " maxRayParam=" + maxRayParam;
desc += "\n";
desc += "minDistance=" + (minDistance * 180.0 / Math.PI)
+ " maxDistance=" + (maxDistance * 180.0 / Math.PI);
return desc;
}
public void dump() {
for(int j = 0; j < dist.length; j++) {
System.out.println(j + " " + dist[j] + " " + rayParams[j]);
}
}
public static void main(String args[]) {
TauModel tMod;
TauModel tModDepth;
try {
if(args.length < 3) {
System.out.println("Usage: SeismicPhase modelfile depth phasename [phasename ...]");
}
tMod = TauModel.readModel(args[0]);
tModDepth = tMod.depthCorrect(Double.valueOf(args[1]).doubleValue());
for(int i = 2; i < args.length; i++) {
System.out.println("-----");
SeismicPhase sp = new SeismicPhase(args[i], tModDepth);
System.out.println(sp);
sp.dump();
}
System.out.println("-----");
} catch(FileNotFoundException e) {
System.out.println(e.getMessage());
} catch(OptionalDataException e) {
System.out.println(e.getMessage());
} catch(StreamCorruptedException e) {
System.out.println(e.getMessage());
} catch(IOException e) {
System.out.println(e.getMessage());
} catch(ClassNotFoundException e) {
System.out.println(e.getMessage());
} catch(TauModelException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}