All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.ma2.MAMath Maven / Gradle / Ivy

Go to download

The NetCDF-Java Library is a Java interface to NetCDF files, as well as to many other types of scientific data formats.

There is a newer version: 4.3.22
Show newest version
/*
 * Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
 *
 * Portions of this software were developed by the Unidata Program at the
 * University Corporation for Atmospheric Research.
 *
 * Access and use of this software shall impose the following obligations
 * and understandings on the user. The user is granted the right, without
 * any fee or cost, to use, copy, modify, alter, enhance and distribute
 * this software, and any derivative works thereof, and its supporting
 * documentation for any purpose whatsoever, provided that this entire
 * notice appears in all copies of the software, derivative works and
 * supporting documentation.  Further, UCAR requests that the user credit
 * UCAR/Unidata in any publications that result from the use of this
 * software or in any product that includes this software. The names UCAR
 * and/or Unidata, however, may not be used in any advertising or publicity
 * to endorse or promote any products or commercial entity unless specific
 * written permission is obtained from UCAR/Unidata. The user also
 * understands that UCAR/Unidata is not obligated to provide the user with
 * any support, consulting, training or assistance of any kind with regard
 * to the use, operation and performance of this software nor to provide
 * the user with any updates, revisions, new versions or "bug fixes."
 *
 * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
 */
package ucar.ma2;

import ucar.nc2.util.Misc;

/**
 * Element by element algebra on Arrays
 *
 * @author caron
 * @see Index
 */
public class MAMath {

  /**
   * Add elements of two arrays together, allocating the result array.
   * The result type and the operation type are taken from the type of a.
   *
   * @param a add values from here
   * @param b add values from here
   * @return result = a + b
   * @throws IllegalArgumentException      a and b are not conformable
   * @throws UnsupportedOperationException dont support this data type yet
   */
  public static Array add(Array a, Array b) throws IllegalArgumentException {

    Array result = Array.factory(a.getElementType(), a.getShape());

    if (a.getElementType() == double.class) {
      addDouble(result, a, b);
    } else
      throw new UnsupportedOperationException();

    return result;
  }

  /**
   * Add elements of two arrays together as doubles, place sum in the result array.
   * The values from the arrays a and b are converted to double (if needed),
   * and the sum is converted to the type of result (if needed).
   *
   * @param result result array
   * @param a operand
   * @param b operand
   * @throws IllegalArgumentException a,b,and result are not conformable
   */
  public static void addDouble(Array result, Array a, Array b)
      throws IllegalArgumentException {

    if (!conformable(result, a) || !conformable(a, b))
      throw new IllegalArgumentException();

    IndexIterator iterR = result.getIndexIterator();
    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterB = b.getIndexIterator();

    while (iterA.hasNext())
      iterR.setDoubleNext(iterA.getDoubleNext() + iterB.getDoubleNext());
  }

  /**
   * Check that two arrays are conformable.
   *
   * @param a operand
   * @param b operand
   * @return true if conformable
   */
  public static boolean conformable(Array a, Array b) {
    return conformable(a.getShape(), b.getShape());
  }

  /**
   * Check that two array shapes are conformable.
   * The shapes must match exactly, except that dimensions of length 1 are ignored.
   *
   * @param shapeA shape of array 1
   * @param shapeB shape of array 2
   * @return true if conformable
   */
  public static boolean conformable(int[] shapeA, int[] shapeB) {
    if (reducedRank(shapeA) != reducedRank(shapeB))
      return false;

    int rankA = shapeA.length;
    int rankB = shapeB.length;

    int dimB = 0;
    for (int dimA = 0; dimA < rankA; dimA++) {
      //System.out.println(dimA + " "+ dimB);

      //skip length 1 dimensions
      if (shapeA[dimA] == 1)
        continue;
      while (dimB < rankB)
        if (shapeB[dimB] == 1) dimB++;
        else break;

      // test same shape (NB dimB cant be > rankB due to first test)
      if (shapeA[dimA] != shapeB[dimB])
        return false;
      dimB++;
    }

    return true;
  }

