This example illustrates how to use FMS in a typical Radar Cross Section program. The example includes using the FMS parallel processing utilities to generate the matrix coefficients in parallel.
        PROGRAM RCSFMS
C-----------------------------------------------------------------------
C	This is a sample program that illustrates the following:
C
C	* How to use the FMS out-of-core solver in a RCS program,
C
C	* How to generate matrix elements efficiently,
C
C	* How to use the FMS parallel processing utilities to generate
C	  matrix elements in parallel.
C
C	It is assumed that the model consists of a mesh composed of
C	triangles, and the unknowns (equations) are the values on the
C	triangle edges.  It is also assumed that each triangle impacts
C	every other triangle, producing a full matrix.
C
C	It is also assumed that the data is double precision complex,
C	(COMPLEX*16).
C
C	The test matrix produced by this example has a solution vector
C	of all (0.5,-0.5)'s.
C
C-----------------------------------------------------------------------
C	DECLARATIONS
C-----------------------------------------------------------------------
C
C	Program name:
	CHARACTER*9 MYNAME
	PARAMETER (MYNAME='EXAMPLE_9')
C
C	Input data functions:
	INTEGER ASK_I
C
C	Subroutines called in parallel (passed to FMSPAR) must be
C	declared external.
        EXTERNAL FILL
C
C	FMS file object handles (attribute lists)
C	Matrix file:
        INTEGER LUA(25)
C	Vector file:
        INTEGER LUX(25)
C
C	Data type:
        INTEGER IDTYPE
C	Complex*16:
        PARAMETER (IDTYPE=2)
C
C	FMS profile vector:
C	LOWEQ(1)=-1 flags a full matrix:
        INTEGER LOWEQ(1)
        DATA LOWEQ/-1/
C
C	Constants used for this test matrix:
        COMPLEX*16 CZERO, CONE, ANSWER
        PARAMETER (CZERO  = (0.0D0, 0.0D0))
        PARAMETER (CONE   = (1.0D0, 0.0D0))
        PARAMETER (ANSWER = (0.5D0,-0.5D0))
C
C	Variables used to check answer:
        REAL*8 ERROR, ETEST
C
C	Local variables:
	INTEGER L_X, LENX
C
C	FMS memory management requires the following arrays:
	POINTER (CMD_PTR, CMD)
	POINTER (RMD_PTR, RMD)
	POINTER (IMD_PTR, IMD)
	COMPLEX*16 CMD(0:1)
	REAL*8     RMD(0:1)
	INTEGER    IMD(0:1)
C-----------------------------------------------------------------------
C (1)	Initialize FMS:
C-----------------------------------------------------------------------
        CALL FMSINI
C	This must be the first statement in your program.
C	It performs the following functions:
C	* Initializes FMS,
C	* Sets FMS Parameters to default values,
C	* If children processes start at the beginning of the program,
C	  they are put to sleep.
C
	CALL FMSPSH(MYNAME)
C
C	The following calls to subroutine FMSIGT obtain the pointers
C	to the FMS memory management arrays IMD, RMD and CMD:
C
C	FMS contains a list of environment variables called FMS
C	Parameters.  These parameters control special features.
C	You may modify the default value of the FMS parameters in one
C	of 3 ways:
C	* Call FMSSET to interactively modify parameters at run time.
C	* Include FMSSET, Parameter statements and RETURN in the license
C	  file to modify the default values,
C	* Call FMSIST, FMSRST or FMSCST in the program.
C
C	Here are some examples:
C
C	If you want to process this matrix with partial pivoting,
C	include the following:
C	CALL FMSIST ('MFMAT',3)
C
C	or
C
C	If you want to average the upper and lower triangle parts to
C	produce a symmetric matrix, include the following:
C	CALL FMSIST ('LUOK', 1)
C	NOTE: For symmetric processing, you must also change the
C	complex nonsymmetric FMS calls (beginning with CN) to
C	complex symmetric (beginning with CS).
C
C	Loop back to here to do next problem:
  100   CONTINUE
C
C	Allow for interactive modification of the FMS parameters:
C	If you do not want to do this, remove this call.
        PRINT *
        PRINT *, 'You may now set any FMS parameter.'
        PRINT *, 'When you have finished, type the letters RETURN'
        CALL FMSSET
	CALL FMSIGT ('MEMPTR', IMD_PTR)
	CALL FMSIGT ('MEMPTR', RMD_PTR)
	CALL FMSIGT ('MEMPTR', CMD_PTR)
C
C	You may also obtain the value of any FMS parameter by calling
C	FMSIGT, FMSRGT or FMSCGT.  Here we get the number of processors.
        CALL FMSIGT ('MAXCPU', MAXCPU)
