unit: Update documentation, create examples, and comment types

Updates 90.
This commit is contained in:
Brendan Tracey
2017-06-20 12:27:13 -06:00
parent aed371df91
commit 3da72f485d
5 changed files with 99 additions and 73 deletions

View File

@@ -1,13 +0,0 @@
# Gonum unit [![Build Status](https://travis-ci.org/gonum/unit.svg)](https://travis-ci.org/gonum/unit) [![Coverage Status](https://coveralls.io/repos/gonum/unit/badge.svg?branch=master&service=github)](https://coveralls.io/github/gonum/unit?branch=master) [![GoDoc](https://godoc.org/github.com/gonum/unit?status.svg)](https://godoc.org/github.com/gonum/unit)
Package unit provides a set of types and constants that facilitate the use of the International System of Units (SI).
## Issues
If you find any bugs, feel free to file an issue on the github issue tracker. Discussions on API changes, added features, code review, or similar requests are preferred on the gonum-dev Google Group.
https://groups.google.com/forum/#!forum/gonum-dev
## License
Please see github.com/gonum/license for general license information, contributors, authors, etc on the Gonum suite of packages.

29
unit/consts.go Normal file
View File

@@ -0,0 +1,29 @@
// Copyright ©2017 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unit
const (
Yotta = 1e24
Zetta = 1e21
Exa = 1e18
Peta = 1e15
Tera = 1e12
Giga = 1e9
Mega = 1e6
Kilo = 1e3
Hecto = 1e2
Deca = 1e1
Deci = 1e-1
Centi = 1e-2
Milli = 1e-3
Micro = 1e-6
Nano = 1e-9
Pico = 1e-12
Femto = 1e-15
Atto = 1e-18
Zepto = 1e-21
Yocto = 1e-24
)

View File

@@ -26,7 +26,7 @@
// func main(){ // func main(){
// t := 300 * unit.Kelvin // t := 300 * unit.Kelvin
// p := 5 * unit.Bar // p := 5 * unit.Bar
// rho := UnitDensity(p, t) // gives compile-time error // rho := UnitDensity(p, t) // compile-time error
// } // }
// gives a compile-time error (temperature type does not match pressure type) // gives a compile-time error (temperature type does not match pressure type)
// while the corresponding code using float64 runs without error. // while the corresponding code using float64 runs without error.
@@ -39,11 +39,11 @@
// rho := Float64Density(p, t) // no error // rho := Float64Density(p, t) // no error
// } // }
// Many types have constants defined representing named SI units (Meter, // Many types have constants defined representing named SI units (Meter,
// Kilogram, etc. ) or SI derived units (Bar, Milligram, etc.). These are // Kilogram, etc. ) or SI derived units (Bar, Hz, etc.). The Unit package
// all defined as multiples of the base unit, so, for example, the following // additionally provides untyped constants for SI prefixes, so the following
// are euqivalent // are all equivalent.
// l := 0.001 * unit.Meter // l := 0.001 * unit.Meter
// k := 1 * unit.Millimeter // k := 1 * unit.Milli * unit.Meter
// j := unit.Length(0.001) // j := unit.Length(0.001)
// //
// 2) // 2)
@@ -72,18 +72,6 @@
// used, however, to create the unit of 'Slide', because in this case slide // used, however, to create the unit of 'Slide', because in this case slide
// is just a measurement of area. Instead, a constant could be defined. // is just a measurement of area. Instead, a constant could be defined.
// const Slide unit.Area = 0.001875 // m^2 // const Slide unit.Area = 0.001875 // m^2
// var WhiteBloodCellDim unit.Dimension
// func init(){
// WhiteBloodCellDim = unit.NewDimension("wbc")
// }
// type WbcPerArea float64
// func (w WbcPerArea) Unit() *Unit{
// return unit.New(float64(w), unit.Dimensions{WhiteBloodCellDim: 1, unit.LengthDim: -2})
// }
// func main(){
// v := WbcPerArea(15)
// fmt.Println(v.Unit()) // prints: 1.5e+01 m^-2 wbc
// }
// Please note that Unit cannot catch all errors related to dimensionality. // Please note that Unit cannot catch all errors related to dimensionality.
// Different physical ideas are sometimes expressed with the same dimensions // Different physical ideas are sometimes expressed with the same dimensions
// and Unit is incapable of catching these mismatches. For example, energy and // and Unit is incapable of catching these mismatches. For example, energy and

29
unit/unitexample_test.go Normal file
View File

@@ -0,0 +1,29 @@
// Copyright ©2017 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package unit_test
import (
"fmt"
"gonum.org/v1/gonum/unit"
)
func ExampleNew() {
// Create an acceleration of 3 m/s^2
accel := unit.New(3.0, unit.Dimensions{unit.LengthDim: 1, unit.TimeDim: -2})
fmt.Println(accel)
// Output: 3 m s^-2
}
func ExampleNewDimension() {
// Create a "trees" dimension
// Typically, this should be used within an init function
treeDim := unit.NewDimension("tree")
countPerArea := unit.New(0.1, unit.Dimensions{treeDim: 1, unit.LengthDim: -2})
fmt.Println(countPerArea)
// Output: 0.1 tree m^-2
}

View File