  /**
   * Convert unsigned data to signed data of a wider type.
   *
   * @param unsigned must be of type byte, short or int
   * @return converted data of type short, int, or long
   */
  public static Array convertUnsigned( Array unsigned) {
    if (unsigned.getElementType().equals(byte.class)) {
      Array result = Array.factory(DataType.SHORT, unsigned.getShape());
      IndexIterator ii = result.getIndexIterator();
      unsigned.resetLocalIterator();
      while (unsigned.hasNext())
        ii.setShortNext( DataType.unsignedByteToShort(unsigned.nextByte()));
      return result;

    } else if (unsigned.getElementType().equals(short.class)) {
      Array result = Array.factory(DataType.INT, unsigned.getShape());
      IndexIterator ii = result.getIndexIterator();
      unsigned.resetLocalIterator();
      while (unsigned.hasNext())
        ii.setIntNext( DataType.unsignedShortToInt(unsigned.nextShort()));
      return result;

    } else if (unsigned.getElementType().equals(int.class)) {
      Array result = Array.factory(DataType.LONG, unsigned.getShape());
      IndexIterator ii = result.getIndexIterator();
      unsigned.resetLocalIterator();
      while (unsigned.hasNext())
        ii.setLongNext( DataType.unsignedIntToLong(unsigned.nextInt()));
      return result;
    }

    throw new IllegalArgumentException("Cant convertUnsigned type= "+unsigned.getElementType());
  }

  /**
   * Convert original array to desired type
   *
   * @param org original array
   * @param wantType desired type
   * @return converted data of desired type, or original array if it is already
   */
  public static Array convert( Array org, DataType wantType) {
    if (org == null) return null;
    Class wantClass = wantType.getPrimitiveClassType();
    if (org.getElementType().equals(wantClass))
      return org;

    Array result = Array.factory(wantType, org.getShape());
    copy(wantType, org.getIndexIterator(), result.getIndexIterator());
    return result;
  }

  /**
   * Copy using iterators. Will copy until !from.hasNext().
   *
   * @param dataType use this operation type (eg DataType.DOUBLE uses getDoubleNext())
   * @param from     copy from here
   * @param to       copy to here
   * @throws IllegalArgumentException      a and b are not conformable
   * @throws UnsupportedOperationException dont support this data type
   */
  public static void copy(DataType dataType, IndexIterator from, IndexIterator to) throws IllegalArgumentException {
    if (dataType == DataType.DOUBLE) {
      while (from.hasNext())
        to.setDoubleNext(from.getDoubleNext());
    } else if (dataType == DataType.FLOAT) {
      while (from.hasNext())
        to.setFloatNext(from.getFloatNext());
    } else if (dataType == DataType.LONG) {
      while (from.hasNext())
        to.setLongNext(from.getLongNext());
    } else if ((dataType == DataType.INT) || (dataType == DataType.ENUM4)) {
      while (from.hasNext())
        to.setIntNext(from.getIntNext());
    } else if ((dataType == DataType.SHORT) || (dataType == DataType.ENUM2)) {
      while (from.hasNext())
        to.setShortNext(from.getShortNext());
    } else if (dataType == DataType.CHAR) {
      while (from.hasNext())
        to.setCharNext(from.getCharNext());
    } else if ((dataType == DataType.BYTE) || (dataType == DataType.ENUM1)) {
      while (from.hasNext())
        to.setByteNext(from.getByteNext());
    } else if (dataType == DataType.BOOLEAN) {
      while (from.hasNext())
        to.setBooleanNext(from.getBooleanNext());
    } else {
      while (from.hasNext())
        to.setObjectNext(from.getObjectNext());
    }
  }