C-----------------------------------------------------------------------
C	Application Specific
C-----------------------------------------------------------------------
C
C	Generate a model
C	================
C	For this sample problem, we will generate a mesh of a cylinder.
C
C	Find out how many equations and RHS vectors to use:
        PRINT *
        PRINT *, 'Enter the model parameters below.'
        PRINT *, 'If you want to stop, enter a value of 0'
        PRINT *
	MAXEQU = ASK_I ('Enter the number of equations')
	MAXEQU = MAX0(MAXEQU,6)
        IF(MAXEQU .LE. 0) GO TO 200
	NUMVEC = ASK_I ('Enter the number of vectors')
        IF(NUMVEC .LE. 0) GO TO 200
C
        PRINT *
        PRINT *,'Start of generating model'
C
C	FMS contains memory management routines.  Subroutines FMSIMG,
C	FMSRMG and FMSCMG allocate INTEGER, REAL*8 and COMPLEX*16
C	memory respectively.  The arguments include a reference
C	array, the returned offset, and the number of words requested.
C	Allocate memory for edge list:
        CALL FMSIMG (IMD, L_IDEDGE, 3*MAXEQU)
C	CALL FMSCST ('SHOW','MEMORY')
C
C	Generate the mesh:
        NEQEST = MAXEQU
        CALL MGEN (NEQEST, NFACES, NEDGES, IMD(L_IDEDGE))
C
        PRINT *,'Number of faces....................=', NFACES
        PRINT *,'Number of edges....................=', NEDGES
        PRINT *,'End of generating model'
C
C	For this problem, the number of equations is the number of
C	edges.
        NUMEQ  = NEDGES
C	
C	Determine order for processing faces
C	====================================
        PRINT *,'Start of computing face order'
C
C	The amount of storage required to accumulate matrix data before
C	transferring to FMS can be reduced by changing the order of
C	processing faces.  The following code determines the optimum
C	order, IORDER(NFACES) and the number of vectors required
C	for accumulation, MAXVEC.
C
C	Allocate storage for sorting faces:
        CALL FMSIMG (IMD, L_IORDER, NFACES)
        CALL FMSIMG (IMD, L_MAXUSE, NEDGES)
        CALL FMSIMG (IMD, L_NLEFT,  NEDGES)
        CALL FMSIMG (IMD, L_IUSE  , NFACES)
        CALL FMSIMG (IMD, L_IAGE  , NEDGES)
C
        CALL ORDERF (
     1   NFACES      , NEDGES       , IMD(L_IDEDGE),
     2   MAXVEC      , IMD(L_IORDER), IMD(L_MAXUSE),
     3   IMD(L_NLEFT), IMD(L_IUSE  ), IMD(L_IAGE  ))
C
        PRINT *,'Maximum number of active vectors...=', MAXVEC
C
C	Return scratch storage:
        CALL FMSIMR (IMD, L_IAGE  , NEDGES)
        CALL FMSIMR (IMD, L_IUSE  , NFACES)
        PRINT *,'End of computing face order'
C-----------------------------------------------------------------------
C (2)	Open FMS files:
C-----------------------------------------------------------------------
        CALL CNDI  (LOWEQ, NUMEQ, 'Matrix', LUA)
        CALL FMSOV (NUMEQ, IDTYPE, NUMVEC, 'Vectors', LUX)
C-----------------------------------------------------------------------
C (3)	Write data to FMS files:
C-----------------------------------------------------------------------
        PRINT *
        PRINT *,'Starting fill'
C
C	Allocate storage for computing matrix elements:
C	===============================================
C
C	MAXVEC shared vectors for accumulators:
C	Increase MAXVEC to allow processes to operate independently:
        MAXVEC = MAXVEC + 3*MAXCPU
        CALL FMSCMG (CMD, L_VGLOB, MAXVEC*NUMEQ)
C
C	Accumulator vector identifiers:
C	(Lists which global equations are currently held)
        CALL FMSIMG (IMD, L_IDGLOB, MAXVEC)
C
C	Process status (private for each process)
        CALL FMSIMG (IMD, L_ISTAT, MAXCPU)
