squants

package squants

Squants

The Scala API for Quantities, Units of Measure and Dimensional Analysis

Overview

Squants is a framework of data types and a domain specific language (DSL) for representing Quantities, their Units of Measure, and their Dimensional relationships. The API supports typesafe dimensional analysis, improved domain models and more. All types are immutable and thread-safe.

The motivating driver for the framework was to ensure developers of VPower, Viridity Energy's flagship energy management product, would have a type safe way to define energy related expressions, operations, domain models and API's.

For example, when using a Double to describe Energy (kWh) and Power/Load (kW), it is possible to compile a program that adds these two values together. This is not appropriate as kW and kWh measure two different quantities. The unit kWh is used to measure an amount of Energy used or produced. The unit kW is used to measure Power/Load, the rate at which Energy is being used or produced, that is, Power/Load is the first time derivative of Energy, Energy / Time.

Consider the following code

val loadKw: Double = 1.2
val energyMwh: Double = 24.2
val sumKw = loadKw + energyMwh

which not only adds different quantity types (load vs energy), it also fails to convert the scales (Mega vs Kilo). Because this code compiles, detection of these errors is pushed further into the development cycle.

The Squants Type Library and DSL helps prevent errors like these by type checking operations at compile time and automatically applying scale and type conversions (see below) at run-time. For example,

val load1: Power = Kilowatts(1.2)
val load2: Power = Megawatts(0.023)
val loadSum = load1 + load2
loadSum should be(Kilowatts(24.2))

is a valid assertion because Kilowatts and Megawatts are both measures of load. Only the scale is different and the framework applies an appropriate conversion. Also, notice that keeping track of the scale within the value name is no longer needed. If necessary, the value can be converted to a specific scale using the methods provided by the quantity class.

val load: Power == Kilowatts(1200)
val kw: Double = load1.toKilowatts  // returns 1200.0
val mw: Double = load1.toMegawatts  // returns 1.2
val gw: Double = load1.toGigawatts  // returns 0.0012

The following code highlights the type safety features.

val load: Power = Kilowatts(1.2)
val energy: Energy = MegawattsHours(0.023)
val sum = load + energy // <--- INVALID OPERATION, DOES NOT COMPILE
sumKw should be(Kilowatts(24.2))

The invalid expression prevents the code from compiling, catching the error made when using Double much earlier.

Domain smart type conversions are supported. Most conversions are implemented by defining relationships between Quantity types using infix operations.

val load: Power = Kilowatts(1.2)
val time: Time = Hours(2)
val energyUsed: Energy = load * time
energyUsed should be(KilowattHours(2.4))

This code demonstrates use of the Power.* method, defined as an infix operator that takes a Time value and returns an Energy value, conversely