  /**
   * Copy array a to array result, the result array will be in canonical order
   * The operation type is taken from the type of a.
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException      a and b are not conformable
   * @throws UnsupportedOperationException dont support this data type yet
   */
  public static void copy(Array result, Array a) throws IllegalArgumentException {
    Class classType = a.getElementType();
    if (classType == double.class) {
      copyDouble(result, a);
    } else if (classType == float.class) {
      copyFloat(result, a);
    } else if (classType == long.class) {
      copyLong(result, a);
    } else if (classType == int.class) {
      copyInt(result, a);
    } else if (classType == short.class) {
      copyShort(result, a);
    } else if (classType == char.class) {
      copyChar(result, a);
    } else if (classType == byte.class) {
      copyByte(result, a);
    } else if (classType == boolean.class) {
      copyBoolean(result, a);
    } else
      copyObject(result, a);
  }

  /**
   * copy array a to array result as doubles
   * The values from the arrays a are converted to double (if needed),
   * and then converted to the type of result (if needed).
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyDouble(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setDoubleNext(iterA.getDoubleNext());
  }

  /**
   * copy array a to array result as floats
   * The values from the arrays a are converted to float (if needed),
   * and then converted to the type of result (if needed).
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyFloat(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setFloatNext(iterA.getFloatNext());
  }

  /**
   * copy array a to array result as longs
   * The values from the array a are converted to long (if needed),
   * and then converted to the type of result (if needed).
   * @param result copy to here
   * @param a copy from here
   *
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyLong(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setLongNext(iterA.getLongNext());
  }


  /**
   * copy array a to array result as integers
   * The values from the arrays a are converted to integer (if needed),
   * and then converted to the type of result (if needed).
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyInt(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setIntNext(iterA.getIntNext());
  }

  /**
   * copy array a to array result as shorts
   * The values from the array a are converted to short (if needed),
   * and then converted to the type of result (if needed).
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyShort(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setShortNext(iterA.getShortNext());
  }

  /**
   * copy array a to array result as char
   * The values from the array a are converted to char (if needed),
   * and then converted to the type of result (if needed).
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyChar(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setCharNext(iterA.getCharNext());
  }


  /**
   * copy array a to array result as bytes
   * The values from the array a are converted to byte (if needed),
   * and then converted to the type of result (if needed).
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyByte(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setByteNext(iterA.getByteNext());
  }

  /**
   * copy array a to array result as bytes
   * The array a and result must be type boolean
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyBoolean(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setBooleanNext(iterA.getBooleanNext());
  }

  /**
   * copy array a to array result as an Object
   * The array a and result must be type object
   *
   * @param result copy to here
   * @param a copy from here
   *
   * @throws IllegalArgumentException a and result are not conformable
   */
  public static void copyObject(Array result, Array a) throws IllegalArgumentException {
    if (!conformable(a, result))
      throw new IllegalArgumentException("copy arrays are not conformable");

    IndexIterator iterA = a.getIndexIterator();
    IndexIterator iterR = result.getIndexIterator();
    while (iterA.hasNext())
      iterR.setObjectNext(iterA.getObjectNext());
  }

  /**
   * Calculate the reduced rank of this shape, by subtracting dimensions with length 1
   * @param shape shape of the array
   * @return rank without dimensions of length 1
   */
  public static int reducedRank(int[] shape) {
    int rank = 0;
    for (int ii = 0; ii < shape.length; ii++) {
      if (shape[ii] > 1)
        rank++;
    }
    return rank;
  }

  public static double getMinimum(Array a) {
    IndexIterator iter = a.getIndexIterator();
    double min = Double.MAX_VALUE;
    while (iter.hasNext()) {
      double val = iter.getDoubleNext();
      if (Double.isNaN(val)) continue;
      if (val < min)
        min = val;
    }
    return min;
  }

  public static double getMaximum(Array a) {
    IndexIterator iter = a.getIndexIterator();
    double max = -Double.MAX_VALUE;
    while (iter.hasNext()) {
      double val = iter.getDoubleNext();
      if (Double.isNaN(val)) continue;
      if (val > max)
        max = val;
    }
    return max;
  }