C
C	Store scalar arguments in shared memory
C	=======================================
C	This is necessary if children are forked or spawned.
C	This is not necessary (but doesn't hurt) if children are threads.
        CALL FMSIMG (IMD, L_NXFACE, 6)
        L_NAVAIL = L_NXFACE + 1
        L_NFACES = L_NAVAIL + 1
        L_NEDGES = L_NFACES + 1
        L_MAXVEC = L_NEDGES + 1
        L_LUA    = L_MAXVEC + 1
        IMD(L_NXFACE) = 0
        IMD(L_NAVAIL) = MAXVEC
        IMD(L_NFACES) = NFACES
        IMD(L_NEDGES) = NEDGES
        IMD(L_MAXVEC) = MAXVEC
C
C	Initialize values:
C	==================
C	Initialize accumulators to available:
        DO I=1,MAXVEC
           IMD(L_IDGLOB + I - 1) = 0
        END DO
C
C	Initialize NLEFT to MAXUSE:
        DO I=1,NEDGES
           IMD(L_NLEFT-1+I) = IMD(L_MAXUSE-1+I)
        END DO
C
C	Initialize addresses of private data:
        LL_ISTAT  = L_ISTAT
C
C	Leave room for local vectors:
        CALL FMSIGT ('MAXMD', MAXMD)
	LENC16 = 2*(3*NUMEQ)
	LENC16 = LENC16 + 2048
	MD_NEW = MAXMD - MAXCPU*LENC16
        CALL FMSIST ('MAXMD', MAXMD)
C
C	Initialize FMSROW:
C	Use all remaining memory for buffers:
C	NOTE: This is a common set of buffers for all processes.
C	Therefore FMSROW is initialized once by parent before starting
C	children.
        CALL FMSROW (-1, CMD(L_VGLOB), LUA)
C
C	Reset MAXMD to allow space for arrays allocated by children:
        CALL FMSIST ('MAXMD', MAXMD)
C
C	Copy LUA(25) to shared memory for children:
C	Note: If children are threads, you may use LUA directly.
	DO I = 1,25
	   IMD(L_LUA+I-1) = LUA(I)
	END DO
C
C	Generate matrix elements in parallel
C	====================================
C	Loop over children processes:
        DO ICPU = 2,MAXCPU
C	   Increment addresses of private data:
           LL_ISTAT  = LL_ISTAT  + 1
C	   Place work for children in a queue:
           CALL FMSPAR (13, FILL,
     1        IMD(L_NXFACE ), IMD(L_NAVAIL ),
     2        IMD(L_NFACES ), IMD(L_NEDGES ), IMD(L_MAXVEC ),
     3        IMD(L_LUA    ), IMD(L_IDEDGE ), IMD(L_IORDER ),
     4        IMD(L_MAXUSE ), IMD(L_NLEFT  ),
     5        CMD(L_VGLOB  ), IMD(L_IDGLOB ), IMD(LL_ISTAT ))
        END DO
C
C	Start the children running:
        IF(MAXCPU .GT. 1) CALL FMSRUN
C
C	Do parent's part:
        LL_ISTAT  = L_ISTAT
        CALL FILL(
     1     IMD(L_NXFACE ), IMD(L_NAVAIL ),
     2     IMD(L_NFACES ), IMD(L_NEDGES ), IMD(L_MAXVEC ),
     3     LUA           , IMD(L_IDEDGE ), IMD(L_IORDER ),
     4     IMD(L_MAXUSE ), IMD(L_NLEFT  ),
     5     CMD(L_VGLOB  ), IMD(L_IDGLOB ), IMD(LL_ISTAT ))
C
C	Wait for the children to complete:
        IF(MAXCPU .GT. 1) CALL FMSYNC
C
C	Check the status of the processes:
        WRITE(6,2002)
        NTOTAL = 0
	LL_ISTAT = L_ISTAT
        DO I=1,MAXCPU
	   WRITE(6,2004) I, IMD(LL_ISTAT)
           IF(IMD(LL_ISTAT) .LT. 0) STOP'Error during fill'
           NTOTAL   = NTOTAL   + IMD(LL_ISTAT)
	   LL_ISTAT = LL_ISTAT + 1
        END DO
        WRITE(6,2003) NTOTAL
        IF(NTOTAL .NE. NFACES) STOP'Not all faces processed'
C
C	End FMSROW:
        CALL FMSROW (NUMEQ+1, CMD(L_VGLOB), LUA)
C
C	Free storage used for fill
C	==========================
        CALL FMSIMR (IMD, L_NXFACE, 5 + 25)
        CALL FMSIMR (IMD, L_ISTAT,  MAXCPU)
        CALL FMSIMR (IMD, L_IDGLOB, MAXVEC)
        CALL FMSCMR (CMD, L_VGLOB,  MAXVEC*NUMEQ)
        CALL FMSIMR (IMD, L_NLEFT,  NEDGES)
        CALL FMSIMR (IMD, L_MAXUSE, NEDGES)
        CALL FMSIMR (IMD, L_IORDER, NFACES)
        CALL FMSIMR (IMD, L_IDEDGE, 3*MAXEQU)
        PRINT *,'Ending fill'
C
C	Write RHS vector
C	================
	LENX = LUX(4)/2
        CALL FMSCMG (CMD, L_RHS, LENX)
        CMD(L_RHS) = CONE
        DO I=2,NUMEQ
           CMD(L_RHS+I-1) = CZERO
        END DO
        LOCD = 1
        DO IVEC = 1,NUMVEC
           CALL FMSWRT (LUX, LOCD, CMD(L_RHS), LUX(4))
           LOCD = LOCD + LUX(4)
        END DO
        CALL FMSCMR (CMD, L_RHS, LENX)
C-----------------------------------------------------------------------
C (4)	Perform matrix algebra:
C-----------------------------------------------------------------------
        CALL CNDF (LUA, LUA, LUX, LUX, 0)
        CALL CNDS (LUA, LUX, LUX, NUMVEC, 0)
C-----------------------------------------------------------------------
C (5)	Read data from FMS files:
C-----------------------------------------------------------------------
        CALL FMSCMG (CMD, L_X, LENX)
        LOCD = 1
        ERROR = 0.0D0
        DO IVEC = 1,NUMVEC
           CALL FMSRED (LUX, LOCD, CMD(L_X), LUX(4))
           LOCD  = LOCD + LUX(4)
           DO I=1,NUMEQ
              ETEST = ABS( CMD(L_X + I - 1) - ANSWER )
              IF(ETEST .GT. ERROR) ERROR = ETEST
           END DO
        END DO
        PRINT *,'MAXIMUM ERROR =', ERROR
        CALL FMSCMR (CMD, L_X, LENX)
C-----------------------------------------------------------------------
C (6)	End FMS:
C-----------------------------------------------------------------------
C	Do the next problem.
        CALL FMSCV (LUX)
        CALL FMSCM (LUA)
        GO TO 100
  200   CONTINUE
	CALL FMSPOP(MYNAME)
        CALL FMSEND
C-----------------------------------------------------------------------
C	FORMAT STATEMENTS
C-----------------------------------------------------------------------
 2002   FORMAT (/
     1  ' Number of faces computed by each process'/
     2  ' PROCESS     FACES'/
     3  ' =======     =====')
 2003   FORMAT (
     1  '             -----'/
     2  ' TOTAL', I12)
 2004	FORMAT (I5,I13)
        END
C=======================================================================
        SUBROUTINE ORDERF
     1   (NFACES, NEDGES, IDEDGE,
     2    MAXVEC, IORDER, MAXUSE,
     3    NLEFT, IUSE, IAGE)
C=======================================================================
C	DESCRIPTION:
C
C	This next section of code computes the order of processing
C	the faces, IORDER(NFACES) that results in the minimum amount
C	of storage required to accumulate the matrix elements.
C	NOTE: For this problem, each matrix element has 2 contributions,
C	one from each face.  Before writing the matrix data to FMS,
C	we must compute both contributions to make the term complete.
C	We also note that the matrix terms are computed by integrals
C	over the triangle faces.  Therefore the order of computing the
C	matrix terms is determined by the order of processing the
C	faces.
C
C	This approach uses a reasonable amount of memory to accumulate
C	face integrals and then writes the parts of the matrix that
C	become complete to FMS.  The benefits are:
C	* the integrals only need to be computed once
C	* there is no rereading of data from disk to do the
C	  accumulation.
C	* the algorithm easily runs in parallel.
C
C	The general scheme is:
C	COMPLEX*16 Z(3,3)
C	COMPLEX*16 VGLOB (NUMEQ,MAXVEC)
C	INTEGER    IORDER(NFACES)
C	Outer loop over faces:
C	DO IFACE=1,NFACES
C	   Change to optimized processing order:
C	   MYFACE = IORDER(IFACE)
C	   DO JFACE=1,NFACES
C	      Compute the integral of MYFACE due to JFACE and store
C	      the results in VLOCAL.
C	   END DO
C	   The vectors in VLOCAL are now complete.  They contain the
C	   contributions to MYFACE from all other faces.
C	
C	   Before the vectors can be written to FMS, we must wait for
C	   their second contribution (each vector will appear twice,
C	   unless it is on an edge).  We therefore store the vectors
C	   into VGLOB, until they become complete.
C
C	   Add VLOCAL TO VGLOB.
C	   IF(Any of the VLOCAL vectors are complete) THEN
C	      Write complete VGLOB vector to FMS
C	      Free VGLOB storage for another vector
C	   END IF
C	END DO
C
C	By properly picking the face processing order, we can reduce
C	the number of VGLOB vectors to a reasonable level.
C	The following code determins this level:
C
C	FORMAL PARAMETERS:
C	   (R ) NFACES = Integer scalar
C	        Number of faces
C
C	   (R ) NEDGES = Integer scalar
C	        Number of edges
C
C	   (R ) IDEDGE(3,NFACES) = Integer array.
C	        Lists the edges associated with a face
C
C	   (RW) IORDER(NFACES) = Integer array.
C	        On input, specifies the list of starting faces.
C	        At least one face must be specified in IORDER(1).
C	        The value following the last face specified must be 0.
C	        On output, specifies the order for processing faces.
C
C	   ( W) MAXVEC = Integer array.
C	        Maximum number of active edges.  This determines the
C	        number of vectors which must be held in memory to
C	        accumulate results.  The objective of this subroutine
C	        is to select the IORDER array to minimize this value.
C
C	   ( W) MAXUSE(NEDGES) = Integer array.
C	        Maximum number of faces using each edge.  For interior
C	        edges, the value will be 2.  For edges on the boundary,
C	        the value will be 1.
C
C	   (  ) NLEFT(NEDGES) = Integer array.
C	        Scratch storage used to hold the number of times the
C	        edge must be found before it can be eliminated.
C	        This vector is initialized to MAXUSE.
C
C	   (  ) IUSE(NFACES) = Integer array.
C	        Scratch storage used to flag available faces.
C
C	   (  ) IAGE(NEDGES) = Integer array.
C	        Scratch storage used to hold the age of active edges.
C-----------------------------------------------------------------------
C	Formal Parameters
C-----------------------------------------------------------------------
	CHARACTER*6 MYNAME
	PARAMETER (MYNAME='ORDERF')
        INTEGER NFACES
        INTEGER NEDGES
        INTEGER IDEDGE(3,NFACES)
        INTEGER IORDER(NFACES)
        INTEGER MAXVEC
        INTEGER MAXUSE(NEDGES)
        INTEGER IUSE  (NFACES)
        INTEGER IAGE  (NEDGES)
        INTEGER NLEFT (NEDGES)
C-----------------------------------------------------------------------
C	Local Variables
C-----------------------------------------------------------------------
        INTEGER IFACE
        INTEGER JFACE
        INTEGER IEDGE
        INTEGER MYEDGE
        INTEGER MAXAGE
        INTEGER NUMACT
C-----------------------------------------------------------------------
	CALL FMSPSH(MYNAME)
C
C	Initialize starting face:
C	You should pick the face that is the furthest from the center.
C	Here we will do something simple:
        IORDER(1) = 1
        IORDER(2) = 0
C
C ( 1)	Initialize IUSE to unused:
        DO IFACE = 1,NFACES
           IUSE(IFACE) = 0
        END DO
C
C ( 2)	Find the maximum number of times each edge is used:
        DO IEDGE = 1,NEDGES
           MAXUSE(IEDGE) = 0
        END DO
        DO IFACE = 1,NFACES
           DO I = 1,3
              IEDGE = IDEDGE(I,IFACE)
              MAXUSE(IEDGE) = MAXUSE(IEDGE) + 1
           END DO
        END DO
C
C ( 3)	Initialize the number of times each edge must be found before
C	it can be eliminated.
        DO IEDGE = 1,NEDGES
           NLEFT(IEDGE) = MAXUSE(IEDGE)
        END DO
C
C ( 4)	Initialize the age of each edge to inactive:
        DO IEDGE = 1,NEDGES
           IAGE(IEDGE) = 0
        END DO
C
C ( 5)	Find the face order:
        NUMACT = 0
        MAXVEC = 0
        NSTART = 0
        DO IFACE = 1,NFACES
           IF(NSTART .EQ. 0) THEN
              IF(IORDER(IFACE) .EQ. 0) THEN
C	         This is the first non-specified face:
                 NSTART = IFACE - 1
                 IF(NSTART .EQ. 0)
     1              STOP 'ORDERF: No starting faces specified'
              ELSE
C	         This face is specified:
                 GO TO 100
              END IF
           END IF
C
C	   Find the oldest edge.
           MAXAGE = -1
           MYEDGE = 0
           DO IEDGE = 1,NEDGES
              IF( IAGE(IEDGE) .GT. MAXAGE ) THEN
                 MYEDGE = IEDGE
                 MAXAGE = IAGE(IEDGE)
              END IF
           END DO
           IF(MYEDGE .EQ. 0) STOP 'Oldest edge not found'
C
C	   Find a face which uses the oldest edge:
           DO JFACE = 1,NFACES
              IF( IUSE(JFACE) .EQ. 0 ) THEN
                 DO I= 1,3
                    IF(IDEDGE(I,JFACE) .EQ. MYEDGE) THEN
                       IORDER(IFACE) = JFACE
                       GO TO 100
                    END IF
                 END DO
              END IF
           END DO
           STOP 'Face not found which uses oldest edge'
C
C	   The new face is IORDER(IFACE):
  100      CONTINUE
           JFACE = IORDER(IFACE)
C	   PRINT *,'ORDERF: IORDER(',IFACE,') =',JFACE
           IUSE(JFACE) = 1
C
C	   Age all the active edges:
           DO IEDGE = 1,NEDGES
              IF(IAGE(IEDGE) .GT. 0) THEN
C	         Edge is active:
                 IAGE(IEDGE) = IAGE(IEDGE) + 1
              END IF
           END DO
C
C	   Start new edges:
           DO I = 1,3
              IEDGE = IDEDGE(I,JFACE)
              IF(IAGE(IEDGE) .EQ. 0) THEN
C	         New active edge:
                 IAGE(IEDGE) = 1
                 NUMACT = NUMACT + 1
              END IF
           END DO
           IF(NUMACT .GT. MAXVEC) MAXVEC = NUMACT
C	   Eliminate edges which are now complete:
           DO I = 1,3
              IEDGE = IDEDGE(I,JFACE)
              NLEFT(IEDGE) = NLEFT(IEDGE) - 1
              IF(NLEFT(IEDGE) .EQ. 0) THEN
C	         Edge is complete:
                 IAGE(IEDGE) = 0
                 NUMACT = NUMACT - 1
              END IF
           END DO
C
C	   Do the next face:
        END DO
	CALL FMSPOP(MYNAME)
        RETURN
        END
C=======================================================================
        SUBROUTINE FILL (
     1   NXFACE, NAVAIL,
     2   NFACES, NEDGES, MAXVEC,
     3   LUA   , IDEDGE, IORDER,
     4   MAXUSE, NLEFT ,
     5   VGLOB , IDGLOB, ISTAT)
C=======================================================================
C	DESCRIPTION:
C	   This subroutine computes the matrix elements.
C	   It is designed to be run in parallel.
C
C	FORMAL PARAMETERS:
C	   (RW) NXFACE = Integer scalar (shared, read, write)
C	        Outer face loop counter.
C
C	   (RW) NAVAIL = Integer scalar (shared, read, write)
C	        Number of global vectors available.
C	        (Initialized to MAXVEC)
C
C	   (R ) NFACES = Integer scalar (shared, read only)
C	        Number of faces
C
C	   (R ) NEDGES = Integer scalar (shared, read only)
C	        Number of edges
C
C	   (R ) MAXVEC = Integer scalar. (shared, read only)
C	        Number of vectors in memory
C
C	   (R ) LUA(25) = Integer array.
C	        Matrix file attributes.
C
C	   (R ) IDEDGE(3,NFACES) = Integer array. (shared, read only)
C	        Lists the edges associated with a face.
C
C	   (R ) IORDER(NFACES) = Integer array (shared, read only)
C	        Lists the order for processing faces
C
C	   (R ) MAXUSE(NEDGES) = Integer array (shared, read only)
C	        Lists the number of times each edge must be used.
C
C	   (RW) NLEFT(NEDGES) = Integer array (shared, read, write)
C	        Scratch storage, initialized to MAXUSE
C
C	   (RW) VGLOB(NEDGES,MAXVEC) = Complex*16 array (shared, r, w)
C	        Scratch storage for accumulating vectors
C
C	   (RW) IDGLOB(MAXVEC) = Integer array (shared, r, w)
C	        Scratch storage for vector numbers in V.
C
C	   ( W) ISTAT = Integer scalar (private, write)
C	                Return status.
C	              =-1, Error
C	              >=0, Number of faces processed.
C-----------------------------------------------------------------------
C	Formal Parameters
C-----------------------------------------------------------------------
	CHARACTER*4 MYNAME
	PARAMETER (MYNAME='FILL')
        INTEGER    NXFACE
        INTEGER    NAVAIL
        INTEGER    NFACES
        INTEGER    NEDGES
        INTEGER    IDEDGE(3,NFACES)
        INTEGER    LUA(25)
        INTEGER    MAXVEC
        INTEGER    IORDER(NFACES)
        INTEGER    MAXUSE(NEDGES)
        INTEGER    NLEFT (NEDGES)
        COMPLEX*16 VGLOB(NEDGES, MAXVEC)
        INTEGER    IDGLOB(MAXVEC)
        INTEGER    ISTAT
C	VOLATILE   NXFACE, NAVAIL
C-----------------------------------------------------------------------
C	Local Variables
C-----------------------------------------------------------------------
        INTEGER    IFACE
        INTEGER    JFACE
	INTEGER    ITEMP
	COMPLEX*16 CMD(0:1)
	POINTER (CMD_PTR,CMD)
C-----------------------------------------------------------------------
	CALL FMSPSH(MYNAME)
        ISTAT = 0
	CALL FMSIGT ('MYNODE', MYNODE)
	CALL FMSIGT ('MEMPTR', CMD_PTR)
        CALL FMSCMG (CMD, L_VLOCAL, 3*NEDGES)
C
C	Get your starting face number:
C	Get exclusive use:
        CALL FMSONE
C          NXFACE = NXFACE + 1
C          MYFACE = NXFACE
	   MYFACE = INTINC(NXFACE)
        CALL FMSALL
C	NOTE: We saved the face number NXFACE in a local variable
C	MYFACE, because NXFACE may be changed by another process
C	as soon as we called FMSALL.
C
C	Outer loop over faces:
  100   CONTINUE
           IF(MYFACE .GT. NFACES) THEN
C	      This process is done.
              CALL FMSCMR (CMD, L_VLOCAL, 3*NEDGES)
	CALL FMSPOP(MYNAME)
	      RETURN
	   END IF
           IFACE  = IORDER(MYFACE)
           ISTAT  = ISTAT  + 1
C
C	   Inner loop over faces:
C	   NOTE: I may process these in any order, because I am doing
C	         all of them and I have room to store the results.
           DO JFACE = 1,NFACES
C
C	      Compute the matrix relating IFACE to JFACE:
C	      There will need to be a lot more information passed
C	      through to this routine.  The following is used to
C	      generate a test matrix only.
C
              CALL FILL2 (IFACE, JFACE, IDEDGE, MAXUSE, NEDGES,
     1	                  CMD(L_VLOCAL))
           END DO
C
C	   The 3 vectors VLOCAL are now complete.  They contain
C	   1/2 of the terms for interior edges.
C	   We now need to accumulate these results into the global
C	   arrays VGLOB.
C
C	   Critical Section
C	   ================
           CALL FMSONE
C
C	      This code writes to shared variables.  Therefore it can
C	      be executed by only one process at a time.
C
C	      Transfer private vectors to global arrays:
	      L_V = L_VLOCAL - NEDGES
              DO I = 1,3
	         L_V   = L_V + NEDGES
                 IEQN  = IDEDGE(I,IFACE)
C	         Look for vector IEQN in the accumulation list:
	         MYVEC = 0
                 DO IVEC = 1,MAXVEC
                    IF(IEQN .EQ. IDGLOB(IVEC)) THEN
C	               Add to existing vector:
	               MYVEC  = IVEC
                       CALL CVADD (VGLOB(1,MYVEC), CMD(L_V), NEDGES)
	               GO TO 220
                    END IF
	            IF(IDGLOB(IVEC) .EQ. 0) THEN
C	               This slot is free:
	               IF(MYVEC .EQ. 0) MYVEC = IVEC
	            END IF
                 END DO
	         IF(MYVEC .EQ. 0) THEN
C	            Error:
                    WRITE(6,2000) MYNODE, IDEDGE(I,IFACE),
     1                 (II, IDGLOB(II),II=1,MAXVEC)
                    ISTAT = -1
                    CALL FMSCMR (CMD, L_VLOCAL, 3*NEDGES)
	CALL FMSPOP(MYNAME)
                    RETURN
	         END IF
C	         New vector:
	         ITEMP = INTDEC(NAVAIL)
                 IDGLOB(MYVEC) = IEQN
                 CALL CVMOVE (VGLOB(1,MYVEC), CMD(L_V), NEDGES)
  220            CONTINUE
                 NLEFT(IEQN) = NLEFT(IEQN) - 1
	         IF(NLEFT(IEQN) .EQ. 0) THEN
C	            Vector is ready to be written out:
                    CALL FMSROW (IEQN, VGLOB(1,MYVEC), LUA)
C	            Free the storage:
                    IDGLOB(MYVEC) = 0
	            ITEMP  = INTINC(NAVAIL)
                 END IF
              END DO
C
C	      Get your next face number:
C             NXFACE = NXFACE + 1
C             MYFACE = NXFACE
	      MYFACE = INTINC(NXFACE)
           CALL FMSALL
C
C	   End of Critical Section
C	   =======================
C
C	   Do the next face:
        GO TO 100
 2000   FORMAT (/
     1  ' FILL: No global vectors available for process',I5/
     2  '       Trying to store vector',I10/
     3  '       Vector     Contents'/
     4  '       ======     ========'/
     5  (I13,I13))
        END
C=======================================================================
        SUBROUTINE FILL2 (IFACE, JFACE, IDEDGE, MAXUSE, NUMEQ, VLOCAL)
C=======================================================================
C	This is a dummy subroutine to generate test matrix elements.
        INTEGER IFACE, JFACE, IDEDGE(3,*), NUMEQ, MAXUSE(NUMEQ)
        REAL*8 TEMP, DTEMP
	COMPLEX*16 ZIJ
        COMPLEX*16 VLOCAL(NUMEQ,3)
	CHARACTER*5 MYNAME
	PARAMETER (MYNAME='FILL2')
	CALL FMSPSH(MYNAME)
        DO I=1,3
           IGLOB = IDEDGE(I,IFACE)
           TEMP  = (1.0D0)/(DFLOAT(MAXUSE(IGLOB)))
           DO J=1,3
              JGLOB = IDEDGE(J,JFACE)
              IF( IGLOB .EQ. JGLOB ) THEN
C	         This is a diagonal element:
                 IF ( IGLOB .EQ. 1) THEN
                    DTEMP = DFLOAT(NUMEQ)*TEMP
                    ZIJ = DCMPLX(DTEMP,DTEMP)
                 ELSE
                    ZIJ = DCMPLX(TEMP,TEMP)
                 END IF
              ELSE IF( (IGLOB .EQ. 1)  .OR. (JGLOB .EQ. 1) ) THEN
C	         Term is in first row or column:
                 ZIJ = DCMPLX(-TEMP,-TEMP)
              ELSE
C	         Interior element:
                 ZIJ = DCMPLX(0.0D0,0.0D0)
              END IF
	      VLOCAL(JGLOB,I) = ZIJ
           END DO
        END DO
	CALL FMSPOP(MYNAME)
        RETURN
        END
C=======================================================================
        SUBROUTINE MGEN (NEQEST, NFACES, NEDGES, IDEDGE)
C=======================================================================
        INTEGER NEQEST
        INTEGER NFACES
        INTEGER NEDGES
        INTEGER IDEDGE(3,NEQEST)
        REAL*8  RN
	CHARACTER*4 MYNAME
	PARAMETER (MYNAME='MGEN')
	CALL FMSPSH(MYNAME)
C	Generate a mesh of quadrilaterals that is
C	approximately square.  Each quadrilateral will
C	be divided into four triangles.  Each interior
C	quadrilateral will add 6 equations (4 interior
C	edges and 2 sides).
C	Compute the number of quadrilaterals:
        RN = NEQEST/6
C	Compute the number of quadrilaterals on an edge:
        RN = SQRT(RN)
        N  = RN
        N2 = N  + N
        N3 = N2 + N
        N4 = N3 + N
        N6 = N4 + N2
        M  = (NEQEST + N2)/N6
C	PRINT *,'MGEN: Mesh is ',N,' by ',M
        NFACES = 0
        NEDGES = 0
C	Loop down the cylinder:
        DO J = 1,M
C	   Loop around the cylinder:
           DO I = 1,N
              I2 = I + I
C	      Generate the 4 face numbers in the square:
              IFACE = NFACES + I
              JFACE = NFACES + N + I2 - 1
              KFACE = JFACE  + 1
              LFACE = NFACES + N3 + I
C	      Generate the edge list for each face:
              IDEDGE(1,IFACE) = NEDGES + I
              IDEDGE(2,IFACE) = NEDGES + N + I2
              IDEDGE(3,IFACE) = IDEDGE(2,IFACE) - 1
              IDEDGE(1,JFACE) = NEDGES + N3 + I
              IDEDGE(2,JFACE) = IDEDGE(3,IFACE)
              IDEDGE(3,JFACE) = NEDGES + N4 + I2 - 1
              IDEDGE(1,KFACE) = IDEDGE(2,IFACE)
              IDEDGE(2,KFACE) = IDEDGE(1,JFACE) + 1
              IDEDGE(3,KFACE) = IDEDGE(3,JFACE) + 1
              IDEDGE(1,LFACE) = IDEDGE(3,JFACE)
              IDEDGE(2,LFACE) = IDEDGE(3,KFACE)
              IDEDGE(3,LFACE) = NEDGES + N6 + I
           END DO
C	   Wrap for a cylinder:
           IDEDGE(2,KFACE) = IDEDGE(2,KFACE) - N
           NFACES = NFACES + N4
           NEDGES = NEDGES + N6
        END DO
C	Leave cylinder open:
        NEDGES = NEDGES + N
C
C	WRITE(6,5000) (I,(IDEDGE(J,I), J=1,3), I=1,NFACES)
	CALL FMSPOP(MYNAME)
        RETURN
C5000   FORMAT(/
C    1  ' FACE   EDGE 1   EDGE 2   EDGE 3'/
C    2  ' ====   ======   ======   ======'/
C    3  (I5,3I9))
        END
C=======================================================================
        SUBROUTINE CVMOVE (A, B, N)
C=======================================================================
        COMPLEX*16 A(N), B(N)
        DO I = 1,N
           A(I) = B(I)
        END DO
        RETURN
        END
C=======================================================================
        SUBROUTINE CVADD (A, B, N)
C=======================================================================
        COMPLEX*16 A(N), B(N)
        DO I = 1,N
           A(I) = A(I) + B(I)
        END DO
        RETURN
        END
C=======================================================================
        INTEGER FUNCTION INTINC (I)
C=======================================================================
C	This function increments a volatile shared variable.  It is
C	placed in a subroutine to prevent some compilers from storing
C	the value in a register and not updating it.
	INTEGER I
	I = I + 1
	INTINC = I
	RETURN
	END
C=======================================================================
        INTEGER FUNCTION INTDEC (I)
C=======================================================================
C	This function decrements a volatile shared variable.  It is
C	placed in a subroutine to prevent some compilers from storing
C	the value in a register and not updating it.
	INTEGER I
	I = I - 1
	INTDEC = I
	RETURN
	END
C=======================================================================
	INTEGER FUNCTION ASK_I(STRING)
C=======================================================================
	CHARACTER* (*) STRING
	WRITE(6,2000) STRING
	READ (5,*) ASK_I
	RETURN
 2000	FORMAT (1X,A,'>')
	END