/** * @param gridWinds * @param grid * @return * @throws IllegalArgumentException if the grid is irregular or has too few points or has no * {@link visad.CoordinateSystem}, or if the reference of the grid's {@link * visad.CoordinateSystem} doesn't contain {@link visad.RealType.Latitude} and {@link * visad.RealType.Longitude}. * @throws VisADException */ private static float[][] trueWind(float[][] gridWinds, SampledSet grid) throws VisADException { if (grid instanceof IrregularSet) { throw new IllegalArgumentException(grid.toString()); } CoordinateSystem cs = grid.getCoordinateSystem(); boolean hasCS = (cs != null); RealTupleType rtt = (hasCS) ? cs.getReference() : ((SetType) grid.getType()).getDomain(); int latI = rtt.getIndex(RealType.Latitude); if (latI == -1) { throw new IllegalArgumentException(rtt.toString()); } int lonI = rtt.getIndex(RealType.Longitude); if (lonI == -1) { throw new IllegalArgumentException(rtt.toString()); } if (grid.getManifoldDimension() < 2) { throw new IllegalArgumentException(grid.toString()); } float[] us = new float[grid.getLength()]; float[] vs = new float[grid.getLength()]; Arrays.fill(us, 0); Arrays.fill(vs, 0); addComponent(grid, gridWinds, cs, 0, latI, lonI, us, vs); addComponent(grid, gridWinds, cs, 1, latI, lonI, us, vs); return new float[][] {us, vs}; }
/** * Sets the input, buoyancy profile. * * @param buoyProfile The input, buoyancy profile. * @throws TypeException if the domain quantity isn't pressure or the range quantity isn't volume * per mass. * @throws VisADException if a VisAD failure occurs. * @throws RemoteException if a Java RMI failure occurs. */ public void setBuoyancyProfile(Field buoyProfile) throws TypeException, VisADException, RemoteException { FunctionType funcType = (FunctionType) buoyProfile.getType(); RealTupleType domainType = funcType.getDomain(); if (!Pressure.getRealType().equalsExceptNameButUnits(domainType)) { throw new TypeException(domainType.toString()); } MathType rangeType = funcType.getRange(); if (!CapeBean.massicVolume.equalsExceptNameButUnits(rangeType)) { throw new TypeException(rangeType.toString()); } this.buoyProfile = buoyProfile; }
/** * The returned {@link visad.FlatField} will have NaN-s for those unit vector components that * could not be computed. * * @param grid The spatial grid. * @param index The index of the manifold dimension along which to compute the unit vector. * @return A field of components of the unit vector for the given manifold dimension. * @throws NullPointerException if the grid is <code>null</code>. * @throws IllegalArgumentException if the manifold dimension of the grid is less than 2 or if the * grid doesn't contain {@link visad.RealType#Latitude} and {@link visad.RealType#Longitude}. * @throws VisADException if a VisAD failure occurs. * @throws RemoteException if a Java RMI failure occurs. */ private static FlatField hatFieldNew(Set grid, int index) throws VisADException, RemoteException { CoordinateSystem cs = grid.getCoordinateSystem(); boolean hasCS = cs != null; RealTupleType rtt = (hasCS) ? cs.getReference() : ((SetType) grid.getType()).getDomain(); int latI = rtt.getIndex(RealType.Latitude); if (latI == -1) { throw new IllegalArgumentException(rtt.toString()); } int lonI = rtt.getIndex(RealType.Longitude); if (lonI == -1) { throw new IllegalArgumentException(rtt.toString()); } if (grid.getManifoldDimension() < 2) { throw new IllegalArgumentException(grid.toString()); } int[][] neighbors = grid.getNeighbors(index); LatLonPointImpl refPt = new LatLonPointImpl(); LatLonPointImpl neiPt = new LatLonPointImpl(); Bearing bearing = new Bearing(); float[] hat1 = new float[2]; float[] hat2 = new float[2]; float[][] hat = new float[2][grid.getLength()]; float[][] refCoords = null; float[][] neiCoords = null; float[][] domainSamples = grid.getSamples(false); refCoords = (hasCS) ? cs.toReference(Set.copyFloats(domainSamples)) : domainSamples; // If the grid is lat/lon or has an IdentityCoordinateSystem // don't do the rotation // TODO: handle rotated lat/lon grids if (!hasCS || (refCoords == domainSamples) || (Arrays.equals(refCoords[latI], domainSamples[latI]) && Arrays.equals(refCoords[lonI], domainSamples[lonI]))) { if (index == 0) { Arrays.fill(hat[0], 1); Arrays.fill(hat[1], 0); } else { Arrays.fill(hat[0], 0); Arrays.fill(hat[1], 1); } } else { float latBefore, lonBefore, latAfter, lonAfter; // int backOffset = (index==0) ? -180 : 0; // int foreOffset = (index==0) ? 0 : -180; int backOffset = -180; int foreOffset = 0; for (int i = 0; i < neighbors.length; i++) { refPt.set(refCoords[latI][i], refCoords[lonI][i]); if ((neighbors[i][0] < 0) || (neighbors[i][0] >= neighbors.length)) { latBefore = Float.NaN; lonBefore = Float.NaN; } else { latBefore = refCoords[latI][neighbors[i][0]]; lonBefore = refCoords[lonI][neighbors[i][0]]; } if ((neighbors[i][1] < 0) || (neighbors[i][1] >= neighbors.length)) { latAfter = Float.NaN; lonAfter = Float.NaN; } else { latAfter = refCoords[latI][neighbors[i][1]]; lonAfter = refCoords[lonI][neighbors[i][1]]; } compute(refPt, neiPt, latBefore, lonBefore, backOffset, bearing, hat1); float d1 = (float) bearing.getDistance(); compute(refPt, neiPt, latAfter, lonAfter, foreOffset, bearing, hat2); float d2 = (float) bearing.getDistance(); boolean bad1 = Double.isNaN(d1); boolean bad2 = Double.isNaN(d2); if (bad1 && bad2) { hat[0][i] = Float.NaN; hat[1][i] = Float.NaN; } else { if (bad1) { hat[0][i] = hat2[0]; hat[1][i] = hat2[1]; } else if (bad2) { hat[0][i] = hat1[0]; hat[1][i] = hat1[1]; } else { float tot = d1 + d2; float c1 = d2 / tot; float c2 = d1 / tot; float xhat = c1 * hat1[0] + c2 * hat2[0]; float yhat = c1 * hat1[1] + c2 * hat2[1]; float mag = (float) Math.sqrt(xhat * xhat + yhat * yhat); hat[0][i] = xhat / mag; hat[1][i] = yhat / mag; } } } } FlatField hatField = new FlatField( new FunctionType( ((SetType) grid.getType()).getDomain(), new RealTupleType( RealType.getRealType("xHat", CommonUnit.dimensionless), RealType.getRealType("yHat", CommonUnit.dimensionless))), grid); hatField.setSamples(hat, false); return hatField; }
/** * Converts a time-series of grid-relative winds to a time-series of true (or absolute) winds. The * U and V components of true wind are {@link WesterlyWind} and {@link SoutherlyWind}, * respectively. The domain of the input {@link visad.Field} must be a temporal {@link * visad.Gridded1DSet} or a {@link visad.SingletonSet}. The range values of the input {@link * visad.Field} must be {@link visad.FlatField}s. The domains of the range {@link * visad.FlatField}s must have a manifold dimension of two or greater and they must have a * reference system which contains {@link visad.RealType#Latitude} and {@link * visad.RealType#Longitude}. The number of components in the range of the {@link * visad.FlatField}s must be two. Both components must have units convertible with {@link * #DEFAULT_SPEED_UNIT}. The first and second components are assumed to be the wind components in * the direction of increasing first and second manifold dimension indexes, respectively. The * domains of the {@link visad.FlatField}s must be equal. The {@link visad.Field} returned by this * method has the same domain as the input {@link visad.Field}. The range values of the returned * {@link visad.Field} are {@link visad.FlatField}s that have the same domain as the input {@link * visad.FlatField}s. The {@link visad.MathType} of the range of the returned {@link * visad.FlatField}s will be <code>CartesianHorizontalWind.getEarthVectorType()</code>. * * @param rel The time-series of grid-relative wind. * @return The time-series of true wind corresponding to the input. * @throws NullPointerException if <code>rel</code> is <code>null</code>. * @throws IllegalArgumentException if the input field doesn't have a time-series domain, or if * the range values aren't {@link visad.FlatField} with the same domain, or if the domain of * the {@link visad.FlatField}s doesn't have a transformation to latitude and longitude, or if * the domain is irregular or has too few points, or if the {@link visad.FlatField}s don't * have two and only two components in their range, or if the default units of the {@link * visad.FlatField}s range aren't equal. * @throws VisADException if a VisAD failure occurs. * @throws RemoteException if a Java RMI failure occurs. * @see CartesianHorizontalWind */ public static Field timeSeriesCartesianHorizontalWind(Field rel) throws VisADException, RemoteException { FunctionType outerFuncType = (FunctionType) rel.getType(); RealTupleType outerDomType = outerFuncType.getDomain(); if (!(RealType.Time.equalsExceptNameButUnits(outerDomType) || !RealType.TimeInterval.equalsExceptNameButUnits(outerDomType))) { throw new IllegalArgumentException(outerDomType.toString()); } MathType innerFuncType = outerFuncType.getRange(); if (!(innerFuncType instanceof FunctionType)) { throw new IllegalArgumentException(innerFuncType.toString()); } Field innerField = (Field) rel.getSample(0); Set innerDom = innerField.getDomainSet(); if (innerDom instanceof SingletonSet) { return rel; } else if (innerDom instanceof GriddedSet) { int[] lengths = ((GriddedSet) innerDom).getLengths(); if ((lengths[0] == 1) && (lengths[1] == 1)) { return rel; } } // account for null units, assume m/sec Unit[] rangeUnits = innerField.getDefaultRangeUnits(); if ((rangeUnits == null) || (rangeUnits[0] == null) || rangeUnits[0].isDimensionless()) { rangeUnits = CartesianHorizontalWind.getEarthVectorType().getDefaultUnits(); } FunctionType innerType = new FunctionType( ((SetType) innerDom.getType()).getDomain(), CartesianHorizontalWind.getEarthVectorType()); FlatField uvField = new FlatField(innerType, innerDom, (CoordinateSystem) null, (Set[]) null, rangeUnits); Field result = new FieldImpl(new FunctionType(outerDomType, uvField.getType()), rel.getDomainSet()); // System.out.println("making rHatField"); Field rHatField = (doNewCode ? hatFieldNew(innerDom, 0) : hatFieldOld(innerDom, 0)); // System.out.println("making sHatField"); Field sHatField = (doNewCode ? hatFieldNew(innerDom, 1) : hatFieldOld(innerDom, 1)); float[][] rHats = rHatField.getFloats(false); // ucar.unidata.util.Misc.printArray("rHats[0]", rHats[0]); // ucar.unidata.util.Misc.printArray("rHats[1]", rHats[1]); // System.out.println("\n"); float[][] sHats = sHatField.getFloats(false); // ucar.unidata.util.Misc.printArray("sHats[0]", sHats[0]); // ucar.unidata.util.Misc.printArray("sHats[1]", sHats[1]); // System.out.println("\n"); float[] us = new float[innerDom.getLength()]; float[] vs = new float[us.length]; for (int i = 0, n = rel.getLength(); i < n; i++) { if (i > 0) { innerField = (Field) rel.getSample(i); Set dom = innerField.getDomainSet(); if (!innerDom.equals(dom)) { // System.out.println("new domain"); innerDom = dom; rHatField = (doNewCode ? hatFieldNew(innerDom, 0) : hatFieldOld(innerDom, 0)); sHatField = (doNewCode ? hatFieldNew(innerDom, 1) : hatFieldOld(innerDom, 1)); rHats = rHatField.getFloats(false); sHats = sHatField.getFloats(false); /* throw new IllegalArgumentException("template=" + innerDom.toString() + "; domain=" + dom.toString()); */ } uvField = new FlatField(innerType, innerDom, (CoordinateSystem) null, (Set[]) null, rangeUnits); us = new float[innerDom.getLength()]; vs = new float[us.length]; } float[][] rsWinds = innerField.getFloats(false); float[] rWinds = rsWinds[0]; float[] sWinds = rsWinds[1]; // ucar.unidata.util.Misc.printArray("rWinds", rWinds); // System.out.println("\n"); // ucar.unidata.util.Misc.printArray("sWinds", sWinds); // System.out.println("\n"); for (int j = 0; j < us.length; j++) { us[j] = rWinds[j] * rHats[0][j] + sWinds[j] * sHats[0][j]; vs[j] = rWinds[j] * rHats[1][j] + sWinds[j] * sHats[1][j]; } // ucar.unidata.util.Misc.printArray("us", us); // System.out.println("\n"); // ucar.unidata.util.Misc.printArray("vs", vs); // System.out.println("\n"); uvField.setSamples(new float[][] {us, vs}, false); result.setSample(i, uvField, false); } return result; }
/** * Computes the output Level of Free Convection (LFC) from an (AirPressure -> MassicVolume) * buoyancy profile. * * @param datums The input data in the same order as during construction: <code>datums[0] * </code> is the input buoyancy profile. * @return The pressure at the LFC of the buoyancy profile. * @throws ClassCastException if an input data reference has the wrong type of data object. * @throws TypeException if a VisAD data object has the wrong type. * @throws VisADException if a VisAD failure occurs. * @throws RemoteException if a Java RMI failure occurs. * @throws IllegalArgumentException if the profile is not ascending. */ protected Data compute(Data[] datums) throws TypeException, VisADException, RemoteException { Field buoyProfile = (Field) datums[0]; Real lfc = noData; // default return value if (buoyProfile != null) { FunctionType funcType = (FunctionType) buoyProfile.getType(); RealTupleType domainType = funcType.getDomain(); if (!Pressure.getRealType().equalsExceptNameButUnits(domainType)) { throw new TypeException(domainType.toString()); } MathType rangeType = funcType.getRange(); Util.vetType(MassicVolume.getRealType(), buoyProfile); Set domainSet = buoyProfile.getDomainSet(); double[] pressures = domainSet.getDoubles()[0]; float[] buoys = buoyProfile.getFloats()[0]; if (pressures.length > 1) { int lastI = pressures.length - 1; boolean ascending = pressures[0] >= pressures[lastI]; Unit presUnit = domainSet.getSetUnits()[0]; int i; if (ascending) { /* * For a level of free convection to exist, the lower * buoyancy must be negative. */ for (i = 0; (i < buoys.length) && (buoys[i] >= 0); i++) ; /* * To find the level of free convection, ascend to * positive buoyancy. */ while ((++i < buoys.length) && (buoys[i] <= 0)) ; if (i < buoys.length) { lfc = interpolatePres(pressures[i], buoys[i], pressures[i - 1], buoys[i - 1], presUnit); } } else { /* * For a level of free convection to exist, the lower * buoyancy must be negative. */ for (i = lastI; (i >= 0) && (buoys[i] >= 0); i--) ; /* * To find the level of free convection, ascend to * positive buoyancy. */ while ((--i >= 0) && (buoys[i] <= 0)) ; if (i >= 0) { lfc = interpolatePres(pressures[i], buoys[i], pressures[i + 1], buoys[i + 1], presUnit); } } } } return lfc; }