  /**
   * Find min and max value in this array, getting values as doubles. Skip Double.NaN.
   *
   * @param a the array.
   * @return MinMax
   */
  public static MAMath.MinMax getMinMax(Array a) {
    IndexIterator iter = a.getIndexIterator();
    double max = -Double.MAX_VALUE;
    double min = Double.MAX_VALUE;
    while (iter.hasNext()) {
      double val = iter.getDoubleNext();
      if (Double.isNaN(val)) continue;
      if (val > max)
        max = val;
      if (val < min)
        min = val;
    }
    return new MinMax(min, max);
  }

  public static double getMinimumSkipMissingData(Array a, double missingValue) {
    IndexIterator iter = a.getIndexIterator();
    double min = Double.MAX_VALUE;
    while (iter.hasNext()) {
      double val = iter.getDoubleNext();
      if ((val != missingValue) && (val < min))
        min = val;
    }
    return min;
  }

  public static double getMaximumSkipMissingData(Array a, double missingValue) {
    IndexIterator iter = a.getIndexIterator();
    double max = -Double.MAX_VALUE;
    while (iter.hasNext()) {
      double val = iter.getDoubleNext();
      if ((val != missingValue) && (val > max))
        max = val;
    }
    return max;
  }

  public static MAMath.MinMax getMinMaxSkipMissingData(Array a, double missingValue) {
    IndexIterator iter = a.getIndexIterator();
    double max = -Double.MAX_VALUE;
    double min = Double.MAX_VALUE;
    while (iter.hasNext()) {
      double val = iter.getDoubleNext();
      if (val == missingValue)
        continue;
      if (val > max)
        max = val;
      if (val < min)
        min = val;
    }
    return new MinMax(min, max);
  }


  /**
   * Set all the elements of this array to the given double value.
   * The value is converted to the element type of the array, if needed.
   *
   * @param result change this Array
   * @param val set all elements to this value
   */
  public static void setDouble(Array result, double val) {
    IndexIterator iter = result.getIndexIterator();
    while (iter.hasNext()) {
      iter.setDoubleNext(val);
    }
  }

  /**
   * sum all of the elements of array a as doubles.
   * The values from the array a are converted to double (if needed).
   * @param a read values from this Array
   * @return sum of elements
   */
  public static double sumDouble(Array a) {
    double sum = 0;
    IndexIterator iterA = a.getIndexIterator();
    while (iterA.hasNext()) {
      sum += iterA.getDoubleNext();
    }
    return sum;
  }

  /**
   * sum all of the elements of array a as doubles.
   * The values from the array a are converted to double (if needed).
   * @param a read values from this Array
   * @param missingValue skip values equal to this, or which are NaNs
   * @return sum of elements
   */
  public static double sumDoubleSkipMissingData(Array a, double missingValue) {
    double sum = 0;
    IndexIterator iterA = a.getIndexIterator();
    while (iterA.hasNext()) {
      double val = iterA.getDoubleNext();
      if ((val == missingValue) || Double.isNaN(val))
        continue;
      sum += val;
    }
    return sum;
  }

  /**
   * Holds a minimum and maximum value.
   */
  public static class MinMax {
    public double min, max;

    public MinMax(double min, double max) {
      this.min = min;
      this.max = max;
    }

    @Override
    public String toString() {
      return "MinMax{" +
              "min=" + min +
              ", max=" + max +
              '}';
    }
  }