val aveLoad: Power = energyUsed / time
aveLoad should be(Kilowatts(1.2)

demonstrates use of the Energy./ method that takes a Time and returns a Power

Other conversions are done using specialized versions of a Quantity's factory methods

val aveLoad = Power(energyUsed, time)  // equates to energyUsed / time
val energyUsed = Energy(aveLoad, time) // equates to aveLoad * time
Natural Language Features

Implicit conversions give the DSL some features that allows client code to express quantities in a more natural way.

val load = 1.2 kW
val time = 2 hours
val energyUsed = load * time
energyUsed should be((2.4 kWh))

Many Quantity classes also provide a factory method that takes a quantity expression

val load = Power("1.2 kW")
val time = Time("2h")
val energy = Energy("23 MWh")

Useful for automatically interpreting strings from user input, json marshaller and other sources

Type Hierarchy

The type hierarchy includes two root base traits: squants.Quantity and squants.UnitOfMeasure

Quantity

Quantity measures the magnitude or multitude of some thing. Classes extending Quantity represent the various types of quantities that can be measured. These are our alternative to just using Double. Common 'Base' Quantities include Mass, Temperature, Length, Time, Energy, etc.

Derived Quantities are based on one or more other quantities. Typical examples include Time Derivatives.

Speed is the 1st Time Derivative of Length (Distance), Acceleration is the 2nd Time Derivative.

val distance: Length = Kilometers(100)
val time: Time = Hours(2)
val speed: Speed = distance / time
speed.toKilometersPerHour should be(50.0)

val acc: Acceleration = Meters(50) / Second(1) / Second(1)
acc.toMetersPerSecondSquared should be(50)

Power is the 1st Time Derivative of Energy, PowerRamp is the 2nd

Unit of Measure

UnitOfMeasure is the scale or multiplier in which the Quantity is being measured.

For each Quantity a series of UOM objects implement a base UOM trait typed to that Quantity. The UOM objects define the unit symbols and conversion settings. Factory methods in each UOM object create instances of the corresponding Quantity.

For example UOM objects extending squants.space.LengthUnit can be used to create Length quantities

val len1: Length = Inches(5)
val len2: Length = Meters(4.3)
val len3: Length = UsMiles(345.2)

Units of Measure for Time include Milliseconds, Seconds, Minutes, Hours, and Days

Units of Measure for Temperature include Celsius, Kelvin, and Fahrenheit

Units of Measure for Mass include Grams, Kilograms, etc.

Domain Modeling

An excellent use case for Squants is stronger types for fields in your domain model.

case class Generator(name: String, maxLoad: Power, rampUpRate: PowerRamp,
aveOperatingCost:  Price[Energy], minDailyDowntime: Time)

Another use case is performing domain specific computations.

val gen = Generators("My Gen", Kilowatt(100), KilowattsPerHour(200), Price(USD(51.2), megawatt), Minutes(45))
 // How long until gen is running at full capacity
val timeToMaxLoad = gen.rampUpRate / gen.maxLoad
Version

0.1

Since

0.1

Linear Supertypes
AnyRef, Any
Ordering
  1. Alphabetic
  2. By inheritance
Inherited
  1. squants
  2. AnyRef
  3. Any
  1. Hide All
  2. Show all
Learn more about member selection
Visibility
  1. Public
  2. All

Type Members

  1. abstract class AbstractQuantityNumeric[A <: Quantity[A]] extends Numeric[A]

    Base class for creating objects to manage quantities as Numeric.

  2. type Acceleration = squants.motion.Acceleration

  3. type Angle = squants.space.Angle

  4. type Area = squants.space.Area

  5. class AreaVector extends QuantityVector[Area]

  6. trait BaseQuantity extends AnyRef

    Base Quantities are the basic, directly measurable, fundamental quantities: Mass, Length, Time, etc.

  7. trait BaseQuantityUnit[QuantityType <: Quantity[QuantityType] with BaseQuantity] extends UnitOfMeasure[QuantityType]

    A unit of measure used for Base (SI) Quantities

  8. trait BaseUnit extends AnyRef

    A marker trait identifying SI Base Units

  9. type ChemicalAmount = squants.mass.ChemicalAmount

  10. type Density = squants.mass.Density

  11. final class Dimensionless extends Quantity[Dimensionless] with TimeIntegral[Frequency]

    Represents a quantity of some thing for which there is no dimension.

  12. trait DimensionlessUnit extends UnitOfMeasure[Dimensionless] with UnitMultiplier

    Base trait for units of squants.Dimensionless

  13. case class DoubleVector(coordinates: Double*) extends Vector[Double] with Product with Serializable

    Double (Real Number) Vector

  14. type ElectricCurrent = squants.electro.ElectricCurrent

  15. type Energy = squants.energy.Energy

  16. type Force = squants.motion.Force

  17. type Jerk = squants.motion.Jerk

  18. type Length = squants.space.Length

  19. class LengthVector extends QuantityVector[Length]

    Strategy #3 Prototype for implementations that support dimensional conversions

  20. trait LikeRatio[A <: Quantity[A]] extends Ratio[A, A]

  21. type LuminousIntensity = squants.photo.LuminousIntensity

  22. type Mass = squants.mass.Mass

  23. type MassFlowRate = squants.motion.MassFlowRate

  24. type Momentum = squants.motion.Momentum

  25. type Money = squants.market.Money

  26. trait PhysicalQuantity extends AnyRef

    A base trait for measurable quantities found within physical systems

  27. type Power = squants.energy.Power

  28. type PowerRamp = squants.energy.PowerRamp

  29. type Price[A <: Quantity[A]] = squants.market.Price[A]

  30. abstract class Quantity[A <: Quantity[A]] extends Ordered[A] with Serializable

    A base trait for measurable quantities

  31. case class QuantityRange[A <: Quantity[A]](lower: A, upper: A) extends Product with Serializable

    Represents a Range starting at one Quantity value and going up to another

  32. type QuantitySeries[A <: Quantity[A]] = IndexedSeq[QuantityRange[A]]

  33. case class QuantityVector[A <: Quantity[A]](coordinates: A*) extends Vector[A] with Product with Serializable

    Quantity Vector

  34. trait Ratio[A <: Quantity[A], B <: Quantity[B]] extends AnyRef

    Defines an interface and partial implementation for types that represent a ratio between any two quantities

  35. type SolidAngle = squants.space.SolidAngle

  36. implicit class SquantifiedDouble extends AnyRef

    Provides implicit conversions that allow Doubles to lead in * operations

  37. implicit class SquantifiedDoubleVector extends AnyRef

  38. implicit class SquantifiedLong extends AnyRef

  39. implicit class SquantifiedNumeric[A] extends AnyRef

  40. type Temperature = squants.thermal.Temperature

  41. type Time = squants.time.Time

  42. trait UnitMultiplier extends AnyRef

    A Unit of Measure that require a simple multiplier for converting to and from the underlying value's unit

  43. trait UnitOfMeasure[A <: Quantity[A]] extends Serializable

    A Unit of Measure is used to define the scale of a quantity measurement

  44. trait ValueUnit extends UnitMultiplier

    Identifies the Unit of Measure used for storing the quantity's underlying value

  45. trait Vector[A] extends AnyRef

    Root trait for representing Vectors

  46. type Velocity = squants.motion.Velocity

  47. type Volume = squants.space.Volume

  48. type VolumeFlowRate = squants.motion.VolumeFlowRate

Value Members

  1. val Amperes: squants.electro.Amperes.type

  2. object BinarySystem

    Singleton defining Metric System multipliers

  3. val Candelas: squants.photo.Candelas.type

  4. object Dimensionless extends Serializable

    Factory singleton for squants.Dimensionless

  5. object DimensionlessConversions

  6. object Dozen extends DimensionlessUnit

    Represents a unit of dozen (12)

  7. object Each extends DimensionlessUnit with ValueUnit

    Represents a unit of singles

  8. object Gross extends DimensionlessUnit

    Represents a unit of gross (144)

  9. val Kelvin: squants.thermal.Kelvin.type

  10. val Kilograms: squants.mass.Kilograms.type

  11. val Meters: squants.space.Meters.type

  12. object MetricSystem

    Singleton defining Metric System multipliers

  13. val Moles: squants.mass.Moles.type

  14. object QuantityVectorConversion

    Strategy #2 Prototype for implementations that support dimensional conversions

  15. val Radians: squants.space.Radians.type

  16. object Score extends DimensionlessUnit

    Represents a unit of scores (20)

  17. val Seconds: squants.time.Seconds.type

  18. val SquareRadians: SquaredRadians.type

  19. package electro

  20. package energy

  21. package market

    Squants Market API

  22. package mass

  23. package motion

  24. package photo

  25. package radio

  26. package space

  27. package thermal

  28. package time

Inherited from AnyRef

Inherited from Any

Ungrouped