scalar.lisp 5.36 KB
Newer Older
1
;; Physical dimension quantity with a magnitude that is a scalar
2
;; Liam Healy 2011-01-01 12:05:37EST grid.lisp
3
;; Time-stamp: <2013-03-23 18:55:13EDT scalar.lisp>
4

5
;; Copyright 2011, 2013 Liam M. Healy
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;; 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 <http://www.gnu.org/licenses/>.

(in-package :antik)

23
24
(export '(physical-quantity physical-quantity-p ; already exported package def
	  *zero-is-dimensionless* make-pq-zero make-pq))
25
26
27
28
29

;;;;****************************************************************************
;;;; The class and functions to make instances
;;;;****************************************************************************

30
(defgeneric pq-dimension (object)
31
  (:documentation "Dimel for the physical dimension quantity.")
32
33
34
35
  (:method ((object number))
	   (dimension 'dimensionless)))

(defgeneric pq-magnitude (object)
36
  (:documentation "Numerical magnitude of a physical dimension quantity.")
37
38
39
  (:method ((object number))
	   object))

40
41
42
43
44
45
(defgeneric scalar-dimension (object)
  (:documentation "Physical dimensions for object are a scalar.")
  (:method ((object t))
	   (declare (ignore object))
	   t))

46
47
(defclass physical-quantity ()
  ((magnitude :initarg :magnitude :reader pq-magnitude)
48
49
   (dimension :initarg :dimension :reader pq-dimension
	      :type (or (satisfies dimelp) array))
50
   (scalar-dimension :initarg :scalar-dimension :reader scalar-dimension))
51
52
53
  (:documentation "A quantity with a dimension and possibly a unit,
  e.g. length or meter."))

54
55
(defun physical-quantity-p (x) (typep x 'antik:physical-quantity))

56
(defun make-pq-object (mag dimel scalar-dimension)
57
  #+debug
58
  (when (physical-quantity-p mag)
59
    (error "Magnitude ~a can't be a PDQ." mag))
60
  #+debug
61
62
  (assert (if scalar-dimension
	      (dimelp dimel)
63
64
65
66
67
68
69
70
	      (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
71
	  (dimel)
72
	  "~a is not a dimel" dimel)
73
  (if (equal dimel (dimension 'dimensionless))
74
75
76
77
      mag
      (make-instance
       'physical-quantity
       :magnitude mag :dimension dimel :scalar-dimension scalar-dimension)))
78
79
80
81
82
83
84
85
86

(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

87
(defun make-pq (magnitude unit-expression &optional (scalar-dimension t))
88
89
90
  "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
91
92
   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
93
94
95
96
   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
97
  (if (and (physical-quantity-p magnitude)
98
99
	   (check-dimension magnitude unit-expression))
      magnitude
100
      (let ((mag
101
102
103
104
105
106
	      ;; 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)))
107
108
109
110
111
112
113
114
	(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
115
116
		  :initial-contents unit-expression)))
	    (make-pq-ue mag unit-expression)))))
117

118
119
(defun make-pq-zero (unit-expression)
  (let ((*zero-is-dimensionless* nil)) (make-pq 0 unit-expression)))
120

121
(defun make-pq-ue (magnitude unit-expression)
122
  "Make the physical dimension quantity with a unit expression from a number or grid; if a grid, the same units apply to all elements."
123
124
125
126
  #+debug
  (check-type
   magnitude
   (and (not physical-quantity) (or number (satisfies grid:gridp))))
127
128
129
130
131
132
  (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)
133
	  (parse-units unit-expression)
134
135
136
137
138
139
140
141
142
	(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))))))