File: pml5.doc.html

package info (click to toggle)
pact 980714-3
  • links: PTS
  • area: main
  • in suites: slink
  • size: 13,096 kB
  • ctags: 26,034
  • sloc: ansic: 109,076; lisp: 9,645; csh: 7,147; fortran: 1,050; makefile: 136; lex: 95; sh: 32
file content (1041 lines) | stat: -rw-r--r-- 66,719 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
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
<html><head><title></title></head>

<body>

<a name="934888">
<h1>1.0   </a>Introduction</h1>
</a>
<a name="934254">
There are many mathematics libraries currently enjoying widespread use. At the time that </a>PML was begun, however, there were very few written in C and fewer still written with </a>portability in mind. The temptation has all too often been to program to a specific hardware platform and forget portability. More recently some very good work has been published by Press, Flannery, Teulkovsky, and Vetterling, </a>Numerical Recipes in C, which is highly portable. The reason for writing PML hasn&#146;t changed in spite this. No one library is apt to have everything that a given application needs and there are other considerations such as interfaces and related functionality to drive the development of a math library.<p>
</a>
<a name="934220">
PML (</a>Portable Mathematics Library) was developed initially as a repository for any routines of a mathematical nature in what has become the </a>PACT system. More recently, I have tried to systematize the library and organize it for future growth.<p>
</a>
<a name="934237">
The library has two fundamental parts. First are the many pure math routines such as equation solvers. Second are the structure definitions and some related functions. The structures which PML supports are provided to aid C programmers in organizing their mathematical computations in a mathematical way. That is, they are encouraged to give primary consideration to the mathematics not the implementation of the mathematics. This also is intended to add to the mathematical rigor of </a>simulations and promote the use of some of the more abstract but critical ideas of </a>mathematics by giving application developers concrete representations of </a>abstract structures.<p>
</a>
<a name="934250">
The data structures for PML are presented first since they support most of the library. A summary of the mathematical functions comes next. A set of examples completes this manual.<p>
</a>
<a name="934889">
I would like to acknowledge the work of Charles F. McMillan in PML. He devised the original </a>matrix structure and wrote many of the routines to manipulate them. The final form that they take in PML is very close to his original conception.<p>
</a>
<a name="934239">
<h1>2.0   </a>Data Structures</h1>
</a>
<a name="934890">
</a>PML currently supports the following structures: </a>complex, </a>matrix; </a>set; </a>mapping; and </a>field. The </a>complex structure and its associated functions provide a rudimentary facility to do arithmetic with complex numbers. The </a>matrix structure contains &#147;all&#148; of the information needed to deal with first and second rank </a>tensors (i.e. </a>vectors and </a>matrices). The </a>set structure is intended to describe a discrete set of </a>elements. It includes information about the type of elements, the dimensionality, topology, and so on. Once the notion of a set has been built up, it is natural to proceed to discuss mappings of sets. This is obviously useful for such tasks as </a>interpolations between </a>computational </a>meshes and </a>visualization. The </a>mapping structure includes a domain and range set and information describing the mapping between them. To support the mapping ideas, it is necessary to consider whether the sets involved are </a></a>algebras, </a>rings, </a>fields, or whatever. To facilitate this thinking, a </a>field structure is defined to provide the set of operations which can be carried out on the elements of sets. This also makes the set and mapping structures very general (which it should be) and still reasonably efficient.<p>
</a>
<a name="934248">
<h2>2.1   </a>Complex</h2>
</a>
<a name="934276">
The </a>complex structure is the natural representation of a complex double precision number.<p>
</a>
<A NAME="934277"><PRE> 
</PRE><A NAME="934309"><PRE> struct s_complex
</PRE><A NAME="934278"><PRE>    {double real;
</PRE><A NAME="934279"><PRE>     double imag;};
</PRE><A NAME="934266"><PRE> typedef struct s_complex complex;
</PRE><a name="934384">
The </a>real part of the complex number is kept in the member </a>real and the </a>imaginary part of the complex number is kept in the member </a>imag.<p>
</a>
<a name="934252">
<h2>2.2   </a>PM_matrix</h2>
</a>
<a name="934227">
The </a>PM_matrix structure is a useful way to group a contiguous block of memory together with two integers which describe the </a>logical </a>shape of the space. <p>
</a>
<A NAME="934264"><PRE> 
</PRE><A NAME="934244"><PRE> struct s_PM_matrix
</PRE><A NAME="934246"><PRE>    {int nrow;
</PRE><A NAME="934253"><PRE>     int ncol;
</PRE><A NAME="934241"><PRE>     REAL *array;};
</PRE><A NAME="934263"><PRE> typedef struct s_PM_matrix PM_matrix;
</PRE><a name="934256">
The </a>nrow member is the </a>number of </a>rows of the matrix and the </a>ncol member is the </a>number of </a>columns of the matrix. The </a>array member is a pointer to the actual space. The type declaration </a>REAL is set in the PACT system to be either </a>float or </a>double. Typically it is set to double, and float is used mainly on the smaller machines to conserve memory.<p>
</a>
<a name="934243">
In this implementation, matrices are assumed to have a </a>one based </a>indexing scheme. That is, all matrix indices go from 1 to ncol or from 1 to nrow. <p>
</a>
<a name="934329">
When operating on matrices it is necessary only to pass the matrix as an argument since the number of rows and columns is contained in the structure. A </a>vector is taken to be simply a matrix with either one row or one column.<p>
</a>
<a name="934229">
<h2>2.3   </a>Set</h2>
</a>
<a name="934330">
The </a>PM_set structure is an attempt to describe a usefully general mathematical set for such purposes as </a>interpolation and </a>visualization. It encapsulates a collection of information about a discrete set of </a>elements of arbitrary type.<p>
</a>
<A NAME="934273"><PRE> 
</PRE><A NAME="934300"><PRE> struct s_PM_set
</PRE><A NAME="934332"><PRE>    {char *name;
</PRE><A NAME="934333"><PRE>     char *element_type;
</PRE><A NAME="934334"><PRE>     int dimension;
</PRE><A NAME="934335"><PRE>     int *max_index;
</PRE><A NAME="934336"><PRE>     int dimension_elem;
</PRE><A NAME="934337"><PRE>     long n_elements;
</PRE><A NAME="934232"><PRE>     byte *elements;
</PRE><A NAME="934236"><PRE>     char *es_type;
</PRE><A NAME="934257"><PRE>     byte *extrema;
</PRE><A NAME="934260"><PRE>     byte *scales;
</PRE><A NAME="934261"><PRE>     PM_field *opers;
</PRE><A NAME="934339"><PRE>     REAL *metric;
</PRE><A NAME="934342"><PRE>     char *symmetry_type;
</PRE><A NAME="934343"><PRE>     byte *symmetry;
</PRE><A NAME="934344"><PRE>     char *topology_type;
</PRE><A NAME="934345"><PRE>     byte *topology;
</PRE><A NAME="934349"><PRE>     char *info_type;
</PRE><A NAME="934350"><PRE>     byte *info;};
</PRE><A NAME="934352"><PRE> typedef struct s_PM_set PM_set;
</PRE><a name="934265">
 The </a>PM_set members are: </a>name, every set has a unique identifier; </a>element_type, the type of element in the set; </a>dimension, the dimension of the set; </a>max_index, an array of the lengths along each dimension (it is dimension long); </a>dimension_elem, the dimensionality of the elements of the set</a>; n_elements, the </a>number of elements in the set; </a>elements, a pointer to the actual elements of the set; </a>es_type, the type of the extrema and the scales arrays (typically, element_type is double ** while es_type is double *); </a>extrema, an array of minimum and maximum values for each component (2*dimension_elem long); </a>scales, an array of scales for each dimension; </a>opers, specifies the </a>field for the set; </a>metric, an appropriate array of metric coefficients for the set if it is a metric set</a>; symmetry_type, an ASCII string naming the type of the data in the symmetry member; </a>symmetry, a pointer to data describing symmetries of the set, if any exist; </a>topology_type, an ASCII string naming the type of the data in the </a>topology member; </a>topology, a pointer to data describing the </a>connectivity of the elements of the set; </a>opers, a collection of functions describing defined </a>operations on elements of the set; </a>info_type, an ASCII string naming the type of the data in the info member; and </a>info, a pointer to data describing any additional information about the set.<p>
