Go to the previous, next section.
This section gives two words. The first, FGIfunc, is the general way of creating a stream filter: you can create a filter that does arbitrarily complicated manipulations of streams. It requires that you write some Ingrid code that specifies how the grids, names, etc, of the streams should be manipulated.
The second word, FGIscalarFilter, makes it possible for Ingrid to call a FORTRAN scalar function, i.e. it is a function of N scalar arguments. Ingrid will apply this function to scalars or streams or a limited mixture.
FGIfunc(NAME,SUB,NIN,NOUT)
SUB(PARAM, IN1, ..., INn, OUT1, ..., OUTm)where IN1 is a chunk of data from the first input stream, IN2 is a chunk of data from the second stream, etc, and OUT1, ..., OUTm correspond to chunks of the output streams, we have the following FGIfunc call:
EXTERNAL SUB ... CALL FGIfunc('sub',SUB,n,m)This creates a word
sub
that has the following stack behavior:
( IN1 ... INn PARAM -- )i.e. it takes n streams from the stack, IN1 through INn, and a parameter block PARAM. We now need to make m calls to
NewBuffer
, one
for each output buffer. These streams will correspond to the output
arrays of SUB.
This is actually less restrictive that it appears: PARAM can be any of several different types of Ingrid objects that have FORTRAN equivalents: integer, real, name, integerarray, realarray, namearray, and structures. IN1 ... INn can be any of those (except structure), but they can also be streams. OUT1 through OUTm are always streams.
For example, suppose we want to write a simple filter that operates on XY blocks of data, reading in an XY block and returning the same size XY block. And suppose we have a stream that is a function of XYT.
So we have a FORTRAN program
SUBROUTINE SUB(p,IN,OUT) STRUCTURE /myS/ INTEGER NX INTEGER NY END STRUCTURE RECORD /myS/ p REAL IN(p.nx,p.ny) , OUT(p.nx,p.ny) C given IN, we cleverly compute OUT ... RETURN ENDIn our main program, after we call createIngrid, we call FGIfunc,
EXTERNAL SUB ... CALL FGIfunc('mysub',SUB,1,1)Now suppose we have a stream called FRED that is a function of XYT. We can apply SUB to it as follows
FRED % our input stream FRED of XYT X Y 2 REORDER % this makes the first coordinate X, % the second coordinate Y, % and the stream is now in chunks of XY. X >npts % gets the number of points from X Y >npts % gets the number of points from Y 2 integerarray astore % stores the two integers into an integer array mysub % feeds the (possibly) reordered FRED into SUB % with the parameters [ nx ny ] LastStream % since the grids of the output stream are the % same as the input stream, we use it as a parent % of our output stream. X >npts Y >npts mul % calculate the size of the output buffer NewBuffer % create a new buffer which is filled by SUB X Y CONTOUR % make a series of plots of the resultsIn practice, we present a clean interface to the outside world by writing a procedure, namely
mygreatsub
/mygreatsub { X Y 2 REORDER X >npts Y >npts 2 integerarray astore mysub LastStream X >npts Y >npts mul NewBuffer (filtered by mygreatsub) addhistory } defThen when we want to use it, we simply say
FRED % the input stream mygreatsub % filtered by mygreatsub X Y CONTOUR % a series of contour plots
On the other hand, suppose we wanted to write the filter so that we
could apply it to any two dimensions, not just X
and Y
.
Then we would have a slightly different definitions of mygreatsub
,
namely
mygreatsubII
/mygreatsubII { 2 REORDER X >npts Y >npts 2 integerarray astore mysub LastStream X >npts Y >npts mul NewBuffer /bufferSIRecord SIRecord def (filtered by mygreatsub) addhistory } defThen when we want to use it, we simply say
FRED % the input stream X Y mygreatsubII % filtered by mygreatsub X Y CONTOUR % a series of contour plotsA minor change in coding, but much more generality.
Though it is quit unecessary for this example, structures offer an
alternative way of passing parameters to FORTRAN subroutines. The
syntax becomes a bit cleaner, and we can pass a mix of integers, real
numbers, strings, etc. The rewritten definition of mygreatsubII
would be as follows:
First define an Ingrid structure that corresponds to the FORTRAN structure.
/mysubS structure /NX integer /NY integer endstructure defThen make a new definition for mygreatsubII.
/mygreatsubII { 2 REORDER % use the two grids to REORDER the stream so % that the two fastest varying grids are the % grids we want to operate along. dup % we make an extra copy of the input stream % for NewBuffer (above we used LastStream). mysubS new % create a new RECORD with structure mysubS store begin % start storing data X >npts NX % store NX Y >npts NY % store NY end % end storing mysub % feeds stream to FORTRAN subroutine mysub X >npts Y >npts mul % number of points in output buffer NewBuffer % uses copy of input stream and npts % to create the output buffer (filtered by mygreatsub) addhistory } def
FGIscalarFilter(NAME,FUNCTION,N,DEFS)
NAME (stream_1 ... stream_n -- stream ) will then filter n input streams, applying DEFS to the output stream.
NAME (num_1 ... num_n -- real ) also works if all the input arguments are numbers, will return the result. In this case DEFS are not used.
NAME (stream_1 ... stream_m num_m+1 ... num_n) also works on sets of streams and numbers mixed, though all the streams have to come first.
NAME
FUNCTION
N
DEFS
NAME combines the histories and fullnames of the input streams, prepending the name defined above (try one of the functions built in to Ingrid, like cos or sin).
Go to the previous, next section.