avector.red 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. module avector; % Vector algebra and calculus package.
  2. create!-package('(avector),'(contrib avector));
  3. global '(avector!-loaded!*);
  4. avector!-loaded!* := t; % To keep CSL happy.
  5. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  6. % %
  7. % Copyright notice %
  8. % ---------------- %
  9. % %
  10. % Author : David Harper %
  11. % Computer Laboratory, %
  12. % University of Liverpool %
  13. % P.O. Box 147 %
  14. % Liverpool L69 3BX %
  15. % ENGLAND %
  16. % %
  17. % (adh@maths.qmw.ac.uk) %
  18. % %
  19. % Date : 29 February 1988 %
  20. % %
  21. % Title : Vector algebra and calculus package %
  22. % %
  23. % Copyright (c) David Harper 1988 %
  24. % %
  25. % %
  26. % Permission is granted to any individual or institution to %
  27. % use, copy or re-distribute this software so long as it is %
  28. % not sold for profit, provided that this copyright notice %
  29. % is retained and the file is not altered. %
  30. % %
  31. % %
  32. % End of copyright notice %
  33. % %
  34. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  35. % %
  36. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  37. % *************** This code is designed to operate *************** %
  38. % *************** with version 3.4 of REDUCE and *************** %
  39. % *************** the Standard Lisp interpreter. *************** %
  40. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  41. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  42. % %
  43. % VECTOR DECLARATIONS MODULE %
  44. % %
  45. % This section contains the routines required to interface the %
  46. % vector package to REDUCE. The most important routine is the %
  47. % vector predicate function VECP which tests an expression to %
  48. % determine whether it must be evaluated using the routine %
  49. % VECSM*. %
  50. % %
  51. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  52. fluid '(!*coords !*csystems !*vectorfunctionlist
  53. !*vtrace !*vectortracelevel!*);
  54. switch vtrace;
  55. symbolic procedure vecp u;
  56. begin scalar x;
  57. return
  58. if null u or numberp u then nil
  59. else if atom u then (get(u,'rtype)='!3vector or threevectorp u)
  60. else if threevectorp u then t
  61. else if (atom(x:=car u) and get(x,'rtype)='!3vector)
  62. then isvectorindex cadr u
  63. else if flagp(x,'vectorfn) then t
  64. else if (flagp(x,'varithop) or flagp(x,'vectormapping))
  65. then hasonevector cdr u
  66. else nil;
  67. end;
  68. % The following procedure checks for a vector with three components
  69. symbolic procedure threevectorp u;
  70. vectorp u and (upbv u = 2);
  71. % The following procedure checks to ensure that the arg list contains
  72. % at least one vector
  73. symbolic procedure hasonevector u;
  74. if null u then nil
  75. else (vecp car u) or (hasonevector cdr u);
  76. % The following procedure checks that the arg list consists entirely
  77. % of vectors
  78. symbolic procedure areallvectors u;
  79. if null u then nil
  80. else if null cdr u then vecp car u
  81. else (vecp car u) and (areallvectors cdr u);
  82. % The following function checks to see whether its argument is a valid
  83. % vector index i.e. whether it evaluates to an integer 0,1 or 2 or
  84. % is equal to one of the coordinate names
  85. symbolic procedure isvectorindex u;
  86. not null getvectorindex(u,t);
  87. % The following function evaluates its argument to a vector index
  88. % or NIL if it isn't an integer 0,1 or 2 or one of the coordinate
  89. % names. Set FLG to true if hard-error is required for invalid
  90. % argument.
  91. symbolic procedure getvectorindex(u,flg);
  92. begin scalar vindx;
  93. vindx := u;
  94. if not fixp vindx then vindx:=locate(vindx,!*coords);
  95. if ((null vindx) or (fixp vindx and (vindx<0 or vindx>2)))
  96. and flg then rerror(avector,1,list(u,"not a valid vector index"));
  97. return vindx
  98. end;
  99. % This routine gives the position of an object in a list. The first
  100. % object is numbered zero. Returns NIL if the object can't be found.
  101. symbolic procedure locate(u,v);
  102. if not (u memq v) then nil
  103. else if u=car v then 0
  104. else 1+locate(u,cdr v);
  105. % We may as well define some utility operators here too.
  106. symbolic smacro procedure first u; car u;
  107. symbolic smacro procedure second u; cadr u;
  108. symbolic smacro procedure third u; caddr u;
  109. % Here we redefine getrtype1 and getrtype2 to handle vectors.
  110. remflag('(getrtype1 getrtype2),'lose); % We must use these definitions.
  111. symbolic procedure getrtype1 u;
  112. if threevectorp u then '!3vector else nil;
  113. symbolic procedure getrtype2 u;
  114. begin scalar x;
  115. % Next line is maybe only needed by EXCALC.
  116. return if vecp u then '!3vector
  117. else if (x:= get(car u,'rtype)) and (x:= get(x,'rtypefn))
  118. then apply1(x,cdr u)
  119. else if x := get(car u,'rtypefn) then apply1(x,cdr u)
  120. else nil
  121. end;
  122. % The following function declares a list of objects as vectors.
  123. symbolic procedure vec u;
  124. begin scalar y;
  125. for each x in u do
  126. << % Check that the identifier is not already declared as an array
  127. % or matrix or function
  128. if not atom x then write("Cannot declare ",x," as a vector")
  129. else
  130. << y := gettype x;
  131. if y memq '(array procedure matrix operator) then
  132. write("Object ",x," has already been declared as ",y)
  133. else makenewvector x
  134. >>;
  135. >>;
  136. return nil
  137. end;
  138. deflist('((vec rlis)),'stat);
  139. % This procedure actually creates a vector.
  140. symbolic procedure makenewvector u;
  141. begin
  142. % write("Declaring ",U," a vector");terpri();
  143. put(u,'rtype,'!3vector);
  144. put(u,'avalue,list('vector,mkvect(2)));
  145. return nil
  146. end;
  147. % Vector function declarations : these are the routines that link
  148. % our new data type into the REDUCE system.
  149. put('!3vector,'letfn,'veclet); % Assignment routine
  150. put('!3vector,'name,'!3vector); % Our name for the data type
  151. put('!3vector,'evfn,'!*vecsm!*); % Evaluation function
  152. put('!3vector,'prifn,'vecpri!*); % Printing function
  153. flag('(!3vector),'sprifn);
  154. symbolic procedure vecpri!*(u,v,w);
  155. vecpri(u,v);
  156. % The following routine prints out a vector in a neat way (cf.
  157. % the way in which matrices are printed !)
  158. symbolic procedure vecpri(u,x);
  159. begin scalar y,v0,v1,v2,xx;
  160. y:= if vectorp u then u else getavalue u;
  161. xx := x;
  162. if null y then return nil;
  163. % if null x then xx := 'vec;
  164. xx := 'vec;
  165. v0 := getv(y,0);
  166. v1 := getv(y,1);
  167. v2 := getv(y,2);
  168. v0 := aeval v0; v1 := aeval v1; v2 := aeval v2;
  169. assgnpri(v0,list list(xx,first !*coords),'only);
  170. assgnpri(v1,list list(xx,second !*coords),'only);
  171. assgnpri(v2,list list(xx,third !*coords),'only);
  172. terpri!* t;
  173. end;
  174. symbolic procedure getavalue u;
  175. (if x then cadr x else nil) where x=get(u,'avalue);
  176. symbolic procedure indexedvectorp u;
  177. (vecp car u) and (isvectorindex cadr u);
  178. put('!3vector,'setelemfn,'setvectorelement);
  179. % The following function sets one element of a vector object
  180. symbolic procedure setvectorelement(u,v);
  181. begin scalar vindx;
  182. vindx := getvectorindex(cadr u,t);
  183. putv(getavalue car u,vindx,v);
  184. return nil
  185. end;
  186. % If SETK is invoked with an vector atom as its first argument, then
  187. % control will be passed to the routine VECLET
  188. symbolic procedure veclet(u,v,utype,b,vtype);
  189. begin
  190. if zerop v then return setvectortozero(u,utype);
  191. if not equal(vtype,'!3vector)
  192. then rerror(avector,2,"RHS is not a vector");
  193. if equal(utype,'!3vector) then put(u,'avalue,list('!3vector,v))
  194. else if utype memq '(array matrix) then
  195. rerror(avector,3,list(u,"already defined as ",utype))
  196. else
  197. << % We force U to be a vector
  198. vec u;
  199. write("*** ",u," re-defined as vector"); terpri();
  200. put(u,'avalue,list('vector,v))
  201. >>;
  202. return v
  203. end;
  204. % A quick and dirty way of declaring a null vector
  205. symbolic procedure setvectortozero(u,utype);
  206. begin scalar x;
  207. x := mkvect 2;
  208. for k:=0:2 do putv(x,k,aeval 0);
  209. return veclet(u,x,utype,t,'!3vector)
  210. end;
  211. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  212. % %
  213. % VECTOR EVALUATION MODULE %
  214. % %
  215. % This section contains the routines required to evaluate vector %
  216. % expressions. The main routine, VECSM!*, is mainly table-driven. %
  217. % If you wish to include your own routines then you should be %
  218. % aware of the mechanism used to invoke vector evaluation. %
  219. % %
  220. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  221. symbolic procedure !*vecsm!*(u,v); vecsm!* u;
  222. % The following routine is the main vector evaluation procedure. It
  223. % takes a single argument which is either a vector atom or a
  224. % vector expression in SLISP function form. The value returned may
  225. % be a vector or scalar.
  226. % Note that if the argument is not a vector expression then an
  227. % error results. This is true in particular if the argument is
  228. % an expression in standard-quotient form i.e. a list whose CAR
  229. % is *SQ.
  230. !*vectortracelevel!* := 0;
  231. symbolic procedure prtblanks n;
  232. for k:=1:min(n,15) do write " ";
  233. symbolic procedure vecsimp!* u;
  234. if vecp u then vecsm!* u else u;
  235. symbolic procedure vecsm!* u;
  236. begin scalar y,vecopr,vargs,v;
  237. !*vectortracelevel!* := !*vectortracelevel!* + 1;
  238. if !*vtrace then <<prtblanks(!*vectortracelevel!*);
  239. write("VECSM called with args ",u);
  240. terpri()>>;
  241. if atom u then v := (if vectorp u then u else getavalue u)
  242. else if threevectorp u then v := u
  243. else if (atom(y:= car u) and get(y,'rtype)='!3vector) then
  244. v := getv(getavalue y,getvectorindex(cadr u,t))
  245. % Those were the simple cases. Now for the tricky operators
  246. % Separate the operator from its operands
  247. else <<
  248. vecopr := car u;
  249. vargs := for each j in cdr u collect vecsimp!* j;
  250. % Select a course of action dependent upon the operator
  251. if y := get(vecopr,'vectorfunction) then
  252. % We must check (if the op is an arithmetic operator)
  253. % to ensure that there are vectors in the argument list
  254. << if (flagp(vecopr,'vectorfn) or (flagp(vecopr,'varithop) and
  255. hasonevector vargs)) then v := apply(y,list vargs)
  256. else v := aeval append(list(vecopr),vargs)
  257. >>
  258. else if flagp(vecopr,'vectormapping) then
  259. << % Check that the argument is really a vector
  260. y := car vargs;
  261. v := if threevectorp y then vectorapply(vecopr,y,cdr vargs)
  262. else scalarapply(vecopr,y,cdr vargs)
  263. >>
  264. else <<!*vectortracelevel!* := 0;
  265. rerror(avector,4,
  266. list(vecopr,"is not a valid vector operator"))>>;
  267. >>;
  268. if !*vtrace then <<
  269. y := threevectorp v;
  270. prtblanks(!*vectortracelevel!*);
  271. write(if y then "** Vector" else "** Scalar"," result is ",v);
  272. terpri()>>;
  273. !*vectortracelevel!* := !*vectortracelevel!* - 1;
  274. return v
  275. end;
  276. % Now we define a function to declare a list of scalar functions as
  277. % valid in vector mode too. This means that we can pass them vector
  278. % arguments and get a vector result.
  279. symbolic procedure vectormapping u;
  280. flag(u,'vectormapping);
  281. deflist('((vectormapping rlis)),'stat);% Deflist used for bootstrapping.
  282. % We will allow the basic transcendental functions to be vector-valued.
  283. % Then we can, for example, evaluate Sin of a vector.
  284. vectormapping 'sin,'cos,'log,'exp,'tan,'asin,'atan,'sinh,'cosh,'tanh;
  285. vectormapping 'quotient,'minus,'df,'int,'sqrt;
  286. % We must put appropriate flags upon the arithmetic operators and
  287. % vector operators too ...
  288. flag('(sub minus difference quotient plus times expt),'varithop);
  289. flag('(avec cross dot vmod grad div curl delsq),'vectorfn);
  290. % We must now define the procedures to carry out vector algebra and
  291. % calculus. They must be given a VECTORFUNCTION property
  292. symbolic smacro procedure vectorfn(oper,vfn);
  293. put(oper,'vectorfunction,vfn);
  294. % Scalar-vector multiplication
  295. vectorfn('times,'vectormultiply);
  296. symbolic procedure vectormultiply vargs;
  297. % This routine multiplies together a list made up of scalars,
  298. % 3x3 matrices and a vector. Note that the combinations
  299. % vector*vector and vector*matrix are illegal.
  300. begin scalar lht,rht,lhtype,rhtype;
  301. lht := aeval car vargs; % Begin with first multiplicand
  302. for each v in cdr vargs do
  303. << % Deal with each multiplicand in turn
  304. rht := if vecp v then vecsm!* v
  305. else v;
  306. lhtype := !*typeof lht;
  307. rhtype := !*typeof rht;
  308. lht :=
  309. if not (lhtype='!3vector or rhtype='!3vector) then
  310. aeval list('times,lht,rht)
  311. else if lhtype='!3vector then
  312. if null rhtype then vectorapply('times,lht,list rht)
  313. else rerror(avector,5,"Illegal operation vec*vec or vec*mat")
  314. else if null lhtype then vectorapply('times,rht,list lht)
  315. else matrixtimesvector(lht,rht)
  316. >>;
  317. return lht
  318. end;
  319. % Multiplication of a vector by a 3x3 matrix from the left
  320. symbolic procedure matrixtimesvector(mymat,myvec);
  321. begin scalar rows,myrow,x;
  322. if atom mymat and idp mymat and null getavalue mymat then
  323. rerror(avector,6,"Unset matrix in vector multiplication");
  324. rows := if idp mymat then cdr getavalue mymat else cdr mymat;
  325. if not (length(rows)=3 and length(car rows)=3) then
  326. rerror(avector,7,"Matrix must be 3x3 for vector multplication");
  327. x := mkvect(2);
  328. for k:=0:2 do
  329. << % Multiply out a row at a time
  330. myrow := car rows;
  331. putv(x,k,aeval list('plus,
  332. list('times, first myrow, getv(myvec,0)),
  333. list('times, second myrow, getv(myvec,1)),
  334. list('times, third myrow, getv(myvec,2))));
  335. rows := cdr rows
  336. >>;
  337. return x
  338. end;
  339. symbolic procedure !*typeof u;
  340. getrtype u;
  341. % if vecp u then '!3vector
  342. % else if matp u then 'matrix
  343. % else if arrayp u then 'array
  344. % else nil;
  345. % Vector addition
  346. vectorfn('plus,'vectorplus);
  347. symbolic procedure vectorplus vargs;
  348. % Add an arbitrarily-long list of vectors
  349. begin scalar x;
  350. x := vecsm!* car vargs;
  351. for each v in cdr vargs do x:=vectoradd(x,vecsm!* v);
  352. return x
  353. end;
  354. symbolic procedure vectoradd(u,v);
  355. % Add two vectors or two scalars
  356. begin scalar x,uisvec,visvec;
  357. uisvec := vecp u; visvec := vecp v;
  358. if uisvec and visvec then
  359. << % Adding two vectors
  360. x :=mkvect(2);
  361. for k:=0:2 do putv(x,k,aeval list('plus, getv(u,k), getv(v,k)));
  362. return x
  363. >>
  364. else if not (uisvec or visvec) then
  365. << % Adding two scalars
  366. return aeval list('plus, u, v)
  367. >>
  368. else rerror(avector,8,"Type mismatch in VECTORADD");
  369. end;
  370. % Difference of two vectors
  371. vectorfn('difference,'vectordiff);
  372. symbolic procedure vectordiff vargs;
  373. % Vector - Vector
  374. begin scalar x,y;
  375. x := vecsm!* car vargs;
  376. y := vecsm!* list('minus,cadr vargs); % Negate the second operand
  377. return vectoradd(x,y)
  378. end;
  379. % General case of a quotient involving vectors
  380. vectorfn('quotient,'vectorquot);
  381. symbolic procedure vectorquot vargs;
  382. % This code deals with the cases
  383. %
  384. % (1) Vector / scalar
  385. % (2) Vector / (scalar-valued vector expression)
  386. % (3) Scalar / (scalar-valued vector expression)
  387. %
  388. begin scalar vdivisor,vdividend;
  389. vdivisor := aeval cadr vargs;
  390. if vecp vdivisor
  391. then rerror(avector,9,"Attempt to divide by a vector");
  392. vdividend := aeval car vargs;
  393. if threevectorp vdividend then return vectorapply('quotient,
  394. vdividend, list vdivisor)
  395. else return aeval list('quotient, vdividend, vdivisor);
  396. end;
  397. % Vector cross product
  398. vectorfn('cross,'vectorcrossprod);
  399. symbolic procedure vectorcrossprod vargs;
  400. begin scalar x,y,u0,u1,u2,v0,v1,v2,w0,w1,w2;
  401. x := vecsm!* car vargs;
  402. y := vecsm!* cadr vargs;
  403. u0 := getv(x,0); u1 := getv(x,1); u2 := getv(x,2);
  404. v0 := getv(y,0); v1 := getv(y,1); v2 := getv(y,2);
  405. % Calculate each component of the cross product
  406. w0 := aeval list('difference,
  407. list('times,u1,v2),
  408. list('times,u2,v1));
  409. w1 := aeval list('difference,
  410. list('times,u2,v0),
  411. list('times,u0,v2));
  412. w2 := aeval list('difference,
  413. list('times,u0,v1),
  414. list('times,u1,v0));
  415. x := mkvect(2);
  416. putv(x,0,w0); putv(x,1,w1); putv(x,2,w2);
  417. return x
  418. end;
  419. % Vector modulus
  420. vectorfn('vmod,'vectormod);
  421. % There are two definitions due to the existence of a bug in the REDUCE
  422. % code for SQRT : in the IBM version of REDUCE 3.3 an attempt to take
  423. % SQRT of 0 results in an error, so I have coded round it.
  424. % The first version which follows is the succinct version which will
  425. % work if SQRT(0) doesn't give an error.
  426. % symbolic procedure vectormod u;
  427. % aeval list('sqrt,list('dot,car u,car u));
  428. % This version is a little longer but it works even if SQRT(0) doesn't.
  429. symbolic procedure vectormod u;
  430. begin scalar v;
  431. v := aeval list('dot, car u, car u);
  432. if zerop v then return 0
  433. else return aeval list('sqrt,v);
  434. end;
  435. % Vector dot product
  436. vectorfn('dot,'vectordot);
  437. symbolic procedure vectordot vargs;
  438. begin scalar x,y,u0,u1,u2,v0,v1,v2;
  439. x := car vargs;
  440. y := cadr vargs;
  441. u0 := getv(x,0); u1 := getv(x,1); u2 := getv(x,2);
  442. v0 := getv(y,0); v1 := getv(y,1); v2 := getv(y,2);
  443. % Calculate the scalar product
  444. return aeval list('plus,
  445. list('times,u0,v0),
  446. list('times,u1,v1),
  447. list('times,u2,v2))
  448. end;
  449. % Component-wise assignment of a vector (AVEC)
  450. vectorfn('avec,'vectoravec);
  451. deflist('((oper vfn)),'vectorfunction); % For bootstrapping.
  452. symbolic procedure vectoravec vargs;
  453. begin scalar x;
  454. % Build a vector from the argument list
  455. if not eqn(length(vargs),3) then
  456. rerror(avector,10,"Incorrect number of args in AVEC");
  457. x := mkvect(2);
  458. putv(x,0,aeval first vargs);
  459. putv(x,1,aeval second vargs);
  460. putv(x,2,aeval third vargs);
  461. return x
  462. end;
  463. % Gradient of a scalar
  464. vectorfn('grad,'vectorgrad);
  465. symbolic procedure vectorgrad vargs;
  466. begin scalar x,y;
  467. x := mkvect(2);
  468. y := aeval car vargs;
  469. putv(x,0,aeval list('quotient,
  470. list('df,y,first !*coords),
  471. !*hfac 0));
  472. putv(x,1,aeval list('quotient,
  473. list('df,y,second !*coords),
  474. !*hfac 1));
  475. putv(x,2,aeval list('quotient,
  476. list('df,y,third !*coords),
  477. !*hfac 2));
  478. return x
  479. end;
  480. % Divergence of a vector
  481. vectorfn('div,'vectordiv);
  482. symbolic procedure vectordiv vargs;
  483. begin scalar x,u0,u1,u2;
  484. x := vecsm!* car vargs;
  485. u0 := getv(x,0); u1 := getv(x,1); u2 := getv(x,2);
  486. u0 := aeval list('times,u0,!*hfac 1,!*hfac 2);
  487. u1 := aeval list('times,u1,!*hfac 0,!*hfac 2);
  488. u2 := aeval list('times,u2,!*hfac 0,!*hfac 1);
  489. x := aeval list('plus,
  490. list('df,u0,first !*coords),
  491. list('df,u1,second !*coords),
  492. list('df,u2,third !*coords));
  493. x := aeval list('quotient,x,list('times,
  494. !*hfac 0,!*hfac 1,!*hfac 2));
  495. return x
  496. end;
  497. % Curl of a vector
  498. vectorfn('curl,'vectorcurl);
  499. symbolic procedure vectorcurl vargs;
  500. begin scalar x,u0,u1,u2,v0,v1,v2,w0,w1,w2;
  501. x := vecsm!* car vargs;
  502. u0 := aeval list('times,getv(x,0),!*hfac 0);
  503. u1 := aeval list('times,getv(x,1),!*hfac 1);
  504. u2 := aeval list('times,getv(x,2),!*hfac 2);
  505. v0 := first !*coords; v1 := second !*coords; v2 := third !*coords;
  506. x := mkvect(2);
  507. w0 := aeval list('times,
  508. list('difference,
  509. list('df,u2,v1),
  510. list('df,u1,v2)),
  511. !*hfac 0);
  512. w1 := aeval list ('times,
  513. list('difference,
  514. list('df,u0,v2),
  515. list('df,u2,v0)),
  516. !*hfac 1);
  517. w2 := aeval list('times,
  518. list('difference,
  519. list('df,u1,v0),
  520. list('df,u0,v1)),
  521. !*hfac 2);
  522. putv(x,0,w0); putv(x,1,w1); putv(x,2,w2);
  523. x := aeval list('quotient,
  524. x,
  525. list('times, !*hfac 0, !*hfac 1, !*hfac 2));
  526. return x
  527. end;
  528. % Del-squared (Laplacian) of a scalar or vector
  529. vectorfn('delsq,'vectordelsq);
  530. symbolic procedure vectordelsq vargs;
  531. begin scalar x,y,v0,v1,v2,w0,w1,w2;
  532. x := vecsm!* car vargs;
  533. if vecp x then
  534. % Cunning definition of Laplacian of a vector in terms of
  535. % grad, div and curl
  536. return aeval list('difference,
  537. list('grad, list('div,x)),
  538. list('curl, list('curl,x)))
  539. else
  540. << % Laplacian of a scalar ... which simply requires lots of
  541. % calculus
  542. if null x then x := car vargs;
  543. y := aeval list('times,!*hfac 0, !*hfac 1, !*hfac 2);
  544. v0 := first !*coords;
  545. v1 := second !*coords;
  546. v2 := third !*coords;
  547. w0 := aeval list('df,
  548. list('quotient,
  549. list('times,
  550. !*hfac 1,
  551. !*hfac 2,
  552. list('df,x,v0)),
  553. !*hfac 0),
  554. v0);
  555. w1 := aeval list('df,
  556. list('quotient,
  557. list('times,
  558. !*hfac 2,
  559. !*hfac 0,
  560. list('df,x,v1)),
  561. !*hfac 1),
  562. v1);
  563. w2 := aeval list('df,
  564. list('quotient,
  565. list('times,
  566. !*hfac 0,
  567. !*hfac 1,
  568. list('df,x,v2)),
  569. !*hfac 2),
  570. v2);
  571. return aeval list('quotient,
  572. list('plus,w0,w1,w2),
  573. y)
  574. >>;
  575. end;
  576. % Vector substitution - definition of SUB as a VECTORFN
  577. % function.
  578. vectorfn('sub,'vectorsub);
  579. % Now we have to define mapping for SUB. It's made a little complicated
  580. % by the fact that the argument list for SUB has the interesting bit
  581. % i.e. the vector, at the end.
  582. symbolic procedure vectorsub vargs;
  583. begin scalar subslist,vexpr,x;
  584. vexpr := car reverse vargs; % That was the easy part !
  585. % Now we need to get the rest of the list
  586. subslist := reverse cdr reverse vargs; % Dirty, but effective !
  587. x := mkvect(2);
  588. for k:=0:2 do
  589. putv(x,k,aeval append('(sub),
  590. append(subslist,list getv(vexpr,k))));
  591. return x
  592. end;
  593. % Component-wise application of a scalar operation to a vector
  594. symbolic procedure vectorapply(vecopr,v,args);
  595. begin scalar vv,x,y;
  596. x := mkvect(2);
  597. vv := if not vectorp v then vecsm!* v
  598. else v;
  599. for k:=0:2 do
  600. << % Apply the operation to each component
  601. y := getv(vv,k);
  602. y := if null args then aeval list(vecopr,y)
  603. else aeval append(list(vecopr,y),args);
  604. putv(x,k,y);
  605. >>;
  606. return x
  607. end;
  608. % We need to define a dummy routine to apply a function to a scalar
  609. symbolic procedure scalarapply(op,v,args);
  610. if null args then aeval list(op,v)
  611. else aeval append(list(op,v),args);
  612. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  613. % %
  614. % COORDINATE SYSTEM MODULE %
  615. % %
  616. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  617. % We begin with a function which declares the names of the coordinates
  618. % to be used
  619. symbolic procedure coordinates u;
  620. begin scalar x;
  621. if not eqn(length(u),3)
  622. then rerror(avector,11,"Wrong number of args");
  623. for each y in u do if (x := gettype y) and not(x eq 'operator) then
  624. rerror(avector,12,"Name declared as coordinate is not a kernel");
  625. remflag(!*coords,'reserved);
  626. !*coords := u;
  627. x := aeval list('avec,first u,second u,third u);
  628. remflag('(coords),'reserved);
  629. setk('coords,x);
  630. flag('(coords),'reserved);
  631. %flag(u,'reserved);
  632. return u
  633. end;
  634. symbolic operator coordinates;
  635. remflag('(dvolume hfactors),'reserved);
  636. algebraic procedure scalefactors(h1,h2,h3);
  637. begin
  638. remflag('(dvolume hfactors),'reserved);
  639. hfactors := avec(h1,h2,h3);
  640. dvolume := h1*h2*h3;
  641. flag('(dvolume hfactors),'reserved);
  642. end;
  643. flag('(dvolume hfactors),'reserved);
  644. % We define a procedure that extracts the n-th scale factor
  645. symbolic procedure !*hfac n;
  646. if not fixp n or n<0 or n>2 then rerror(avector,13,"Invalid index")
  647. else getv(getavalue 'hfactors,n);
  648. % Now we define two useful operators that allow us to define and
  649. % refer to a coordinate system by name
  650. symbolic procedure getcsystem u;
  651. begin scalar x,y;
  652. if not atom u
  653. then rerror(avector,14,"Invalid name for coordinate system");
  654. if not flagp(u,'coordinatesystem)
  655. then rerror(avector,15,"Unknown system");
  656. x := get(u,'coordinates);
  657. y := get(u,'scalefactors);
  658. if x and y then
  659. << % Put the coordinates and scalefactors in place
  660. remflag(!*coords,'reserved);
  661. !*coords := x;
  662. remflag('(coords),'reserved);
  663. setk('coords,aeval list('avec,first x, second x, third x));
  664. flag('(coords),'reserved);
  665. %flag(x,'reserved);
  666. put('hfactors,'avalue,list('!3vector,y));
  667. remflag('(dvolume),'reserved);
  668. setk('dvolume,aeval list('times, !*hfac 0, !*hfac 1, !*hfac 2));
  669. flag('(dvolume),'reserved);
  670. return x
  671. >>
  672. else rerror(avector,16,"Incompletely specified coordinate system")
  673. end;
  674. symbolic procedure putcsystem u;
  675. begin
  676. if not atom u
  677. then rerror(avector,17,"Invalid name for coordinate system");
  678. flag(list u,'coordinatesystem);
  679. put(u,'coordinates,!*coords);
  680. put(u,'scalefactors,getavalue 'hfactors);
  681. !*csystems := union(list u,!*csystems);
  682. return u
  683. end;
  684. deflist('((coordinates rlis)),'stat);
  685. !*coords := '(x y z);
  686. !*csystems := nil;
  687. % The following procedure calculates the derivative of a vector
  688. % function of a scalar variable, including the scale factors in
  689. % the coefficients.
  690. % symbolic operator vecdf;
  691. % Commented out by M.MacCallum or surfint fails
  692. flag('(vecdf), 'vectorfn);
  693. % To replace previous line - M. MacCallum
  694. vectorfn('vecdf,'vectordf);
  695. symbolic procedure vectordf u;
  696. begin scalar v,idv,x;
  697. v := vecsm!* car u;
  698. idv := cadr u;
  699. if not vecp v then rerror(avector,18,"First arg is not a vector");
  700. if not atom idv then rerror(avector,19,"Second arg is not an atom");
  701. x := mkvect(2);
  702. for k:=0:2 do
  703. << % Calculate components one at a time
  704. putv(x,k,aeval list('times,
  705. !*hfac k,
  706. list('df,
  707. getv(v,k),
  708. idv)))
  709. >>;
  710. return x
  711. end;
  712. % We define three popular curvilinear coordinate systems :
  713. % Cartesian, spherical polar and cylindrical
  714. algebraic;
  715. vec coords,hfactors;
  716. % flag('(coords hfactors),'reserved); % Interferes with EXCALC.
  717. infix dot,cross;
  718. precedence dot,*;
  719. precedence cross,*;
  720. coordinates x,y,z;
  721. scalefactors(1,1,1);
  722. putcsystem 'cartesian;
  723. coordinates r,theta,phi;
  724. scalefactors(1,r,r*sin(theta));
  725. putcsystem 'spherical;
  726. coordinates r,z,phi;
  727. scalefactors(1,1,r);
  728. putcsystem 'cylindrical;
  729. % And we choose to use Cartesians initially ...
  730. getcsystem 'cartesian;
  731. % Extensions to REDUCE vector package
  732. % Definite-integral routine ... trivially simple
  733. algebraic procedure defint(fn,x,xlower,xupper);
  734. begin scalar indefint;
  735. indefint:=int(fn,x);
  736. return sub(x=xupper,indefint)-sub(x=xlower,indefint)
  737. end;
  738. vectormapping 'defint; % DEFINT is now a vector function too
  739. % Component-extraction utility - allows us to get components
  740. % of vectors which are arguments to algebraic procedures
  741. symbolic procedure component(v,n);
  742. if not vecp v then rerror(avector,20,"Argument is not a vector")
  743. else getv(vecsm!* v,n);
  744. symbolic operator component,vecp;
  745. algebraic procedure volintegral(fn,vlower,vupper);
  746. begin scalar integrand,idpvar,xlower,xupper,kindex;
  747. integrand := fn*hfactors(0)*hfactors(1)*hfactors(2);
  748. for k:=0:2 do
  749. << % Perform each integration separately. The order of integration
  750. % is determined by the control vector VOLINTORDER
  751. kindex := volintorder(k);
  752. idpvar := coords(kindex);
  753. xlower := component(vlower,kindex);
  754. xupper := component(vupper,kindex);
  755. integrand := defint(integrand,idpvar,xlower,xupper);
  756. >>;
  757. return integrand
  758. end;
  759. % Define the initial setting of VOLINTORDER
  760. volintorder := avec(0,1,2);
  761. % Line integral
  762. algebraic procedure lineint(v,curve,ivar);
  763. begin scalar scalfn,vcomp,hcomp,dcurve;
  764. scalfn := 0;
  765. for k:=0:2 do
  766. << % Form the integrand
  767. vcomp := component(v,k);
  768. hcomp := hfactors(k);
  769. dcurve := df(component(curve,k),ivar);
  770. scalfn := scalfn + vcomp*hcomp*dcurve
  771. >>;
  772. scalfn := vecsub(coords,curve,scalfn) ; % Added by M. MacCallum
  773. return int(scalfn,ivar)
  774. end;
  775. algebraic procedure deflineint(v,curve,ivar,ilb,iub);
  776. begin scalar indfint;
  777. indfint := lineint(v,curve,ivar);
  778. return sub(ivar=iub,indfint)-sub(ivar=ilb,indfint)
  779. end;
  780. % Attempt to implement dot and cross as single-character infix
  781. % operators upon vectors
  782. symbolic;
  783. % Cross-product is easy : we simply tell Reduce that up-arrow is a
  784. % synonym for CROSS
  785. newtok '((!^) cross);
  786. % Dot is more difficult : the period (.) is already defined as the
  787. % CONS function, and unfortunately REVAL1 spots this before it
  788. % checks the type of the arguments, so declaring CONS to be
  789. % VECTORMAPPING won't work. What is required is a hack to the
  790. % routine that carries out CONS at SYMBOLIC level.
  791. % We now redefine RCONS which is the function invoked when CONS is used
  792. % in Reduce.
  793. remflag('(rcons),'lose); % We must use this definition.
  794. symbolic procedure rcons u;
  795. begin scalar x,y;
  796. argnochk ('cons . u);
  797. if (y := getrtype(x := reval cadr u)) eq 'vector
  798. then return mk!*sq simpdot u
  799. % The following line was added to enable . to be used as vector product
  800. % (Amended by M. MacCallum)
  801. else if (y eq '!3vector)
  802. then return apply('vectordot,
  803. {for each j in u collect vecsimp!* j})
  804. else if not(y eq 'list) then typerr(x,"list")
  805. else return 'list . reval car u . cdr x
  806. end;
  807. vectorfn('cons,'vectordot);
  808. % Rest added by M. MacCallum
  809. flag('(surfint vecsub),'vectorfn);
  810. vectorfn('surfint,'vsurfint);
  811. symbolic procedure vsurfint vargs;
  812. begin scalar sivar1, sivar2, sivar3, sivar4, sivar5 ;
  813. if not (length vargs = 8) then rerror(avector,21,
  814. "Wrong number of args to SURFINT");
  815. if not (vecp(sivar1 := car vargs) and
  816. vecp(sivar2 := cadr vargs) and
  817. idp car(sivar3 := cddr vargs) and
  818. idp car(sivar4 := cdddr sivar3))
  819. then rerror(avector,22,
  820. "Wrong type(s) of arguments supplied to SURFINT");
  821. sivar2 := vecsm!* sivar2 ;
  822. sivar3 := reverse cdddr reverse sivar3 ;
  823. sivar5 := aeval list('cross,
  824. list('vecdf, sivar2, car sivar3),
  825. list('vecdf, sivar2, car sivar4)) ;
  826. sivar1 := vecsm!* sivar1 ;
  827. sivar5 := aeval list('dot, sivar1, sivar5) ;
  828. sivar5 := aeval list('vecsub,'coords,sivar2,sivar5);
  829. return aeval append(list('defint,
  830. append(list('defint, sivar5), sivar3)),
  831. sivar4) ;
  832. end ;
  833. vectorfn('vecsub,'vvecsub);
  834. symbolic procedure vvecsub vargs ;
  835. begin scalar vsarg1, vsarg2, vsarg3;
  836. if not (length vargs = 3) then rerror(avector,23,
  837. "Wrong number of arguments to VECSUB");
  838. if not (vecp car vargs and vecp cadr vargs) then
  839. rerror(avector,24,
  840. "First two arguments to VECSUBS must be vectors");
  841. vsarg1 := vecsm!* car vargs;
  842. vsarg2 := vecsm!* cadr vargs;
  843. vsarg3 := caddr vargs;
  844. if not vecp vsarg3 then vsarg3 := prepsq cadr vsarg3;
  845. return aeval list('sub,
  846. list('equal, !*a2k component(vsarg1, 0),
  847. component(vsarg2, 0)),
  848. list('equal, !*a2k component(vsarg1, 1),
  849. component(vsarg2, 1)),
  850. list('equal, !*a2k component(vsarg1, 2),
  851. component(vsarg2, 2)),
  852. vsarg3);
  853. end ;
  854. endmodule;
  855. end;