@@ -10,26 +10,17 @@ import (
"sort" "sort"
) )
// Uniter is an interface representing a type that can be converted // Uniter is a type that can be converted to a Unit.
// to a unit.
type Uniter interface { type Uniter interface {
Unit() *Unit Unit() *Unit
} }
// Dimension is a type representing an SI base dimension or other // Dimension is a type representing an SI base dimension or a distinct
// orthogonal dimension. If a new dimension is desired for a // orthogonal dimension. Non-SI dimensions can be created using the NewDimension
// domain-specific problem, NewDimension should be used. Integers // function, typically within an init function.
// should never be cast as type dimension
// // Good: Create a package constant with an init function
// var MyDimension unit.Dimension
// init(){
// MyDimension = NewDimension("my")
// }
// main(){
// var := MyDimension(28.2)
// }
type Dimension int type Dimension int
// String returns the string for the dimension.
func (d Dimension) String() string { func (d Dimension) String() string {
switch { switch {
case d == reserved: case d == reserved:
@@ -65,7 +56,8 @@ var (
AngleDim: "rad", AngleDim: "rad",
} }
// for guaranteeing there aren't two identical symbols // dimensions guarantees there aren't two identical symbols
// SI symbol list from http://lamar.colostate.edu/~hillger/basic.htm
dimensions = map[string]Dimension{ dimensions = map[string]Dimension{
"A": CurrentDim, "A": CurrentDim,
"m": LengthDim, "m": LengthDim,
@@ -132,10 +124,6 @@ var (
} }
) )
// TODO: Should we actually reserve "common" SI unit symbols ("N", "J", etc.) so there isn't confusion
// TODO: If we have a fancier ParseUnit, maybe the 'reserved' symbols should be a different map
// map[string]string which says how they go?
// Dimensions represent the dimensionality of the unit in powers // Dimensions represent the dimensionality of the unit in powers
// of that dimension. If a key is not present, the power of that // of that dimension. If a key is not present, the power of that
// dimension is zero. Dimensions is used in conjunction with New. // dimension is zero. Dimensions is used in conjunction with New.
@@ -178,53 +166,58 @@ func (u unitPrinters) Len() int {
} }
func (u unitPrinters) Less(i, j int) bool { func (u unitPrinters) Less(i, j int) bool {
return (u[i].pow > 0 && u[j].pow < 0) || u[i].String() < u[j].String() // Order first by positive powers, then by name.
if u[i].pow*u[j].pow < 0 {
return u[i].pow > 0
}
return u[i].String() < u[j].String()
} }
func (u unitPrinters) Swap(i, j int) { func (u unitPrinters) Swap(i, j int) {
u[i], u[j] = u[j], u[i] u[i], u[j] = u[j], u[i]
} }
// NewDimension returns a new dimension variable which will have a // NewDimension creates a new orthogonal dimension with the given symbol, and
// unique representation across packages to prevent accidental overlap. // returns the value of that dimension. The input symbol must not overlap with
// The input string represents a symbol name which will be used for printing // any of the any of the SI base units or other symbols of common use in SI ("kg",
// Unit types. This symbol may not overlap with any of the SI base units // "J", etc.), and must not overlap with any other dimensions created by calls
// or other symbols of common use in SI ("kg", "J", "μ", etc.). A list of // to NewDimension. The SymbolExists function can check if the symbol exists.
// such symbols can be found at http://lamar.colostate.edu/~hillger/basic.htm or // NewDimension will panic if the input symbol matches an existing symbol.
// by consulting the package source. Furthermore, the provided symbol is also //
// forbidden from overlapping with other packages calling NewDimension. NewDimension
// is expecting to be used only during initialization, and as such it will panic
// if the symbol matching an existing symbol
// NewDimension should only be called for unit types that are actually orthogonal // NewDimension should only be called for unit types that are actually orthogonal
// to the base dimensions defined in this package. Please see the package-level // to the base dimensions defined in this package. Please see the package-level
// documentation for further explanation. Calls to NewDimension are not thread safe. // documentation for further explanation.
func NewDimension(symbol string) Dimension { func NewDimension(symbol string) Dimension {
_, ok := dimensions[symbol] _, ok := dimensions[symbol]
if ok { if ok {
panic("unit: dimension string \"" + symbol + "\" already used") panic("unit: dimension string \"" + symbol + "\" already used")
} }
symbols = append(symbols, symbol)
d := Dimension(len(symbols)) d := Dimension(len(symbols))
symbols = append(symbols, symbol)
dimensions[symbol] = d dimensions[symbol] = d
return d return d
} }
// Unit is a type a value with generic SI units. Most useful for // SymoblExists returns whether the given symbol is already in use.
// translating between dimensions, for example, by multiplying func SymbolExists(symbol string) bool {
// an acceleration with a mass to get a force. Please see the _, ok := dimensions[symbol]
// package documentation for further explanation. return ok
}
// Unit represents a dimensional value. The dimensions will typically be in SI
// units, but can also include dimensions created with NewDimension. The Unit type
// is most useful for ensuring dimensional consistency when manipulating types
// with different units, for example, by multiplying an acceleration with a
// mass to get a force. Please see the package documentation for further explanation.
type Unit struct { type Unit struct {
dimensions Dimensions // Map for custom dimensions dimensions Dimensions
formatted string formatted string
value float64 value float64
} }
// New creates a new variable of type Unit which has the value // New creates a new variable of type Unit which has the value and dimensions
// specified by value and the dimensions specified by the // specified by the inputs. The built-in dimensions are always in SI units
// base units struct. The value is always in SI Units. // (meters, kilograms, etc.).
//
// Example: To create an acceleration of 3 m/s^2, one could do
// myvar := CreateUnit(3.0, &Dimensions{unit.LengthDim: 1, unit.TimeDim: -2})
func New(value float64, d Dimensions) *Unit { func New(value float64, d Dimensions) *Unit {
u := &Unit{ u := &Unit{
dimensions: make(map[Dimension]int), dimensions: make(map[Dimension]int),