</a>
<a name="934395">
In order to be general and useful, many types of information are described by two members of the </a>PM_set. For example, an application may have a mesh (handled as a set) with an array of integers to define the </a>connectivity. The </a>topology member should point to that array and the </a>topology_type member should be set to integer or int. The application would have easy access to the connectivity and another application which might work with sets of other kinds could be programmed to distinguish between the </a>topological information associated with these mesh sets and that of other </a>sets it knows about.<p>
</a>
<a name="934394">
<h3>2.3.1   </a>Example of </a>building a </a>set</h3>
</a>
<a name="934385">
This example is taken from the source for PML and illustrates how a set can be built. The routine uses the </a>SCORE functions </a>MAKE, </a>MAKE_N, </a>SC_arrlen, and </a>SC_strsave as well as the SCORE variable </a>SC_DOUBLE_S.<p>
</a>
<A NAME="934393"><PRE> 
</PRE><A NAME="934242"><PRE> #include &#147;pml.h&#148;
</PRE><A NAME="934292"><PRE> 
</PRE><A NAME="934267"><PRE> main()
</PRE><A NAME="934270"><PRE>    {int i, k, l, kmax, lmax, kxl;
</PRE><A NAME="934285"><PRE>     REAL *x, *y, *u, *v, r, t;
</PRE><A NAME="934286"><PRE>     REAL xmin, xmax, ymin, ymax, km, lm;
</PRE><A NAME="934288"><PRE>     PM_set *domain, *range;
</PRE><A NAME="934341"><PRE> 
</PRE><A NAME="934346"><PRE> /* set up data */
</PRE><A NAME="934347"><PRE>     kmax      = 20;
</PRE><A NAME="934408"><PRE>     lmax      = 20;
</PRE><A NAME="934409"><PRE>     xmin      = -5.0;
</PRE><A NAME="934410"><PRE>     xmax      = 5.0;
</PRE><A NAME="934411"><PRE>     ymin      = -5.0;
</PRE><A NAME="934413"><PRE>     ymax      = 5.0;
</PRE><A NAME="934420"><PRE> 
</PRE><A NAME="934421"><PRE>     kxl = kmax*lmax;
</PRE><A NAME="934422"><PRE>     x   = MAKE_N(REAL, kxl);
</PRE><A NAME="934423"><PRE>     y   = MAKE_N(REAL, kxl);
</PRE><A NAME="934424"><PRE>     u   = MAKE_N(REAL, kxl);
</PRE><A NAME="934425"><PRE>     v   = MAKE_N(REAL, kxl);
</PRE><A NAME="934428"><PRE> 
</PRE><A NAME="934429"><PRE>     km = 2.0*PI/((double) (kmax - 1));
</PRE><A NAME="934430"><PRE>     lm = 2.0*PI/((double) (lmax - 1));
</PRE><A NAME="934431"><PRE>     for (k = 0; k &lt; kmax; k++)
</PRE><A NAME="934435"><PRE>         for (l = 0; l &lt; lmax; l++)
</PRE><A NAME="934436"><PRE>             {i    = l*kmax + k;
</PRE><A NAME="934437"><PRE>              x[i] = k*km;
</PRE><A NAME="934439"><PRE>              y[i] = l*lm;
</PRE><A NAME="934443"><PRE>              u[i] = sin(k*km);
</PRE><A NAME="934459"><PRE>              v[i] = sin(l*lm);};
</PRE><A NAME="934480"><PRE> 
</PRE><A NAME="934483"><PRE> /* build the domain set */
</PRE><A NAME="934487"><PRE>     domain = PM_make_set(&#147;{x, y}&#148;, SC_REAL_S, FALSE,
</PRE><A NAME="934491"><PRE> 			 2, kmax, lmax, 2, x, y);
</PRE><A NAME="934498"><PRE> 
</PRE><A NAME="934500"><PRE> /* build the range set */
</PRE><A NAME="934502"><PRE>     range = PM_make_set(&#147;{u, v}&#148;, SC_REAL_S, FALSE,
</PRE><A NAME="934504"><PRE> 			2, kmax, lmax, 2, u, v);
</PRE><A NAME="934511"><PRE> 
</PRE><A NAME="934597"><PRE>                .
</PRE><A NAME="934601"><PRE>                .
</PRE><A NAME="934271"><PRE>                .
</PRE><A NAME="934287"><PRE> 
</PRE><A NAME="934607"><PRE>     return(0);}
</PRE><A NAME="934615"><PRE> 
</PRE><a name="934233">
<h2>2.4   </a>Mapping</h2>
</a>
<a name="934584">
Once the </a>PM_set structure is there to organize data into some semblance of a mathematical </a>set, it is quite natural to express objects such as </a>computational meshes as sets. In fact, considering the needs of </a>numerical </a>simulation codes and </a>visualization codes, the concepts of </a>domain sets and </a>range sets arise and lead to the need for a structure to characterize a mathematical mapping.<p>
</a>
<A NAME="934371"><PRE> 
</PRE><A NAME="934353"><PRE> struct s_PM_mapping
</PRE><A NAME="934255"><PRE>    {char *name;
</PRE><A NAME="934268"><PRE>     char *category;
</PRE><A NAME="934272"><PRE>     PM_set *domain;
</PRE><A NAME="934374"><PRE>     PM_set *range;
</PRE><A NAME="934375"><PRE>     char *map_type;
</PRE><A NAME="934274"><PRE>     byte *map;
</PRE><A NAME="934280"><PRE>     in file_type;
</PRE><A NAME="934282"><PRE>     byte *file_info;
</PRE><A NAME="934283"><PRE>     char *file;
</PRE><A NAME="934380"><PRE>     struct s_PM_mapping *next;};
</PRE><A NAME="934382"><PRE> typedef struct s_PM_mapping PM_mapping;
</PRE><a name="934574">
The members of the </a>PM_mapping are: </a>name, a name for the mapping; </a>category, the category to which the mapping belongs (e.g., Logical-Rectangular, Arbitrarily-Connected); </a>domain, the mapping&#146;s domain; </a>range, (a subset of) the image of domain under map; </a>map_type, an ASCII string naming the type of the data pointed to by map; </a>map, a pointer to data describing the mapping between domain and range; </a>file_type, file type </a>ASCII, </a>BINARY, or </a>PDB; </a>file_info, </a>file information (cast to some struct with information); </a>file, the file name for the mapping; and </a>next; the next PM_mapping in the </a>chain.<p>
</a>
<a name="934377">
At this point, many of these ideas are evolving. To insulate the developer somewhat against these changes, the map member was used. It allows different structures to be used to capture the description of the mapping from domain to range. For example, to handle the case when one has a domain and a function, the range could be NULL. Another potential issue to handle is whether the mapping is a </a>bijection, </a>surjection, or </a>injection.<p>
</a>
<a name="934379">
A very immediate issue that arises instantly is the </a>centering of the range set relative to the domain set. For example, the domain might be a </a>mesh with </a>coordinates defining </a>node centers while the range set might be a </a>zone centered quantity. One small structure is provided to address this issue:<p>
</a>
<A NAME="934569"><PRE> 
</PRE><A NAME="934284"><PRE> struct s_PM_map_info
</PRE><A NAME="934366"><PRE>    {char *name;
</PRE><A NAME="934368"><PRE>     int centering;};
</PRE><A NAME="934370"><PRE> typedef struct s_PM_map_info PM_map_info;
</PRE><a name="934580">
The </a>PM_map_info structure only has a </a>name member and an integer member, </a>centering, to specify the relative centering as being one of: </a>Z_CENT for </a>zone centered; N_CENT for </a>node centered; </a>F_CENT for </a>face centered; </a>E_CENT for </a>edge centered; or </a>U_CENT for </a>uncentered.<p>
</a>
<a name="934400">
<h2>2.5   Connectivity</h2>
</a>
<a name="934402">
To support general computational meshes PML has a model for arbitrarily connected meshes which is independent of dimensionality and a priori assumptions about shapes of zones or elements. We give a complete discussion below including definitions of terms. Hopefully this will suffice to give a useful account of this topic.<p>
</a>
<a name="934427">
<h3>2.5.1   Concepts</h3>
</a>
<a name="934434">
The goal is to capture non-trivial topological information describing computational meshes which is efficient in space usage and amenable to fast algorithms for visualization rendering and analytical operations.<p>
</a>
<a name="934447">
The representation of the connectivity scheme must accomodate a heirarchy of information.  At the bottom of the heirarchy is the minimal information needed by a minimal connectivity scheme. At the top is a maximal set of information which is computable from the minimal information but is kept in memory rather than recomputing it.<p>
</a>
<a name="934471">
<h3>2.5.2   Definitions</h3>
</a>
<A NAME="934479"><PRE>     </PRE> Computational mesh or mesh - a collection of discrete points in some vector space 
along with the specification of relationships between points especially the neighbor 
relationship.
<BR><A NAME="934489"><PRE>     </PRE>Connectivity - the pattern of specifications of node neighbors.
<BR><A NAME="934497"><PRE>     </PRE>N-cell - an N dimensional volume unit. Example: 3 dimensional mesh. A zone is a 3-
cell, a face is a 2-cell, an edge is a 1-cell, and a node is a 0-cell.
<BR><A NAME="934503"><PRE>     </PRE>N-Center - the center of an N-cell is defined by some weighted average of the real 
nodes which imply the N-cell. A 0-Center corresponds to a real node.
<BR><A NAME="934514"><PRE>     </PRE>Boundary - the boundary of an N-cell is a set of (N-1)-cells such that the boundary of 
the boundary is 0.  Although each (N-1)-cell may have a boundary, the set of them may 
not.  This is a closure property.  Each boundary (N-1)-cell has an orientation.  The outward normal is the canonical orientation.
<BR><A NAME="934528"><PRE>     </PRE>Zone - for a mesh in an N dimensional vector space a zone is the smallest N dimensional volume unit which is delineated by real nodes. In an N dimensional mesh, a zone 
is an N-cell.
<BR><A NAME="934532"><PRE>     </PRE>Node - for a mesh in an N dimensional vector space a node is a single point in the N 
dimensional vector space. A node is always a 0-cell. The collection of nodes is one of 
the defining elements of the mesh.
<BR><A NAME="934541"><PRE>     </PRE>Real Node - a node in the mesh which is explicitly specified and is a part of the connectivity of the mesh.
<BR><A NAME="934544"><PRE>     </PRE>Virtual Node - a node which is implicitly defined by the mesh connectivity scheme.  For 
example, the points defining the zone centers may be virtual nodes.
<BR><A NAME="934561"><PRE>     </PRE>Decomposition - an N-cell which is implied by a set of real nodes can be expressed as a 
sum of N-cells which are implied by the set of real nodes and at least one virtual node.
<BR><A NAME="934568"><PRE>     </PRE>Irreducible Decomposition - an N-cell has a special, unique decomposition which is the 
sum of N-cells each of which has: 1 0-Center, 1 1-Center, ..., 1 N-Center in their boundary and which collectively cover the N-cell.
<BR><a name="934269">
<p>
</a>
<a name="934290">
<p>
</a>
<a name="934291">
<p>
</a>
<a name="934293">
<p>
</a>
<a name="934294">
<p>
</a>
<a name="934295">
<p>
</a>
<a name="934296">
<p>
</a>
<a name="934297">
<p>
</a>
<a name="934298">
<p>
</a>
<a name="934299">
<p>
</a>
<A NAME="934314"><B>Figure: The cell A is one cell in the irreducible decomposition of the rectangular 2-Cell shown
</B><HR><a name="934575">
<h3>2.5.3   Axioms</h3>
</a>
<ul><a name="934577">
<li>The mimimum information necessary to describe a computational mesh is: 1) a list of nodes; and 2) for each node a list of its neighboring nodes.
</a>
<a name="934583">
<li>The information that will typically be available in a mesh description is: 1) a list of nodes; 2) a list of zones; and 3) for each node a list of its neighboring nodes.
</a>
</ul><a name="934599">
<h3>2.5.4   Representation Requirements</h3>
</a>
<a name="934602">
To represent an N-cell obviously depends on how much information you want to maintain.<p>
</a>
<a name="934405">
<p>
</a>
<ul><a name="934608">
<li>Minimal:
</a>
<A NAME="934404"><PRE>   Boundary specification     (necessary and sufficient)
</PRE><a name="934609">
<li>Maximal:
</a>
<A NAME="934403"><PRE>   Center node
</PRE><A NAME="934610"><PRE>   (N+1)-cell for which this N-cell is a boundary segment
</PRE><A NAME="934611"><PRE>   Neighboring (N+1)-cell
</PRE><A NAME="934612"><PRE>   Decomposition specification
</PRE><A NAME="934613"><PRE>   Irreducible Decomposition specification
</PRE><a name="934614">
A flexible approach is taken by representing a cell as a block of contiguous integers in an array of cells. This means that an array of cells is in effect a two dimensional array of integers. The fastest changing dimension is bounded by the number of parameters used in representing the cells and the slowest changing dimension is the cell number.<p>
</a>
<a name="934623">
PML defines the following constants to name the cell description parameters<p>
</a>
<A NAME="934462">#define BND_CELL_MIN  0
<P><A NAME="934418">#define BND_CELL_MAX  1
<P><A NAME="934433">#define OPPOSITE_CELL 2
<P><A NAME="934441">#define PARENT_CELL   3
<P><A NAME="934444">#define NGB_CELL      4
<P><A NAME="934446">#define CENTER_CELL   5
<P><A NAME="934451">#define DEC_CELL_MIN  6
<P><A NAME="934456">#define DEC_CELL_MAX  7
<P></ul><a name="934626">
<h3>2.5.5   Representation of Connectivity</h3>
</a>
<a name="934627">
The PM_mesh_topology structure attempts to describe in complete generality a mesh of points and their neighbors as a heirarchy in which the highest dimensional volumes sit at the top and the line segment descriptions sit at the bottom.<p>
</a>
<a name="934463">
At each level in the heirarchy an N-cell is described by a contiguous block of (N-1)-cell boundary sections (next lower down in the heirarchy).<p>
</a>
<a name="934507">
Assuming the dimensionality to be n the heirarchy goes like:<p>
</a>
<A NAME="934515">boundaries[n]					the description for the n-dim surfaces
<P><A NAME="934516">boundaries[n-1]				the description for the (n-1)-dim surfaces
<P><A NAME="934518">            .
<P><A NAME="934468">            .
<P><A NAME="934476">            .
<P><A NAME="934530">boundaries[1]				the description for the 1-dim surfaces, i.e. line segments whose endpoints are the node indices
<P><A NAME="934538">boundaries[0]				a mimimal description for the nodes
<P><a name="934542">
<p>
</a>
<A NAME="934543"><PRE> struct s_PM_mesh_topology
</PRE><A NAME="934550"><PRE>    {int n_dimensions;       /* number of dimensions */
</PRE><A NAME="934555"><PRE>     int *n_bound_params;     /* number of bound params at each level */
</PRE><A NAME="934557"><PRE>     int *n_cells;           /* number of cells at each level */
</PRE><A NAME="934562"><PRE>     long **boundaries;};    /* boundary info array at each level */
</PRE><a name="934563">
<p>
</a>
<a name="934565">
typedef struct s_PM_mesh_topology PM_mesh_topology;<p>
</a>
<a name="934570">
For example, in a 3 dimensional problem, the &#147;zones&#148; at the top of the hierarchy are 3-cells. Each &#147;zone&#148; has some number of &#147;faces&#148; or 2-cells bounding it.  Each &#147;face&#148; has some number of &#147;edges&#148; or 1-cells bounding it.  Implicitly, each &#147;edge&#148; has two points or 0-cells bounding it. Then boundaries[3] would contain the indices into boundaries[2] of boundary faces and, if that level of information is kept, indices of other zones in boundaries[3] which are neighbors.<p>
</a>
<a name="934638">
<p>
</a>
<a name="934315">
<p>
</a>
<a name="934316">
<p>
</a>
<a name="934317">
<p>
</a>
<a name="934318">
<p>
</a>
<a name="934319">
<p>
</a>
<a name="934320">
<p>
</a>
<a name="934414">
<p>
</a>
<a name="934415">
<p>
</a>
<a name="934416">
<p>
</a>
<a name="934417">
<p>
</a>
<a name="934419">
<p>
</a>
<A NAME="934440"><B>Figure: Diagram of hierarchy for 3 dimensional mesh connectivity
</B><HR><a name="934519">
<p>
</a>
<a name="934240">
<h2>2.6   </a>Field</h2>
</a>
<a name="934258">
Depending on which </a>operations are defined on a </a>set of </a>elements (in addition to other properties), one has a </a>vector space, an </a>algebra, a </a>ring, a </a>group, a </a>field, or variations on that same theme. As a practical matter, given a set of elements, applications need to know how to combine them (add, subtract, etc.). The </a>PM_field structure provides a way to make those connections in a generic way.<p>
</a>
<A NAME="934355"><PRE> 
</PRE><A NAME="934259"><PRE> struct s_PM_field
</PRE><A NAME="934262"><PRE>    {byte (*add)();
</PRE><A NAME="934356"><PRE>     byte (*sub)();
</PRE><A NAME="934357"><PRE>     byte (*scalar_mult)();
</PRE><A NAME="934358"><PRE>     byte (*mult)();
</PRE><A NAME="934359"><PRE>     byte (*div)();};
</PRE><A NAME="934579"><PRE> typedef struct s_PM_field PM_field;
</PRE><a name="934249">
<h1>3.0   </a>Summary of the PML C API</h1>
</a>
<a name="934624">
Here is a brief summary of the routines in </a>PML. They are broken down by category as: matrix manipulation routines; other equation solvers; geometry routines; mathematical functions; constructors and destructors; field operators; and sorting routines.<p>
</a>
<a name="934281">
<h2>3.1   </a>MATRIX Manipulation Routines</h2>
</a>
<A NAME="934310"><BR><B>
</B><BR><A NAME="934289"><BR><B>PM_matrix *</a>PM_create(int nrow, int ncol)
</B><BR><a name="934587">
</a>Create and return a new PM_matrix with nrow </a>rows and ncol </a>columns. If space cannot be allocated, NULL is returned.<p>
</a>
<A NAME="934661"><BR><B>PM_matrix *</a>PM_decompose(PM_matrix *a, int *ip, int flag)
</B><BR><a name="934481">
Perform an </a>LU </a>decomposition on the PM_matrix a. The </a>pivoting information is returned in ip. The space for ip must be passed into this function. If flag is TRUE a new matrix is allocated to hold the decomposed matrix. Otherwise the original matrix is overwritten by the decomposed matrix. NULL is returned if the routine fails.<p>
</a>
<A NAME="934666"><BR><B>PM_matrix *</a>PM_decb(int n, int ml, int mu, PM_matrix *b, int *ip)
</B><BR><a name="934677">
Perform an </a>LU decomposition on the </a>banded matrix b. The arguments n, ml, and mu are the </a>order of the matrix, the </a>lower </a>band width, and the </a>upper band width respectively. The </a>pivoting information is returned in ip. The space for ip must be passed into the routine. This routine replaces the contents of b with the decomposed matrix and returns a pointer to b if successful. If the routine fails it returns NULL.<p>
</a>
<A NAME="934674"><BR><B>int </a>PM_destroy(PM_matrix *m)
</B><BR><a name="934678">
</a>Release the storage associated with the matrix m. Both the array and the matrix structure are freed. TRUE is returned if successful otherwise FALSE is returned.<p>
</a>
<A NAME="934486"><BR><B></a>PM_element(PM_matrix *m, int row, int column)
</B><BR><a name="934449">
This </a>macro accesses the </a>matrix </a>element, m(row, column). Its value can either be read or set.<p>
</a>
<A NAME="934663"><BR><B>PM_matrix *</a>PM_inverse(PM_matrix *a)
</B><BR><a name="934478">
The matrix a is inverted via an </a>LU decomposition. A new space is allocated to hold the </a>inverse. If the inversion is successful the inverse matrix is returned otherwise NULL is returned.<p>
</a>
<A NAME="934668"><BR><B>int </a>PM_inv_times_ds(PM_matrix *b, PM_matrix *c, PM_matrix *t, int *ip)
</B><BR><a name="934485">
Using an </a>LU decomposed matrix b compute (b-1).c and return it in t. The </a>pivoting information from the decomposition is supplied in ip.<p>
</a>
<A NAME="934665"><BR><B>PM_matrix *</a>PM_lower(PM_matrix *a)
</B><BR><a name="934470">
</a>Create and fill a matrix consisting of the </a>sub-diagonal part of the input matrix a. The new matrix is returned.<p>
</a>
<A NAME="934659"><BR><B>PM_matrix *</a>PM_minus(PM_matrix *a, PM_matrix *b)
</B><BR><a name="934474">
</a>Create and fill a matrix consisting of the difference, a-b. The new matrix is returned.<p>
</a>
<A NAME="934499"><BR><B>PM_matrix *</a>PM_minus_s(PM_matrix *a, PM_matrix *b, PM_matrix *c)
</B><BR><a name="934473">
Compute the </a>difference, b-c. The result is returned in the matrix a.<p>
</a>
<A NAME="934658"><BR><B>PM_matrix *</a>PM_plus(PM_matrix *a, PM_matrix *b)
</B><BR><a name="934582">
</a>Create and fill a matrix consisting of the </a>sum, a+b. The new matrix is returned.<p>
</a>
<A NAME="934670"><BR><B>PM_matrix *</a>PM_plus_s(PM_matrix *a, PM_matrix *b, PM_matrix *c)
</B><BR><a name="934472">
Compute the </a>sum, b+c. The result is returned in the matrix a.<p>
</a>
<A NAME="934667"><BR><B>PM_matrix *</a>PM_print(PM_matrix *a)
</B><BR><a name="934495">
</a>Print the matrix a to stdout. The matrix a is returned<p>
</a>
<A NAME="934662"><BR><B>PM_matrix *</a>PM_sol(PM_matrix *ul, PM_matrix *b, int *ip, int flag)
</B><BR><a name="934669">
Using the </a>LU decomposed matrix ul and the </a>pivoting information in ip as computed by PM_decompose, </a>solve for the </a>solution to the equation A.x = b. If flag is TRUE a new matrix is allocated to contain the solution and is returned. If flag is FALSE the solution is returned in b and a pointer to b is the return value of the function.<p>
</a>
<A NAME="934482"><BR><B>PM_matrix *</a>PM_solb(int n, int ml, int mu, PM_matrix *b, PM_matrix *y, int *ip)
</B><BR><a name="934671">
Using the </a>LU decomposed </a>matrix b and the </a>pivoting information in ip as computed by PM_decb, </a>solve for the </a>solution to the equation A.x = y where A is a </a>banded matrix. The arguments n, ml, and mu are the </a>order of the matrix, the </a>lower </a>band width, and the </a>upper band width respectively. The solution is returned in y and a pointer to y is the return value of the function.<p>
</a>
<A NAME="934660"><BR><B>PM_matrix *</a>PM_solve(PM_matrix *a, PM_matrix *b)
</B><BR><a name="934475">
The equation a.x = b is </a>solved and the result returned in b.<p>
</a>
<A NAME="934657"><BR><B>PM_matrix *</a>PM_times(PM_matrix *a, PM_matrix *b)
</B><BR><a name="934588">
</a>Create and fill a matrix consisting of the </a>product, a.b. The new matrix is returned.<p>
</a>
<A NAME="934496"><BR><B>PM_matrix *</a>PM_times_s(PM_matrix *a, PM_matrix *b, PM_matrix *c)
</B><BR><a name="934675">
Compute the </a>product, b.c. The result is returned in the matrix a.<p>
</a>
<A NAME="934676"><BR><B>PM_matrix *</a>PM_transpose(PM_matrix *a)
</B><BR><a name="934477">
Compute the </a>transpose of the matrix a. A new matrix is created to contain the transpose and is returned.<p>
</a>
<A NAME="934664"><BR><B>PM_matrix *</a>PM_upper(PM_matrix *a)
</B><BR><a name="934648">
</a>Create and fill a </a>matrix consisting of the </a>super-diagonal part of the input matrix a. The new matrix is returned.<p>
</a>
<a name="934656">
<h2>3.2   Other </a>Equation </a>Solvers</h2>
</a>
<a name="934354">
The </a>matrix solvers presented here are for </a>sparse matrices. Sparse matrices do not fit very naturally into the </a>matrix structure discussed above. Hence the solvers here do not use matrix structures.<p>
</a>
<A NAME="934450"><PRE> 
</PRE><A NAME="934452"><BR><B>int </a>PM_block_tridi(REAL *a, REAL *b, REAL *c, REAL *u, int ns, int nb)
</B><BR><a name="934457">
</a>Solve the equation M.x = u where M is </a>block </a>tridiagonal. There are nb </a>blocks each ns squared in size. The matrices a, b, and c are the </a>sub-diagonal, </a>diagonal, and </a>super-diagonal parts respectively. They are (ns*ns)*nb long and both the a[0] and c[nb-1] blocks are unused since the </a>off-diagonal parts are one block shorter than the diagonal. The result is returned in u. All </a>vectors are passed into the routine which returns TRUE if successful and FALSE otherwise.<p>
</a>
<A NAME="934454"><PRE>    
</PRE><A NAME="934585"><BR><B>REAL </a>PM_iccg(int km, int lm, double eps, int ks, int maxit, REAL *a0,         
REAL *a1, REAL *b0, REAL *b1, REAL *bm1,                       
REAL *x, REAL *y)
</B><BR><a name="934455">
Use the </a>ICCG method to </a>solve the </a>system of equations, M.x = y. The array M is </a>symmetric and </a>positive definite.   Its components are passed in as the arrays: a0, a1, b0, b1, and bm1. The known values are passed in via the y array. The input arrays are all twice as long as needed to specify the problem; the second half is workspace. That is, the input arrays should all have dimension at least 2*(km*lm).The matrix M to be solved is symmetric, and has the structure:<p>
</a>
<A NAME="934458"><PRE>    a01
</PRE><A NAME="934460"><PRE>    a11  a02
</PRE><A NAME="934461"><PRE>         a12  a03
</PRE><A NAME="934488"><PRE>              .
</PRE><A NAME="934490"><PRE>                    .
</PRE><A NAME="934492"><PRE>                           .
</PRE><A NAME="934493"><PRE>                         a1km-2  a0km-1
</PRE><A NAME="934494"><PRE>                                a1km-1  a0km
</PRE><A NAME="934586"><PRE> 
</PRE><A NAME="934589"><PRE>    b01  bm11                                a0km+1
</PRE><A NAME="934590"><PRE> 
</PRE><A NAME="934655"><PRE>    b11  b02  bm12                           a1km+1  a0km+2
</PRE><A NAME="934672"><PRE> 
</PRE><A NAME="934673"><PRE>         b12  b03  bm13                             a1km+2  a0km+3
</PRE><A NAME="934301"><PRE>         .                                              .
</PRE><a name="934302">
Note that elements a1km etc. are zero due to the </a>block structure. The parameters km and lm are the dimensions of the underlying </a>computational mesh. The </a>index 0 &lt;= k &lt; km is the rapidly varying index of the </a>mesh array. The </a>convergence criterion is controlled by the </a>dimensionless parameter eps. When the </a>solution changes by less than eps, </a>convergence is assumed. When </a>PML runs on a </a>vector machine, the </a>incomplete </a>Cholesky </a>decomposition is done by </a>cyclic reduction. To control the </a>number of levels of cyclic reduction the ks parameter is supplied. One more level than specified will be performed. The minimum possible value of ks is 0, and the maximum possible value of ks is kp-1, where kp is the highest power of 2 with 2kp &lt;= lm. A good choice for ks is 4 for &#147;most&#148; problems. On a </a>scalar machine this parameter is ignored. The </a>number of iterations in the </a>conjugate gradient step is controlled by the parameter maxit. The return value, ret, is the actual number of </a>conjugate gradient </a>iterations plus the </a>norm of the </a>residual. The value is multiplied by -1 if certain exceptional conditions arise. Failure of the algorithm then is indicated if any of the following is true: <p>
</a>
<a name="934484">
                                                           ret &lt; 0<p>
