Forgot to load lapack in a few examples
[maxima.git] / share / contrib / unit / unit.mac
blob9748394116c911da73ba1af30d9eb0dbbb7f36d0
1 /* Filename unit.mac
3        ***************************************************************
4        *            Unit Conversion and Definition Package           *
5        *                                                             *
6        *                                                             *
7        *         This package gives one the ability to convert       *
8        *         between units, define globally all units of a       *
9        *         given property (mass, for example) in terms of      *
10        *         a base unit, and various other abilities.           *
11        *                                                             *
12        *                       Version 0.50                          *
13        *                       May 25, 2005                          *
14        *                       License:  GPL                         *
15        *                                                             *
16        *          ------------------------------------------         *
17        *      Programmed by Cliff Yapp with invaluable help from     *
18        *       Barton Willis, Robert Dodier and the Maxima list      *
19        ***************************************************************
20        
21        
22     General Design Notes:
23     
24        The variables current_let_rule_package and letrat are set on a
25        per function level in order to cause the minimum of disruption
26        to the default system behavior.
28      Changes:
30        0.50  -  convert now works on derived units, added usersetunits
31                 option to allow user to change default behavior.  New 
32                 command uforget will respect usersetunits.  Other misc
33                 changes.  Still in flux, but it's coming together.
34        0.40  -  Display of units has been fixed, with optional settings
35                 to allow grouping over the plus operator as well.  A
36                 variety of improvements, not all of them fully functional
37                 as yet.
38        0.11  -  As of this version, setunits and convert should be
39                 fully operational, although definitions are still
40                 somewhat lacking.
41        0.10  -  total rewrite using more advanced rule based
42                 techniques for handling units has begun.
43        0.03  -  fixed unitfactor so it would work on standard unit
44                 names even if setunits had already defined them. Added
45                 command findnonpivotunit which will return if defined
46                 the non pivot unit corresponding to a given conversion 
47                 factor times a given pivot unit.
48        0.02  -  added ability to control how many metric prefixes are
49                 used to generate the meta list, and a killunits function
50                 to undo what setunits does.
51        0.01  -  first development version - metalist generateable,
52                 unitfactor and setunits operational.  Only basic Mass
53                 and Time arrays present.  Metric prefixes entered.
54 ============================================================================*/ 
56 ttyoff: nolabels, true$
59 Metric prefixes
61 metricprefull : [[yotta,10^24,4],[zetta,10^21,4],[exa,10^18,4],[peta,10^15,3],
62                 [tera,10^12,3],[giga,10^9,2],[mega,10^6,2],[kilo,10^3,1],
63                 [hecto,10^2,2],[deka,10^1,2],[deci,10^-1,2],[centi,10^-2,1],
64                 [milli,10^-3,1],[micro,10^-6,2],[nano,10^-9,2],[pico,10^-12,3],
65                 [femto,10^-15,3],[atto,10^-18,4],[zepto,10^-21,4],
66                 [yocto,10^-24,4]];
67 metricpre :     [[Y,10^24,4],[Z,10^21,4],[E,10^18,4],[P,10^15,3],[T,10^12,3],
68                 [G,10^9,2],[M,10^6,2],[k,10^3,1],[h,10^2,2],[da,10^1,2],
69                 [d,10^-1,2],[c,10^-2,1],[%%m,10^-3,1],[%mu,10^-6,2],[n,10^-9,2],
70                 [p,10^-12,3],[f,10^-15,3],[a,10^-18,4],[z,10^-21,4],
71                 [y,10^-24,4]];
73 /*============================ Unit Information ============================*/ 
75 /* Seven Mutually Independent Base Dimensions 
77   The globalbaseunitlisting maintains a list of all dimensions considered to be
78   Base Dimensions.  This means all other units result from combinations of 
79   these units.  The list associated with each dimension is named %dimensionlisting
80   and is structured as follows:
81    
82    -  The first unit in each list is called the pivot unit. All other units
83       for this dimension, and all derived units containing this dimension, will be 
84       defined in terms of this unit.  The pivot unit is also the standard SI
85       unit in the case of Base Dimensions
86       
87    -  Each entry in the top level list is itself a list, containing as its 
88       first entry a list of the names to be associated with this unit.  The 
89       second position contains the conversion factor between the unit in 
90       question and the pivot unit.  In the case of a pivot unit, this value
91       is 1.  The third position is a flag which tells Maxima whether this 
92       unit is metric or not.  Metric units can be automatically generated
93       using the base name and the metric prefix lists, which contain both the
94       prefix and the power of ten associated with it.  These definitions will
95       make the following assumptions:
96         - The first unit name is the abbreviation of the unit's full name, 
97           and will append the metric abbreviation prefix.  In the case of a 
98           % at the beginning of the name, this is removed before appending
99           the prefix.   
100         - All other unit names in the list are assumed to be full names, and 
101           will have the full metric prefix appended to them.
105 globalbaseunitlisting : [%length,%mass,%time,%current,%temperature,
106 %amount_of_substance,%luminous_intensity];
108 /*Length */
109 %lengthlisting : [[[m,meter,meters],1,m],[[%in,inch,inches],0.0254,e]];
111 /*Mass*/
112 %masslisting : [[[kg,kilogram,kilograms],1,0],[[g,gram,grams],1/1000,m],
113 [[gr,grain,grains],6.479891*10^-5,e],[[slug,slugs],1.459390,e]];
115 /*Time*/
116 %timelisting : [[[s,second,seconds],1,m],[[%min,minute,minutes],60,e]];
118 /*Electric Current*/
119 %currentlisting : [[[A,ampere,amperes],1,m]];
121 /*Thermodynamic Temperature*/
122 %temperaturelisting : [[[K,kelvin,kelvins],1,m]];
124 /*Amount of Substance*/
125 %amount_of_substancelisting : [[[mol,mole,moles],1,m]];
127 /*Luminous Intensity*/
128 %luminous_intensitylisting: [[[cd,candela],1,m],[[lm,lumen],1,m]];
131 /* Derived Units
133    The structure of these lists is slightly different from the Base
134    Dimensions.  The first entry in each list describes the Derived Dimension 
135    in terms of the relevant Base Dimensions, using the pivot unit of the
136    Base Dimensions. It is a list of two entries, the definition in abbreviated
137    units and fullnames. After that, the structure is the same as Base Dimension
138    lists. globalderivedunitlisting lists all Derived Dimensions known to
139    Maxima.  A second global listing, called globalderivedunitlisting_use, is
140    used to define the search order for substitution in unit processing.*/
142 globalderivedunitlisting:[%volume,%frequency,%force,%pressure,
143 %energy,%power,%electric_charge,%electric_potential_difference,
144 %capacitance,%resistance,%conductance,%magnetic_flux,
145 %magnetic_flux_density,%inductance,%illuminance,%absorbed_dose,
146 %catalytic_activity];
148 /*Volume*/
149 %volumelisting : [m^3,[[L,liter,liters],0.001,m]];
152 /* Derived Units with Special SI Names and Symbols 
154    These are similar in structure to the Derived Unit lists above, but
155    because they have a unique name associated with them in SI the policy
156    is to have the first entry after the base dimension definition be the
157    SI unit.  Overall structure is the same.  These are listed in the same
158    globalderivedunitlisting as the previous Derived Units.
159    
160    Note - depending on circumstances, it is sometimes of interest to
161           have energy reported as work or heat in dimensional analysis.
162           Need to work on this.*/
164 /*Frequency*/
165 %frequencylisting : [1/s,[[Hz,hertz],1,m]];
167 /*Force*/
168 %forcelisting : [kg*m/s^2,[[N,%Newton,Newtons],1,m],
169 [[dyn,dyne,dynes],1*10^-5,d]];
171 /*Pressure*/
172 %pressurelisting : [kg/(m*s^2),[[Pa,pascal,pascals],1,m],[[torr],133.32239,m]];
174 /*Energy, Work, Quantity of Heat */
175 %energylisting : [kg*m^2/s^2,[[J,joule,joules],1,m],
176 [[eV,electron_volt,electron_volts],1.6021765*10^-19,m]];
178 /*Power, Radiant */
179 %powerlisting : [kg*m^2/s^3,[[W,watt,watts],1,m]];
181 /*Electric Charge, Quantity of Electricity*/
182 %electric_chargelisting : [s*A,[[C,coulomb,coulombs],1,m]];
184 /*Electric Potential Difference, Electromotive Force */ 
185 %electric_potential_differencelisting : [kg*m^2/(s^3*A),[[V,volt,volts],1,m]];
187 /*Capacitance*/
188 %capacitancelisting : [s^4*A^2/(m^2*kg),[[F,farad,farads],1,m]];
190 /*Electric Resistance*/
191 %resistancelisting : [m^2*kg/(s^3*A^2),[[Ohm,ohm,ohms],1,m]];
193 /*Electric Conductance*/
194 %conductancelisting : [s^3*A^2/(m^2*kg),[[S,siemens],1,m]];
196 /*Magnetic Flux*/
197 %magnetic_fluxlisting : [m^2*kg/(s^2*A),[[Wb,weber,webers],1,m]];
199 /*Magnetic Flux Density*/
200 %magnetic_flux_densitylisting : [kg/(s^2*A),[[T,tesla],1,m]];
202 /*Inductance*/
203 %inductancelisting : [m^2*kg/(s^2*A^2),[[H,henry,henries],1,m]];
205 /*Illuminance */
206 %illuminancelisting : [cd/m^2,[[lx,lux],1,m]];
208 /*Absorbed Dose, Specific Energy (imparted), Kerma */
209 %absorbed_doselisting : [m^2/s^2,[[Gy,gray,grays],1,m]];
211 /*Catalytic Activity */
212 %catalytic_activitylisting : [mol/s,[[kat,katal,katals],1,m]];
215 /*===================== Package Functions ====================*/ 
217 /*The default behavior of let doesn't work for the purposes of this
218   package, so we define unitlet here using Jeff Golden's trick of
219   having apply evaluate the inputs.  (Thanks to Barton Willis
220   for an elegant solution.) A source level fix to let is also
221   possible but causes a default behavior change in let:
222   
223   nisimp.lisp, line 66
224   <              (setq replacement (cdr l))
225   >              (setq replacement (list (meval(cadr l))))
226   
227   if it is ever applied we can convert back to the default let, but
228   it shouldn't matter one way or the other*/
230 unitlet(expression,ruleset) := apply('let,[expression,ruleset]);
232 /* A similar device is needed for a looping application of letsimp*/
233 unitletsimp(expression,ruleset) := apply('letsimp,[expression,ruleset]);
235 /* Something similar is also needed to kill rulesets */
236 killruleset(ruleset) := apply('kill,[ruleset]);
238 /* Tool to check for duplicates in a list - returns false if found,
239    true otherwise */
240    
241 checkforuniqueness(list1) := block([i,j,return],
242    return : true,
243    for i : 1 thru length(list1) do (
244       for j : i+1 thru length(list1) do (
245         if ?equal(list1[i],list1[j]) then (
246            return : false)
247          )),
248    return);
250 /* This function is used to remove up to two preceding % from 
251 a name when doing the concat operation (thanks to Dr. Willis)*/
252 unitconcat(a,b) := concat(a,(?stripdollar(?stripdollar(b))));
254 /* Add all desired metric dimensions into %dimensionlisting_use lists.  
255    %dimensionlisting_use lists are the one actually used in functions.  This allows
256    the program to regenerate a new metric list from "original" package lists
257    in order to avoid multiple definitions of metric prefix dimensions.  An
258    exception is made for kg, kilogram, and kilograms because they are defined
259    already as the pivot unit for mass.  This is a consequence of the nature of
260    the SI system.*/
262 metricexpandall(%limit) := block([i,j,k,l,unitlistname,
263                                   unitlistusename,tempentry],
264    modedeclare([i,j,k,l], fixnum),
265    for i : 1 thru length(globalbaseunitlisting) do  (
266      unitlistname : concat(globalbaseunitlisting[i],listing),
267      unitlistusename : concat(globalbaseunitlisting[i],listing_use),
268      unitlistusename :: ev(unitlistname),
269      for j : 1 thru length(ev(unitlistname)) do  (
270         if (?equal(unitlistname[j][3],m)) then (
271           for k : 1 thru length(metricpre) do (
272           tempentry : [[0],0,expanded],
273           if is(metricpre[k][3] <= %limit) then (
274             if (not(?equal(metricpre[k][1],'k) and 
275                 ?equal(unitlistname[j][1][1],g))) then (
276               tempentry[1][1] : unitconcat(metricpre[k][1],unitlistname[j][1][1]),
277               tempentry[2] : metricpre[k][2]*unitlistname[j][2])),
278             if is(metricprefull[k][3] <= %limit) then (
279               for l : 2 thru length(unitlistname[j][1]) do(
280               if (not(?equal(metricprefull[k][1],kilo) and 
281                      (?equal(unitlistname[j][1][l],gram) or
282                       ?equal(unitlistname[j][1][l],grams)))) then (
283               tempentry[1] : append(tempentry[1],[unitconcat(metricprefull[k][1],
284                                       unitlistname[j][1][l])])
285               ))),
286           if not(?equal(tempentry[1][1],0)) then (    
287           unitlistusename :: append(ev(unitlistusename),[tempentry])))))),
288           
289    for i : 1 thru length(globalderivedunitlisting) do  (
290      unitlistname : concat(globalderivedunitlisting[i],listing),
291      unitlistusename : concat(globalderivedunitlisting[i],listing_use),
292      unitlistusename :: ev(unitlistname),
293      for j : 2 thru length(ev(unitlistname)) do  (
294         if (?equal(unitlistname[j][3],m)) then (
295           for k : 1 thru length(metricpre) do (
296           tempentry : [[0],0,expanded],
297           if is(metricpre[k][3] <= %limit) then (
298               tempentry[1][1] : unitconcat(metricpre[k][1],unitlistname[j][1][1]),
299               tempentry[2] : metricpre[k][2]*unitlistname[j][2]),
300             if is(metricprefull[k][3] <= %limit) then (
301               for l : 2 thru length(unitlistname[j][1]) do(
302                 tempentry[1] : append(tempentry[1],[unitconcat(metricprefull[k][1],
303                                       unitlistname[j][1][l])])),
304                 unitlistusename :: append(ev(unitlistusename),[tempentry]))))))
305    );
307 /* This function creates a list of all defined units */
308 makeallunitslist() := block([i,j,k,b,allunitslisttemp],
309     allunitslisttemp : [],
310     modedeclare([i,j,k], fixnum),
311     for i : 1 thru length(globalbaseunitlisting) do  (
312      unitlistname : concat(globalbaseunitlisting[i],listing_use),
313      for j : 1 thru length(ev(unitlistname)) do  (
314        for k : 1 thru length(unitlistname[j][1]) do (
315          allunitslisttemp : append([unitlistname[j][1][k]],allunitslisttemp)
316     ))),
317     
318     /* j needs to start at the second entry for Derived Dimension Arrays */
319     for i : 1 thru length(globalderivedunitlisting_use) do  (
320      unitlistname : concat(globalderivedunitlisting_use[i],listing_use),
321      for j : 2 thru length(ev(unitlistname)) do  (
322        for k : 1 thru length(unitlistname[j][1]) do (
323          allunitslisttemp : append([unitlistname[j][1][k]],allunitslisttemp)
324     ))),
325     reverse(sort(allunitslisttemp))
326     );
328     
329 /* This function creates a ruleset converting all dimensions to one */
330 makedimentoonerules() := block([i,j,k,b,allunitslisttemp],
331     allunitslisttemp : [],
332     modedeclare([i,j,k], fixnum),
333     for i : 1 thru length(globalbaseunitlisting) do  (
334      unitlet([globalbaseunitlisting[i],1],dimentoone)
335     ),
336     
337     /* j needs to start at the second entry for Derived Dimension Arrays */
338     for i : 1 thru length(globalderivedunitlisting_use) do  (
339      unitlet([globalderivedunitlisting[i],1],dimentoone)    
340     ));
342 /* This command is used to define the display order of units, in order to
343 ensure that units appear at the end of an expression when possible.  Uses
344 the array created by makeallunitslist */
345 unitgreat(list1) := apply('ordergreat, list1);    
347           
348 /* Create unitsimplify ruleset
350    This ruleset is used to simplify unit processing.  When a unit is processed
351    a simplification of that unit using todimension will return the 
352    dimension it represents.  This enables subsequent operations on the unit to 
353    know what list to look at for conversions and at the same time simplifies 
354    the programming of dimensional analysis functionality.  The addunit command
355    will automatically add on information to this list in order to maintain the 
356    validity of the operation.  The metricexpandall function must be called 
357    before this command is run.*/
358    
359 maketodimensionruleset() := block([i,j,k,b,unitlistname],
360     modedeclare([i,j,k], fixnum),
361     for i : 1 thru length(globalbaseunitlisting) do  (
362      unitlistname : concat(globalbaseunitlisting[i],listing_use),
363      for j : 1 thru length(ev(unitlistname)) do  (
364        for k : 1 thru length(unitlistname[j][1]) do (
365          unitlet([unitlistname[j][1][k],globalbaseunitlisting[i]],
366                  todimension)
367     ))),
368     
369     /* j needs to start at the second entry for Derived Dimension Arrays */
370     for i : 1 thru length(globalderivedunitlisting_use) do  (
371      unitlistname : concat(globalderivedunitlisting_use[i],listing_use),
372      for j : 2 thru length(ev(unitlistname)) do  (
373        for k : 1 thru length(unitlistname[j][1]) do (
374          unitlet([unitlistname[j][1][k],globalderivedunitlisting_use[i]],
375                  todimension)
376     )))
377     );
379 /* Determines if an input is a Unit.  Note that units must be
380    atoms for this test to work, so this package will adopt a
381    policy of unit names being atoms only. */
383 isunit(candidate) := block([letrat:true,result],
384         if not(?atom(candidate)) then (
385            /*Error message if argument to isunit isn't an atom*/
386            (error ("Argument to isunit was not an atom.  Maxima requires units to be atoms.  Erroneous input: ",candidate))
387         ) else (
388         /* This test simply checks if the candidate will return
389            a dimension under the previously defined rules.  If it
390            does, then it is a unit.  If it does not, then it will
391            not be impacted by those simplification rules and the
392            condition of equality will be satisfied.  In such a
393            case the result is that candidate is not a unit. */
394         if ?equal(candidate,letsimp(candidate,todimension)) then (
395              /*Error if input isn't a unit - mentions the addunit command.*/
396            (error ("Input is not a unit.  To define your own unit, use the addunit command."),candidate)) 
397         else (
398         true)));
400 /* Determines if a unit is a Base Dimension */
401 isbase(unit) :=  block([letrat:true,result,dimension],
402         /*Uses the simplify to dimension ruleset and checks if the dimension
403           is present in the base list.*/
404         if not(lfreeof(globalbaseunitlisting,
405                                letsimp(unit,todimension))) then (
406                 result : true)
407         else (
408         if isunit(unit) then (result : false)),
409         result);
411 /* Returns the definition of a unit in terms of the seven base dimensions.*/
412 base(unit):= block([letrat:true,result,i,j,listname],
413         modedeclare([i,j], fixnum),
414         if not(isbase(unit)) then (
415            listname:concat(letsimp(unit,todimension),listing_use),
416            for i:1 thru length(ev(listname)) do (
417              for j:1 thru length(listname[i][1]) do (
418                 if (?equal(unit,listname[i][1][j])) then (
419                     result : listname[1]*listname[i][2] )                   
420                     )))
421         else (
422         result : unit),
423         result);
425 /* Returns the mks base units for a dimension.*/
426 dimentomks(dimension):= block([letrat:true,result,listname],
427         listname:concat(dimension,listing_use),
428         if not(lfreeof(globalbaseunitlisting,dimension)) then (
429             result : listname[1][1])
430         else (
431         if not(lfreeof(globalderivedunitlisting,dimension)) then (
432             result : listname[1])
433         else (
434         error ("dimension not found"))),
435         result);
437 /* This utility makes a list of dimensions from a list of units.  It is
438    used to validate input for setunits, and also in the derived dimension
439    ruleset process. */
440 makedimenlist(unitlist) := block([current_let_rule_package : todimension, 
441                                   return1], 
442          return1 : map('letsimp,unitlist));
444 /* This defines rules for converting all base units to MKS.  This is only
445 updated when a new base unit is added - otherwise these rules are constant. */
446 makebasetoMKSrules():= block([letrat:true,i,j,a,b,fakerule],
447    killruleset(basetoMKSrules),
448    unitlet([fakerule,fakerule],basetoMKSrules),
449    for a : 1 thru length(globalbaseunitlisting) do (
450     unitlistname : concat(globalbaseunitlisting[a],listing_use),
451     for i : 2 thru length(ev(unitlistname)) do (
452       for j : 1 thru length(unitlistname[i][1]) do (
453       unitlet([unitlistname[i][1][j], unitlistname[1][1][1]*unitlistname[i][2]],basetoMKSrules)
454            )))
457 /* This routine handles the creation of rulesets used for converting from
458 MKS base units to user selected base units. */
459 makeMKStobaserules(currentunitlist,unitlistname,baseunitrules):= 
460  block([letrat:true,i,j,a,b,fakerule],
461    killruleset(baseunitrules),
462    unitlet([fakerule,fakerule],baseunitrules),
463    for i : 1 thru length(ev(unitlistname)) do (
464      if lfreeof(unitlistname[i][1],currentunitlist[1]) then (
465        for j : 1 thru length(unitlistname[i][1]) do (
466          if ?equal(abbrevsimp,1) then (
467              unitlet([unitlistname[i][1][j],
468                 unitlistname[i][2]/currentunitlist[2]*
469                 first(currentunitlist[1])],baseunitrules)
470          )else(
471          if ?equal(abbrevsimp,2) then (
472              unitlet([unitlistname[i][1][j],
473                 unitlistname[i][2]/currentunitlist[2]*
474                 last(currentunitlist[1])],baseunitrules)
475          )else(
476              a : length(unitlistname[i][1]),
477              b : length(currentlistname[1]),
478              
479              if (?equal(a,b) or ?equal(j,1)) then (
480                  unitlet([unitlistname[i][1][j],
481                     unitlistname[i][2]/currentunitlist[2]*
482                     currentunitlist[1][j]],baseunitrules)
483              )else(
484                  unitlet([unitlistname[i][1][j],
485                     unitlistname[i][2]/currentunitlist[2]*
486                     last(currentunitlist[1])],baseunitrules)
487              ))
488         ))))
491         
492 /* This defines rules for converting all derived units to MKS.  This is only
493 updated when a new derived unit is added - otherwise these rules are constant. */
494 makederivedtoMKSrules():= block([letrat:true,i,j,a,b,fakerule],
495    killruleset(derivedtoMKSrules),
496    unitlet([fakerule,fakerule],derivedtoMKSrules),
497    for a : 1 thru length(globalderivedunitlisting_use) do (
498     unitlistname : concat(globalderivedunitlisting_use[a],listing_use),
499     for i : 2 thru length(ev(unitlistname)) do (
500       for j : 1 thru length(unitlistname[i][1]) do (
501       unitlet([unitlistname[i][1][j], base(unitlistname[i][1][j])],derivedtoMKSrules)
502            )))
506 /* This routine takes a list of definitions for a derived unit, and makes a
507 ruleset for converting from MKS to the derived dimension.*/
508 makeMKStoderivedrules(currentunit,unitlistname,derivedunitrules):= 
509  block([letrat:true,i,j,fakerule],
510    killruleset(derivedunitrules),
511    unitlet([fakerule,fakerule],derivedunitrules),
512    if lfreeof(dontuselist,letsimp(currentunit,todimension)) then (
513     for i : 2 thru length(ev(unitlistname)) do (
514       if not(lfreeof(unitlistname[i][1],currentunit)) then (
515         unitlet([unitlistname[1],unitlistname[i][1][1]/unitlistname[i][2]],derivedunitrules)
516       ))));
518 /* Sets rad, deg, Rad, Deg, RAD, and DEG to the appropriate values to do
519 what the user expects.  Maxima normally calculates in radians, hence rad is 1.*/
520 rad : 1;
521 Rad : 1;
522 RAD : 1;
523 deg : %pi/180;
524 Deg : %pi/180;
525 DEG : %pi/180;
528 /* This command is used to do final global simplification of units */
529 processunits(expression) := block([letrat:true,unitrules,rattemp,result1],
530    rattemp : ratprint,
531    if ?equal(unitverbose,off) then (ratprint:false),
532    if ?equal(unitverbose,on) then (ratprint:true),
533    unitformatresults:true,
534    if ?equal(currentdoeval,true) then (
535    result1 : letsimp(expression,derivedtoMKSrules),
536    result1 : letsimp(result1,basetoMKSrules),
537    for i : 1 thru length(globalderivedunitlisting_use) do (
538         unitrules : concat(globalderivedunitlisting_use[i],'rules),
539         result1 : unitletsimp(result1,unitrules)
540    ), 
541    result1 : letsimp(result1,%lengthrules),
542    result1 : letsimp(result1,%massrules),
543    result1 : letsimp(result1,%timerules),
544    result1 : letsimp(result1,%currentrules),
545    result1 : letsimp(result1,%temperaturerules),
546    result1 : letsimp(result1,%amount_of_substancerules),
547    result1 : letsimp(result1,%luminous_intensityrules)   
548    ) else (
549    result1 : expression),
550    currentdoeval : true,
551    ratprint : rattemp,
552    result1);
554 /* This function is used to reset variables for the next calculation */
555 resetunitvars(args) := block(unitformatresults:false,args);
557 /*====================== User Functions ======================*/ 
559 /* Returns the Maxima abbreviation used for fullunitname */
560 showabbr(fullunitname):= block([letrat:true,i,j,unittype,unitlistname,
561                          currentunitlist,flag,return1],
562       isunit(fullunitname),
563       unittype : letsimp(fullunitname,todimension),
564       unitlistname : concat(unittype,listing_use),
565       flag : 0, i:0,
566       if not(isbase(fullunitname)) then (i : i+1),
567       while ?equal(flag,0) do(
568         i : i+1,
569         if not(lfreeof(unitlistname[i][1],fullunitname)) then (
570            return1 : unitlistname[i][1][1],
571            flag : 1),
572         if ?equal(i, length(ev(unitlistname))) then (flag : 1)
573       ),
574    currentdoeval : false, /* This tells processunits not to simplify 
575                              for this operation only */
576    return1);
579 /* Returns all the full names of the unit abbr */
580 showfullname(abbr):= block([letrat:true,i,j,unittype,unitlistname,
581                          currentunitlist,flag,return1],
582       isunit(abbr),
583       unittype : letsimp(abbr,todimension),
584       unitlistname : concat(unittype,listing_use),
585       flag : 0, i:0,
586       if not(isbase(abbr)) then (i : i+1),
587       while ?equal(flag,0) do(
588         i : i+1,
589         if not(lfreeof(unitlistname[i][1],abbr)) then (
590            return1 : rest(unitlistname[i][1],1),
591            flag : 1),
592         if ?equal(i, length(ev(unitlistname))) then (flag : 1)
593       ),
594    currentdoeval : false, /* This tells processunits not to simplify 
595                              for this operation only */
596    return1);
598 /* Command to set user selected units as defaults.  Checks to ensure the
599 selected units don't contain two units of the same dimension. */
600 setunits(units):= block([letrat:true,unittype,unitlistname,
601                          unitrules,currentunitlist, flag, i, j, derivedunits],
602   if listp(units) then (
603     if (checkforuniqueness(makedimenlist(units))) then (
604       for i : 1 thru length(units) do (
605         unittype : letsimp(units[i],todimension),
606         unitlistname : concat(unittype,listing_use),
607         unitrules : concat(unittype,'rules),
608         if (not(isbase(units[i])) and (isunit(units[i]))) then (
609            makeMKStoderivedrules(units[i],unitlistname,unitrules)
610         ) else (
611         flag : 0, j:0,
612         while ?equal(flag,0) do (
613           j : j+1,
614           if not(lfreeof(unitlistname[j][1],units)) then (
615             currentunitlist : unitlistname[j],
616             flag : 1),
617           if ?equal(j, length(ev(unitlistname))) then (flag : 1)
618           ),
619         makeMKStobaserules(currentunitlist,unitlistname,unitrules)
620         ))
621     ) else (                                
622     error ("Error - two or more of these units describe the same dimension."))
623     ) else (
624     setunits([units]))
627 /* These two functions control the dontuselist array.  usedimension will remove
628 a dimension from the do not use list*/
629 usedimension(input1):= block([letrat:true,unittype,unitlistname,
630                          unitrules,currentunitlist, flag, i, j, derivedunits],
631    if ?equal(input1,letsimp(input1,todimension)) then (
632       dontuselist : delete(input1,dontuselist))
633    else (
634       temp : letsimp(input1,todimension),
635       dontuselist : delete(temp,dontuselist)
636    ),
637    print("Done.  To have Maxima simplify to this dimension, use setunits([unit])"),
638    print("to select a unit."),
639    true);
640 dontusedimension(input1):= block([letrat:true,unittype,unitlistname,
641                          unitrules,currentunitlist, temp, i, j, derivedunits],
642    if ?equal(input1,letsimp(input1,todimension)) then (
643       unitrules : concat(input1,'rules),
644       killruleset(unitrules),
645       dontuselist : append([input1],dontuselist)
646    ) else (
647       temp : letsimp(input1,todimension),
648       unitrules : concat(temp,'rules),
649       killruleset(unitrules),
650       dontuselist : append([temp],dontuselist)
651    ));
653 /* This function will reset a particular dimension to its default state, but
654 not place it on the dontuse list */   
655 uforget(input1) := block([letrat:true,unittype,unitlistname,
656                          unitrules,currentunitlist, temp, i, j, derivedunits],
657    if listp(input1) then (map('uforget,input1)) else (
658    if ?equal(input1,letsimp(input1,todimension)) then (
659       unitrules : concat(input1,'rules),
660       killruleset(unitrules),
661       if listp(usersetunits) then (
662       for i : 1 thru length(usersetunits) do (
663         if ?equal(input1,letsimp(usersetunits[i],todimension)) then (
664            setunits([usersetunits[i]]))))
665    ) else (
666       temp : letsimp(input1,todimension),
667       unitrules : concat(temp,'rules),
668       killruleset(unitrules),
669       if listp(usersetunits) then (
670       for i : 1 thru length(usersetunits) do (
671         if ?equal(temp,letsimp(usersetunits[i],todimension)) then (
672            setunits([usersetunits[i]]))))
673    )));
675 /* This function returns the dimension described by a unit */
676 dimension(input1) := letsimp(input1,todimension);
678 /* This function allows a user to set a common prefix for all unit names,
679 e.g. unit_m */
680 setunitprefix(prefix) := block([i,j,k,b],
681     print("Renaming units..."),
682     modedeclare([i,j,k], fixnum),
683     for i : 1 thru length(globalbaseunitlisting) do  (
684      unitlistname : concat(globalbaseunitlisting[i],listing_use),
685      for j : 1 thru length(ev(unitlistname)) do  (
686        for k : 1 thru length(unitlistname[j][1]) do (
687          unitlistname[j][1][k] : unitconcat(prefix,unitlistname[j][1][k])
688     ))),
689     
690     /* j needs to start at the second entry for Derived Dimension Arrays */
691     for i : 1 thru length(globalderivedunitlisting_use) do  (
692      unitlistname : concat(globalderivedunitlisting_use[i],listing_use),
693      for j : 2 thru length(ev(unitlistname)) do  (
694        for k : 1 thru length(unitlistname[j][1]) do (
695         unitlistname[j][1][k] : unitconcat(prefix,unitlistname[j][1][k]) 
696     ))),
697     print("Rebuilding unit conversion rules..."),
698     maketodimensionruleset(),
699     makebasetoMKSrules(),
700     makederivedtoMKSrules(),
701     makedimentoonerules(),
702     allunitslist : makeallunitslist(),
703     currentunitprefix : prefix,
704     print("done")
707 /* Undoes setunitprefix, or more properly resets things to their
708 initial state */
709 clearunitprefix():= block([i,j,k,b],
710     print("Renaming units..."),
711     metricexpandall(%unitexpand),
712     print("Rebuilding unit conversion rules..."),
713     maketodimensionruleset(),
714     makebasetoMKSrules(),
715     makederivedtoMKSrules(),
716     makedimentoonerules(),
717     allunitslist : makeallunitslist(),
718     currentunitprefix : "",
719     print("done")
720 );    
722 /*  Not yet implemented
724 /* Prints out information about a unit */
725 unitinfo(unit):= block([letrat:true,result,dimension],
727 /*  Allows the user to create their own units */
728 addunits([[unitname(s)],definition,0]):=
731 /*  Converts an expression into one using the specified units. Or the
732 user can specify MKS instead of a list of units, to have all units
733 rendered in terms of the seven base dimensions and their associated MKS
734 units. */
735 convert(expression,desiredunits):= block([letrat:true,convertrules,result1,
736                                           currentunitlist,flag,i,j],
737    if (?equal(desiredunits,MKS) or ?equal(desiredunits,mks)) then (
738     result1 : letsimp(expression,derivedtoMKSrules),
739     result1 : letsimp(result1,basetoMKSrules)
740    ) else (
741    result1 : letsimp(expression,derivedtoMKSrules),
742    result1 : letsimp(result1,basetoMKSrules),
743    if listp(desiredunits) then (
744     if (checkforuniqueness(makedimenlist(desiredunits))) then (
745       for i : 1 thru length(desiredunits) do (
746         unittype : letsimp(desiredunits[i],todimension),
747         unitlistname : concat(unittype,listing_use),
748         if (not(isbase(desiredunits[i])) and (isunit(desiredunits[i]))) then (
749            makeMKStoderivedrules(desiredunits[i],unitlistname,convertrules)
750         ) else (
751         if isunit(desiredunits[i]) then (
752         flag : 0, j:0,
753         while ?equal(flag,0) do (
754           j : j+1,
755           if not(lfreeof(unitlistname[j][1],desiredunits)) then (
756             currentunitlist : unitlistname[j],
757             flag : 1),
758           if ?equal(j, length(ev(unitlistname))) then (flag : 1)
759           ),
760         makeMKStobaserules(currentunitlist,unitlistname,convertrules)
761         ) else (
762         error("Non unit argument to convert"))
763         ),
764         result1 : letsimp(result1,convertrules))
765     ) else (                                
766     error ("Error - two or more of these units describe the same dimension."))
767     ) else (
768     result1 : convert(expression,[desiredunits]))),
769  currentdoeval : false, /* This tells processunits not to simplify 
770                            for this operation only */
771  result1);    
773 /* Associates a variable and a dimension/dimension, e.g. t and %time */
774 assigndimension(exp,dimension) := block([],
775      if (not(lfreeof(globalbaseunitlisting,dimension)) or 
776                 not(lfreeof(globalderivedunitlisting))) then (
777      variabledimensionslist : append([[exp,dimension]],variabledimensionslist),
778      apply('qput,[exp,dimension,%dimension])
779      ) else (
780      (error ("Invalid dimension.  Valid entries are:")),
781      print(globalbaseunitlisting),
782      print(globalderivedunitlisting)));
784 /* Basic dimensional function.  This doesn't have the power of dimension.mac but
785 it will work for basic cases */
786 dimension(exp) := block([letrat:true,tmp1,tmp2,a1,a2,result1,i,j],
787      killruleset(dimenrules),
788      unitlet([fakerule,fakerule],dimenrules),
789      unitlet([fakerule,fakerule],dimentoone),
790      for i:1 thru length(variabledimensionslist) do (
791         unitlet([variabledimensionslist[i][1],variabledimensionslist[i][2]],dimenrules)),
792      exp : processunits(exp),
793      tmp1 : letsimp(letsimp(exp,dimenrules),todimension),
794      tmp2 : letsimp(letsimp(letsimp(exp,todimension),dimenrules),dimentoone),
795      result1 : tmp1/tmp2,
796      result1);
798 /* These functions turn on and off grouping common units over addition */
799 enablegroupbyadd() := block(post_eval_functions : [processunits,?groupadd]);
800 disablegroupbyadd() := block(post_eval_functions : [processunits]);
802 /*====================== Initialization =======================*/
804 /* Startup Message */
805 print("*******************************************************************");
806 print("*                       Units version 0.50                        *");
807 print("*          Definitions based on the NIST Reference on             *");
808 print("*              Constants, Units, and Uncertainty                  *");
809 print("*       Conversion factors from various sources including         *");
810 print("*                   NIST and the GNU units package                *");
811 print("*******************************************************************");
812 print("");
813 print("Redefining necessary functions...");
815 /* This file contains the lisp level definitions required to properly
816 work with units */
817 load("contrib/unit/unit-functions.lisp")$
819 print("Initializing unit arrays...");
821 /* This variable controls how many of the metric prefixes are added
822    on to the default lists*/
823 %unitexpand : 2;
825 /* This is a list which contains dimensions the user doesn't want
826 simplified by the derived units simplifier.  Default is empty. */
827 dontuselist : [];
829 /* By default, we want to simplify addition of terms with units.  Currently
830 this is disabled due to bugs */
831 unitsimpadd : false;
833 /* Places all relevant metric definitions into the %dimensionlist lists.*/
834 metricexpandall(%unitexpand);
836 /* This list dictates in what order Maxima will look for derived units.
837 Initially an attempt was made to automate this process, but it proved
838 rather difficult to automate.  (For example, given an expression:
839          3
840      kg m
841      -----
842         2
843        s
844 this can be simplified to:
845      kg L          2            2
846      ----   or  N m   or kg*L*Hz
847        2
848       s                                            2
849 Normal practice would of course be to adopt the N m  answer, but
850 this convention cannot be readily expressed in an algorithmic fashion
851 for all cases dealt with in this package. Hence, for the time being a 
852 manual definition of the simplification order has been adopted.*/
854 globalderivedunitlisting_use : [%capacitance,%resistance,%conductance,
855 %electric_potential_difference,%magnetic_flux,
856 %magnetic_flux_density,%inductance,%electric_charge,%power,
857 %energy,%force,%pressure,%volume,%frequency,%illuminance,%absorbed_dose,
858 %catalytic_activity];
860 /* Variable to control how simplification behaves with respect to 
861    fullnames and abbreviations.  The default, 0, simplifies abbrev. to
862    abbrev. and fullnames to fullnames, also preserving plurals when
863    possible.  There are two other possible settings:
864       1 : fullname -> abbreviation (singular and plural fullnames -> abbrev.)
865       2 : abbreviation -> fullname (will use plural form) */
866 abbrevsimp : 0;
867       
868 /* Create default rulesets*/
869 maketodimensionruleset();
870 makebasetoMKSrules();
871 makederivedtoMKSrules();
872 makedimentoonerules();
874 /* Create a list of all units */
875 allunitslist : makeallunitslist();
876 print("Done.");
878 /* Basic dimension functionality - initialize list of dimensions in use */    
879 variabledimensionslist : [];
881 /* This variable is used to ensure Maxima only formats units at the end
882 of an evaluation */
883 unitformatresults : false;
885 /* This option controls the reporting of numerical warning messages in rat
886 substitutions during unit processing.  Default is off since this output is
887 rather verbose and normally not needed. */
888 unitverbose : off;
890 /* This routine will create a default user environment according to pre-set
891 user requests.  uforget will also revert to this state. */
892 if listp(usersetunits) then (
893   print("User defaults found..."),
894   setunits(usersetunits),
895   print("User defaults initialized.")
898 /* Use pre eval to reset unitformatresults to false after a calculation - this
899 is necessary because if it is reset at the end of the earlier calculation the
900 unit formatting will be undone in the display process */
901 pre_eval_functions : [resetunitvars];
903 /* Activate processunits as a post_eval_function in order to enable
904    automatic simplification of all unit output, and groupadd to enable
905    sorting by common unit. */
906 post_eval_functions : [processunits,?groupadd];
908 /* This will ensure processunits starts out in working mode. */
909 currentdoeval : true;
911 ttyoff: nolabels, false$