  /**
   * Calculate the scale/offset for an array of numbers.
   * 
   * If signed:
   *   then
   *     max value unpacked = 2^(n-1) - 1 packed
   *     min value unpacked = -(2^(n-1) - 1) packed
   *   note that -2^(n-1) is unused, and a good place to map missing values
   *   by solving 2 eq in 2 unknowns, we get:
   *     scale = (max - min) / (2^n - 2)
   *     offset = (max + min) / 2
   * If unsigned then
   *     max value unpacked = 2^n - 1 packed
   *     min value unpacked = 0 packed
   *   and:
   *     scale = (max - min) / (2^n - 1)
   *     offset = min
   *   One could modify this to allow a holder for missing values.
   * 
* @param a array to convert (not changed) * @param missingValue skip these * @param nbits map into this many bits * @param isUnsigned use signed or unsigned packed values * @return ScaleOffset, calculated as above. */ public static MAMath.ScaleOffset calcScaleOffsetSkipMissingData(Array a, double missingValue, int nbits, boolean isUnsigned) { MAMath.MinMax minmax = getMinMaxSkipMissingData(a, missingValue); if (isUnsigned) { long size = (1 << nbits) - 1; double offset = minmax.min; double scale =(minmax.max - minmax.min) / size; return new ScaleOffset(scale, offset); } else { long size = (1 << nbits) - 2; double offset = (minmax.max + minmax.min) / 2; double scale =(minmax.max - minmax.min) / size; return new ScaleOffset(scale, offset); } } public static Array convert2packed(Array unpacked, double missingValue, int nbits, boolean isUnsigned, DataType packedType) { MAMath.ScaleOffset scaleOffset = calcScaleOffsetSkipMissingData(unpacked, missingValue, nbits, isUnsigned); Array result = Array.factory(packedType, unpacked.getShape()); IndexIterator riter = result.getIndexIterator(); while (unpacked.hasNext()) { double uv = unpacked.nextDouble(); double pv = (uv - scaleOffset.offset) / scaleOffset.scale; riter.setDoubleNext( pv); } return result; } public static Array convert2Unpacked(Array packed, ScaleOffset scaleOffset) { //boolean isUnsigned = packed.isUnsigned(); Array result = Array.factory(DataType.DOUBLE, packed.getShape()); IndexIterator riter = result.getIndexIterator(); while (packed.hasNext()) { riter.setDoubleNext( packed.nextDouble() * scaleOffset.scale + scaleOffset.offset); } return result; } /** * Holds a scale and offset. */ public static class ScaleOffset { public double scale, offset; public boolean isUnsigned; public ScaleOffset(double scale, double offset) { this.scale = scale; this.offset = offset; } } public static boolean isEqual(Array data1, Array data2) { if (data1.getSize() != data2.getSize()) return false; DataType dt = DataType.getType(data1.getElementType()); IndexIterator iter1 = data1.getIndexIterator(); IndexIterator iter2 = data2.getIndexIterator(); if (dt == DataType.DOUBLE) { while (iter1.hasNext() && iter2.hasNext()) { double v1 = iter1.getDoubleNext(); double v2 = iter2.getDoubleNext(); if (!Double.isNaN(v1) || !Double.isNaN(v2)) if (!Misc.closeEnough(v1, v2, 1.0e-8)) return false; } } else if (dt == DataType.FLOAT) { while (iter1.hasNext() && iter2.hasNext()) { float v1 = iter1.getFloatNext(); float v2 = iter2.getFloatNext(); if (!Float.isNaN(v1) || !Float.isNaN(v2)) if (!Misc.closeEnough(v1, v2, 1.0e-5)) return false; } } else if (dt == DataType.INT) { while (iter1.hasNext() && iter2.hasNext()) { int v1 = iter1.getIntNext(); int v2 = iter2.getIntNext(); if (v1 != v2) return false; } } else if (dt == DataType.SHORT) { while (iter1.hasNext() && iter2.hasNext()) { short v1 = iter1.getShortNext(); short v2 = iter2.getShortNext(); if (v1 != v2) return false; } } else if (dt == DataType.BYTE) { while (iter1.hasNext() && iter2.hasNext()) { byte v1 = iter1.getByteNext(); byte v2 = iter2.getByteNext(); if (v1 != v2) return false; } } else if (dt == DataType.LONG) { while (iter1.hasNext() && iter2.hasNext()) { long v1 = iter1.getLongNext(); long v2 = iter2.getLongNext(); if (v1 != v2) return false; } } return true; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy