Physical Quantities and Units

Another very common problem in scientific applications is the proper treatment of units. At the current stage, \tensorics{} currently uses internally an external library for this purpose (JScience \cite{jscience}). However, as this library is not actively maintained anymore, it is foreseen to replace this implementation either by a different library or an internal implementation of physical quantities.

For this reason, \tensorics{} already provides its own abstraction of units. A physical unit is represented by the class \code{Unit} and a value-unit pair is represented by the class \code{QuantifiedValue}. Factory methods for quantified values are available in the \code{Tensorics} class. Convenience overrides are provided which support both \tensorics{} internal unit objects and JScience instances of units. Operations are available in the support classes for the corresponding value types, like for doubles e.g. in the class \code{TensoricDoubles}. With this, operations like the following are possible:

QuantifiedValue<Double> distance = Tensorics.quantityOf(10.0, SI.METER);

QuantifiedValue<Double> time = Tensorics.quantityOf(5.0, SI.SECOND);

QuantifiedValue<Double> speed = calculate(distance).dividedBy(time);
/* results in 2 m/s */

Double value = speed.value(); // 2.0
Unit unit = speed.unit(); // m/s

Also support methods to work with tensors of quantified values are provided, e.g.:

Tensor<QuantifiedValue<Double>> measurement;
Tensor<QuantifiedValue<Double>> reference;
/* construction omitted */

Tensor<QuantifiedValue<Double>> difference = calculate(measurement).minus(reference);

Error and Validity Propagation

Especially when using tensors for measured values, it is important to understand the errors after a series of calculations. Further, it is can be that individual points in a tensor contain invalid data. It then makes no sense to do calculations with them. \Tensorics{} provides dedicated mechanisms for this cases. The \code{QuantifiedValue}s contain two additional fields: a (boolean) validity flag and an optional value for an error (uncertainty). All the operations on quantified values (and on tensors of quantified values) take this fields into account. The exact behavior can again be configured by the use of explicit strategies. The defaults are: --- * If an invalid value is used in a calculation, then the resulting value will be invalid. * The values involved in the calculations will be treated as independent variables and the error is propagated to the resulting value accordingly \cite{error-propagation-wikipedia}. ---

Comparisons between quantities take into account their associated errors assuming Gaussian statistics. The confidence level is 95\% unless specified otherwise. This allows to conveniently check if a quantity is significantly less, equal, or greater than another. For example, $90 \pm 1 \mathrm{m}$ is significantly less than $100 \pm 10 \mathrm{m}$ at a confidence level of 68\% but not at 95\%.

QuantifiedValue<Double> q90pm1 = quantityOf(90.0, METER).withError(1.0);
QuantifiedValue<Double> q100pm10 = quantityOf(100.0, METER).withError(10.0);

/* false at 95% confidence (default): */
testIf(q90pm1).isLessThan(q100pm10);

/* true at 68% confidence: */
with(confidenceLevelOf(0.68)).testIf(q90pm1).isLessThan(q100pm10);

Tensorbacked Domain Objects

While working with tensors gives all the flexibility of transformations and calculations, very often it is desirable to give more meaning to objects. Usually one would create dedicated domain objects in these cases. However, this would mean giving up all the convenient support methods. To combine the best of both approaches, \tensorics{} provides a built-in mechanism for creating domain objects which wrap tensors inside and allow almost the same calculations and transformations as plain tensors. These objects are called \code{Tensorbacked}s and can be defined by the user as required. The simplest way to do so is to inherit from \code{AbstractTensorbacked}. An important property of tensorbacked objects is that each of them has a fixed set of dimensions, which are defined through the dedicated annotation \code{@Dimensions}. For example, if one would like to define some domain object that contains temperatures, one could do so by

@Dimensions({Time.class, City.class})
public class TemperatureMap
    extends AbstractTensorbacked<Double> {
    /* empty (except a constructor) */
}

Instances of these classes can then be created using simply an existing tensor or a builder. Calculations can be performed like with bare tensors.

TemperatureMap measured = Tensorics.construct(TemperatureMap.class).from(degrees);

TemperatureMap reference = Tensorics
    .builderFor(TemperatureMap.class)
      .put(at(SF, T1), 10.0)
      .build();

TemperatureMap diff = DoubleTensorics.calculate(measured).minus(reference);

When using a builder, the dimensions do not have to be given explicitly, as they are already defined through the annotation.

Expression Language

All the examples in the previous sections described directly Java executable code. In addition to this, \tensorics{} provides a Java internal domain specific language (DSL) to only describe calculation steps using the same operations as described before. This DSL does not directly execute the calculations, but instead creates an expression tree, which can be evaluated (resolved) in a separate step. Since these expressions can be resolved in different contexts, this can e.g. be used for subscription based online evaluation (e.g. processing data from devices) or processing logged data. This expression language is one of the cornerstones of a recently developed online analysis framework. More details can be found in the corresponding publication \cite{analysis-framework}.

Last build: 2019-07-30 00:16:37 CEST