;; Physical dimension quantity with a magnitude that is a scalar
;; Liam Healy 2011-01-01 12:05:37EST grid.lisp
;; Time-stamp: <2013-03-23 18:55:13EDT scalar.lisp>
;; Copyright 2011, 2013 Liam M. Healy
;; Distributed under the terms of the GNU General Public License
;;
;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
(in-package :antik)
(export '(physical-quantity physical-quantity-p ; already exported package def
*zero-is-dimensionless* make-pq-zero make-pq))
;;;;****************************************************************************
;;;; The class and functions to make instances
;;;;****************************************************************************
(defgeneric pq-dimension (object)
(:documentation "Dimel for the physical dimension quantity.")
(:method ((object number))
(dimension 'dimensionless)))
(defgeneric pq-magnitude (object)
(:documentation "Numerical magnitude of a physical dimension quantity.")
(:method ((object number))
object))
(defgeneric scalar-dimension (object)
(:documentation "Physical dimensions for object are a scalar.")
(:method ((object t))
(declare (ignore object))
t))
(defclass physical-quantity ()
((magnitude :initarg :magnitude :reader pq-magnitude)
(dimension :initarg :dimension :reader pq-dimension
:type (or (satisfies dimelp) array))
(scalar-dimension :initarg :scalar-dimension :reader scalar-dimension))
(:documentation "A quantity with a dimension and possibly a unit,
e.g. length or meter."))
(defun physical-quantity-p (x) (typep x 'antik:physical-quantity))
(defun make-pq-object (mag dimel scalar-dimension)
#+debug
(when (physical-quantity-p mag)
(error "Magnitude ~a can't be a PDQ." mag))
#+debug
(assert (if scalar-dimension
(dimelp dimel)
(when (arrayp dimel)
;; If 'dimel is a list but not an array for
;; scalar-dimension=nil, then this will give an error
;; that says it's not a dimel, which is wrong, but you
;; get the idea.
(if (vectorp dimel)
(or (every 'dimelp dimel) (notany 'identity dimel))
t))) ; only check vectors for non-scalar units
(dimel)
"~a is not a dimel" dimel)
(if (equal dimel (dimension 'dimensionless))
mag
(make-instance
'physical-quantity
:magnitude mag :dimension dimel :scalar-dimension scalar-dimension)))
(defparameter *zero-is-dimensionless* t
"Numbers with zero magnitude are made dimensionless.")
;;; (make-pq 33.4 "meters/second")
;;; (make-pq 88 '(/ feet minute))
;;; (make-pq 5 'speed)
;;; #_5.0_KM/S
(defun make-pq (magnitude unit-expression &optional (scalar-dimension t))
"Make a physical dimension quantity (PDQ). Allow for the
possiblity that magnitude is already a PDQ; in this case, check
that the physical dimension is correct, and pass it on. This can
be used as a way of assigning default units to a number. Unitless dimensions (e.g., 'length) are
interpeted as units in the :system-of-units. If scalar-dimension is T and
magnitude is not a scalar, the unit-expression applies to each
element of the magnitude. Otherwise it must be a grid or list of
the shape as the magnitude."
;; Check that magnitude is a pdq
(if (and (physical-quantity-p magnitude)
(check-dimension magnitude unit-expression))
magnitude
(let ((mag
;; If the magnitude has pdq elements (i.e., array), take them out.
;; Note, unlike above, no consistency check is made.
(if (grid:gridp magnitude)
(funcall (grid:elementwise #'pqval)
magnitude)
magnitude)))
(if (and (grid:gridp mag) (not scalar-dimension))
(make-pq-grid
mag
(if (grid:gridp unit-expression)
unit-expression
(make-array
(grid:dimensions unit-expression)
:element-type t
:initial-contents unit-expression)))
(make-pq-ue mag unit-expression)))))
(defun make-pq-zero (unit-expression)
(let ((*zero-is-dimensionless* nil)) (make-pq 0 unit-expression)))
(defun make-pq-ue (magnitude unit-expression)
"Make the physical dimension quantity with a unit expression from a number or grid; if a grid, the same units apply to all elements."
#+debug
(check-type
magnitude
(and (not physical-quantity) (or number (satisfies grid:gridp))))
(when (complexp magnitude)
(cerror "Accept" "Magnitude ~a is complex." magnitude))
(if (or (and *zero-is-dimensionless* (zerop magnitude))
(not unit-expression))
magnitude
(multiple-value-bind (dimel convfact)
(parse-units unit-expression)
(if (equal dimel (dimension 'dimensionless))
magnitude
(if convfact
(make-pq-object (antik:* convfact magnitude) dimel t)
(error
"~a is not a unit of measure but a physical dimension ~
and cannot be interpreted as a unit of measure ~
without a system of units."
unit-expression))))))