Wednesday, January 18, 2012

Modeling Scientific Attributes in Java using JScience


A couple of years ago the Mars Climate Orbiter famously wrecked into Mars's upper atmosphere due to a unit conversion error between two teams of developers (one using English units and the other metric).  The result was the loss of $125m satellite and years of development due to a simple programming snafu [1].  While the problem probably should have been identified during testing, this small mistake highlights the important of domain models, especially in scientific computing.

Working on a side project in the field of "Environmental Intelligence," a shameless attempt for me to utilize my years of Meteorological experience, I found myself spending an exorbitant amount of time writing a domain model that handled the conversion between different types of scientific units for environmental attributes like temperature, pressure, velocity, direction, time, and location.  I also found myself relatively uninspired in how I wanted to actually model these attributes.  Since the intent of this spike was not to reinvent the wheel in modeling the domain, but in fact to write a parser for METAR data, I decided to look online to see if someone had already modeled this domain and had a reasonable set of functions for converting between units.

Much to my surprise, there was an existing library that did exactly what I needed and more.  More importantly, I discovered that this library was actually the "reference implementation" for a JSR (275) regarding the use of "Units and Measures" in Java.  

Meet JScience.  JScience is a humble little library for scientific and mathematical computations on the JVM.  JScience (http://jscience.org/) is powered by the Javolution (http://javolution.org/) a library for building real-time systems.  JScience hides the complexity of modeling environmental attributes and dealing with unit conversions allowing you to get to the core problems in your domain.

In this post, I will demonstrate the basics of using JScience in your application.

For those using Maven, for some reason, JScience hasn't made it into the Maven Central Repository.  Funny enough, I found an instance of JScience 4.3 being hosted out of the JCurl ("Olympic sport of Curling") repository.  So, thank you maintainers of JCurl for making my life easier!  If you want to use geospatial units, you will also need to include the Geotoolkit repository as well.

The following is the repository configuration in your Maven pom.xml file.

<repositories>
  <repository>
    <id>geotoolkit</id>
    <name>Geotoolkit repository</name>
    <url>http://maven.geotoolkit.org</url>
  </repository>
  <repository>
    <id>jcurl</id>
    <name>JCurl Library</name>
    <url>http://jcurl.berlios.de/m2/repo</url>
  </repository>
</repositories>

Here are the dependencies required to use JScience (also in the pom.xml file):

<dependencies>
  <dependency>
    <groupId>org.jscience</groupId>
    <artifactId>jscience</artifactId>
    <version>4.3.1</version>
  </dependency>
  <dependency>
    <groupId>javolution</groupId>
    <artifactId>javolution</artifactId>
    <version>5.4.5</version>
  </dependency>
  <dependency>
    <groupId>org.opengis</groupId>
    <artifactId>geoapi-pending</artifactId>
    <version>2.3-RC1</version>
  </dependency>
</dependencies>

Update your Maven dependencies and you should be set to use all of JScience's units and measures, including the geospatial constructs.

Let's start by demonstrating some really naive code from a domain modeling standpoint.  I have the requirement to deliver the daily maximum and minimum temperatures to my users on a website, so perhaps I create a DailyMaxMin class to represent this data structure.

public class DailyMaxMin {

  protected double minimumTemperature = Double.MIN_VALUE;
  
  protected double maximumTemperature = Double.MIN_VALUE;

  public DailyMaxMin(){}
  
  public DailyMaxMin(double minimumTemperature, double maximumTemperature) {

    this.minimumTemperature = minimumTemperature;
    this.maximumTemperature = maximumTemperature;
  }

  public double getMinimumTemperature() {
    
    return minimumTemperature;
  }

  public void setMinimumTemperature(double minimumTemperature) {
    
    this.minimumTemperature = minimumTemperature;
  }

  public double getMaximumTemperature() {
    
    return maximumTemperature;
  }

  public void setMaximumTemperature(double maximumTemperature) {
    
    this.maximumTemperature = maximumTemperature;
  }
  
}

Ok, if we learned anything from the anecdote about the Mars Climate Orbiter, it's that we need to be specific about the type of temperature we are working with.  Carrying around these primitives was the problem they ran into.

Weather guys deal a lot with temperatures.  We have ambient air temperature (which you normally would call "the temperature") and dew point temperatures, but we also deal with "wet bulb temperatures", "heat indexes", "globe temperatures", "potential temperature", etc.  Since all are measured in the same way, some form of temperature unit, we will create a convenient little class for them:

public class Temperature {

  protected double valueAsCelsius = Double.MIN_VALUE;
  
  public Temperature(){}
  
  public Temperature(double celsiusValue){
    
    setValue(celsiusValue);
  }
  
  public void setValue(double celsiusValue){
    
    this.valueAsCelsius = celsiusValue;
  }
  
  public double getValue(){
    
    return this.valueAsCelsius;
  }
  
}

Like I said, this class is naive from a domain modeling standpoint, and is just as bad as simply using a primitive in the aggregate's parent class to represent a temperature value (like we did in the DailyMaxMin class).  For instance, what keeps another developer from giving us the temperature in Fahrenheit?  We must trust that other developers will see that we named the parameter in the 'setter' "celciusValue".  Not exactly the best practice from a "defensive programming" perspective.

We will mature this example by adding some guards that ensure the Temperature class is as intended:

public class Temperature {

  public enum Units {
    Celsius,
    Fahrenheit
  }
  
  protected double valueAsCelsius = Double.MIN_VALUE;
  
  public Temperature(){}
  
  public Temperature(double value, Units unit){
    
    setValue(value, unit);
  }
  
  public void setValue(double value, Units unit){
  
    if(unit == Units.Celsius){
      
      this.valueAsCelsius = value;
    }
    else {
      
      this.valueAsCelsius = fahrenheitToCelsius(value);
    }
  }
  
  public double getValue(Units unit){
    
    if(unit == Units.Fahrenheit){
      
      return celsiusToFahrenheit(this.valueAsCelsius);
    }
    
    return this.valueAsCelsius;
  }
  
  public static double celsiusToFahrenheit(double celsius){
  
    return celsius * 9d / 5d + 32;
  }
  
  public static double fahrenheitToCelsius(double fahrenheit){
  
    return (fahrenheit - 32) * 5d / 9d;
  }
}

Now we have an enum of Temperatures and we force developers to specify what unit the value was measured in.  This kind of works.  The problem is, we are forced to continuously refactor this class every type a new unit of measurement is added to the system.  For instance, what if you get the requirement to immediately include both Kelvin and Rankine to your model?  You could expand the number of labels in the Units enumeration and modify the getters and setters to use some sort of switch statement.  Probably the better alternative would be to develop a more complex model to elegantly represent your units of measure.

For the sake of argument, let's pretend that using our Temperature class was the route we chose to pursue.  Let's modify the DailyMaxMin class to represent our new way of handling temperatures.

public class DailyMaxMin {

  protected Temperature minimumTemperature = null;
  
  protected Temperature maximumTemperature = null;

  public DailyMaxMin(){}
  
  public DailyMaxMin(
      Temperature minimumTemperature,
      Temperature maximumTemperature) {

    this.minimumTemperature = minimumTemperature;
    this.maximumTemperature = maximumTemperature;
  }

  public Temperature getMinimumTemperature() {
    
    return minimumTemperature;
  }

  public void setMinimumTemperature(Temperature minimumTemperature) {
    
    this.minimumTemperature = minimumTemperature;
  }

  public Temperature getMaximumTemperature() {
    
    return maximumTemperature;
  }

  public void setMaximumTemperature(Temperature maximumTemperature) {
    
    this.maximumTemperature = maximumTemperature;
  }
  
}

We've just went through a lot of trouble to get a simple class to carry a more robust representation of maximum and minimum temperatures.  If you had to deal with more than just temperatures (like all the other factors in weather: wind, pressure, precipitation, etc.), you will find yourself spending months writing tedious model code to represent the simpler concepts in your application.

JScience addresses this problem through a highly-flexible Generics-based model.  The model is centered around the Measurable<Q extends Quantity> interface.  Quantity is a "marker interface" (an interface with no behavior [e.g.: no methods]) which is extended by the types of physical attributes we would want to work with in a scientific applications (temperature, force, pressure, power, money, mass, energy, etc.).

We can create concrete instances of Measurable objects using static factory methods provided by the JScience Amount class. 

import javax.measure.Measurable;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Pressure;
import javax.measure.quantity.Length;
import javax.measure.quantity.Velocity;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;

import org.jscience.physics.amount.Amount;

//...

double temperatureAsDouble = 42.0d;

Measurable<Temperature> temperature 
    = Amount.valueOf(temperatureAsDouble, SI.CELSIUS);

double pressureAsDouble = 1013.2d;

Measurable<Pressure> pressure 
    = Amount.valueOf(pressureAsDouble, SI.MILLI(NonSI.BAR));

long lengthAsLong = 10l;

Measurable<Length> length 
    = Amount.valueOf(lengthAsLong, NonSI.FOOT);

long velocityAsLong = 15l;

Measurable<Velocity> velocity 
    = Amount.valueOf(velocityAsLong, NonSI.KNOT);

In the following example, we are using the Amount.valueOf method to create instances of measurable objects of various types of Quantity by providing either a long or double value and the corresponding Unit of measure.  One interesting variation you might notice with the pressure example is the clever use of the Decorator pattern, where SI.MILLI is used to magnify the quantity of the NonSI.BAR measurement, to effectively create Millibars (the standard unit of measure for atmospheric pressure used in meteorological models and products).  For those that didn't get the reference, SI refers to the standard scientific unit of measurement, while NonSI is the grouping for everything else.

Extracting values from Measurable objects is equally simple.

import javax.measure.Measurable;
import javax.measure.quantity.Temperature;
import javax.measure.quantity.Pressure;
import javax.measure.quantity.Length;
import javax.measure.quantity.Velocity;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;

import org.jscience.physics.amount.Amount;

//...

Measurable<Temperature> temperature 
    = Amount.valueOf(42.0d, SI.CELSIUS);

double tempFahrenheit = temperature.doubleValue(NonSI.FAHRENHEIT);
double tempRankine = temperature.doubleValue(NonSI.RANKINE);
double tempKelvin = temperature.doubleValue(SI.KELVIN);

Measurable<Pressure> pressure 
    = Amount.valueOf(1013.2d, SI.MILLI(NonSI.BAR));

double pressureInchesOfMercury = pressure.doubleValue(NonSI.INCH_OF_MERCURY);
double pressureAtmospheres = pressure.doubleValue(NonSI.ATMOSPHERE);

Measurable<Length> length 
    = Amount.valueOf(10l, NonSI.FOOT);

long lengthMiles = length.longValue(NonSI.MILE);
long lengthMeters = length.longValue(SI.METER);
long lengthYards = length.longValue(NonSI.YARD);

Measurable<Velocity> velocity 
    = Amount.valueOf(15l, NonSI.KNOT);

long velocityMetersPerSecond = velocity.longValue(SI.METERS_PER_SECOND);
long velocityMilesPerHour = velocity.longValue(NonSI.MILES_PER_HOUR);
long velocityKilometersPerHour = velocity.longValue(NonSI.KILOMETERS_PER_HOUR);

So to wrap this up, JScience offers a nice open source alternative to the code intensive practice of writing those model objects yourself.  Do yourself a favor and start replacing those scientific primitives with Measurable objects:

public class DailyMaxMin {

  protected Measurable<Temperature> minimumTemperature = null;
  
  protected Measurable<Temperature> maximumTemperature = null;

  public DailyMaxMin(){}
  
  public DailyMaxMin(
      Measurable<Temperature> minimumTemperature,
      Measurable<Temperature> maximumTemperature) {

    this.minimumTemperature = minimumTemperature;
    this.maximumTemperature = maximumTemperature;
  }

  public Measurable<Temperature> getMinimumTemperature() {
    
    return minimumTemperature;
  }

  public void setMinimumTemperature(
    Measurable<Temperature> minimumTemperature) {
    
    this.minimumTemperature = minimumTemperature;
  }

  public Measurable<Temperature> getMaximumTemperature() {
    
    return maximumTemperature;
  }

  public void setMaximumTemperature(
    Measurable<Temperature> maximumTemperature) {
    
    this.maximumTemperature = maximumTemperature;
  }
  
}

There's a little more, but not much, to the JScience package.  Probably the thing I love the most about the framework is how absolutely simple it is.  If you are doing any scientific or mathematical computing in Java, I highly recommend this library.  Need a better reason?  NASA could provide you a 150 million why.


Works Cited:

[1].  CNN Tech.  "NASAs metric confusion caused Mars Orbiter loss."  Sept 30, 1999. Retrieved Jan 18, 2012 from http://articles.cnn.com/1999-09-30/tech/9909_30_mars.metric_1_mars-orbiter-climate-orbiter-spacecraft-team?_s=PM:TECH.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.