</a>
<a name="934467">
                                                      maxit &lt;= int(ret)<p>
</a>
<a name="934469">
                                                          eps &lt; frac(ret)<p>
</a>
<A NAME="934891"><PRE> The x array contains the </a>solution on return.
</PRE><A NAME="934308"><PRE> 
</PRE><A NAME="934307"><BR><B>int </a>PM_tridi(REAL *a, REAL *b, REAL *c, REAL *r, REAL *u, int n)
</B><BR><a name="934649">
</a>Solve the equation M.u = r where M is </a>tridiagonal. The matrices a, b, and c are the </a>sub-diagonal, </a>diagonal, and </a>super-diagonal respectively and both a[0] and c[n-1] are unused since the </a>off-diagonal v</a>ectors are one element shorter than the diagonal. The number of equations is n. The result is returned in u. All vectors are passed into the routine which returns TRUE if successful and FALSE otherwise.<p>
</a>
<a name="934621">
<h2>3.3   </a>Geometry Routines</h2>
</a>
<A NAME="934501"><PRE> 
</PRE><A NAME="934465"><BR><B>REAL </a>PM_dot(REAL *x, REAL *y, int n)
</B><BR><a name="934464">
Take the </a>inner product of two </a>vectors of length n and return the result.<p>
</a>
<A NAME="934466"><PRE> 
</PRE><A NAME="934505"><BR><B>int </a>PM_cross(double x1, double y1, double x2, double y2, double x3,   double y3, 
double x4, double y4, double *px0, double *py0)
</B><BR><a name="934652">
Return the </a>intersection point of the </a>line segment defined by (X1 - X2) and the </a>ray defined by (X3 - X4). The point X3 is the end point of the ray. Each </a>vector Xi has the components Xi = (xi, yi). The intersection point is returned in (px0, py0). If the ray does not cross the </a>segment (not the line, the segment!) FALSE is returned and the vector (</a>HUGE, HUGE) is passed back in px0 and py0. If ray does cross TRUE is returned. HUGE is a </a>#</a>define&#146;d constant signifying a large floating point number<p>
</a>
<a name="934388">
<p>
</a>
<A NAME="934397"><BR><B>int </a>PM_cross_seg(double x1, double y1, double x2, double y2, double x3, double 
y3, double x4, double y4, double *px0, double *py0)
</B><BR><a name="934398">
Lik PM_cross this function will return the intersection point of the lines implied by the two segments given.  However, it returns TRUE if and only if the segments cross.<p>
</a>
<A NAME="934401"><BR><B>int </a>PM_cross_line_plane(double x1, double y1, double z1, double x2, double y2, 
double z2, REAL *px, REAL *py, REAL *pz, REAL *px0, REAL 
*py0, REAL *pz0, int line)
</B><BR><a name="934389">
This function tests for the intersection of lines and a plane. The value of line may be 0, 1, 2 for segment, ray, or line.  The line is defined by the vectors: X1 and X2 the plane is defined by the vectors: X3, X4, X5.  For rays X1 is the terminations of the ray (i.e. tail of vector) and X2 is X1 - dX (dX defines the direction of the ray).  Vectors are defined as X = (x, y).  TRUE is returned if and only if the segment, ray, or line crosses the plan.<p>
</a>
<A NAME="934406"><BR><B>int </a>PM_colinear_2d(REAL *px, REAL *py, int n)
</B><BR><a name="934390">
Return TRUE if and only if the n points from the px and py arrays are colinear in 2 dimensions.<p>
</a>
<A NAME="934407"><BR><B>int </a>PM_colinear_3d(REAL *px, REAL *py, REAL *pz, int n)
</B><BR><a name="934391">
Return TRUE if and only if the n points from the px,  py, and pz arrays are colinear in 3 dimensions.<p>
</a>
<A NAME="934412"><BR><B>int </a>PM_contains_2d(double x, double y, REAL *px, REAL *py, int n)
</B><BR><a name="934392">
Return TRUE if and only if the point (x, y) is contained in the plane bounded by the polygon defined by the n points in the px and py arrays.<p>
</a>
<A NAME="934426"><BR><B>int </a>PM_contains_3d(double x, double y, double z, REAL *px, REAL *py, REAL 
*pz, int bnd)
</B><BR><a name="934399">
Return TRUE if and only if the point (x, y, z) is contained in the plane bounded by the polygon defined by the n points in the px,  py, and pz arrays. If bnd it TRUE points on the boundary are included.<p>
</a>
<A NAME="934432"><BR><B>int </a>PM_intersect_line_polygon(REAL *pxmn, REAL *pymn, REAL *pxmx, 
REAL *pymx, REAL *ppx, REAL *ppy, int np, int *pic)
</B><BR><a name="934438">
This routine computes the intersection points of the line given by (pxmn, pymn) and (pxmx, pymx) and the given polygon as defined by the np points in ppx and ppy. The number of intersection points is returned in pic and the intersection points are returned in (pxmn, pymn) and (pxmx, pymx). FALSE is returned if the line segment is completely outside the polygon.<p>
</a>
<A NAME="934442"><BR><B>void </a>PM_convex_hull(REAL *px, REAL *py, int nh, REAL **ppx, REAL **ppy, 
int *pnp)
</B><BR><a name="934396">
This routine allocates and returns arrays (ppx, ppy) containing pnp points defining the convex polygon which contains the nh points specified in the px and py arrays.<p>
</a>
<A NAME="934445"><BR><B>void </a>PM_nearest_point(REAL *px, REAL *py, int n, double xs, double ys, REAL 
*pxt, REAL *pyt, int *pi)
</B><BR><a name="934453">
This routine finds the point in the curve defined by the n points in the px and py arrays which is nearest to the point (xs, ys). The point is returned in (pxt, pyt) and the index of that point in pi.<p>
</a>
<a name="934506">
<h2>3.4   </a>Mathematical Functions</h2>
</a>
<A NAME="934508"><PRE> 
</PRE><A NAME="934510"><BR><B>double </a>PM_</a>fix(double value)
</B><BR><a name="934509">
Return the </a>integer part of value as a REAL number. Unlike the C library function floor, PM_fix(-2.3) = -2.0;<p>
</a>
<A NAME="934512"><BR><B>double </a>PM_frac(double x)
</B><BR><a name="934303">
Return the </a>fractional part of x as a REAL number.<p>
</a>
<A NAME="934304"><BR><B>double </a>PM_</a>power(double x, int p)
</B><BR><a name="934305">
Return xp.<p>
</a>
<A NAME="934517"><BR><B>double </a>PM_random(double x)
</B><BR><a name="934596">
Return a </a>random number in the range -1.0 to 1.0.<p>
</a>
<A NAME="934592"><BR><B>double </a>PM_round(double x)
</B><BR><a name="934598">
</a>Round the number x to the nearest integer returned as a REAL number.<p>
</a>
<A NAME="934594"><BR><B>double </a>PM_sgn(double value, double sign)
</B><BR><a name="934600">
Return </a>sign(sign)*abs(value).<p>
</a>
<A NAME="934513"><BR><B>double </a>PM_sign(double x)
</B><BR><a name="934533">
Return the </a>sign of x (-1.0 or 1.0) or 0 if x = 0.<p>
</a>
<A NAME="934603"><BR><B>double </a>PM_sqr(double x)
</B><BR><A NAME="934653"><PRE> Return x2.
</PRE><a name="934650">
<h2>3.5   </a>Constructors and </a>Destructors</h2>
</a>
<A NAME="934534"><PRE> 
</PRE><A NAME="934525"><BR><B>PM_set *</a>PM_make_set(char *name, char *type, int cp, int nd, ...)
</B><BR><a name="934529">
</a>Allocate and return a </a>PM_set with name name and data of type type. The cp flag specifies the data given will be copied if the value is TRUE otherwise the data given will be used directly. This can be very important because some applications require that the data in the set have been dynamically allocated by SCORE and the copy would guarantee that. Also this feature gives the application more control of data flow. The parameter nd is the </a>dimension of the set. It is followed by nd integers representing the maximum value of an index for that dimension. The indices start at 0. Next comes the dimensionality of the </a>elements of the </a>set, nde. This is followed by nde arrays whose values are double precision floating point numbers. Each of these arrays must be as long as the product of the nd maximum indices.<p>
</a>
<a name="934531">
This is not remotely the most general PM_set that can be represented, but it is one of the most frequently encountered types of set.<p>
</a>
<a name="934537">
See the examples in the discussion of the </a>PM_set structure.<p>
</a>
<A NAME="934540"><PRE> 
</PRE><A NAME="934571"><BR><B>void </a>PM_rel_set(PM_set *set, int rel)
</B><BR><a name="934572">
</a>Release the space associated with the specified </a>set. If rel is TRUE the data arrays will be released as well as the set structure.<p>
</a>
<a name="934306">
<p>
</a>
<A NAME="934536"><BR><B>PM_mapping *</a>PM_make_mapping(char *name, char *cat, PM_set *domain, 
PM_set *range, int centering, PM_mapping *next)
</B><BR><a name="934521">
</a>Allocate and return a </a>PM_mapping with name name, type cat, domain, range, and centering. The mapping type is one of &#147;Logical-Rectangular&#148; or &#147;Arbitrarily-Connected&#148;. If a linked list of mappings is desired the mappings to which the new one points is passed in next.<p>
</a>
<a name="934522">
See the examples in the discussion of the PM_mapping structure.<p>
</a>
<A NAME="934520"><PRE> 
</PRE><A NAME="934539"><BR><B>void </a>PM_rel_mapping(PM_mapping *f, int rld, int rlr)
</B><BR><a name="934679">
</a>Release the space associated with the specified mapping. If rld is TRUE the data arrays of the domain set will be released and if rlr is TRUE the data arrays of the range set will be released.<p>
</a>
<a name="934523">
<h2>3.6   </a>Field </a>Operators</h2>
</a>
<a name="934526">
The following functions come in groups. They are provided to have at hand the functions for the </a>PM_field structure which is used in connection with </a>PM_set&#146;s and </a>PM_mapping&#146;s. They are also useful in applications where a </a>pointer to a function is needed and the function in question is </a>addition, </a>subtraction, </a>multiplication, or </a>division. <p>
</a>
<A NAME="934524"><BR><B>int </a>PM_iplus(int x, int y)
</B><BR><A NAME="934545"><BR><B>int </a>PM_iminus(int x, int y)
</B><BR><A NAME="934546"><BR><B>int </a>PM_itimes(int x, int y)
</B><BR><A NAME="934547"><BR><B>int </a>PM_idivide(int x, int y)
</B><BR><a name="934548">
Add, subtract, multiply, or divide integer x and y values and return an integer result.<p>
</a>
<A NAME="934535"><BR><B>int </a>PM_imodulo(int x, int y)
</B><BR><a name="934549">
Return </a>mod(x, y) as an integer value.<p>
</a>
<A NAME="934551"><BR><B>long </a>PM_lplus(long x, long y)
</B><BR><A NAME="934552"><BR><B>long </a>PM_lminus(long x, long y)
</B><BR><A NAME="934553"><BR><B>long </a>PM_ltimes(long x, long y)
</B><BR><A NAME="934554"><BR><B>long </a>PM_ldivide(long x, long y)
</B><BR><a name="934604">
Add, subtract, multiply, or divide long integer x and y values and return a long integer result.<p>
</a>
<A NAME="934605"><BR><B>long </a>PM_lmodulo(long x, long y)
</B><BR><a name="934556">
Return </a>mod(x, y) as a long integer value.<p>
</a>
<A NAME="934558"><BR><B>double </a>PM_fplus(double x, double y)
</B><BR><A NAME="934560"><BR><B>double </a>PM_fminus(double x, double y)
</B><BR><A NAME="934559"><BR><B>double </a>PM_ftimes(double x, double y)
</B><BR><A NAME="934606"><BR><B>double </a>PM_fdivide(double x, double y)
</B><BR><A NAME="934680"><PRE> Add, subtract, multiply, or divide double precision floating point x and y values and 
return a double result.
</PRE><a name="934527">
<h2>3.7   </a>Sorting Routines</h2>
</a>
<A NAME="934564"><PRE> 
</PRE><A NAME="934566"><BR><B>int *</a>PM_t_sort(int *in, int n_dep, int n_pts, int *ord)
</B><BR><a name="934654">
This function implements the </a>topological sort routine described in Knuth&#146;s The Art of Computer Programming, Vol 1. Input is an array of indices in, which is 2*n_dep long, containing n_dep pairs of integers specifying the topology of the set to be sorted in terms of relations, j &lt; k (&#147;j precedes k&#148;). The number of points in the set is n_pts. If ord is NULL this routine will allocate space to return the ordering, otherwise it builds the ordering into ord. The array ord must be n_pts long if it is passed in. In any case, if successful, a pointer to the ordering array is returned. If there are loops in the topology specified by in, or there are other errors NULL is returned.<p>
</a>
<a name="934313">
<h1>4.0   </a>Summary of the PML FORTRAN API</h1>
</a>
<a name="934245">
Here is a brief summary of the routines in </a>PML. They are broken down by category as: matrix manipulation routines; other equation solvers; geometry routines; mathematical functions; constructors and destructors; field operators; and sorting routines.<p>
</a>
<A NAME="934324"><BR><B></a>pmszft(integer n)
</B><BR><a name="934322">
Returns the nearest integer power of 2 in the argument n. This is used to check the size of the output arrays passed into </a>pmrfft or </a>pmcfft. The actual array sizes must be one larger than this value (this is for the zero frequence component).<p>
</a>
<A NAME="934327"><BR><B></a>pmrfft(real outyr, real outyi, real outx, integer n, real inx, real iny, real xn, real xx, 
integer o)
</B><BR><a name="934323">
Performs an FFT on n non-evenly spaced real data points.<p>
</a>
<a name="934326">
The input arguments are: n an integer number of data points; inx, an array of n real x values; iny, an array of n real y values; xn, the minimum x value; xx, the maximum real value; and o, an integer flag signalling the ordering of the transformed data. If o is 1 then the FFT data is returned in increasing order of &#147;frequency&#148;.  If o is 0 then the FFT data is returned with frequency starting at 0 and increasing followed by the most negative frequency increasing to 0.<p>
</a>
<a name="934325">
The output arguments are: outyr, an array of real values constituting the real part of the transformed range; outyi, an array of real values constituting the imaginary part of the transformed range; and outx, an array of real values constituting the transformed domain (e.g. frequency). The calling routine must supply the output arrays. The size of the arrays is one greater than the value return by a call to pmszft with the size of the input arrays.<p>
</a>
<A NAME="934361"><BR><B></a>pmcfft(real outyr, real outyi, real outx, integer n, real inx, real inyr, real inyi, real 
xn, real xx, integer f, integer o)
</B><BR><a name="934362">
Performs an FFT on n non-evenly spaced complex data points.<p>
</a>
<a name="934331">
The input arguments are: n an integer number of data points; inx, an array of n real x values; inyr, an array of n real y values constituting the real part of the input range; inyi, an array of n real y values constituting the imaginary part of the input range; xn, the minimum x value; xx, the maximum real value; f, an integer 1 for an FFT and -1 for in inverse FFT; and o, an integer flag signalling the ordering of the transformed data. If o is 1 then the FFT data is returned in increasing order of &#147;frequency&#148;.  If o is 0 then the FFT data is returned with frequency starting at 0 and increasing followed by the most negative frequency increasing to 0.<p>
</a>
<a name="934367">
The output arguments are: outyr, an array of real values constituting the real part of the transformed range; outyi, an array of real values constituting the imaginary part of the transformed range; and outx, an array of real values constituting the transformed domain (e.g. frequency). The calling routine must supply the output arrays. The size of the arrays is one greater than the value return by a call to pmszft with the size of the input arrays.<p>
</a>
<A NAME="934573"><BR><B>integer </a>pmbset(integer nn, char *name, integer nt, char *type, integer cp, integer 
nd, integer nde, integer maxes, integer topid, integer nextid)
</B><BR><a name="934576">
Start building a </a>PM_set. This function builds a set upto the elements of the set which are added with the </a>pmaset routine.  After all the components have been added a call to </a>pmeset must be made to complete the set. An integer identifier for the set is returned.  If its value is -1 then an error has occured and the set does not exist. The arguments are:<p>
</a>
<A NAME="934578">nn		number of characters in the set name
<P><A NAME="934581">name		the name of the set
<P><A NAME="934591">nt		number of characters in the set type
<P><A NAME="934593">type		the type of the elements of the set
<P><A NAME="934595">cp		if 1 the elements added with pmaset will be copied
<P><A NAME="934616">nd		the number of dimensions of the set
<P><A NAME="934617">nde		the number of dimensions of the set elements
<P><A NAME="934618">maxes		an array of dimensions for logical rectangular sets
<P><A NAME="934619">topid		an identifier for a PM_mesh_topology structure
<P><A NAME="934620">nextid		an identifier for the next set in a linke list
<P><a name="934328">
If the set is </a>logically rectangular then maxes should be an array containing the lengths of each dimension and topid should be 0.  If the set is </a>arbitrarily connected maxes should be 0 and topid should contain a valid mesh topology structure.<p>
</a>
<a name="934645">
The return value is an integer which is used as an identifier in functions which require a reference to a set.  If there is an error -1 will be returned.<p>
</a>
<A NAME="934625"><BR><B>integer </a>pmaset(integer setid, integer component, void element)
</B><BR><a name="934622">
Add </a>component number component to the set.  The data in element must be an array of numbers of the type defined in the set and with length matching the number of elements of the set.  Typically this will be called nde times where nde is the number of dimensions of the set elements as supplied to pmbset. If the set was made with the copy flag on (1), then the values in element will be copied into a newly allocated space, otherwise the array element will be used directly. This point should always be very carefully considered since dynamic memory heaps can be corrupted if this is used improperly.  The argument setid is the value returned from a call to </a>pmbset.<p>
</a>
<A NAME="934629"><BR><B>integer </a>pmeset(integer setid)
</B><BR><a name="934628">
This completes the construction of the set begun with </a>pmbset and </a>pmaset.  The argument setid is the value return from a call to </a>pmbset.<p>
</a>
<A NAME="934631"><BR><B>integer </a>pmmtop(integer nd, integer nc, integer bp, integer bnd)
</B><BR><a name="934632">
Make a </a>PM_mesh_topology structure from the given </a>connectivity informations.  You should be thoroughly familiar with the discussion of connectivity given earlier. The arguments are:<p>
</a>
<A NAME="934630">nd	the number of dimensions
<P><A NAME="934633">nc	an array of numbers of cells of each dimension
<P><A NAME="934634">bp	an array of numbers of boundary parameters at each dimension
<P><A NAME="934635">bnd	an array containing the cell descriptions
<P><a name="934636">
The layout of nc has nc(1) being the number of 0-cells (nodes), nc(2) being the number of 1-cells (edges), nc(3) being the number of 2-cells (faces), and so on.  The layout of bp has bp(1) is 0, bp(2) the number of parameters describing all 1-cells, bp(3) the number of parameters describing all 2-cells, and so on. The layout of bnd is as follows:<p>
</a>
<A NAME="934637">bnd(n1) thru bnd(n2-1) contains all of the 1-cell information
<P><A NAME="934639">bnd(n2) thru bnd(n3-1) contains all of the 2-cell information
<P><A NAME="934640">bnd(n3) thru bnd(n4-1) contains all of the 3-cell information
<P><A NAME="934643">                                         .
<P><A NAME="934641">                                         .
<P><A NAME="934644">                                         .
<P><a name="934646">
where<p>
</a>
<A NAME="934642">ni = 1 + nc(i)*bp(i)
<P><a name="934647">
It is crucial to remember that the values in bnd are indeces which are 0 based! Be sure to review the section on connectivity.<p>
</a>
<a name="934321">
<h1>5.0   </a>EXAMPLES</h1>
</a>
<a name="934729">
To provide some examples of the use of these routines, the source code for some of the </a>PML test programs is provided here. The first piece of code demonstrates the use of most of the matrix routines. This is followed by the ICCG test program and the test program for the topological sort routine. The routine to generate a </a>PM_set was included in the discussion of the </a>PM_set structure. Users may contact the author for further examples.<p>
</a>
<a name="934730">
<h2>5.1   </a>Examples of MATRIX Functions</h2>
</a>
<A NAME="934681"><PRE> 
</PRE><A NAME="934731"><PRE> #include &#147;pml.h&#148;
</PRE><A NAME="934682"><PRE> 
</PRE><A NAME="934683"><PRE> REAL a1[5][3] = {0, 0, 1, 1, 0, 1, 0, 1, 1,
</PRE><A NAME="934651"><PRE>                 0.5, 0.5, 1, 0.7, 0.9, 1};
</PRE><A NAME="934684"><PRE> REAL b1[5][1] = {0.5, 1.4, .93589, 1.16795, 1.52230};
</PRE><A NAME="934688"><PRE> 
</PRE><A NAME="934689"><PRE> /* MAIN - a sample program */
</PRE><A NAME="934690"><PRE> 
</PRE><A NAME="934691"><PRE> main()
</PRE><A NAME="934692"><PRE>    {int i, j, count;
</PRE><A NAME="934693"><PRE>     int nrow = 5, ncol = 3;
</PRE><A NAME="934694"><PRE>     </a>PM_matrix *m, *t, *b, *a, *c;
</PRE><A NAME="934695"><PRE> 
</PRE><A NAME="934696"><PRE>     m = </a>PM_create(nrow, ncol);
</PRE><A NAME="934697"><PRE>     b = PM_create(nrow, 1);
</PRE><A NAME="934698"><PRE> 
</PRE><A NAME="934699"><PRE>     count = 0;
</PRE><A NAME="934700"><PRE>     for (i = 1; i &lt;= nrow; i++, count++)
</PRE><A NAME="934701"><PRE>         {for (j = 1; j &lt;= ncol; j++, count++)
</PRE><A NAME="934702"><PRE>              {</a>PM_element(m, i, j) = count;};
</PRE><A NAME="934703"><PRE>          PM_element(b, i, 1) = count;};
</PRE><A NAME="934704"><PRE>     </a>PM_print(m);
</PRE><A NAME="934705"><PRE> 
</PRE><A NAME="934386"><PRE>     m-&gt;array = (REAL *)a1;
</PRE><A NAME="934706"><PRE>     b-&gt;array = (REAL *)b1;
</PRE><A NAME="934707"><PRE>         
</PRE><A NAME="934708"><PRE>     PM_print(m);
</PRE><A NAME="934709"><PRE> 
</PRE><A NAME="934710"><PRE>     PM_print(b);
</PRE><A NAME="934711"><PRE> 
</PRE><A NAME="934712"><PRE>     PM_print(t = </a>PM_transpose(m));
</PRE><A NAME="934713"><PRE> 
</PRE><A NAME="934714"><PRE>     PM_print(a = </a>PM_times(t, m));
</PRE><A NAME="934715"><PRE> 
</PRE><A NAME="934716"><PRE>     PM_print(c = PM_times(t, b));
</PRE><A NAME="934717"><PRE> 
</PRE><A NAME="934718"><PRE>     PM_print(</a>PM_solve(a, c));
</PRE><A NAME="934719"><PRE> 
</PRE><A NAME="934720"><PRE>     PM_print(t = PM_times(PM_transpose(m), m));
</PRE><A NAME="934721"><PRE> 
</PRE><A NAME="934722"><PRE>     PM_print(c = </a>PM_inverse(t));
</PRE><A NAME="934723"><PRE> 
</PRE><A NAME="934724"><PRE>     PM_print(PM_times(c, t));
</PRE><A NAME="934685"><PRE> 
</PRE><A NAME="934686"><PRE>     exit(0);}
</PRE><A NAME="934725"><PRE> 
</PRE><a name="934727">
<h2>5.2   </a>Example of </a>ICCG Routine</h2>
</a>
<A NAME="934733"><PRE> 
</PRE><A NAME="934734"><PRE> #include &#147;pml.h&#148;
</PRE><A NAME="934735"><PRE> 
</PRE><A NAME="934736"><PRE> #define KM     4
</PRE><A NAME="934737"><PRE> #define LM     3
</PRE><A NAME="934738"><PRE> #define KXL    24                                      /* 2*KM*LM */
</PRE><A NAME="934739"><PRE> #define KL     12                                        /* KM*LM */
</PRE><A NAME="934740"><PRE> #define EPS    1.0e-6
</PRE><A NAME="934741"><PRE> #define KS     4
</PRE><A NAME="934742"><PRE> #define MAXIT  100
</PRE><A NAME="934743"><PRE> 
</PRE><A NAME="934746"><PRE> /* MAIN - solve a Laplace equation */
</PRE><A NAME="934747"><PRE> 
</PRE><A NAME="934748"><PRE> main()
</PRE><A NAME="934749"><PRE>    {int i, j, k;
</PRE><A NAME="934750"><PRE>     REAL ret;
</PRE><A NAME="934751"><PRE>     REAL a0[KXL], a1[KXL], b0[KXL], b1[KXL], bm1[KXL],
</PRE><A NAME="934687"><PRE>          x[KXL], y[KXL];
</PRE><A NAME="934752"><PRE> 
</PRE><A NAME="934753"><PRE> /* solve a Laplacian in the l direction */
</PRE><A NAME="934745"><PRE>     for (i = 0; i &lt; KXL; i++)
</PRE><A NAME="934754"><PRE>         	{a0[i]  = 0.0;
</PRE><A NAME="934755"><PRE>         	 a1[i]  = 0.0;
</PRE><A NAME="934756"><PRE>         	 b0[i]  = 0.0;
</PRE><A NAME="934757"><PRE>         	 b1[i]  = 0.0;
</PRE><A NAME="934758"><PRE>         	 bm1[i] = 0.0;
</PRE><A NAME="934759"><PRE>         	 x[i]   = 0.0;
</PRE><A NAME="934760"><PRE>         	 y[i]   = 0.0;};
</PRE><A NAME="934761"><PRE> 
</PRE><A NAME="934762"><PRE>     for (i = KM; i &lt; 2*KM; i++)
</PRE><A NAME="934763"><PRE>         	{a0[i]  = 2.0;
</PRE><A NAME="934764"><PRE>         	 a1[i]  = -1.0;
</PRE><A NAME="934765"><PRE>         	 b0[i]  = 0.0;
</PRE><A NAME="934766"><PRE>         	 b1[i]  = 0.0;
</PRE><A NAME="934767"><PRE>         	 bm1[i] = 0.0;};
</PRE><A NAME="934768"><PRE> 
</PRE><A NAME="934769"><PRE>     a1[2*KM-1] = 0.0;
</PRE><A NAME="934770"><PRE>     for (i = 0; i &lt; KM; i++)
</PRE><A NAME="934771"><PRE>         	{k = KM + i;
</PRE><A NAME="934772"><PRE>         	 y[k] = (REAL) (i+1);};
</PRE><A NAME="934773"><PRE> 
</PRE><A NAME="934774"><PRE>     printf(&#147;\nProblem y :\n&#148;);
</PRE><A NAME="934775"><PRE>     for (j = 0; j &lt; LM; j++)
</PRE><A NAME="934776"><PRE>         	{printf(&#147;\nRow #%d: \n&#148;, j+1);
</PRE><A NAME="934777"><PRE>         	 for (i = 0; i &lt; KM; i++)
</PRE><A NAME="934778"><PRE>         	     {k = j*KM + i;
</PRE><A NAME="934779"><PRE>         	      printf(&#147; y(%2d, %2d) = %g &#147;, j+1, i+1, y[k]);};};
</PRE><A NAME="934780"><PRE>     printf(&#147;\n&#148;);
</PRE><A NAME="934781"><PRE>     
</PRE><A NAME="934782"><PRE>     ret = </a>PM_iccg(KM, LM, EPS, KS, MAXIT,
</PRE><A NAME="934726"><PRE>                   a0, a1, b0, b1, bm1, x, y);
</PRE><A NAME="934783"><PRE> 
</PRE><A NAME="934784"><PRE>     printf(&#147;\nSolution x (should be (4, 7, 8, 6)) : %g\n&#148;, ret);
</PRE><A NAME="934785"><PRE>     for (j = 0; j &lt; LM; j++)
</PRE><A NAME="934786"><PRE>         	{printf(&#147;\nRow #%d: \n&#148;, j+1);
</PRE><A NAME="934787"><PRE>         	 for (i = 0; i &lt; KM; i++)
</PRE><A NAME="934788"><PRE>         	     {k = j*KM + i;    
</PRE><A NAME="934728"><PRE>          	     printf(&#147; x(%2d, %2d) = %g &#147;,j+1, i+1, x[k]);};};
</PRE><A NAME="934789"><PRE>     printf(&#147;\n\n&#148;);
</PRE><A NAME="934790"><PRE> 
</PRE><A NAME="934791"><PRE>     SC_pause();
</PRE><A NAME="934792"><PRE> 
</PRE><A NAME="934793"><PRE> /* solve a Laplacian in the k direction */
</PRE><A NAME="934744"><PRE>     for (i = 0; i &lt; KXL; i++)
</PRE><A NAME="934794"><PRE>         	{a0[i] 	 = 0.0;
</PRE><A NAME="934795"><PRE>         	 a1[i]  = 0.0;
</PRE><A NAME="934796"><PRE>         	 b0[i]  = 0.0;
</PRE><A NAME="934797"><PRE>         	 b1[i]  = 0.0;
</PRE><A NAME="934798"><PRE>         	 bm1[i] = 0.0;
</PRE><A NAME="934799"><PRE>         	 x[i]   = 0.0;
</PRE><A NAME="934800"><PRE>         	 y[i]   = 0.0;};
</PRE><A NAME="934801"><PRE> 
</PRE><A NAME="934802"><PRE>     for (i = 1; i &lt; KXL; i += LM)
</PRE><A NAME="934803"><PRE>         	{a0[i]  = 2.0;
</PRE><A NAME="934804"><PRE>         	 a1[i]  = 0.0;
</PRE><A NAME="934805"><PRE>         	 b0[i]  = -1.0;
</PRE><A NAME="934806"><PRE>         	 b1[i]  = 0.0;
</PRE><A NAME="934807"><PRE>         	 bm1[i] = 0.0;};
</PRE><A NAME="934808"><PRE> 
</PRE><A NAME="934809"><PRE>     for (k = 1, i = 1; i &lt; KXL; i += LM, k++)
</PRE><A NAME="934810"><PRE>         	y[i] = (REAL) k;
</PRE><A NAME="934811"><PRE> 
</PRE><A NAME="934812"><PRE>     printf(&#147;\nProblem y :\n&#148;);
</PRE><A NAME="934813"><PRE>     for (j = 0; j &lt; KM; j++)
</PRE><A NAME="934814"><PRE>         	{printf(&#147;\nRow #%d: \n&#148;, j+1);
</PRE><A NAME="934815"><PRE>         	 for (i = 0; i &lt; LM; i++)
</PRE><A NAME="934816"><PRE>         	     {k = j*LM + i;
</PRE><A NAME="934817"><PRE>         	      printf(&#147; y(%2d, %2d) = %g &#147;, j+1, i+1, y[k]);};};
</PRE><A NAME="934818"><PRE>     printf(&#147;\n&#148;);
</PRE><A NAME="934819"><PRE>     
</PRE><A NAME="934820"><PRE>     ret = </a>PM_iccg(LM, KM, EPS, KS, MAXIT,
</PRE><A NAME="934732"><PRE>                  a0, a1, b0, b1, bm1, x, y);
</PRE><A NAME="934821"><PRE> 
</PRE><A NAME="934822"><PRE>     printf(&#147;\nSolution x (should be (4, 7, 8, 6)) : %g\n&#148;, ret);
</PRE><A NAME="934823"><PRE>     for (j = 0; j &lt; KM; j++)
</PRE><A NAME="934824"><PRE>         	{printf(&#147;\nRow #%d: \n&#148;, j+1);
</PRE><A NAME="934825"><PRE>         	 for (i = 0; i &lt; LM; i++)
</PRE><A NAME="934826"><PRE>         	     {k = j*LM + i;
</PRE><A NAME="934827"><PRE>         	      printf(&#147; x(%2d, %2d) = %g &#147;, j+1, i+1, x[k]);};};
</PRE><A NAME="934828"><PRE>     printf(&#147;\n\n&#148;);
</PRE><A NAME="934829"><PRE> 
</PRE><A NAME="934831"><PRE>     exit(0);}
</PRE><a name="934832">
<h2>5.3   </a>Example of </a>Topological </a>Sort Routine</h2>
</a>
<A NAME="934830"><PRE> 
</PRE><A NAME="934835"><PRE> #include &#147;pml.h&#148;
</PRE><A NAME="934836"><PRE> 
</PRE><A NAME="934837"><PRE> #define NDEP 10
</PRE><A NAME="934838"><PRE> #define NPTS  9
</PRE><A NAME="934839"><PRE> 
</PRE><A NAME="934842"><PRE> main()
</PRE><A NAME="934843"><PRE>    {int i, rel[20], *ord;
</PRE><A NAME="934844"><PRE> 
</PRE><A NAME="934845"><PRE>     rel[0] = 9;
</PRE><A NAME="934846"><PRE>     rel[1] = 2;
</PRE><A NAME="934847"><PRE> 
</PRE><A NAME="934848"><PRE>     rel[2] = 3;
</PRE><A NAME="934849"><PRE>     rel[3] = 7;
</PRE><A NAME="934850"><PRE> 
</PRE><A NAME="934851"><PRE>     rel[4] = 7;
</PRE><A NAME="934852"><PRE>     rel[5] = 5;
</PRE><A NAME="934853"><PRE> 
</PRE><A NAME="934854"><PRE>     rel[6] = 5;
</PRE><A NAME="934855"><PRE>     rel[7] = 8;
</PRE><A NAME="934856"><PRE> 
</PRE><A NAME="934857"><PRE>     rel[8] = 8;
</PRE><A NAME="934858"><PRE>     rel[9] = 6;
</PRE><A NAME="934859"><PRE> 
</PRE><A NAME="934860"><PRE>     rel[10] = 4;
</PRE><A NAME="934861"><PRE>     rel[11] = 6;
</PRE><A NAME="934862"><PRE> 
</PRE><A NAME="934863"><PRE>     rel[12] = 1;
</PRE><A NAME="934864"><PRE>     rel[13] = 3;
</PRE><A NAME="934865"><PRE> 
</PRE><A NAME="934866"><PRE>     rel[14] = 7;
</PRE><A NAME="934867"><PRE>     rel[15] = 4;
</PRE><A NAME="934868"><PRE> 
</PRE><A NAME="934869"><PRE>     rel[16] = 9;
</PRE><A NAME="934870"><PRE>     rel[17] = 5;
</PRE><A NAME="934871"><PRE> 
</PRE><A NAME="934872"><PRE>     rel[18] = 2;
</PRE><A NAME="934873"><PRE>     rel[19] = 8;
</PRE><A NAME="934874"><PRE> 
</PRE><A NAME="934875"><PRE>     printf(&#147;\nRelations List:\n&#148;);
</PRE><A NAME="934876"><PRE>     for (i = 0; i &lt; 2*NDEP; i += 2)
</PRE><A NAME="934877"><PRE>         printf(&#147;%2d &lt; %2d\n&#148;, rel[i], rel[i+1]);
</PRE><A NAME="934878"><PRE>     printf(&#147;\n&#148;);
</PRE><A NAME="934879"><PRE> 
</PRE><A NAME="934880"><PRE>     ord = </a>PM_t_sort(rel, NDEP, NPTS, NULL);
</PRE><A NAME="934881"><PRE> 
</PRE><A NAME="934882"><PRE>     printf(&#147;Partially ordered List:\n&#148;);
</PRE><A NAME="934883"><PRE>     for (i = 0; i &lt; NPTS; i++)
</PRE><A NAME="934884"><PRE>         printf(&#147;A(%2d) = %2d\n&#148;, i, ord[i]);
</PRE><A NAME="934885"><PRE> 
</PRE><A NAME="934886"><PRE>     printf(&#147;\nThe correct order is:\n 1 9 3 2 7 4 5 8 6\n\n&#148;);
</PRE><A NAME="934887"><PRE> 
</PRE><A NAME="934231"><PRE>     exit(0);}
</PRE><a name="934311">
<h1>6.0   </a>Related </a>Documentation</h1>
</a>
<a name="934275">
</a>PML is a part of the </a>PACT </a>portable </a>code development and </a>visualization tool set. Interested readers may with to consult other PACT documents.<p>
</a>
<a name="934312">
<p>
</a>
<a name="934338">
The list of PACT Documents is:<p>
</a>
<A NAME="934348"><PRE>   PACT User&#146;s Guide, UCRL-MA-112087
</PRE><A NAME="934351"><PRE>   SCORE User&#146;s Manual, UCRL-MA-108976 Rev.1
</PRE><A NAME="934360"><PRE>   PPC User&#146;s Manual UCRL-MA-108964 Rev.1
</PRE><A NAME="934365"><PRE>   PML User&#146;s Manual, UCRL-MA-108965 Rev.1 (this document)
</PRE><A NAME="934369"><PRE>   PDBLib User&#146;s Manual, M-270 Rev.2
</PRE><A NAME="934372"><PRE>   PGS User&#146;s Manual, UCRL-MA-108966 Rev.1
</PRE><A NAME="934373"><PRE>   PANACEA User&#146;s Manual, M-276 Rev.2
</PRE><A NAME="934376"><PRE>   ULTRA II User&#146;s Manual, UCRL-MA-108967 Rev.1
</PRE><A NAME="934378"><PRE>   PDBDiff User&#146;s Manual, UCRL-MA-108975 Rev.1
</PRE><A NAME="934381"><PRE>   PDBView User&#146;s Manual, UCRL-MA-108968 Rev.1
</PRE><A NAME="934340"><PRE>   SX User&#146;s Manual, UCRL-MA-112315
</PRE><a name="934228">
<p>
</a>
<a name="934363">
Additional reading:<p>
</a>
<A NAME="934364"><PRE>          Abramowitz, Stegun, Handbook of Mathematical Functions, Dover.
</PRE><A NAME="934387"><PRE>          Knuth, D.E. The Art of Computer Programming, Vol I - III, Addison-Wesley.
</PRE><A NAME="934383"><PRE>          Press, Flannery, Teukolsky, and Vetterling, Numerical Recipes in C, Cambridge Press.
</PRE>
<p><hr>

</body></html>