File: dimension.mac

package info (click to toggle)
maxima 5.9.1-9
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 32,272 kB
  • ctags: 14,123
  • sloc: lisp: 145,126; fortran: 14,031; tcl: 10,052; sh: 3,313; perl: 1,766; makefile: 1,748; ansic: 471; awk: 7
file content (327 lines) | stat: -rw-r--r-- 9,501 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
eval_when(batch,ttyoff : true)$
/*
Copyright (C) 2002 Barton Willis

Brief Description:
   Maxima code for dimensional analysis.

Author:
   Barton Willis, willisb@unk.edu
   Department of Mathematics
   University of Nebraska at Kearney
   Kearney NE 68849

License:
   GPL
*/

put('dimension,1,'version)$

/*
If e is a list, return true iff e[i] = e[j] for all
1 <= i,j <= length(e) or if e is empty.  If e isn't a
list, return false.
*/

all_equalp (e) := block([n, e1, b],
   modedeclare(n, fixnum),
   if b : listp(e) then (
      if (n : length(e)) > 1 then (
         e1 : part(e,n),
         while (b and n > 1) do (
           n : n - 1,
           b : b and is(part(e,n) = e1)))),
   b);

/*
fundamental_dimensions is a Maxima list that is used by the functions
dimensionless, natural_unit, and dimension_as_list; the function
dimension doesn't use this list.  A user may insert an remove
elements form the list; for example, to use quantities that
involve temperature, you could cons "temp" onto fundamental_dimensions.

You'll get into trouble if you call any one of the functions
dimensionless, natural_unit, or  dimension_as_list with expressions
that have dimensions not in the list fundamental_dimensions.
Be careful.

The default value of fundamental_dimensions is ["mass","length","time"];
I used strings here to try and minimize the changes of interfering
with one of your variables.  If you don't like this, it is
fine to change fundamental_dimensions to something like

fundamental_dimensions : [m,l,t];

or 

fundamental_dimensions : ["M","L","T"];
*/

assume("mass" > 0);
assume("length" > 0);
assume("time" > 0);

fundamental_dimensions : ["mass","length","time"];


/* Notes

0.  When e is a list, map dimension over the list.

1.  Use logcontract so that log(a) - log(b) is okay when a and b have the
    same dimensions.

1.5.  Under commerical macsyma a string is not a symbol; thus
    symbolp("mass") is false.  So under commerical macsyma, 
    dimension("mass") misbehaves.

2.  Maybe this should return "Unknown dimensions."

2.5  dimension(a[x]) = dimension(a) regardless of what x is.

3.  In a sum, check that all terms have the same dimensions; if not,
    signal the error "Expression is dimensionally inconsistent."

4.  ABS, SQRT, MAX, and MIN  are the only prefix functions that may
    take an argument that isn't dimensionless.

5.  Check that all function arguments are dimensionless; if not,
    signal the error "Expression is dimensionally inconsistent."

Return the dimensions of the expression e; if e is dimensionally
inconsistent, signal an error "Expression is dimensionally inconsistent."

The infix binary operators +, *, /, ^, ^^, ., = and ~ are supported.
(Maxima's vector package defines ~ to be the cross product
operator.)  For the = operator, the expression is dimensionally
inconsistent if the dimension of the right and left sides differ.

It supports the prefix unary negation and the abs function. The nary
min and max functions are supported; for the min and max functions,
all arguments must have the same dimension, otherwise, the
expression is dimensionally inconsistent.
*/


dimension (e) := block([op, dim, var, i, n],

  modedeclare([i, n], fixnum),

  if listp(e) then (                  /* 0 */
     map ('dimension, e))

  else (
  
  if not atom(e) then (
     op : part(e,0)),

  e : logcontract (e),                 /* 1 */

  if constantp (e) then (
     1)

  else if symbolp (e)  then (          /* 1.5 */
     if member(e, fundamental_dimensions) then (
        e)
     else (
        dim : get (e, 'dimension),
        if dim = false then (
            funmake ('dimension, [e]))  /* 2 */
        else (
           dim)))

  else if subvarp (e) then (          /* 2.5 */
     dimension(part(e,0)))

   else if op = "*" or op = "." or op = "~" then (
     apply ("*", map ('dimension, args (e))))

  else if op = "//" or op = "/" then (
     dimension(num(e)) / dimension(denom(e)))
      /* apply (op, map ('dimension, args (e)))) */

  else if (op = "^" or op = "^^") and dimension (part (e,2)) = 1 then (
      dimension(part(e,1)) ^ part(e,2))

  else if op = "-" then (
      dimension(part(e,1)))

  else if op = "+" or ?equal(op, 'MAX) or ?equal(op, 'MIN) then ( /*3*/ 
     dim : map ('dimension, args (e)),
     if all_equalp (dim) then (
         first (dim))
     else (
         error ("Expression is dimensionally inconsistent.")))

  else if ?equal(op, 'MATRIX) then (
     dim : map ('dimension, args (e)),
     dim : apply ('append, dim),
     if all_equalp (dim) then (
        first(dim))
     else (
         error ("Expression is dimensionally inconsistent.")))

  else if (op = "="  or op = "#" or op = "<" or op = ">" or
       op = "<=" or op = ">=" ) then (
     if part(e,1) = 0 then (
        dimension (part(e,2)))
     else if part (e,2) = 0 then (
        dimension (part(e,1)))
     else (
        dim : dimension(part(e,1)),
        if dim = dimension(part(e,2)) then (
            dim )
         else (
            error ("Expression is dimensionally inconsistent."))))

  else if ?equal(op, 'ABS) then (     /*  4 */
     dimension (part (e, 1)))

  else if ?equal(op, 'SQRT) then (
     dimension (part(e,1)) ^ (1/2))

  else if op = nounify('diff) then (
    var : args(e),
    n : length(var) - 1,
    dim : dimension (part(var,1)),
    for i : 2 thru n step 2 do (
       dim : dim / dimension (part(var,i)) ^ part(var,i+1)),
    dim)

  else if op = nounify('INTEGRATE) then (
    var : args(e),
    dim : dimension(part(var,1)) * dimension(part(var,2)),

    if length (e) = 4 and not all_equalp([dimension(part(var,2)),
        dimension(part(var,3)),dimension(part(var,4))]) then (
       error ("Expression is dimensionally inconsistent."))

    else (
       dim))

   else if op = nounify('SUM) or op = nounify('PRODUCT) then (
      error("The function dimension doesn't handle sums or products."))    

   else if op = "DIV" or  op = "div" or op = "GRAD" or op = "grad"  or
     op = "CURL" or op = "curl" then (
     if listp(part(e,1)) then (
        dim : map ('dimension, part(e,1)),
        if all_equalp(dim) then (
           dim : first(dim))
        else (
           error ("Expression is dimensionally inconsistent.")))
     else (
         dim : dimension (part(e,1))),
     dim / "length")

   else if op = "LAPLACIAN" then (
     dimension(part(e,1) / "length"^2))

   else (                                /*  5 */
     dim : map ('dimension, args(e)),
     if all_equalp (cons (1,dim)) then (
        dim : dimension(part(e,0)),
        if not atom(dim) and ?equal(part(dim,0),'dimension) then (
           1)
        else (
           dim ))
     else (
         error ("Expression is dimensionally inconsistent.")))));

/*
For the expression e,  return a list of the exponents of the
fundamental_dimensions. Thus if  fundamental_dimensions =
[mass, length, time] and c is a velocity, dimension_as_list(c)
returns [0,1,-1].
*/

dimension_as_list (e) := block([s : [ ], i, n],
  modedeclare([i,n], fixnum, s, list),
  if listp(e) then (
     map ('dimension_as_list, e))
  else (
    e : dimension (e),
    n : length(fundamental_dimensions),
    for i : 1 thru n do (
       s : endcons(hipow(e, part(fundamental_dimensions,i)), s)),
    s));



/*
Maxima translates makelist into ugly Lisp and compiled code sometimes
doesn't work; here is my version of makelist that translates into reasonable 
looking Lisp that also works when compiled.
*/

my_makelist(e,i,low,high) := block([s : [ ]],
   modedeclare([j, low, high], fixnum, s, list),
   for j from low thru high do (
      s : endcons(subst(j,i,e), s)),
   s);


/*
Return a basis for the dimensionless quantities that can be formed
from a product of powers of elements of the list e.  The basis excludes
the constants.
*/

dimensionless (e) := block([s, vars, linsolve_params : true,
    back_subst : true,globalsolve : false, solve_inconsistent_error : false,
    p, n, basis, sub, i, k, si],

  modedeclare([i, k, n], fixnum, s, list),

  if not listp(e) then (
     merror ("Argument to DIMENSIONLESS must be a list.")),

  s : map ('dimension_as_list, e),
  vars : my_makelist (p[k], k, 1,length (s)),
  s : linsolve (s . vars, vars),
  n : length(%rnum_list),
  basis : [1],

  if (n > 0 and s # [ ]) then (
     s : map ('rhs, s),
     s : apply ("*", map("^",e,s)),
     sub : map("=", %rnum_list, my_makelist(0,i,1,n)),
     for i : 1 thru n do (
        si : subst(1,%rnum_list[i], s),
        si : sublis(sub,si),
        basis : cons(si, basis))),

  basis);


 natural_unit(dim,e) := block([s,vars,k,i,n,p,basis,si,sub,linsolve_params : true,
     back_subst : true, globalsolve : false,
     solve_inconsistent_error : false],
   modedeclare([k,i,n], fixnum),

   if not listp(e) then (
      merror ("Second argument to NATURAL_UNIT must be a list.")),

   dim : dimension_as_list(dim),
   s : map ('dimension_as_list,e),
   vars : my_makelist(p[k],k,1,length(s)),
   s : linsolve(s . vars - dim, vars),
   n : length(%rnum_list),
   basis : [ ],

   if (s # [ ] and n = 0) then (
      s : map('rhs,s),
      basis : [apply("*", map ("^", e, s))])

   else if (s # [ ] and n > 0) then (
     s : map('rhs, s),
     sub : map("=", %rnum_list, my_makelist(0,i,1,n)),
     for i : 1 thru n do  (
        si : subst(1,%rnum_list[i],s),
        si : sublis(sub, si),
        si : apply("*",map ("^",e,si)),
        basis : cons(si, basis))),
     basis);


eval_when(batch,ttyoff : false)$